in_app_purchase苹果内购

Catalogue   

使用in_app_purchase这个库

流程

1. 修改XCode配置文件,支持内购

2. 项目中添加in_app_purchase配置

1
2
3
4
5
6
7

dependencies:
flutter:
sdk: flutter
in_app_purchase:
in_app_purchase_storekit:

3. 编写内购代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

class ShopPayUtil {
static ShopPayUtil? _instance;

static ShopPayUtil getInstance() {
_instance ??= ShopPayUtil._internal();
return _instance!;
}

ShopPayUtil._internal();

factory ShopPayUtil() => getInstance();

///购买商品
void buy(GoodsModel item) async {
try {
EasyLoading.show();
// 根据商品信息创建订单
var res = await rechargeCreate(item.code!);
if(res != null) {
// 调用苹果内购
await applePay(res);
}
} catch (e, s) {
Logger.error('$e $s');
} finally {
EasyLoading.dismiss();
}
}

/// 创建充值订单
Future<dynamic> rechargeCreate(String goodsCode) async {
// todo 调用服务器接口
}

/// 验单
static Future paymentIpa(String orderNo, String payload, String transactionId) async {
// todo 检查订单准确性
}

/// 消耗虚拟币
Future reviewModeConsume(String outlay) async {
// todo 调用后台接口
}

static StreamSubscription<List<PurchaseDetails>>? _subscription;

/// 苹果内购 (购买)
static Future<void> applePay(Map dataMap) async {
final Stream<List<PurchaseDetails>> purchaseUpdated = InAppPurchase.instance.purchaseStream;
_subscription?.cancel();
// 添加监听器
_subscription = purchaseUpdated.listen((List<PurchaseDetails> purchaseDetailsList) {
_listenToPurchaseUpdated(purchaseDetailsList, dataMap);
}, onDone: () {
_subscription?.cancel();
Logger.debug('=applePay=====onDone');
}, onError: (Object error) {
Logger.error(error);
ToastUtil.show(error.toString());
});


final bool isAvailable = await InAppPurchase.instance.isAvailable();
// 判断是否可用
if (!isAvailable) {
ToastUtil.show('The store cannot be reached, check your network connection please.');
return;
}

if (Platform.isIOS) {
final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
InAppPurchase.instance.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
await iosPlatformAddition.setDelegate(PaymentQueueDelegate());
}

Set<String> ids = {dataMap['goodsCode']};
// 查询商品的信息(需要先在苹果后台配置)
final ProductDetailsResponse productDetailResponse = await InAppPurchase.instance.queryProductDetails(ids);

if (productDetailResponse.error != null) {
WSToastUtil.show('Failed to obtain product information.');
return;
}

if (productDetailResponse.productDetails.isEmpty) {
ToastUtil.show('No product');
return;
}
List<ProductDetails> _products = productDetailResponse.productDetails;
// 查询成功
ProductDetails productDetails = _products[0];
PurchaseParam purchaseParam = PurchaseParam(
productDetails: productDetails,
applicationUserName: '${dataMap['orderNo']}',
);
//向苹果服务器发起支付请求
var res = await InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
Logger.debug('buyConsumable result: $res');
}

static Future<void> _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList, Map dataMap) async {
Logger.debug('===>>>_listenToPurchaseUpdated callback');
for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
Logger.debug(
'===>>>_listenToPurchaseUpdated status=${purchaseDetails.status} productID=${purchaseDetails.productID} '
'transactionDate=${purchaseDetails.transactionDate}'
'purchaseID=${purchaseDetails.purchaseID} transactionDate=${purchaseDetails.transactionDate}'
'verificationData.localVerificationData=${purchaseDetails.verificationData.localVerificationData}'
'verificationData.serverVerificationData=${purchaseDetails.verificationData.serverVerificationData}'
'verificationData.source=${purchaseDetails.verificationData.source}');
if (purchaseDetails.status == PurchaseStatus.pending) {
/// 等待购买中
} else if (purchaseDetails.status == PurchaseStatus.canceled) {
/// 取消订单
InAppPurchase.instance.completePurchase(purchaseDetails);
} else {
if (purchaseDetails.status == PurchaseStatus.error) {
/// 购买出错
WSToastUtil.show('Purchase error');
InAppPurchase.instance.completePurchase(purchaseDetails);
} else if (purchaseDetails.status == PurchaseStatus.purchased ||
purchaseDetails.status == PurchaseStatus.restored) {
if (purchaseDetails.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchaseDetails);
}
if (purchaseDetails.productID == dataMap['goodsCode']) {
// 支付成功,发放商品
await deliverProduct(purchaseDetails, dataMap);
}
}
}
}
}

/// 调用后台接口,发放商品
static Future<void> deliverProduct(PurchaseDetails purchaseDetails, Map dataMap) async {
String code = dataMap['goodsCode'];
String payload = purchaseDetails.verificationData.serverVerificationData;
String transactionId = purchaseDetails.purchaseID!;
var res = await paymentIpa(dataMap['orderNo'], payload, transactionId);
// todo 调用后台接口
}
}

class PaymentQueueDelegate implements SKPaymentQueueDelegateWrapper {
@override
bool shouldContinueTransaction(SKPaymentTransactionWrapper transaction, SKStorefrontWrapper storefront) {
return true;
}

@override
bool shouldShowPriceConsent() {
return false;
}
}


参考