status 21002 php,php – 验证appReceiptStoreURL返回21002状态

我创建了一个类来处理应用程序内购买的购买以及验证收据.前一段时间我曾经在SKPaymentTransaction上使用transactionReceipt属性,但已经更新了我的代码,现在在[NSBundle mainBundle]上使用了appStoreReceiptURL.

基本上似乎我的收据是以可接受的方式发送到Apple的服务器,但我一直得到21002的状态代码.在自动续订订阅中我知道这意味着收据不是可接受的格式,但我有不知道这个状态对于应用程序内购买收据的意义.

以下是验证收据的本地方法:

/**

* Validates the receipt.

*

* @param transaction The transaction triggering the validation of the receipt.

*/

- (void)validateReceiptForTransaction:(SKPaymentTransaction *)transaction

{

// get the product for the transaction

IAPProduct *product = self.internalProducts[transaction.payment.productIdentifier];

// get the receipt as a base64 encoded string

NSData *receiptData = [[NSData alloc] initWithContentsOfURL:[NSBundle mainBundle].appStoreReceiptURL];

NSString *receipt = [receiptData base64EncodedStringWithOptions:kNilOptions];

NSLog(@"Receipt: %@", receipt);

// determine the url for the receipt verification server

NSURL *verificationURL = [[NSURL alloc] initWithString:IAPHelperServerBaseURL];

verificationURL = [verificationURL URLByAppendingPathComponent:IAPHelperServerReceiptVerificationComponent];

NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:verificationURL];

urlRequest.HTTPMethod = @"POST";

NSDictionary *httpBody = @{@"receipt" : receipt,

@"sandbox" : @(1)};

urlRequest.HTTPBody = [NSKeyedArchiver archivedDataWithRootObject:httpBody];

[NSURLConnection sendAsynchronousRequest:urlRequest

queue:[[NSOperationQueue alloc] init]

completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)

{

// create a block to be called whenever a filue is hit

void (^failureBlock)(NSString *failureMessage) = ^void(NSString *failureMessage)

{

[[NSOperationQueue mainQueue] addOperationWithBlock:

^{

// log the failure message

NSLog(@"%@", failureMessage);

// if we have aready tried refreshing the receipt then we close the transaction to avoid loops

if (self.transactionToValidate)

product.purchaseInProgress = NO,

[[SKPaymentQueue defaultQueue] finishTransaction:transaction],

[self notifyStatus:@"Validation failed." forProduct:product],

self.transactionToValidate = nil;

// if we haven't tried yet, we'll refresh the receipt and then attempt a second validation

else

self.transactionToValidate = transaction,

[self refreshReceipt];

}];

};

// check for an error whilst contacting the server

if (connectionError)

{

failureBlock([[NSString alloc] initWithFormat:@"Failure connecting to server: %@", connectionError]);

return;

}

// cast the response appropriately

NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

// parse the JSON

NSError *jsonError;

NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];

// if the data did not parse correctly we fail out

if (!json)

{

NSString *responseString = [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode];

NSString *failureMessage = [[NSString alloc] initWithFormat:@"Failure parsing JSON: %@\nServer Response: %@ (%@)",

data, responseString, @(httpResponse.statusCode)];

failureBlock(failureMessage);

return;

}

// if the JSON was successfully parsed pull out status code to check for verification success

NSInteger statusCode = [json[@"status"] integerValue];

NSString *errorDescription = json[@"error"];

// if the verification did not succeed we fail out

if (statusCode != 0)

{

NSString *failureMessage = [[NSString alloc] initWithFormat:@"Failure verifying receipt: %@", errorDescription];

failureBlock(failureMessage);

}

// otherwise we have succeded, yay

else

NSLog(@"Successfully verified receipt."),

[self provideContentForCompletedTransaction:transaction productIdentifier:transaction.payment.productIdentifier];

}];

}

服务器上重要的PHP函数执行此操作:

/**

* Validates a given receipt and returns the result.

*

* @param receipt Base64-encoded receipt.

* @param sandbox Boolean indicating whether to use sandbox servers or production servers.

*

* @return Whether the reciept is valid or not.

*/

function validateReceipt($receipt, $sandbox)

{

// determine url for store based on if this is production or development

if ($sandbox)

$store = 'https://sandbox.itunes.apple.com/verifyReceipt';

else

$store = 'https://buy.itunes.apple.com/verifyReceipt';

// set up json-encoded dictionary with receipt data for apple receipt validator

$postData = json_encode(array('receipt-data' => $receipt));

// use curl library to perform web request

$curlHandle = curl_init($store);

// we want results returned as string, the request to be a post, and the json data to be in the post fields

curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);

curl_setopt($curlHandle, CURLOPT_POST, true);

curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $postData);

$encodedResponse = curl_exec($curlHandle);

curl_close($curlHandle);

// if we received no response we return the error

if (!$encodedResponse)

return result(ERROR_VERIFICATION_NO_RESPONSE, 'Payment could not be verified - no response data. This was sandbox? ' . ($sandbox ? 'YES' : 'NO'));

// decode json response and get the data

$response = json_decode($encodedResponse);

$status = $response->{'status'};

$decodedReceipt = $response->{'receipt'};

// if status code is not 0 there was an error validation receipt

if ($status)

return result(ERROR_VERIFICATION_FAILED, 'Payment could not be verified (status = ' . $status . ').');

// log the returned receipt from validator

logToFile(print_r($decodedReceipt, true));

// pull out product id, transaction id and original transaction id from infro trurned by apple

$productID = $decodedReceipt->{'product_id'};

$transactionID = $decodedReceipt->{'transaction_id'};

$originalTransactionID = $decodedReceipt->{'original_transaction_id'};

// make sure product id has expected prefix or we bail

if (!beginsWith($productID, PRODUCT_ID_PREFIX))

return result(ERROR_INVALID_PRODUCT_ID, 'Invalid Product Identifier');

// get any existing record of this transaction id from our database

$db = Database::get();

$statement = $db->prepare('SELECT * FROM transactions WHERE transaction_id = ?');

$statement->bindParam(1, $transactionID, PDO::PARAM_STR, 32);

$statement->execute();

// if we have handled this transaction before return a failure

if ($statement->rowCount())

{

logToFile("Already processed $transactionID.");

return result(ERROR_TRANSACTION_ALREADY_PROCESSED, 'Already processed this transaction.');

}

// otherwise we insert this new transaction into the database

else

{

logToFile("Adding $transactionID.");

$statement = $db->prepare('INSERT INTO transactions(transaction_id, product_id, original_transaction_id) VALUES (?, ?, ?)');

$statement->bindParam(1, $transactionID, PDO::PARAM_STR, 32);

$statement->bindParam(2, $productID, PDO::PARAM_STR, 32);

$statement->bindParam(3, $originalTransactionID, PDO::PARAM_STR, 32);

$statement->execute();

}

return result(SUCCESS);

}

正在执行的PHP脚本是:

$receipt = $_POST['receipt'];

$sandbox = $_POST['sandbox'];

$returnValue = validateReceipt($receipt, $sandbox);

header('content-type: application/json; charset=utf-8');

echo json_encode($returnValue);

解决方法:

将PHP与我的(我知道有效)进行比较很困难,因为我使用的是HTTPRequest,而不是原始的curl API.但是,在我看来,您将“{receipt-data:..}”JSON字符串设置为POST数据中的字段而不是原始POST数据本身,这正是我的代码所做的.

curl_setopt($curlHandle, CURLOPT_POST, true);

curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $postData); // Possible problem

$encodedResponse = curl_exec($curlHandle);

相比:

$postData = '{"receipt-data" : "'.$receipt.'"}'; // yay one-off JSON serialization!

$request = new HTTPRequest('https://sandbox.itunes.apple.com/verifyReceipt', HTTP_METH_POST);

$request->setBody($postData); // Relevant difference...

$request->send();

$encodedResponse = $request->getResponseBody();

我已经更改了我的变量名称,使它们与您的示例匹配.

标签:php,ios,objective-c,receipt

来源: https://codeday.me/bug/20190624/1281575.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值