关键词
com.apple.developer.payment-pass-provisioning
PKAddPaymentPassRequestConfiguration
PKAddPaymentPassViewController
PKAddPaymentPassRequest
PKPassLibrary
PKAddPassButton
PKPaymentPass
下面是开发的步骤及遇到的问题
1.向苹果公司的人员申请Wallet(官方说是passKit)权限
2.权限配好后,在开发者中心配置一下Application Services:
3.编辑描述文件选择下图所示,并重新生成,没有其他的什么文件
4.xcode工程配置 打开wallet
5.然后配置一下工程中的文件 填入com.apple.developer.payment-pass-provisioning字段,确定这个entitlements文件已加到工程里
6.申请沙盒账号:就是假的icloud账号,可以用第7步的账号直接在wallet试试
7.官方网站提供的测试账号可以直接在wallet应用里加上
https://developer.apple.com/apple-pay/sandbox-testing/
8.申请测试卡宾:即测试账号的前6位
9.开发中遇到报错:苹果查需要提供SEID,银联查需要提供卡号
10:有效期的格式需要传斜杠
10.报错"无法添加卡"有很多原因
如果把密文给苹果直接报错“无法添加卡”,那就是加密错误,秘钥是一个数组需要都传给后台。
11.报错“卡片有误”,说明加解密已过,是我们自己这边的卡可能被锁
12.开发此功能的设置支持的iOS版本可以低于10.3,如果使用tesfflight测试此功能需要提高到10.3.
13.上代码
//设置加密类型
PKAddPaymentPassRequestConfiguration *con = [[PKAddPaymentPassRequestConfiguration alloc]initWithEncryptionScheme:PKEncryptionSchemeRSA_V2];
con.cardholderName = @"界面展示用";
con.localizedDescription = @"加卡";//描述信息
if (IOS10_OR_LATER) {
con.paymentNetwork = PKPaymentNetworkChinaUnionPay;//银联卡
}
con.primaryAccountSuffix = @“1111”;卡号结尾4位
PKAddPaymentPassViewController *vc = [[PKAddPaymentPassViewController alloc] initWithRequestConfiguration:con delegate:self];
[XXX.rootViewController pushViewController:vc animated:YES];
然后实现下面的方法
- (void)addPaymentPassViewController:(PKAddPaymentPassViewController *)controller
generateRequestWithCertificateChain:(NSArray<NSData *> *)certificates
nonce:(NSData *)nonce
nonceSignature:(NSData *)nonceSignature
completionHandler:(void(^)(PKAddPaymentPassRequest *request))handler;{
NSString *leafBase64Encoded = [certificates[0] base64EncodedStringWithOptions:0];
NSString *subBase64Encoded = [certificates[1] base64EncodedStringWithOptions:0];
NSArray * cerArray = @[leafBase64Encoded,subBase64Encoded];
NSString *nonceStr = [self convertDataToHexStr:nonce];
NSString *nonceSignatureStr = [self convertDataToHexStr:nonceSignature];
//然后往后台发接口 注意cerArray直接发给后台
//后台返回密码等数据
然后回传给sdk
NSString *encryptedPassString = params[@"encryptedPassData"];
encryptedPassString = [encryptedPassString stringByReplacingOccurrencesOfString:@" " withString:@""];
encryptedPassString = [encryptedPassString stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];
encryptedPassString = [encryptedPassString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *encryptedPassData=[[NSData alloc] initWithBase64EncodedString:encryptedPassString options:0];
NSString *activationString = params[@"activationData"];
activationString = [NSString stringWithFormat:@"%@|%@",nonceSignatureStr, params[@"activationData"]];
NSData *activationData = [activationString dataUsingEncoding:NSUTF8StringEncoding];
NSString *wrappedKeyString = params[@"wrappedKey"];
wrappedKeyString = [wrappedKeyString stringByReplacingOccurrencesOfString:@" " withString:@""];
wrappedKeyString = [wrappedKeyString stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];
wrappedKeyString = [wrappedKeyString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *wrappedKey=[[NSData alloc] initWithBase64EncodedString:wrappedKeyString options:0];
wrappedKey = [EncodeAndDecode dataFromHexString:wrappedKeyString];
encryptedPassData = [EncodeAndDecode dataFromHexString:encryptedPassString];
PKAddPaymentPassRequest *request = [[PKAddPaymentPassRequest alloc] init];
request.activationData = activationData;
request.encryptedPassData = encryptedPassData;
request.wrappedKey = wrappedKey;
if (request == nil) {
return;
}
if (handler) {
handler(request);
}
//下面这个方法 失败会走但没什么有用的信息
- (void)addPaymentPassViewController:(PKAddPaymentPassViewController *)controller didFinishAddingPaymentPass:(nullable PKPaymentPass *)pass error:(nullable NSError *)error;{
}
//进制转换
- (NSString *)convertDataToHexStr:(NSData *)data {
if (!data || [data length] == 0) {
return @"";
}
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[data length]];
[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
unsigned char *dataBytes = (unsigned char*)bytes;
for (NSInteger i = 0; i < byteRange.length; i++) {
NSString *hexStr = [NSString stringWithFormat:@"%x", (dataBytes[i]) & 0xff];
if ([hexStr length] == 2) {
[string appendString:hexStr];
} else {
[string appendFormat:@"0%@", hexStr];
}
}
}];
return string;
}
注意:1.证书要编码传给后台,对证书编码这里用base64和平时用的没有区别
2.nonce要转成16进制传给后台 不是做base64。使用convertDataToHexStr方法或下面这个都行
NSString *nOnce = [NSString stringWithFormat:@"%@",nonce];
nOnce = [nOnce stringByReplacingOccurrencesOfString:@"<" withString:@""];
nOnce = [nOnce stringByReplacingOccurrencesOfString:@">" withString:@""];
nOnce = [nOnce stringByReplacingOccurrencesOfString:@" " withString:@""];
3.nonceSignature同上
4.后台返回的数据如何给苹果,encryptedPassData和wrappedKey需要直接从16进制字符串转为nsdata
方法是
+ (NSData *) dataFromHexString:(NSString*)hexString
{
NSString * cleanString = [SM4EncodeAndDecode cleanNonHexCharsFromHexString:hexString];
if (cleanString == nil) {
return nil;
}
NSMutableData *result = [[NSMutableData alloc] init];
int i = 0;
for (i = 0; i+2 <= cleanString.length; i+=2) {
NSRange range = NSMakeRange(i, 2);
NSString* hexStr = [cleanString substringWithRange:range];
NSScanner* scanner = [NSScanner scannerWithString:hexStr];
unsigned int intValue;
[scanner scanHexInt:&intValue];
unsigned char uc = (unsigned char) intValue;
[result appendBytes:&uc length:1];
}
NSData * data = [NSData dataWithData:result];
// [result autorelease];//add先去掉 可能有闪退
return data;
}
5.返回的activationData 转成data就可以 和后台约定一下就行
6.然后可能报错 错误1 "无法添加卡 稍后再试或联系发卡机构来获取更多信息 好"
上图的警告框下边就一个按钮“好”,这种情况应该是返给苹果的数据类型不对
7.错误2 这个下边两个按钮 如果在协议之前弹出来应该是加解密错误(后台要用test vector验仔细证) 或银联问题
如果在协议之后弹出来应该是自己后台验证的问题
在wallet里添加或在客户端里添加应该走的链路没啥区别都报相同的错
8.错误3 如果在协议之前弹出来应该是已经过了加解密,而且这个是信用卡报出来的几率大,可以验证第7步是否加解密没有问题
9.如果下面的错 加解密没问题 卡宾没有加到测试苹果数据里
10.如果银联没有问题会弹出协议
11.然后加挂成功
12.注意的问题
(1)- (NSArray<PKPass *> *)passesOfType:(PKPassType)passType
这个方法如果获取不到数据需要后台配置一下参数:teamid.bundleid
后台代码类似
"cardMetaData":{"associatedApplicationIdentifiers":[":["P2V7337495.com.elem.e","P","","P2V7337495.com.nihaom.hao"],""],"associatedStoreIdentifiers":["473489211"]
(2)名词 fpan不太懂应该用不到
en_otp:加密验证码 应该指的是activationData
(3)- canAddPaymentPassWithPrimaryAccountIdentifier: 传入的参数应该是fpan后台给的
20181214补充
银联还有一些限制,同一张卡同设备一天只能绑定成功两次,一天只能错10次
20181221补充
这两个地方传入的primaryAccountIdentifier是银联给的不是自己拼的 应该叫实体卡id 类似FAPLUPCEBb660e3aac30447599dd06ecfbf53a9f1 不写的话测试环境没事
// Returns YES if either the current device or an attached device both supports adding payment passes and does not already contain
// a payment pass with the supplied primary account identifier.
- (BOOL)canAddPaymentPassWithPrimaryAccountIdentifier:(NSString *)primaryAccountIdentifier API_AVAILABLE(ios(9.0));
/* Pass Library Filters:
* If the filtered set is empty, then all filter will be ignored.
*/
@property (nonatomic, copy, nullable) NSString *primaryAccountIdentifier;
后台投产后需要把下面两个发邮件给苹果加白名单才可以正式在生产上使用,只能用生产证书打包
team id 从Membership中查
adam ID从此app的appstore下载链接中取
20190124 如何判断某张卡是不是其他设备上的:BOOL canAdd = [passLibrary canAddPaymentPassWithPrimaryAccountIdentifier:fpanID]; 通过这个方法返回的NO表示本设备已经加载了