Implementing Non-renewable subscription in Hybrid app

I am using in-app purchase (non-renewable subscription) in our hybrid application which is developed using ionic framework using the in-app-purchase-2 plugin provided by them ionic in-app purchase.

How do we know if the payment got declined by the apple server ? Is there any response from apple server?

Replies

If the payment got declined, you shouldn't receive the approved event in your javascript application. An error event should be triggered.
@jchoelt Ok, thank you. And if I hadn't added any payment cards in my device and I'm initiating a purchase does it redirects automatically to my app store to add card details? When I checked it nothing happened that's why I'm asking.

Also, you've said that if there is error , the error event is triggered right? Even if the user cancels the payment, the same error event is triggered. How do we know that the payment is cancelled due to error in Apple's server or due to any issue in our card (like expired card or the bank experience server issue) ?
Any other updates please ?
In response to the question about the lack of credit card information when an in-app purchase is detected, I can't speak for how the ionic framework behaves, but I can describe what happens in the production environment. This is a production case only. I'm told that Xcode 12 and the new StoreKit test process provides test support for this complicated process, but my response below provides a description as to how a StoreKit app can handle this situation.

The interrupted purchase flow situation occurs whenever StoreKit determines that there is a problem with the user account making the purchase - the name stems from the fact that the application purchase process is interrupted and the application is backgrounded while the Settings app comes forward to either.
  1. have the user fix / update their credit card info

  2. agree to a new StoreKit Terms and Conditions.

Upon fixing the issue, the user is asked whether they want to continue with the purchase process. When they agree, StoreKit processes the purchase request, then will present a dialog that the purchase was successful.

While this is happening the iAP app is in the background, there is no notification to the app that the purchased was successful. So how is the app informed of the successful purchase. The notification process relies on the transactionObserver being and remaining active through the lifecycle of the app.

On the server side, when the interrupted transaction flow is detected, the server automatically queues a failed transaction event for the app and user. This occurs because the user might not fix the failure issue. When the user does fix the issue, then the successful transaction event is queued behind the failure event - again on the server - there is no notification to the app while it remains in the background.

In the normal situation, after the successful purchase, the user brings the app to the foreground. Assuming that the transactionObserver is active - it gets activated when the app is moved from the background to the foreground. The transactionObserver sends a query to StoreKit asking if there are any queued transactions to process - In the interrupted flow situation, the failed transaction is returned first. The updatedTransactions delegate method is called with the failed transaction indication. As per the StoreKit guide, the application must call finishTransaction on this failed transaction.

Next, the StoreKit returns the queued successful transaction to the app. The updatedTransactions delegate method is called with the successful transaction indication. After processing the successful transaction indication, the app calls finishTransaction. Until the finishTransaction is called, a transaction is considered “incomplete”.

Note that in the case of the interrupted purchase flow - a single call to addPayment can result in both a failed transaction event followed by a successful transaction event.

Observe that the user experience is different from that of the app. The user is taken to the App Store where they correct the situation - either by updating the credit card or agreeing to the new App Store Terms and Conditions. The user could choose not to update the credit case or agree to the T&C’s, and it’s clear to the user that the purchase will not complete. However, if the user corrects the situation, the user is again asked whether they want to pursue with the transaction. If the user agrees, the account is charged and StoreKit alerts the user to the successful transaction. Compare this with what the application sees - first the failed transaction followed by the successful transaction.

So what happens if after fixing the situation as described above, if the user does not return immediately to the app, and for some reason, iOS jettisons the app to make room for some other app. In this case, the failed and successful transaction remain queued on the StoreKit server. When the app is relaunched, and the addTransactionObserver is called in the didFinishLaunchingWithOptions delegate method as per the StoreKit Program Guide the queued transactions are processed. As the queued transactions have not been processed with the finishTransaction call, they remain incomplete and the restoreCompletedTransactions method will not restore them.

One could argue that the app could check the application receipt, however the app receipt is updated when the transactionObserver processes a successful transaction or when the SKReceiptRefreshRequest is issued.

Make sure to verify that your app calls addTransactionObserver in the didFinishLaunchingWithOptions delegate method.

Some additional notes
  1. To replicate the interrupted purchase flow situation, install the production app, then enter the Settings app and clear out the credit card info for the current user. One can also launch the iTunes app on a macOS system, enter the Account settings and set the credit card information to “none”. Now launch the in app purchase app and attempt a purchase

  2. the interrupted purchase flow process can be replicated in the development environment - using Xcode 12, however it's not clear to me that the behavior matches that in the production environment.

rich kubota - rkubota@apple.com
developer technical support CoreOS/Hardware/MFI
Post not yet marked as solved Up vote reply of rich Down vote reply of rich