我正在使用我的后端服务器来验证 iOS 收据。 为此,我所做的只是在 post 请求中将 purchase.transactionReceipt 传递给应用程序服务器。 我想知道 purchase.transactionReceipt 是否返回 base64 编码的收据数据或什么? 我收到错误:21002 与当前的实现
requestServerForReceiptVerification = (purchase: InAppPurchase | SubscriptionPurchase) => {
const receipt = purchase.transactionReceipt;
if (receipt) {
// Hit Server API for Receipt Validation
if (Platform.OS == 'ios') {
let headers = {
'Content-Type': 'application/json'
}
Stores.UserStore.hitVerifyiOSReceiptAPI(receipt, headers, this.dropdown, (response: any) => {
finishTransaction(purchase).then(() => {
console.warn('Trasaction Finished');
}).catch((error) => {
console.warn(error.message);
})
})
}
}
}
@hyochan你能在这里
我现在正在研究同样的问题。 在 ios 上,transactionReceipt“看起来”像一个 base64 字符串,但它不是。 如果我在设备上对其进行解码并输出到控制台,我会看到带有偶尔随机单词的垃圾屏幕。
然后我尝试发送它,就像我的 BE 服务器一样。 在那里,我使用 dotnet core 使用以下代码对其进行了解码:
string base64Decoded;
byte[] data = System.Convert.FromBase64String(receiptString);
base64Decoded = System.Text.Encoding.ASCII.GetString(data);
Console.WriteLine("ios receiptString:");
Console.WriteLine(base64Decoded);
输出是:
我的想法是收据对每个条目进行了编码……然后对整个收据进行了编码。 在我开始测试该理论之前,我希望在这里找到答案……但我将继续这样做。
我取得了一些进展。 我永远无法让 validateReceiptIos 函数返回任何可以解码的东西。 我的想法是 Apple 对其进行编码的方式发生了变化。
我能够通过将原始字符串传递到我的服务器然后将其发布到 verifyReceipt 端点来获取可用信息。 我用它作为指南: https :
@李兰克莱:
“通过将原始字符串传递到我的服务器,然后将其发布到 verifyReceipt 端点,我能够获得可用信息。”
您是否刚刚将该 transactionReceipt 字符串发送给 BE ?
我已经阅读了 Apple 文档,但在那里创建收据数据的方法是针对本地 iOS 应用程序,我们在其中获取 appStoreReceiptURL。
是的。 将您从 iap 获得的对象传递到服务器,然后将其发送到网站,然后您将返回一个 JSON 对象。
好的...这就是我现在所处的位置....
这是我的应用程序中的代码:
this.purchaseUpdateSubscription = purchaseUpdatedListener(
(purchase: InAppPurchase | SubscriptionPurchase) => {
console.log('purchaseUpdateSubscription called');
if (purchase) {
try {
sendTransactionReceipt(this.state.profile.userId, purchase.transactionReceipt, this.state.token)
.then(transactionReceipt => {
finishTransaction(purchase, false)
.then(finish => {
this.setState({ transactionReceipt }, () => { this.sendingTransactionReceipt = false; });
})
.catch(err => {
console.log('FinishTransaction ERROR: ' + err);
this.sendingTransactionReceipt = false;
});
})
.catch(() => {
this.sendingTransactionReceipt = false;
});
} catch (ackErr) {
console.warn('ackErr', ackErr);
}
}
},
);
在 BE 服务器上,我只需创建以下对象并将其发布到 /verifyReceipt 端点(使用上述文档中的服务器位置)。
{
"receipt-data": <purchase.transactionReceipt received from app>,
"password": <secret password from within your apple account>,
"exclude-old-transactions": true
}
我能够得到有效信息的回复。 响应的一部分是“latest_receipt”条目(稍后使用)。 我将所有内容都存储在我的数据库中,并将信息传递回应用程序,以便它可以调用 finishTransaction 并将有效收据存储在要使用的状态中。
我还在从事一项 cron 作业,该作业将每天执行一次以获取所有显示为活动且已过期的订阅。 就将信息发送到 verifyReceipt 而言,这遵循相同的过程。 我传入的“receipt-data”是我从之前的 verifyReceipt 调用中存储的“latest_receipt”。
显然,沙箱处理自动续订订阅的方式有些奇怪,所以我正在等待 Apple Developer Forums 上关于我真正应该寻找的内容的回复。
我看到的情况是,购买订阅后,我收到一个响应,显示自动续订为真(来自 verifyReceipt 的 pending_renewal_info[0].auto_renew_status 显示为 1),并且 expires_date 条目(有 3 个)显示它在未来(5 分钟)。 如果我等到过期时间后再次调用 verifyReceipt,它将显示 expires_date 和自动更新的完全相同的值。
我的计划是使用自动续订 === false 和 expires time < 当前时间的条件来确定是否停用订阅。 有没有其他人看到过这个来自 verifyReceipt 的回复???
检查这个
const IOS_SHARED_PWD = "********";
async function validateIOS() {
const latestPurchase = await getLatestPurchase();
if (latestPurchase) {
return false;
}
return RNIap.validateReceiptIos(
{
"receipt-data": latestPurchase.transactionReceipt,
"password": IOS_SHARED_PWD,
"exclude-old-transactions": false
},
false
)
.then(uncheckedValidation => {
//check test receipt
if (uncheckedValidation?.status === 21007) {
return RNIap.validateReceiptIos(
{
"receipt-data": latestPurchase.transactionReceipt,
"password": IOS_SHARED_PWD,
"exclude-old-transactions": false
},
true
);
} else {
return uncheckedValidation;
}
})
.then(checkedValidation => {
return isValidReceipt(checkedValidation);
});
}
function getLatestPurchase() {
return RNIap.getAvailablePurchases().then(purchases => {
return purchases?.sort((a, b) => Number(b.transactionDate) - Number(a.transactionDate))?.[0] || null;
});
}
function isValidReceipt(checkedValidation) {
if (!checkedValidation) {
return false;
}
// check is valid validation request
if (checkedValidation.status === 21006 || checkedValidation.status === 21010) {
return false;
}
const { latest_receipt_info: latestReceiptInfo } = checkedValidation;
const latestReceipt = latestReceiptInfo
?.sort((a, b) => Number(b.purchase_date_ms) - Number(a.purchase_date_ms))
?.find(receipt => receipt.product_id === "some.product.id")?.[0];
// no receipt
if (!latestReceipt) {
return false;
}
// refunded receipt
if (latestReceipt.cancellation_date) {
return false;
}
// expired receipt
if (Number(latestReceipt.expires_date_ms) < Date.now()) {
return false;
}
return checkedValidation.status === 0;
}
您好,最近好像没有关于这个问题的活动。 问题已经解决了,还是仍然需要社区的关注? 如果没有进一步的活动发生,这个问题可能会被关闭。 您也可以将此问题标记为“供讨论”或“好的第一期”,我将保持开放。 感谢你的贡献。
@leelandclay感谢您提出解决方案。 这真的很有帮助,我们终于能够验证我们的收据。 谢谢 !!
最有用的评论
检查这个