苹果应用内购买收据验证返回21002错误码

5

我尝试向沙盒(和线上)的verifyReceipt端点提交,但我总是从苹果得到以下JSON响应...

{"status":21002, "exception":"java.lang.IllegalArgumentException"}

这是一个 Postman 的截图。

Postman testing

我正在发送POST请求并设置头部(application/json)。
{"receipt-data":"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"}

这是从在iOS设备上运行的应用程序通过TestFlight内部测试部署执行的。
我不确定Base64字符串是否格式不正确 - 它确实有一些空格,但现在我已经删除了它们(因为我看到有报告说这会有所帮助)。
以下是我在移动应用程序中捕获base64编码的方法,请注意这是Xamarin代码(C#),但它与您在objective-c / swift中所做的非常相似...
String purchaseToken = null;
NSUrl receiptUrl = NSBundle.MainBundle.AppStoreReceiptUrl;
NSData receipt = null;

if (receiptUrl != null) {
    receipt = NSData.FromUrl (receiptUrl);
}

if (receipt == null) {
    // No local receipt -- handle the error
    // https://forums.xamarin.com/discussion/25304/how-do-i-exit-with-reason
    Environment.Exit (173);
} else {
    // http://stackoverflow.com/questions/37505020/example-of-parsing-a-receipt-for-an-in-app-purchase-using-ios-xamarin
    NSDictionary requestContents = NSDictionary.FromObjectAndKey ((NSString)receipt.GetBase64EncodedString (
                NSDataBase64EncodingOptions.None),
                (NSString)"receipt-data");
     string receiptData = (requestContents ["receipt-data"] as NSString).ToString ();

    purchaseToken = receiptData;
}

this.iapConsumableEvent.PurchaseToken = purchaseToken;

服务器代码(Java)使用 HttpClient 将此传输给 Apple...
HttpClient client = HttpClientHelper.createHttpClient(new DefaultRedirectStrategy());
final HttpPost postRequest = new HttpPost(appleEnvironmentUrl);
StringEntity input = new StringEntity(receiptRequestJson);
input.setContentType("application/json");
postRequest.setEntity(input);
HttpResponse response = client.execute(postRequest);

String rawResponse = EntityUtils.toString(response.getEntity(), "UTF-8");
BasicDBObject appleJson = BasicDBObject.parse(rawResponse);

int statusCode = appleJson.getInt("status", 99999);

//ALWAYS RETURNING 21002 The data in the receipt-data property was malformed or missing.

我非常感谢任何想法/答案!

3个回答

8

我曾尝试使用Postman进行操作,但出现了问题。

这个在node.js中对我有用。在成功之前,我必须使用JSON.stringify:

function createReceiptRequestJSON(base64String, secret) {
    return JSON.stringify({
        "receipt-data":base64String,
        password:secret
    });
}

您可以通过iTunes Connect找到您的密钥,但我相当确定您需要在请求中包含它。

编辑:

使用Postman再次查看。从此答案Apple receipt_data sample获取收据。

enter image description here

似乎有效...


1
谢谢@jeh - 我不需要密码,因为我没有处理订阅,只是验证IAP可消耗收据。当你说你不能通过postman使其工作时,你是指上面的receipt-data示例还是你自己的?我想知道为什么你不能在postman中使其工作 - 这意味着我们都在有效载荷中缺少某些东西(或添加了太多!) - RobbiewOnline
尝试按照我在链接问题中的最后一个答案所编辑的方式进行操作 - 也许你的base64有误。 - jeh

1

这是一段简单的代码,可以帮助您检查当前购买的产品:

它适用于iOS 13.1

func verifyPurchaseWithPayment() {
    let appsToreUrlString = "https://sandbox.itunes.apple.com/verifyReceipt"
    let receiptUrl = Bundle.main.appStoreReceiptURL
    do {
        let receipt = try Data(contentsOf: receiptUrl!)
        let encodedString = receipt.base64EncodedString()
        let requestContents = ["receipt-data" : encodedString] as [String : Any]
        do {
            let requestJsonData = try JSONSerialization.data(withJSONObject: requestContents, options: [])
            let session = URLSession.shared
            let request = NSMutableURLRequest(url: URL(string: appsToreUrlString)!)
            request.httpMethod = "POST"
            request.httpBody = requestJsonData
            let dataTask = session.dataTask(with: request as URLRequest) { (data, response, err) in
                let json = try? JSONSerialization.jsonObject(with: data!, options: [])
                print(json ?? "")
            }
            dataTask.resume()

        } catch { return }
    } catch { return }
}

我在使用Alamofire库时遇到了一个问题,它改变了一些符号,所以在将base64字符串发送到服务器进行验证之前,我对其进行了屏蔽。

let receiptString = receiptData.base64EncodedString()
let allowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
let allowedCharSet = CharacterSet(charactersIn: allowedCharacters)
let encodedString = receiptString.addingPercentEncoding(withAllowedCharacters: allowedCharSet)

0
原来我是将令牌作为查询参数通过HTTP GET发送到服务器的,一旦我将其添加到POST正文中,一切都没问题了!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接