Commit 1d8c2b03 by ethanlamzs

test

1 parent cf893e75
......@@ -77,7 +77,7 @@ class SubscriptionService {
// 有产品未在 App Store Connect 配置或配置错误
debugPrint('未找到产品: ${response.notFoundIDs}');
}
_products = response.productDetails;
debugPrint('获取到 ${_products.length} 个产品');
for (var p in _products) {
......@@ -86,8 +86,8 @@ class SubscriptionService {
}
// ---------- 购买订阅 ----------
Future<bool> buySubscription(ProductDetails product, String userId , String tbxStuId, String yWOrderId) async {
// ---------- 购买订阅 ----------
Future<bool> buySubscription(ProductDetails product, String userId , String tbxStuId, String yWOrderId) async {
_pendingContexts[product.id] = {'userId': userId,'tbxStuId': tbxStuId,"orderId":yWOrderId};
_orderIdMap[product.id] = yWOrderId; // 按产品 ID 存储
......@@ -110,126 +110,124 @@ class SubscriptionService {
}
// ---------- 交易更新回调 ----------
void _onPurchaseUpdate(List<PurchaseDetails> purchaseDetailsList) {
for (final purchaseDetails in purchaseDetailsList) {
print('Purchase update: ${purchaseDetails.productID} - ${purchaseDetails.status}');
// 只处理订阅类型产品
if (!_isSubscription(purchaseDetails.productID)){
print("Purchase update !_isSubscription ");
continue;
}
// ---------- 交易更新回调 ----------
void _onPurchaseUpdate(List<PurchaseDetails> purchaseDetailsList) {
for (final purchaseDetails in purchaseDetailsList) {
print('Purchase update: ${purchaseDetails.productID} - ${purchaseDetails.status}');
// 只处理订阅类型产品
if (!_isSubscription(purchaseDetails.productID)){
print("Purchase update !_isSubscription ");
continue;
}
// 🔁 去重措施 1:根据原始交易 ID 去重,防止同一笔交易被重复处理
final transactionId = purchaseDetails.verificationData.serverVerificationData.hashCode.toString();
// 如果能获取到 purchaseID 更可靠,这里用 purchaseID 作为唯一标识
final uniqueId = purchaseDetails.purchaseID;
if (_processedTransactionIds.contains(uniqueId)) {
// 已经处理过,直接结束交易并跳过
if (purchaseDetails.status == PurchaseStatus.purchased ||
purchaseDetails.status == PurchaseStatus.restored) {
_iap.completePurchase(purchaseDetails);
// 🔁 去重措施 1:根据原始交易 ID 去重,防止同一笔交易被重复处理
final transactionId = purchaseDetails.verificationData.serverVerificationData.hashCode.toString();
// 如果能获取到 purchaseID 更可靠,这里用 purchaseID 作为唯一标识
final uniqueId = purchaseDetails.purchaseID;
if (_processedTransactionIds.contains(uniqueId)) {
// 已经处理过,直接结束交易并跳过
if (purchaseDetails.status == PurchaseStatus.purchased ||
purchaseDetails.status == PurchaseStatus.restored) {
_iap.completePurchase(purchaseDetails);
}
continue;
}
// 标记为已处理
if (uniqueId != null) {
_processedTransactionIds.add(uniqueId);
}
continue;
}
// 标记为已处理
if (uniqueId != null) {
_processedTransactionIds.add(uniqueId);
}
//var orderId = _orderIdMap.remove(purchaseDetails.productID); // 取出并删除
final ctx = _pendingContexts[purchaseDetails.productID] ;
print(ctx);
if (ctx == null) {
// 打印错误日志,防止崩溃
print('错误:未找到 productID 对应的上下文缓存');
return;
}
//var orderId = _orderIdMap.remove(purchaseDetails.productID); // 取出并删除
final ctx = _pendingContexts[purchaseDetails.productID] ;
print(ctx);
if (ctx == null) {
// 打印错误日志,防止崩溃
print('错误:未找到 productID 对应的上下文缓存');
return;
}
final orderId = ctx['orderId'] as String?;
final userId = ctx['userId'] as String?;
final tbxStuId = ctx['tbxStuId'] as String?;
print("orderId ==> "+orderId.toString()+" "+ userId.toString() +". "+ tbxStuId.toString());
// 根据交易状态处理
switch (purchaseDetails.status) {
case PurchaseStatus.purchased:
// 购买成功或续期成功
// 1. 验证收据(必须!)
//_verifyReceipt(purchaseDetails,orderId??'',userId??'',"purchased");
_handlePurchaseSuccess(purchaseDetails,orderId??'',userId??'');
break;
case PurchaseStatus.error:
// 处理错误
_handlePurchaseError(purchaseDetails.error!);
break;
case PurchaseStatus.restored:
// 恢复购买成功
// 🔁 去重措施 2:恢复事件只处理最后一个有效的,或者收集后统一处理
// 这里演示:仍然逐个验证,但后端会基于 original_transaction_id 去重
//_verifyReceipt(purchaseDetails,orderId??'',userId??'',"restored");
_handleRestoreSuccess(purchaseDetails,orderId??'',userId??'');
break;
case PurchaseStatus.canceled:
// 用户取消支付
//_iap.completePurchase(purchaseDetails);
break;
default:
break;
}
final orderId = ctx['orderId'] as String?;
final userId = ctx['userId'] as String?;
final tbxStuId = ctx['tbxStuId'] as String?;
print("orderId ==> "+orderId.toString()+" "+ userId.toString() +". "+ tbxStuId.toString());
// 根据交易状态处理
switch (purchaseDetails.status) {
case PurchaseStatus.purchased:
// 购买成功或续期成功
// 1. 验证收据(必须!)
//_verifyReceipt(purchaseDetails,orderId??'',userId??'',"purchased");
_handlePurchaseSuccess(purchaseDetails,orderId??'',userId??'');
break;
case PurchaseStatus.error:
// 处理错误
_handlePurchaseError(purchaseDetails.error!);
break;
case PurchaseStatus.restored:
// 恢复购买成功
// 🔁 去重措施 2:恢复事件只处理最后一个有效的,或者收集后统一处理
// 这里演示:仍然逐个验证,但后端会基于 original_transaction_id 去重
//_verifyReceipt(purchaseDetails,orderId??'',userId??'',"restored");
_handleRestoreSuccess(purchaseDetails,orderId??'',userId??'');
break;
case PurchaseStatus.canceled:
// 用户取消支付
//_iap.completePurchase(purchaseDetails);
break;
default:
break;
}
// // ✅ 关键:只要不是 pending,都立刻完成交易,清出队列
// if (purchaseDetails.status != PurchaseStatus.pending) {
// _iap.completePurchase(purchaseDetails);
// print("[debug] _iap.completePurchase execute ");
// }
// 🔁 去重措施 3:所有恢复交易处理完后,统一刷新一次状态(防多次通知)
final hasRestored = purchaseDetailsList.any((d) => d.status == PurchaseStatus.restored);
if (hasRestored) {
// 统一通知 UI 更新,避免多次刷新
onSuccess?.call('购买已恢复');
onStateChanged?.call();
// // ✅ 关键:只要不是 pending,都立刻完成交易,清出队列
// if (purchaseDetails.status != PurchaseStatus.pending) {
// _iap.completePurchase(purchaseDetails);
// print("[debug] _iap.completePurchase execute ");
// }
// 🔁 去重措施 3:所有恢复交易处理完后,统一刷新一次状态(防多次通知)
final hasRestored = purchaseDetailsList.any((d) => d.status == PurchaseStatus.restored);
if (hasRestored) {
// 统一通知 UI 更新,避免多次刷新
onSuccess?.call('购买已恢复');
onStateChanged?.call();
}
}
}
}
// ---------- 服务端验证收据 ----------
Future<void> _verifyReceipt(PurchaseDetails purchaseDetails,String orderid,String userid,String scence) async {
debugPrint("[info] _verifyReceipt is checking 3 ["+scence+"] "+purchaseDetails.productID+" orderid:"+orderid+" userid:"+userid);
if (orderid != null && orderid.length > 6) {
debugPrint("[info] _verifyReceipt is start scence ["+scence+"] "+purchaseDetails.productID+" orderid:"+orderid);
// 将收据发送到你的后端进行验证
final String receipt = purchaseDetails.verificationData.serverVerificationData;
// 把 base64Receipt 作为 receipt-data 发给你的服务端验证
final bool isValid = await _sendReceiptToBackend(userid,receipt,orderid);
if (isValid) {
// 更新本地订阅状态
_hasActiveSubscription = true;
// 保存到期时间等信息
}
// ---------- 服务端验证收据 ----------
Future<bool> _verifyReceipt(PurchaseDetails purchaseDetails,String orderid,String userid,String scence) async {
debugPrint("[info] _verifyReceipt is checking 3 ["+scence+"] "+purchaseDetails.productID+" orderid:"+orderid+" userid:"+userid);
if (orderid != null && orderid.length > 6) {
debugPrint("[info] _verifyReceipt is start scence ["+scence+"] "+purchaseDetails.productID+" orderid:"+orderid);
// 将收据发送到你的后端进行验证
final String receipt = purchaseDetails.verificationData.serverVerificationData;
// 把 base64Receipt 作为 receipt-data 发给你的服务端验证
final bool isValid = await _sendReceiptToBackend(userid,receipt,orderid);
if (isValid) {
// 更新本地订阅状态
_hasActiveSubscription = true;
// 保存到期时间等信息
}
return isValid;
}
return false;
}
}
// 示例:发送收据到自己的服务器进行二次验证
Future<bool> _sendReceiptToBackend(String userid,String receipt,String orderid) async {
var result = _userAuthRepository.orderCheck(userid,receipt,orderid,"ios",1);
//var data = result['data'] as Map<String, dynamic>?;
//debugPrint(result);
var result = await _userAuthRepository.orderCheck(userid,receipt,orderid,"ios",1);
debugPrint("[info] _sendReceiptToBackend is done");
return true;
return result!=null;
}
// ---------- 辅助方法 ----------
bool _isSubscription(String productId) {
......@@ -242,18 +240,19 @@ class SubscriptionService {
}
// ---------- 购买成功处理 ----------
void _handlePurchaseSuccess(PurchaseDetails detail,String orderId,String userId) {
// 发送收据到后端验证,携带 userId
_verifyReceipt(detail,orderId??'',userId??'',"purchased");
// 完成交易
_iap.completePurchase(detail);
// 乐观更新本地状态(等后端最终确认再真正解锁功能)
_hasActiveSubscription = true;
onSuccess?.call('订阅成功');
onStateChanged?.call();
Future<void> _handlePurchaseSuccess(PurchaseDetails detail,String orderId,String userId) async {
// 发送收据到后端验证,携带 userId
final ok = await _verifyReceipt(detail,orderId??'',userId??'',"purchased");
if(ok){
_hasActiveSubscription = true;
if(detail.pendingCompletePurchase){
await _iap.completePurchase(detail);
}
}else{
// 不complete
// 等待下次重新验证
}
}
// ---------- 恢复成功处理 ----------
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!