问题描述
我发现 iOS7 收据的 base64 编码存在一些非常奇怪的问题.
I've found some very odd issues with base64 encoding of iOS7 receipts.
目前 (Xcode5/iOS7) 有两种获取应用内购买收据的方法:
At the moment (Xcode5/iOS7) have 2 methods for getting a receipt for an in-app purchase:
- 返回单个收据的已弃用方法.
[SKPaymentTransaction transactionReceipt]
- 来自位置
appStoreReceiptURL
的所有收据包
- Deprecated method that returns a single receipt.
[SKPaymentTransaction transactionReceipt]
- A bundle of all receipts from location
appStoreReceiptURL
我的应用程序使用信用系统销售基于网站的服务,这使得服务器收据验证成为必要.该应用程序还销售可下载的扩展程序.所以是消耗品和非消耗品的混合.非消耗品是从 Apple 下载的,因此无需验证.消耗品是用于在网站上购买服务的积分包.
My App sells web site based services using a credit system which makes server receipt validation necessary. The App also sells downloadable extensions. So a mixture of consumables and non-consumables. The non-consumables are downloaded from Apple so no verification required. The consumables are packages of credits used for purchasing services on the website.
当 iOS7 &XCode5 启动我更新了我的应用程序,但在 appStoreReceiptURL 的新捆绑收据上遇到了困难.使用新样式,将所有收据捆绑在一起,我的服务器从 Apple 的 verifyReceipt 沙箱中恢复了此错误
When iOS7 & XCode5 launched I updated my App but struggled with the new bundled receipt located at appStoreReceiptURL. With the new style, all receipts bundled in one, my server got back this error from Apple's verifyReceipt sandbox
[status] => 21002
[exception] => java.lang.IllegalArgumentException
在看到 stackoverflow 上的帖子说其他人遇到了同样的问题并且当时的意见是 Apple 的错误或尚未添加的功能后,我放弃了.但是,我一直看到使用不推荐使用的旧方法出现问题,这迫使我重新尝试.经过大量调试和搜索后,我终于找出了一些问题所在 - 以及让它工作,但以一种非常丑陋的方式工作,我对继续使用没有信心.
I gave up after seening posts on stackoverflow saying others had experienced the same issue and that the opinion at the time was it was a bug, or yet to be added feature, on Apple's part.However I've been seeing issues with using the old deprecated method which has forced me back to trying again. After a lot of debugging and searching I finally worked out some of what was going wrong - as well as getting it working but in a very ugly way which I'm not confident with going live with.
我原以为 NSData(纯字节缓冲区)编码的输出不会有太大差异.这是我看到的奇怪部分.
I would have thought that NSData (plain byte buffer) encoded output wouldn't differ much.Here's the weird part I'm seeing.
原始弃用的单张收据可以是 base64 编码为 64 个字符行,末尾带有 或不带.苹果验证服务器不在乎.不管怎样都很开心.
The original deprecated single receipts can be base64 encode either as 64 character lines with on the end or without. Apple verification server doesn't care. It's happy either way.
对于新的收据包,除非完成两件事,否则它将无法工作.首先base64编码不能有换行符.我注意到 Apple 已将其自己的 base64 编码器添加到 iOS7 中.这就是我用来获取适用于两种收据类型的编码输出的方法.
For the new receipt bundle it won't work unless 2 things are done. First the base64 encoding cannot have line breaks. I notice Apple has added it's own base64 encoder into iOS7. This is what I'm using to get an encoded output that works for both receipt types.
NSString *receiptDataString = [transactionReceipt base64EncodedStringWithOptions:0];
需要做的第二件事是从服务器上接收到的编码收据包中搜索和替换所有空格字符.即
The 2nd thing that needs to be done is to search and replace all space characters from the received encoded receipt bundle on the server. i.e.
$receiptdata = str_replace(' ', '+', $receiptdata);
我碰巧注意到这是我的服务器收到的收据之间的差异.为什么我不知道?我正在使用 AFNetworking-1.3.2 的 JSONRequestOperationWithRequest
I happened to noticed this was a difference between the receipts being received by my server.Why I don't know? I'm using AFNetworking-1.3.2's JSONRequestOperationWithRequest
NSMutableArray *pairs = [[NSMutableArray alloc] initWithCapacity:0];
for (NSString *key in parameters) {
[pairs addObject:[NSString stringWithFormat:@"%@=%@", key, [parameters objectForKey:key]]];
}
postData = [[pairs componentsJoinedByString:@"&"] dataUsingEncoding:NSUTF8StringEncoding];
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://myserver.com/index.php"]];
[request setHTTPMethod:@"POST"];
[request setValue:[NSString stringWithFormat:@"%d", postData.length] forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
Chris Prince 在回复 这个问题
Chris Prince claims to have it working with AFNetworking 2 in his reply to this question
为什么这两个 NSData 收据会导致我看到的编码/解码问题?我已经尽可能详细和清楚地在这里帮助他人,因为我看到有很多人在使用 Apple 推出的这种新收据方法时感受到同样的痛苦.
Why could these two NSData receipts cause encoding/decoding problems like I'm seeing? I've been as detailed and clear as I can be here to help others as I see there's a lot of folks feeling the same pain here with this new receipt method Apple's introduced.
推荐答案
答案竟然是由于
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding
不转义所有特殊字符,例如 :/?#[]@!$&'()*+,;= 等.Apple 提供的新收据在经过 base64 编码后,包含需要转义但未被上述 iOS lib 转义编码器转义的字符.环顾四周,我在这里找到了一个效果很好的.请参阅下面的工作代码.(服务器现在不需要解析收据码,可以直接从$_POST中使用)
not escaping all special characters like :/?#[]@!$&’()*+,;= etc.The new receipt Apple provides, once base64 encoded, contains characters which need escaping but are not escaped by the above iOS lib escape encoder. Looking around I found one on here which works well. See below for working code. (Server doesn't need to parse receipt code now and can use it straight out of $_POST)
// Lifted from:
// http://stackoverflow.com/questions/2159341/nsstring-method-to-percent-escape-for-url
//
- (NSString *)urlEncodeValue:(NSString *)str {
NSString *result = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)str, NULL, CFSTR(":/?#[]@!$&’()*+,;="), kCFStringEncodingUTF8));
return result;
}
// Encode and pair basic parameters
NSMutableArray *pairs = [[NSMutableArray alloc] initWithCapacity:0];
NSString *part;
for (NSString *key in parameters) {
NSString *encodedValue = [[parameters objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *encodedKey = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
part = [NSString stringWithFormat: @"%@=%@", encodedKey, encodedValue];
[pairs addObject:part];
[pairs addObject:[NSString stringWithFormat:@"%@=%@", key, [parameters objectForKey:key]]];
}
// Receipt encoded and paired separately
receiptDataString = [self urlEncodeValue:receiptDataString];
part = [NSString stringWithFormat: @"receipt=%@", receiptDataString];
[pairs addObject:part];
// Post data.
postData = [[pairs componentsJoinedByString:@"&"] dataUsingEncoding:NSUTF8StringEncoding];
这篇关于iOS7 应用内购买收据服务器验证的 Base64 编码怪异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!