苹果应用内购买收据-服务器端验证

9
我在服务器端验证苹果收据时遇到了问题。我尝试在互联网上找到解决方案,但没有成功。
首先,该应用程序是为iOS7设计的。其次,我有几个项目(类型=非续订订阅)。因此,用户可以购买一个或多个项目,然后他应该手动续订它们(再次购买)。
应用程序向服务器端发送收据,我向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_app”中的每个“receipt”都有一个transaction_id。但是我该如何识别当前购买的transactionId?我还想验证它,并确保它是唯一的。
我的担忧是:如果有人获得了一个有效的收据,他将能够入侵我们的服务器端API,并使用相同有效的收据进行无限次应用内购买。
我是否应该解密并检查“原始”收据的transaction_id,即我发送给苹果进行验证的那个收据?
非常感谢您的帮助/建议。 提前致谢。
敬礼, Maksim

3
我认为关键是在应用程序端获取当前购买的交易 ID,并将其与收据一起发送到服务器。你可以从交易对象中在应用程序端获取交易 ID。 - Chris Prince
谢谢您的评论。但是这种方法的缺点在于:无法理解此收据与此交易ID相关联。 - user3489820
2个回答

1

@Doug Smith

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

如果您在此页面上浏览不同的字段,您会发现:
原始交易标识符: 对于恢复以前的交易的交易,原始交易的交易标识符。否则,与交易标识符相同。 此值对应于原始交易的transactionIdentifier属性。 自动续订订阅的所有续订链中的所有收据都具有此字段的相同值。
因此,对于非自动续订订阅,您必须在服务器端跟踪两件事情:
1. 您正在验证与itunes服务器一起使用的收据的原始交易标识符,请将其与数据库中的用户ID关联起来。 2. 您从客户端收到的请求是购买还是还原购买。
一旦您拥有这两个参数,就可以根据这两个参数编写您的逻辑,如下所示:
- 如果请求类型为“购买”,并且您已经将该收据的原始交易标识符与其他某个用户ID相关联,则可以阻止该购买。 - 如果请求类型为“还原购买”,并且请求来自与原始交易标识符在您的DB中关联的相同用户ID,则允许他,否则阻止他的还原。
此外,您可以根据自己的需求基于这些东西推导出自己的逻辑。
如果您还有任何疑问,请告诉我。

我的问题是,在收据JSON中,transaction_id属性从未更新为原始值。 - Doug Smith
但是您会收到两个JSON字典。一个对应于原始交易,另一个对应于您刚刚执行的交易。 - Vikas Dadheech

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
}

这段代码存在一些问题: 1)-[SKPaymentTransaction transactionReceipt]自iOS7以来已被弃用,应使用[[NSBundle mainBundle] appStoreReceiptURL]。 2)无论交易完成状态如何,都应该完成所有交易,因此在所有情况下(除了默认情况),都应调用-[SKPaymentQueue finishTransaction:] - Daniel Lahyani

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