Skip to content

Commit

Permalink
Fix hyochan#581 finishTransaction called automatically
Browse files Browse the repository at this point in the history
  • Loading branch information
solidfox committed Jul 9, 2019
1 parent 49a0dfb commit da519dc
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 3 deletions.
27 changes: 25 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,17 @@ export const buyProduct = (sku) => {
/**
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
* @param {string} sku The product's sku/ID
* @param {boolean} andDangerouslyFinishTransactionAutomatically You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.
* @returns {Promise<string>}
*/
export const requestPurchase = (sku) => Platform.select({
export const requestPurchase = (sku, andDangerouslyFinishTransactionAutomatically) => Platform.select({
ios: async() => {
andDangerouslyFinishTransactionAutomatically = (andDangerouslyFinishTransactionAutomatically === undefined) ? true : andDangerouslyFinishTransactionAutomatically;
if (andDangerouslyFinishTransactionAutomatically) {
console.warn('You are dangerously allowing react-native-iap to finish your transaction automatically. You should set andDangerouslyFinishTransactionAutomatically to false when calling requestPurchase and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.');
}
checkNativeiOSAvailable();
return RNIapIos.buyProduct(sku);
return RNIapIos.buyProduct(sku, andDangerouslyFinishTransactionAutomatically);
},
android: async() => {
checkNativeAndroidAvailable();
Expand Down Expand Up @@ -245,6 +250,24 @@ export const requestPurchaseWithQuantityIOS = (sku, quantity) => Platform.select
android: async() => Promise.resolve(),
})();

/**
* Finish Transaction (iOS only)
* Similar to `consumePurchaseAndroid`. Tells StoreKit that you have delivered the purchase to the user and StoreKit can now let go of the transaction.
* Call this after you have persisted the purchased state to your server or local data in your app.
* `react-native-iap` will continue to deliver the purchase updated events with the successful purchase until you finish the transaction. **Even after the app has relaunched.**
* @param {string} transactionKey The transactionKey of the function that you would like to finish.
* @returns {null}
*/
export const finishTransactionIOS = (transactionKey) => {
Platform.select({
ios: async() => {
checkNativeiOSAvailable();
return RNIapIos.finishTransaction(transactionKey);
},
android: async() => Promise.resolve(),
})();
};

/**
* Clear Transaction (iOS only)
* Finish remaining transactions. Related to issue #257
Expand Down
23 changes: 22 additions & 1 deletion ios/RNIapIos.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ @interface RNIapIos() <IAPPromotionObserverDelegate, SKRequestDelegate> {
NSMutableDictionary *promisesByKey;
dispatch_queue_t myQueue;
BOOL hasListeners;
BOOL pendingTransactionWithAutoFinish;
void (^receiptBlock)(NSData*, NSError*); // Block to handle request the receipt async from delegate
}
@end
Expand All @@ -21,6 +22,7 @@ @implementation RNIapIos
-(instancetype)init {
if ((self = [super init])) {
promisesByKey = [NSMutableDictionary dictionary];
pendingTransactionWithAutoFinish = false;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[IAPPromotionObserver sharedObserver].delegate = self;
}
Expand Down Expand Up @@ -129,8 +131,10 @@ - (BOOL)shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)produ
}

RCT_EXPORT_METHOD(buyProduct:(NSString*)sku
andDangerouslyFinishTransactionAutomatically:(BOOL)finishAutomatically
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
pendingTransactionWithAutoFinish = finishAutomatically;
SKProduct *product;
for (SKProduct *p in validProducts) {
if([sku isEqualToString:p.productIdentifier]) {
Expand Down Expand Up @@ -223,6 +227,7 @@ - (BOOL)shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)produ
}
}

// The following buyProductWithoutAutoConfirm seems to be completely unused and identical to buyProduct. It's existance is confusing so could we remove it?
RCT_EXPORT_METHOD(buyProductWithoutAutoConfirm:(NSString*)sku
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
Expand Down Expand Up @@ -294,6 +299,10 @@ - (BOOL)shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)produ
}];
}

RCT_EXPORT_METHOD(finishTransaction:(NSString*)transactionKey) {
[self finishTransactionWithKey:transactionKey];
}

#pragma mark ===== StoreKit Delegate

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
Expand Down Expand Up @@ -385,6 +394,14 @@ -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)trans
}
}

-(void)finishTransactionWithKey:(NSString *)transactionKey {
for(SKPaymentTransaction *transaction in queue.transactions) {
if(RCTKeyForInstance(transaction) == transactionKey) {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
}

-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { //////// RESTORE
NSLog(@"\n\n\n paymentQueueRestoreCompletedTransactionsFinished \n\n.");
NSMutableArray* items = [NSMutableArray arrayWithCapacity:queue.transactions.count];
Expand All @@ -411,7 +428,10 @@ -(void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWi
}

-(void)purchaseProcess:(SKPaymentTransaction *)transaction {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if (pendingTransactionWithAutoFinish) {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
pendingTransactionWithAutoFinish = false;
}
[self getPurchaseData:transaction withBlock:^(NSDictionary *purchase) {
[self resolvePromisesForKey:RCTKeyForInstance(transaction.payment.productIdentifier) value:purchase];

Expand Down Expand Up @@ -648,6 +668,7 @@ - (void) getPurchaseData:(SKPaymentTransaction *)transaction withBlock:(void (^)
}
else {
NSMutableDictionary *purchase = [NSMutableDictionary dictionaryWithObjectsAndKeys:
RCTKeyForInstance(transaction), @"transactionKey",
@(transaction.transactionDate.timeIntervalSince1970 * 1000), @"transactionDate",
transaction.transactionIdentifier, @"transactionId",
transaction.payment.productIdentifier, @"productId",
Expand Down

0 comments on commit da519dc

Please sign in to comment.