iOS PassKit Wallet 开发

前言

根据目前系统钱包中的能够添加的卡,大致可分为三类

  • 银行卡/交通卡
image.png
  • 电子卡(例如:美团闪付/京东闪付)
image.png
  • 消费凭证 (电影票/机票/优惠券)
image.png

消费凭证

消费凭证,大致分为五类,具体请看下图,对于每种凭证的包含信息,点我

image.png

制作凭证并添加到Wallet

创建pass包
  1. 先学习一波,----> 请看大屏幕

  2. 创建一个 Pass 包

首先要明白 Pasees 是以 Pass 包 形式创建的,Pass 包里面包含一个 pass.json 文件,一些图片资源(像 icon, logo 等)
创建一个 Pass 包:
(1)在 Finder 的 Desktop 初 创建一个 名为 Lollipop.pass 的文件夹
(2)下载苹果提供的资源文件(包括一些 Pass 包例子, 一个签名工具, 一个测试服务器)
下载资源:在官方文档中点击 developer downloads area ,即可下载

image.png

(3)在苹果资源处将 Event.pass 文件夹中的所有内容拷贝到 Lollipop.pass 文件夹中。


image.png
  1. 设置 Pass Type Identifier 和 Team ID
    每一个 pass 都有和开发者账号相关连的 Pass Type Identifier

官方文档如下:

To register a pass type identifier, do the following:

In Certificates, Identifiers & Profiles, select Identifiers.

Under Identifiers, select Pass Type IDs.

Click the plus (+) button.

Enter the description and pass type identifier, and click Submit.

To find your Team ID, do the following:

Open Keychain Access, and select your certificate.
Select File > Get Info, and find the Organizational Unit section under Details. This is your Team ID.
The pass type identifier appears in the certificate under the User ID section.

按照苹果的意思在自己的开发账号内生成一个 Pass Type ID, 然后在 pass.json 文件内替换生成的Pass Type ID, Team ID 同理,开发者账号内找到并且在 pass.json 文件内替换。


步骤如下:

  • 在 Certificates, Identifiers & Profiles, 选择 Identifiers

  • 在 Identifiers 下,选择 Pass Type IDs.

  • 选择你已经创建好的 pass type identifier, 点击编辑。

  • 如果已经存在证书文件,直接点击下载即可。如果没有,点击创建,按照提示创建一个(与创建 APNs 推送证书基本一样)。

image.png
image.png

如果有选择已经有的,如果没有则从电脑导出来

生成好之后如下:


image.png

下载之后,双击安装到电脑。

  1. 修改pass.json文件的ID
  • 查看 Team ID:

  • 到开发者账号下,选择 MemberShip 。

image.png
  • 添加 PassTypeID 和 Team ID 到 pass.json

PassTypeID: pass.com.cuilinhao.TestPassKit

Team ID: P6E2X8E5T9

打开 Lollipop.pass 文件夹下的 pass.json , 将 PassTypeID 和 Team ID 替换为自己开发者账号下的。

{
...
"passTypeIdentifier" : "your pass type identifier",
"teamIdentifier" : "your Team ID",
...
}
  1. 生成 .pkpass 后缀的压缩文件
  • 找到之前下载的文件,WalletDemo-master/WalletTest.xcodeproj
    使用xcode运行

  • 选中 Xcode 中 Products 文件夹下的 signpass 文件,右击鼠标,Show in Finder。

image.png
  • 拷贝 signpass 文件到 filmTicket.pass 文件夹的同级目录(Desktop)下。

  • 执行以下语句:

cd ~/Desktop
./signpass -p Lollipop.pass

创建Xcode 项目 添加凭证到Wallet

直接放代码,其他文件配置可下载查看,点我


- (void)viewDidLoad {
    [super viewDidLoad];
    
    PKAddPassButton *pkAddBtn = [[PKAddPassButton alloc] initWithAddPassButtonStyle:PKAddPassButtonStyleBlack];
    pkAddBtn.titleLabel.font = [UIFont systemFontOfSize:12];
    pkAddBtn.frame = CGRectMake(100, 100, 220, 40);
    [self.view addSubview:pkAddBtn];
    
    [pkAddBtn addTarget:self action:@selector(pkClick:) forControlEvents:UIControlEventTouchUpInside];
}


- (void)pkClick:(PKAddPassButton *)sender
{
    NSString *passPath = [[NSBundle mainBundle] pathForResource:@"Lollipop" ofType:@"pkpass"];
    NSData *passData = [[NSData alloc] initWithContentsOfFile:passPath];
    NSError *error = nil;
    PKPass *pass = [[PKPass alloc] initWithData:passData error:&error];
    if (error) {
        NSLog(@"创建pass 过程中发生错误,错误信息:%@", error.localizedDescription);
    }
    
    PKAddPassesViewController *vc = [[PKAddPassesViewController alloc] initWithPass:pass];
    vc.delegate = self;
    
    [self presentViewController:vc animated:YES completion:nil];

}


#pragma mark -  delegate

- (void)addPassesViewControllerDidFinish:(PKAddPassesViewController *)controller
{
    NSLog(@"add pass finished");
    
    [self dismissViewControllerAnimated:YES completion:nil];
    
}

修改凭证信息,重新生成
  1. 原有pass.json 文件,
{
  "formatVersion" : 1,
  "passTypeIdentifier" : "pass.com.cuilinhao.TestPassKit",
  "serialNumber" : "nmyuxofgna",
  "teamIdentifier" : "P6E2X8E5T9",
  "webServiceURL" : "https://example.com/passes/",
  "authenticationToken" : "vxwxd7J8AlNNFPS8k0a0FfUFtq0ewzFdc",
  "relevantDate" : "2011-12-08T13:00-08:00",
  "associatedStoreIdentifiers" : [728326385],
  "appLaunchURL" : "http://www.xingshulin.com",
  "locations" : [
    {
      "longitude" : -122.3748889,
      "latitude" : 37.6189722
    },
    {
      "longitude" : -122.03118,
      "latitude" : 37.33182
    }
  ],
  "barcode" : {
    "altText" : "订单号:123456",
    "message" : "123456789",
    "format" : "PKBarcodeFormatQR",
    "messageEncoding" : "iso-8859-1"
  },
  "organizationName" : "Apple Inc.",
  "description" : "Apple Event Ticket",
  "foregroundColor" : "rgb(255, 255, 255)",
  "backgroundColor" : "rgb(60, 65, 76)",
  "headerFields" : [
    {
      "key" : "filmName",
      "label" : "影片",
      "value" : "加勒比海盗5:死无对证",
      "textAlignment" : "PKTextAlignmentNatural"
    }
  ],
  "eventTicket" : {
    "primaryFields" : [
      {
        "key" : "filmName",
        "label" : "影片",
        "value" : "test1111111",
        "textAlignment" : "PKTextAlignmentNatural"
      }
    ],
  "secondaryFields" : [
      {
        "key" : "orderNumber",
        "label" : "订单号",
        "value" : "test2",
        "textAlignment" : "PKTextAlignmentLeft"
      },
      {
        "key" : "verificationNumber",
        "label" : "验证码",
        "value" : "test222",
        "textAlignment" : "PKTextAlignmentRight"
      }
    ],
  "auxiliaryFields" : [
      {
        "key" : "site",
        "label" : "影院",
        "value" : "test333",
        "textAlignment" : "PKTextAlignmentLeft"
      },
      {
        "key" : "order",
        "label" : "场次",
        "value" : "test3",
        "textAlignment" : "PKTextAlignmentCenter"
      },
      {
        "key" : "seat",
        "label" : "座位",
        "value" : "test333333",
        "textAlignment" : "PKTextAlignmentRight"
      }
    ],
    "backFields" : [
      {
        "numberStyle" : "PKNumberStyleSpellOut",
        "label" : "spelled out",
        "key" : "numberStyle",
        "value" : 200
      },
      {
        "label" : "in Reals",
        "key" : "currency",
        "value" : 200,
        "currencyCode" : "BRL"
      },
      {
        "dateStyle" : "PKDateStyleFull",
        "label" : "full date",
        "key" : "dateFull",
        "value" : "1980-05-07T10:00-05:00"
      },
      {
        "label" : "full time",
        "key" : "timeFull",
        "value" : "1980-05-07T10:00-05:00",
        "timeStyle" : "PKDateStyleFull"
      },
      {
        "dateStyle" : "PKDateStyleShort",
        "label" : "short date and time",
        "key" : "dateTime",
        "value" : "1980-05-07T10:00-05:00"
      },
      {
        "dateStyle" : "PKDateStyleShort",
        "label" : "relative date",
        "key" : "relStyle",
        "value" : "2013-04-24T10:00-05:00",
        "isRelative" : true
      }
    ]
  }
}


image.png

上述代码和制作出的卡包是对应的,test11111对应代码如下:

"eventTicket" : {
    "primaryFields" : [
      {
        "key" : "filmName",
        "label" : "影片",
        "value" : "test1111111",
        "textAlignment" : "PKTextAlignmentNatural"
      }
    ],

其他部分,依次类推,其中下方的二维码部分,对应代码如下:

// 修改前
"barcode" : {
    "message" : "123456789",
    "format" : "PKBarcodeFormatPDF417",
    "messageEncoding" : "iso-8859-1"
}
// 修改后
"barcode" : {
    "altText" : "订单号:123456",
    "message" : "123456789",
    "format" : "PKBarcodeFormatQR",
    "messageEncoding" : "iso-8859-1"
}

  1. 更新好pass.json 之后再次执行

cd Desktop/
./signpass -p Lollipop.pass

然后将下面三个文件替换,Xcode运行项目安装

image.png
  1. pass.json 官方说明
更新凭证信息

流程图如下:

image.png
  • pass 被设置为支持更新和安装,并且用户设备注册到你的服务器获取更新。
  • 如果有变更,则触发更新,你的服务器发送推送通知。
  • 用户收到推送后,从你的服务器查询更新的列表。
  • 用户从你的服务器获取每个 pass 的最新版本到自己的设备。
附上Demo OC版&& Swift版本

京东闪付卡/美团闪付卡/银行卡
image.png

这一类的卡,是通过公司接入银联支付产生的,具体可参考银联官网,并可直接电话联系咨询,京东和美团的闪付卡都是接入银联中的一个手机闪付业务做成闪付卡,然后添加到系统钱包。

iOS Developer Wallet中,也介绍到,使用PKAddPaymentPassViewController可以添加这类卡,苹果官方描述如下:

Adding payment passes requires a special entitlement issued by Apple. Your app must include this entitlement before this class can be instantiated. For more information on requesting this entitlement, see the Card Issuers section at [developer.apple.com/apple-pay/](https://developer.apple.com/apple-pay/).

>>>
添加付款通行证需要Apple颁发特殊权利。 您的应用程序必须在此类可实例化之前包含此权利。有关请求此权利的更多信息,请参阅“发卡机构”部分

如何做一个凭证,从钱包跳回App

In your pass.json file you can include a key to define an associated app:

"associatedStoreIdentifiers" : [Adam ID],

where Adam ID can be obtained from the link to the app in the app store:

[https://itunes.apple.com/app/id](https://itunes.apple.com/app/id)<Adam ID> ..

e.g. to get the Facebook App, go to:

[https://itunes.apple.com/us/app/facebook/id284882215](https://itunes.apple.com/us/app/facebook/id284882215)

To include it in a Pass, add this to pass.json:

"associatedStoreIdentifiers" : [284882215],


image.png

步骤如下:

  • 拿到你的App 上架的时候的App ID

  • 在制作凭证的.json文件中 设置该ID 为associatedStoreIdentifiers的value值,即:
    "associatedStoreIdentifiers" : [1395841695]

  • 官方文档说明如下:

image.png
passKit 库用到的API

PKAddPassButton : 添加钱包按钮,主要有两种类型,PKAddPassButtonStyleBlack 、 PKAddPassButtonStyleBlackOutline

创建方式有两种:

  • (instancetype)addPassButtonWithStyle:(PKAddPassButtonStyle)addPassButtonStyle;
  • (instancetype)initWithAddPassButtonStyle:(PKAddPassButtonStyle)style NS_DESIGNATED_INITIALIZER;

PKPass 该类集成自 PKObject
PKPassType:枚举类型,
PKPassTypeBarcode:条码
PKPassTypePayment:支付
PKPassTypeAny:其它类型

构造方法:

  • (instancetype)initWithData:(NSData *)data error:(NSError *)error;

PKPassLibrary 提供用户传递库

检测PKPassLibrary 是否可用

  • (BOOL)isPassLibraryAvailable API_AVAILABLE(ios(6.0), watchos(3.0));

根据passtypeID 和serialNumber 返回pass

  • (nullable PKPass *)passWithPassTypeIdentifier:(NSString *)identifier serialNumber:(NSString *)serialNumber;

根据passType返回pass

  • (NSArray<PKPass *> *)passesOfType:(PKPassType)passType API_AVAILABLE(ios(8.0), watchos(3.0));

移除pass

  • (void)removePass:(PKPass *)pass;
    判断是否包含pass
  • (BOOL)containsPass:(PKPass *)pass;
    替换一个pass
  • (BOOL)replacePassWithPass:(PKPass *)pass;
    添加pass
  • (void)addPasses:(NSArray<PKPass *> *)passes withCompletionHandler:(nullable void(^)(PKPassLibraryAddPassesStatus status))completion API_AVAILABLE(ios(7.0), watchos(3.0));

PKPaymentPass 继承自 PKPass

PKPaymentPassActivationState 枚举类型,
PKPaymentPassActivationStateActivated,(已激活)
PKPaymentPassActivationStateRequiresActivation,(需要被激活)
PKPaymentPassActivationStateActivating,(正在激活)
PKPaymentPassActivationStateSuspended, (挂起)
PKPaymentPassActivationStateDeactivated (停用)

PKAddPaymentPassRequestConfiguration

该类包含初始化一个新的PKAddPaymentPassViewController实例的配置数据。加密机制,持卡人姓名,卡号后四位需要被提供。配置信息仅用来设置和显示。它不包含任何的敏感信息

重要说明:
添加Payment Pass支付卡需要一个特殊的由苹果发行的授权。在使用这个类之前app中必须包括这个授权

  • (nullable instancetype)initWithEncryptionScheme:(PKEncryptionScheme)encryptionScheme

初始化一个新的配置对象

emcryptionScheme 用于该请求的加密机制。所有可能的值可以查看Encryption Schemes.
返回值:一个新的实例化的配置对象。
当实例化一个配置对象之后,在使用它创建一个PKAddPaymentPassViewController实例之前,也必须设置cardholderName和primaryAccountSuffix属性

cardholderName
显示在卡上面的姓名

encryptionScheme
用于该请求的加密机制

localizedDescription
对卡片简短的描述。

paymentNetwork
支付系统,默认为nil。该属性判断哪些卡片可以展示在PKAddPaymentPassViewController类的实例中并显示到屏幕上。PKAddPaymentPassViewController展示卡片区域的所有支持的系统。为了指定一个单一的系统,可指派给该属性一个常量。可查看PKPyamentRequest类的Payment Networks的介绍。

primaryAccountSuffix
后四位或五位卡号

Filtering Pass Libraries 筛选卡库
primaryAccountIdentifier
卡号账户的标识,筛选卡库(来自不同的设备 iPhone,iPhone Watch)

Constants 常量
Encryption Schemes
加密体制
NSString * const PKEncryptionSchemeECC_V2

PKAddPaymentPassViewController

使用提供的配置和代理,返回一个初始化的添加支付的视图控制器实例
/* This controller should be presented with -[UIViewController presentViewController:animated:completion:].
*/

  • (nullable instancetype)initWithRequestConfiguration:(PKAddPaymentPassRequestConfiguration *)configuration
    delegate:(nullable id<PKAddPaymentPassViewControllerDelegate>)delegate

configuration 配置实例:定义视图控制器的外观
delegate 添加支付视图控制器的代理
返回值:一个新的添加支付的视图控制器

添加Payment Pass支付卡需要一个特殊的由苹果发行的授权。如果app中不包括这个授权,该方法返回值为nil

PKAddPaymentPassViewControllerDelegate 代理
PKAddPaymentPassViewController类的代理必须遵守该协议。该协议定义了两个必需实现的方法。这些方法使系统提示添加支付请求和当请求失败或成功的时候法通知app。

  • (void)addPaymentPassViewController:(PKAddPaymentPassViewController *)controller
    generateRequestWithCertificateChain:(NSArray<NSData *> *)certificates
    nonce:(NSData *)nonce
    nonceSignature:(NSData *)nonceSignature
    completionHandler:(void(^)(PKAddPaymentPassRequest *request))handler;

controller 添加支付请求的视图控制器
certificates NSData对象的数组。每个对象包括一个DER编码的证书。必须下载根目录CA验证整个链。
nonce 苹果服务器生成的一次性随机值,该随机值必须被包含在添加支付请求的加密数据中。
nonceSignature 有特定设备的签名的随机值。该签名必须被包含在添加支付请求的加密数据中。
handler 完工的处理者。当创建支付请求之后回调该Block。Block中的参数:request 一个新创建的添加支付请求,必须20秒之内传送该请求实例给处理者否则该请求将失败,系统将为用户显示一个错误信息。

该方法提供需要创建一个添加支付请求的书。通过证书束缚在发行者服务器上。该服务器返回一个包含卡数据的加密的JSON文件。当收到加密数据之后,创建一个添加支付请求并回调处理者。
更多关于加密卡数据的信息,可以查看PKPaymentRequest类里的encryptedPassData属性。

  • (void)addPaymentPassViewController:(PKAddPaymentPassViewController *)controller didFinishAddingPaymentPass:(nullable PKPaymentPass *)pass error:(nullable NSError *)error;
    controller 添加支付请求的视图控制器
    pass 完成的卡,如果有错误,返回nil
    error如果请求失败,该参数包含错误对象(PKPassKitErrorDomamin域错误) 。更多可能的错误代码,可查看枚举PKAddPaymentPassError。

当请求成功地添加卡片到Apple Pay或者失败时,调用该方法。

//----苹果支付-----
PKPaymentRequest 订单请求对象
PKPaymentSummaryItem 商品订单信息对象

配置商品价格,送货方式等

  • (instancetype)summaryItemWithLabel:(NSString *)label amount:(NSDecimalNumber *)amount;
  • (instancetype)summaryItemWithLabel:(NSString *)label amount:(NSDecimalNumber *)amount type:(PKPaymentSummaryItemType)type API_AVAILABLE(ios(9.0), watchos(3.0));

PKPaymentAuthorizationViewController 苹果支付请求控制器
初始化方法

  • (nullable instancetype)initWithPaymentRequest:(PKPaymentRequest *)request NS_DESIGNATED_INITIALIZER;

PKPaymentAuthorizationViewControllerDelegate 代理方法

//支付银行卡回调,如果需要根据不同的银行调整付费金额,可以实现该代理

  • (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectPaymentMethod:(PKPaymentMethod *)paymentMethod completion:(void (^)(NSArray<PKPaymentSummaryItem *> * _Nonnull))completion

//支付凭据,发给服务端进行验证支付是否真实有效

  • (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment completion:(void (^)(PKPaymentAuthorizationStatus status))completion

//支付完成

  • (void) paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,048评论 18 139
  • Swift Package Manager 教程 翻译自原文 是时候学习如何使用 Swift Package Ma...
    ZyonPaul阅读 1,078评论 0 1
  • 亲爱的爸爸妈妈们,你知道孩子真正需要的是什么吗?或许有时候我们因为太忙而疏忽了。但下面这10样东西,才是孩子真正需...
    锦虹心理阅读 264评论 0 0
  • 香港回归已经20年了,我这个内地的小人物还没有去过,签证也办下来好几年了,这个冬日,我终于踏上行程。 六点起床,七...
    山西朱小云阅读 444评论 5 8
  • 二月二,龙抬头。 春回大地,万物复苏。 晚上,朋友打来电话。 第一句,你吃了吗? 我刚在附近面馆享用完一碗三鲜粉干...
    葉鹿阅读 684评论 0 0