Strange behavior was found for subscription upgrades/downgrades for Sandbox users.
Step 1: Select from the internal app UI productId.
Step 2: Get the product from StoreKit by Product.products(for: [productID])
Step 3: Purchase fetched product: product.purchase(options: [options])
Step 4: Analyze the received Product.PurchaseResult.
And what we have here:
Works perfectly on the first try. The correct and successful transaction was received.
But when tried to change the subscription, using the same flow, in Step 4 received a successful transaction with a different productID. Usually the previous subscription productID.
Can't get how it can be. Request purchase with one productID, but receive successful result with another.
Any ideas of what is going on would be appreciated.
Related
My problem is, the users cannot get the non-consumable item by redeeming the code.
I can confirm the non-consumable items can be bought correctly.
I used the app receipt to check whether the user owns it. I'll find through all the products to see if there's one the product id is matching.
I cannot find any useful docs to debug the redeeming process. I don't even know if there should be a callback when the app starts. Like the transactions are not completed.
How is redeeming handled behind the scene?
If the app is open after I redeemed a code, how should the app know the user 'bought' the item? How should I know when to send them the contents?
If the users try to buy the item they redeemed, they are told they can get it for free, and I see the purchase succeed callback. But then I check app receipt, the product still not exists. I got these logs from the users.
By the way I'm using in_app_purchase plugin from Flutter. And for checking the app receipt I'm using https://github.com/robotmedia/RMStore.
Products redeemed with a promo code will appear on the receipt just as a purchased product would - you won't be able to differentiate from the receipt a purchase vs promo code.
There isn't a callback on the device for promos specifically, if you refresh the receipt or the user restores transactions you should see the product in the receipt.
So I followed the official manual and implemented this:
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1
user pays in app
app gets receipt info from Apple
this receipt info is sent to my server
my server verified receipt by calling Apple API and activates membership for client
The obvious problem is that 3. can fail. I have clients complaining they paid, they are sending me SS of the amount being deducted, but my server was never notified. And I have no way to find these users. Is there some CP where I can search by customer e-mail or transaction ID to check if this is Photoshopped screenshot or valid one?
Is there some API that can be called to list transactions by product and e-mail of client?
https://appstoreconnect.apple.com/ - Apple CP is useless for this
There is no method to correlate the customers details with your transaction details. Only Apple can do this.
My first suspicion is that you may have a logic problem in your purchasing process. If implemented correctly, a transient failure at step 3 doesn't matter.
You should:
Create your transaction queue observer a soon as your app starts. This will enable any pending transactions to be delivered to your observer
When you get a purchase transaction in your observer you verify with your server
Only once you have a response from your server that the purchase has been recorded successfully do you call finishTransaction.
This way if something goes wrong with your server or the app crashes the transaction is still pending in the queue.
If you are using auto-renewing and/or non-consumable IAP then I
strongly suggest you provide a "restore purchases" button in your
UI. This makes it simple for the user if something goes wrong or when
they move to a new device.
If you have users who claim that they did not get what they paid for then you can refer them to Apple App Store support who can refund the transaction.
If step three fails you can fall back to local verification and then let the user through for this session (or some number of sessions before you require it to succeed). Unfortunately local authentication is a pain in the ass because the receipt is encrypted. See this link for an example: https://github.com/andrewcbancroft/SwiftyLocalReceiptValidator . You can also report failures of step 3 to your analytics tool so you can see who is actually affected by this issue (obviously this only works if the analytic eventually get an internet connection.
I have created Auto renewable products and testing in sandbox environment. My question is I have purchased one product, and If I try to purchase same product with same Apple account it is showing alert as already purchased as expected but I could not able to fetch that transaction details as it is executing failed state in updated transactions. How I can get that purchased details (atleast original receiptID) as we get in Initial transaction.
There is two way to get user purchased transaction details:
Refresh receipt
Restore completed transaction
To know more about difference between refreshing receipt & restore transaction in details, Please check:- SKReceiptRefreshRequest vs restoreCompletedTransactions
In Simply,
Refreshing the receipt asks the App Store for the latest copy of the receipt.
Restoring completed transactions creates a new transaction for every completed transaction the user made, essentially replaying history for your transaction queue observer.
Question:
How I can get that purchased details (atleast original receiptID) as we get in Initial transaction.
Answer: If you want to verify user with original receiptID every time each time than user SKReceiptRefreshRequest for validating user transaction.
Note: Anytime if you validating user with RestoreCompletedTransactions, Your transactionID will change.
Find list of parameter that change while restoreTransaction: https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1
You have to implement a mechanism of restoring the purchased products in your app.
For more info about what exactly is restoring purchased products:
Restore Purchased Products
To get to the implementation part, you can follow this tutorial:
In App Purchase tutorial
The later part of the tutorial will guide you through the implementaiton of restore purchase.
By validating Receipts With the App Store you can able to find which products identifiers are already purchased. There are Cocoapods libraries you can use to find out the purchased products.
SwiftyStoreKit
DHAppleReceiptParser
IAPValidation
I hope it will helps to you.
I am testing my In-App Purchase code. I am presented with the following dialogue in the Xcode console:
Transaction is initiated:
About to fetch the products
Checks if user can make payments:
User can make purchases and will fetch products from Apple Store now
Product(s) exist and are retrieved from Apple servers ...
Full Access To xxxxxx xxxxx xxxx (my product description)
Sending the Payment Request to Apple
Received Payment Transaction Response from Apple
And transaction is stuck here. NO response from Apple servers received.
I have read a lot of content that states sandbox accounts testing is often riddled with bugs.
Is this one of them?
I have played around a lot with sandbox testing accounts and have never been presented with this particular situation.
Is anyone able to shed any light on the situation for me?
Thank you in advance.
My IAP code works now on my test device.
What I did was:
Perform a soft reset of my device; and
Completed some app updates that had not completed under my 'normal' Apple ID.
The combination of the two steps resolved my inability to receive a Payment Transaction Response.
I've been implementing auto-renewing In App Purchases and, using the info found here, have had little trouble the purchasing, renewing, & restoring transaction flows.
The problem I am having is finding a way to test when a user renews (after, presumably, canceling some time in the past) from their account page in either iTunes or the App Store app (Detailed here). I'm assuming, since those exist outside of the sandbox and when you log into one of them using a sandbox account that account is invalidated as a test account, there is no way to actually test this use case so I'm just looking for more information on the expected behavior to try and account for it.
I know that keeping a copy of the receipt around to validate will give the latest receipt as part of the JSON payload, I'm more curious for information on how StoreKit will handle this renewal. Will paymentQueue: updatedTransaction: fire with a new SKPaymentTransaction as soon as I add a TransactionObserver or will it stay silent until calling restoreCompletedTransactions and then the new SKPaymentTransaction will be part of that?
Yes, you are correct; paymentQueue:updatedTransactions: will fire, just as if you were making the initial subscription purchase.
You can actually test this since renewals within the sandbox kick in at a much faster pace (up to six times a day) as explained here (scroll down to the bottom under "The Test Environment").
Also when you verify the transaction with Apple's verification service right after you made a test subscription purchase, look for the expires_date field in the response, it will tell you when the next renewal will be triggered.