Apple In-App Purchase Receipt - 在服务器端进行验证

时间:2015-01-14 10:42:07

标签: ios in-app-purchase

我在服务器端验证苹果收据时遇到问题。 我试图在互联网上找到解决方案,但没有成功。

所以,描述: 首先,应用程序是针对iOS7的。其次,我有一些项目(type = Non-Renewing Subscription)。因此,用户可以购买一件或多件商品,然后他应该手动续订(再次购买)。

应用程序向服务器端发送收据,我向Apple发出请求,并使用很多 in_app收据获取结果。类似的东西:

"in_app":[
{
"quantity":"1", "product_id":"...", "transaction_id":"...",
"original_transaction_id":"...", "purchase_date":"...", 
"purchase_date_ms":"...", "purchase_date_pst":"...", 
"original_purchase_date":"...", 
"original_purchase_date_ms":"...", "original_purchase_date_pst":"...",
"is_trial_period":"..."}, 
{
"quantity":"1", "product_id":"...", 
"transaction_id":"...","original_transaction_id":"...", 
"purchase_date":"...", "purchase_date_ms":"...", 
"purchase_date_pst":"...", "original_purchase_date":"...", 
"original_purchase_date_ms":"...", "original_purchase_date_pst":"...", 
"is_trial_period":"..."}
]

所以,每个"收据" in" in_app"有transaction_id。但我如何识别当前购买的transactionId?我也想验证它,并确保这是独一无二的。

我担心的是:如果有人会收到一张有效收据,他就可以破解我们的服务器端API,并使用相同的有效收据进行无限数量的应用内购买。

我应该以某种方式解密并检查"原始"收据,我发送给Apple验证的那个?

任何帮助/建议都将受到高度赞赏。 提前谢谢。

此致 马克西姆

2 个答案:

答案 0 :(得分:0)

@Doug Smith

https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html

如果您浏览此页面上的其他字段,则会找到

  

原始交易标识符::   对于恢复先前事务的事务,原始事务的事务标识符。否则,与事务标识符相同。   此值对应于原始事务的transactionIdentifier属性。   自动续订订阅的续订链中的所有收据对此字段具有相同的值。

因此,对于非自动续订订阅,您必须在服务器端跟踪两件事:

  1. 您正在使用itunes服务器验证的收据的原始交易标识符,将其与您数据库中的用户ID相关联。
  2. 您从客户端收到的请求是购买还是恢复购买。
  3. 一旦你掌握了这两件事,就可以在下面这两个参数上写下你的逻辑:

    ::如果请求的类型为"购买"并且您已经拥有与其他用户ID相关联的收据的原始交易标识符,您可以阻止该购买。

    ::如果请求的类型为"还原购买"和请求来自与您的数据库中原始事务标识符关联的相同用户ID,而不是允许他阻止恢复。

    此外,您可以根据自己的需要,根据这些内容推导出自己的逻辑。

    如果您有任何疑问,请告诉我。

答案 1 :(得分:0)

对于每个新的交易,苹果发送一个唯一的新收据,对其进行编码,以便没有人可以伪造数据。

从已完成的交易中获取交易收据对其进行编码并将其发送到您的服务器,并在服务器端解码并与发送给服务器的苹果匹配。

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    _transactionArray = transactions;
    for (SKPaymentTransaction * transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased: {
                NSData *receipt = transaction.transactionReceipt;
                [self sendReceiptToServer];
            } break;

            case SKPaymentTransactionStateFailed: {
                // finish this transaction
            } break;

            case SKPaymentTransactionStateRestored:
                NSData *receipt = transaction.transactionReceipt;
                [self sendReceiptToServer:receipt];
            } break;

            default:
                break;
        }
    };
}


-(void)sendReceiptToServer:(NSData *)receipt {
    // encode receipt
    // send receipt to server
    // add success and error callback
}

-(void) receiptSuccess {
    // finish transaction here
}

-(void) receiptError {
    // try again sending receipt to server
}