更新ios

This commit is contained in:
2026-01-21 15:59:37 +08:00
parent 010a12cd0e
commit 57c1dab17b
3 changed files with 53 additions and 18 deletions

View File

@@ -405,7 +405,14 @@ const SubscriptionScreen = () => {
const purchase = await iapService.purchaseProduct(productInfo.productId); const purchase = await iapService.purchaseProduct(productInfo.productId);
// 验证收据并激活订阅 // 验证收据并激活订阅
// 注意purchaseUpdatedListener 也可能触发验证,但后端有防重复机制
if (purchase && purchase.transactionReceipt) { if (purchase && purchase.transactionReceipt) {
// 标记此交易正在处理中(防止监听器重复处理)
if (purchase.transactionId) {
iapService.processingTransactions.add(purchase.transactionId);
}
try {
const result = await iapService.verifyAndActivateSubscription( const result = await iapService.verifyAndActivateSubscription(
purchase.transactionReceipt, purchase.transactionReceipt,
productInfo.productId productInfo.productId
@@ -424,6 +431,12 @@ const SubscriptionScreen = () => {
[{ text: '好的', onPress: () => navigation.goBack() }] [{ text: '好的', onPress: () => navigation.goBack() }]
); );
} }
} finally {
// 移除处理中标记
if (purchase.transactionId) {
iapService.processingTransactions.delete(purchase.transactionId);
}
}
} }
} catch (error) { } catch (error) {
console.error('[SubscriptionScreen] 订阅失败:', error); console.error('[SubscriptionScreen] 订阅失败:', error);

View File

@@ -110,6 +110,8 @@ class IAPService {
this.subscriptions = []; this.subscriptions = [];
this.purchaseUpdateSubscription = null; this.purchaseUpdateSubscription = null;
this.purchaseErrorSubscription = null; this.purchaseErrorSubscription = null;
// 用于防止重复验证的标记
this.processingTransactions = new Set();
} }
/** /**
@@ -170,11 +172,26 @@ class IAPService {
} }
// 购买成功/更新监听器 // 购买成功/更新监听器
// 注意:这个监听器主要用于处理后台恢复的购买或 App 重启后的未完成交易
// 正常购买流程由 SubscriptionScreen 的 handleSubscribe 处理
this.purchaseUpdateSubscription = purchaseUpdatedListener(async (purchase) => { this.purchaseUpdateSubscription = purchaseUpdatedListener(async (purchase) => {
console.log('[IAPService] 购买更新:', purchase); console.log('[IAPService] 购买更新:', purchase);
const transactionId = purchase.transactionId;
// 检查是否已经在处理中(防止重复验证)
if (transactionId && this.processingTransactions.has(transactionId)) {
console.log('[IAPService] 交易已在处理中,跳过:', transactionId);
return;
}
if (purchase.transactionReceipt) { if (purchase.transactionReceipt) {
try { try {
// 标记为处理中
if (transactionId) {
this.processingTransactions.add(transactionId);
}
// 验证收据 // 验证收据
const verifyResult = await this.verifyAndActivateSubscription( const verifyResult = await this.verifyAndActivateSubscription(
purchase.transactionReceipt, purchase.transactionReceipt,
@@ -188,6 +205,11 @@ class IAPService {
} }
} catch (error) { } catch (error) {
console.error('[IAPService] 处理购买失败:', error); console.error('[IAPService] 处理购买失败:', error);
} finally {
// 移除处理中标记
if (transactionId) {
this.processingTransactions.delete(transactionId);
}
} }
} }
}); });

6
app.py
View File

@@ -3995,6 +3995,9 @@ def apple_webhook():
文档: https://developer.apple.com/documentation/appstoreservernotifications 文档: https://developer.apple.com/documentation/appstoreservernotifications
""" """
import base64
import json as json_lib
try: try:
# 获取请求数据 # 获取请求数据
data = request.get_json() data = request.get_json()
@@ -4011,9 +4014,6 @@ def apple_webhook():
# TODO: 实现 JWT 签名验证(使用苹果的公钥) # TODO: 实现 JWT 签名验证(使用苹果的公钥)
# 目前先解码 payload 部分(不验证签名,生产环境应验证) # 目前先解码 payload 部分(不验证签名,生产环境应验证)
try: try:
import base64
import json as json_lib
# JWT 格式: header.payload.signature # JWT 格式: header.payload.signature
parts = signed_payload.split('.') parts = signed_payload.split('.')
if len(parts) >= 2: if len(parts) >= 2: