-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathRNInAppPurchaseModule.m
353 lines (314 loc) · 12.6 KB
/
RNInAppPurchaseModule.m
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
//
// RNInAppPurchaseModule.m
// zybx
//
// Created by 刘乙灏 on 2018/9/5.
// Copyright © 2018年 Facebook. All rights reserved.
//
////沙盒测试环境验证
//#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
////正式环境验证
//#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
/*
内购验证凭据返回结果状态码说明
21000 App Store无法读取你提供的JSON数据
21002 收据数据不符合格式
21003 收据无法被验证
21004 你提供的共享密钥和账户的共享密钥不一致
21005 收据服务器当前不可用
21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证
21008 收据信息是产品环境中使用,但却被发送到测试环境中验证
*/
#import "RNInAppPurchaseModule.h"
#import <React/RCTLog.h>
// 未验证订单持久化参数
#define kIapUnverifyOrders @"iap_unverify_orders"
@interface SKProduct (StringPrice) // 格式化价格字符串
@property (nonatomic, readonly) NSString *priceString;
@end
@implementation SKProduct (StringPrice)
- (NSString *)priceString {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.formatterBehavior = NSNumberFormatterBehavior10_4;
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
formatter.locale = self.priceLocale;
return [formatter stringFromNumber:self.price];
}
@end
@interface RNInAppPurchaseModule() <RCTBridgeModule, SKPaymentTransactionObserver, SKProductsRequestDelegate>
@end
@implementation RNInAppPurchaseModule
{
NSArray *products; // 所有可卖商品
NSMutableDictionary *_callbacks; // 回调,key是商品id
RCTResponseSenderBlock _lostCallBack; // 丢单数据的重新监听回调
NSMutableDictionary *_myProductIds; // 业务服务器商品ID,key是商品id
}
- (instancetype)init
{
if ((self = [super init])) {
_callbacks = [[NSMutableDictionary alloc] init];
_myProductIds = [[NSMutableDictionary alloc] init];
}
return self;
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_MODULE()
/**
* 添加商品购买状态监听
* @params:
* callback 针对购买过程中,App意外退出的丢单数据的回调
*/
RCT_EXPORT_METHOD(addTransactionObserverWithCallback:(RCTResponseSenderBlock)callback) {
// 监听商品购买状态变化
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
_lostCallBack = callback;
}
/**
* 服务器验证成功,删除缓存的凭证
*/
RCT_EXPORT_METHOD(removePurchase:(NSDictionary *)purchase) {
NSMutableArray *iapUnverifyOrdersArray = [NSMutableArray array];
if ([[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders] != nil) {
[iapUnverifyOrdersArray addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders]];
}
for (NSDictionary *unverifyPurchase in iapUnverifyOrdersArray) {
if ([unverifyPurchase[@"transactionIdentifier"] isEqualToString:purchase[@"transactionIdentifier"]]) {
[iapUnverifyOrdersArray removeObject:unverifyPurchase];
}
}
[[NSUserDefaults standardUserDefaults] setObject:[iapUnverifyOrdersArray copy] forKey:kIapUnverifyOrders];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSDictionary *)constantsToExport
{
// 获取当前缓存的所有凭证
NSMutableArray *iapUnverifyOrdersArray = [NSMutableArray array];
if ([[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders] != nil) {
[iapUnverifyOrdersArray addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders]];
}
return @{ @"iapUnverifyOrdersArray": iapUnverifyOrdersArray };
}
/**
* 购买某个商品
* @params:
* productIdentifier: 商品id
* callback: 回调,返回
*/
RCT_EXPORT_METHOD(purchaseProduct:(NSString *)productIdentifier
myProductId:(NSString *)myProductId
callback:(RCTResponseSenderBlock)callback)
{
SKProduct *product;
for(SKProduct *p in products)
{
if([productIdentifier isEqualToString:p.productIdentifier]) {
product = p;
break;
}
}
if(product) {
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
_callbacks[RCTKeyForInstance(payment.productIdentifier)] = callback;
_myProductIds[RCTKeyForInstance(payment.productIdentifier)] = myProductId;
} else {
callback(@[@"无效商品"]);
}
}
/**
* 恢复购买
*/
RCT_EXPORT_METHOD(restorePurchases:(RCTResponseSenderBlock)callback)
{
NSString *restoreRequest = @"restoreRequest";
_callbacks[RCTKeyForInstance(restoreRequest)] = callback;
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
/**
* 加载所有可卖的商品
*/
RCT_EXPORT_METHOD(loadProducts:(NSArray *)productIdentifiers
callback:(RCTResponseSenderBlock)callback)
{
if([SKPaymentQueue canMakePayments]){
SKProductsRequest *productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]];
productsRequest.delegate = self;
_callbacks[RCTKeyForInstance(productsRequest)] = callback;
[productsRequest start];
} else {
callback(@[@"not_available"]);
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
// 购买失败
case SKPaymentTransactionStateFailed: {
NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);
RCTResponseSenderBlock callback = _callbacks[key];
if (callback) {
if(transaction.error.code != SKErrorPaymentCancelled){
NSLog(@"购买失败");
callback(@[@"购买失败"]);
} else {
NSLog(@"购买取消");
callback(@[@"购买取消"]);
}
[_callbacks removeObjectForKey:key];
} else {
RCTLogWarn(@"No callback registered for transaction with state failed.");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
// 购买成功
case SKPaymentTransactionStatePurchased: {
NSLog(@"购买成功");
NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);
RCTResponseSenderBlock callback = _callbacks[key];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if (callback) {
// 购买成功,获取凭证
[self buyAppleStoreProductSucceedWithPaymentTransactionp:transaction callback:callback];
} else if (_lostCallBack) {
// 购买过程中出现意外App推出,下次启动App时的处理
// 购买成功,获取凭证
[self buyAppleStoreProductSucceedWithPaymentTransactionp:transaction callback:_lostCallBack];
} else {
RCTLogWarn(@"No callback registered for transaction with state purcahsed.");
}
break;
}
// 恢复购买
case SKPaymentTransactionStateRestored:{
NSLog(@"恢复购买成功");
NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);
RCTResponseSenderBlock callback = _callbacks[key];
if (callback) {
callback(@[@"恢复购买成功"]);
[_callbacks removeObjectForKey:key];
} else {
RCTLogWarn(@"No callback registered for transaction with state failed.");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
// 正在购买
case SKPaymentTransactionStatePurchasing:
NSLog(@"正在购买");
break;
// 交易还在队列里面,但最终状态还没有决定
case SKPaymentTransactionStateDeferred:
NSLog(@"推迟");
break;
default:
break;
}
}
}
// 苹果内购支付成功,获取凭证
- (void)buyAppleStoreProductSucceedWithPaymentTransactionp:(SKPaymentTransaction *)transaction callback:(RCTResponseSenderBlock)callback {
NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);
NSString *transactionReceiptString= nil;
// 验证凭据,获取到苹果返回的交易凭据
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURLRequest * appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];
NSError *error = nil;
NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequest returningResponse:nil error:&error];
if (!receiptData) {
callback(@[@"获取交易凭证失败"]);
} else {
transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);
NSString *myProductId = _myProductIds[key];
NSDictionary *purchase = @{
@"transactionIdentifier": transaction.transactionIdentifier,
@"productIdentifier": transaction.payment.productIdentifier,
@"receiptData": transactionReceiptString,
@"myProductId": myProductId
};
// 将凭证缓存,后台验证结束后再删除
NSMutableArray *iapUnverifyOrdersArray = [NSMutableArray array];
if ([[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders] != nil) {
[iapUnverifyOrdersArray addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders]];
}
[iapUnverifyOrdersArray addObject:purchase];
[[NSUserDefaults standardUserDefaults] setObject:[iapUnverifyOrdersArray copy] forKey:kIapUnverifyOrders];
[[NSUserDefaults standardUserDefaults] synchronize];
callback(@[[NSNull null], purchase]);
[_callbacks removeObjectForKey:key];
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue
restoreCompletedTransactionsFailedWithError:(NSError *)error
{
NSString *key = RCTKeyForInstance(@"restoreRequest");
RCTResponseSenderBlock callback = _callbacks[key];
if (callback) {
callback(@[@"恢复购买失败"]);
[_callbacks removeObjectForKey:key];
} else {
RCTLogWarn(@"No callback registered for restore product request.");
}
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSString *key = RCTKeyForInstance(@"restoreRequest");
RCTResponseSenderBlock callback = _callbacks[key];
if (callback) {
NSMutableArray *productsArrayForJS = [NSMutableArray array];
for(SKPaymentTransaction *transaction in queue.transactions){
if(transaction.transactionState == SKPaymentTransactionStateRestored) {
[productsArrayForJS addObject:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
callback(@[[NSNull null], productsArrayForJS]);
[_callbacks removeObjectForKey:key];
} else {
RCTLogWarn(@"No callback registered for restore product request.");
}
}
// 所有可卖商品回调
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
NSString *key = RCTKeyForInstance(request);
RCTResponseSenderBlock callback = _callbacks[key];
if (callback) {
products = [NSMutableArray arrayWithArray:response.products];
NSMutableArray *productsArrayForJS = [NSMutableArray array];
for(SKProduct *item in response.products) {
NSDictionary *product = @{
@"identifier": item.productIdentifier,
@"priceString": item.priceString,
@"downloadable": item.downloadable ? @"true" : @"false" ,
@"description": item.localizedDescription ? item.localizedDescription : @"商品描述",
@"title": item.localizedTitle ? item.localizedTitle : @"商品名称",
};
[productsArrayForJS addObject:product];
}
callback(@[[NSNull null], productsArrayForJS]);
[_callbacks removeObjectForKey:key];
} else {
RCTLogWarn(@"No callback registered for load product request.");
}
}
- (void)dealloc
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
#pragma mark Private
static NSString *RCTKeyForInstance(id instance)
{
return [NSString stringWithFormat:@"%p", instance];
}
@end