Solved

Customer getting charged twice when error occurs on first try

  • 27 December 2021
  • 8 replies
  • 80 views

Badge +7

We have had 3 instances where a user has been charged twice for a Consumable Product. We have noticed that this happens when the user receives an error (example of error response below) from the API method in React Native await Purchases.purchaseProduct(productId, null, Purchases.PURCHASE_TYPE.INAPP). Below is a snippet of how we use the API method in a try/catch block.

 

try {
setIapLoading(true)
await Purchases.setAttributes(userAttributes)
log.info('Set user attributed', userAttributes)
await Purchases.purchaseProduct(productId, null, Purchases.PURCHASE_TYPE.INAPP)
log.info('Purchase successful')
navigation.navigate(ROUTE_SIGN_UP)
} catch (e: any) {
if (e.code !== '1') {
log.error('IAP Error for Product: ' + productId, JSON.stringify(e))
setIsOpen(true)
}
}

 

Error Response:

{"code":"2","message":"There was a problem with the App Store.","domain":"RCPurchasesErrorDomain","userInfo":{"readable_error_code":"STORE_PROBLEM","readableErrorCode":"STORE_PROBLEM","NSLo…

 

When the above error occurs we catch the error in a catch block and allow the user to retry purchasing. However, this is causing the product to be purchased twice. Below is a screenshot of the customer history in the RC dashboard.

 

 

icon

Best answer by ryan 7 January 2022, 16:16

View original

8 replies

Userlevel 5
Badge +9

Hey @Walter Holohan!

 

The Store Problem error from Apple could still mean that the customer was charged, and there was a problem somewhere else after the purchase - for example an issue validating the receipt. Apple would prevent the customer from re-purchasing subscription or non-consumable products, but with consumables the customer can purchase as many times as they like. Because of this, prompting the customer to try the purchase again may not be the best approach with these consumables. Maybe if the product is a non-subscription and the Store Problem error is returned you prompt the customer to Restore Purchases instead? If no purchases are returned after the Restore then you can prompt them to purchase? 

Hi Ryan, thanks for this! We saw one of your team answered in the below thread that we should direct users to close and reopen the app. We also thought about doing a poll for ~5 seconds to wait for a webhook from revenuecat confirming a successful purchase. Therefore which of the options do you recommend please?

 

  • Doing a poll for ~5 seconds to wait for a successful purchase webhook, incase the transaction was just slow
  • Polling the restore purchases API to see if a successful purchase is logged in there
  • Asking the user to close and reopen the app (and having to put in a lot of logic to immediately be checking the successful purchase webhook upon loading)

 

 

Badge +7

@ryan to give more context on the proposed webhook workaround below is the timeline on when we received the NON_RENEWING_PURCHASE webhook event

  • 17:20:16 - received STORE_PROBLEM error in app
  • 17:21:12 - received NON_RENEWING_PURCHASE webhook with a purchased at timestamp of 17:21:05

Would you be able to give further details into what is happening from a RC perspective when the STORE_PROBLEM is returned? Does the payment still get processed or in the above scenario did the RC SDK retry the payment. As you can see it took nearly 1 minute for the transaction webhook to be triggered. 

Badge +7

@ryan I also noticed that the React Native SDK has the following methods:

  1. static restoreTransactions(): Promise<PurchaserInfo>;

  2. static getPurchaserInfo(): Promise<PurchaserInfo>

  3. static syncPurchases(): Promise<void>
     

Which method would you suggest to use if we were to go down the route of trying to restore a user’s purchase if the first attempt failed on the STORE_PROBLEM error?

 

Badge +7

@ryan do you have recommendations for above?

Userlevel 5
Badge +9

Would you be able to give further details into what is happening from a RC perspective when the STORE_PROBLEM is returned?

Apple is returning an SKErrorCode 0, and RevenueCat is relaying that error to the developer. Often this happens on the device before any API call to RevenueCat.

 

Which method would you suggest to use if we were to go down the route of trying to restore a user’s purchase if the first attempt failed on the STORE_PROBLEM error?

You shouldn’t programmatically retry a STORE_PROBLEM error because you don’t know if the purchase completed or not. Personally I would let the user know an error occurred with the payment, and to try restoring purchases or repurchasing.

Setting up the PurchaserInfo listener could help here if the payment does complete shortly after if this was an error from the Apple SCA flow or something: https://docs.revenuecat.com/docs/getting-started#optional-listening-for-purchaser-info-updates

Userlevel 5
Badge +9

@Dom Maskell I would look into the purchase listener: https://docs.revenuecat.com/docs/getting-started#optional-listening-for-purchaser-info-updates. If a payment is completing after a STORE_PROBLEM error from the Apple SCA flow it will get picked up there. 

Userlevel 5
Badge +9

More info on the SCA flow here: https://www.revenuecat.com/blog/strong-customer-authentication

Reply