My organization validates receipts of purchases of our product from the App store using the verify receipt endpoint. Our logs indicate that beginning April 1, 2020, roughly 400 of these verifications failed with status code 503 being returned. This has resulted in these customers not appearing in our database as users of our product. How can I "synchronise" these "missed" users with our database? Ideally we could reverify all those "missed" receipts. Is there something these customers can do to re-send their receipt to our servers so that our servers can call the verify endpoint again?
Related
Out of Apple's status codes for an in-app purchase (in Table 2-1 here), some should obviously be tried again, and some not and should be considered to say that the receipt in invalid.
But what about "21003 - The receipt could not be authenticated."? Is it similar to 21010 or to 21005? I.e. should this be sent again later or considered an invalid purchase?
From my experience, the "21003 - The receipt could not be authenticated." status is related to the App-Specific Shared Secret.
When you validate receipts with the AppStore, the App-Specific Shared Secret is used to set the value of the password field in the JSON request that you sent to the AppStore validation endpoint. (See Validating Receipts with the App Store documentation)
Keep in mind that according to Apple's documentation, this apps-specific shared secret is only necessary when validating receipt for apps that use auto-renewable subscriptions. However, in my experience, it might be worth setting it for any app receipt validation, just to avoid the 21003 error.
I am attempting to validate a receipt in the sandbox environment (https://sandbox.itunes.apple.com/verifyReceipt). The response looks successful and it is showing me the LatestReceiptInfo and any InApp transactions.
The part that is confusing me is that I am able to do it with an invalid secret as well. I am even able to verify with a receipt that was generated from a very different app. It appears as if validation is ignoring the shared secret entirely.
I would expect a status code of 21003 for the valid receipt that wasn't created by my app. I would also expect a 21004 for the invalid secret. In both cases I get a status code of 0 and the receipt is available.
There is something that I am not understanding about how verification works. Is this expected behavior for the sandbox?
Below is the response body that i am sending to verify.
{
"receipt-data": "MyReceiptData",
"password": "FakePassword",
"exclude-old-transactions": true
}
If your purchase item isn't an subscription. then apple will ignore the password.
Only verify the password when it's an subscription.
we have an application that utilizes auto renewable subscriptions in android.
The users are going subscribing through the app normally and the receipt is sent for our backend to be validated using the IAP Google API.
until this point everything is fine, but we recently discovred a loophole in our system, some users are subscribe and cancel their subscriptions and re-enable the auto subscription, this way google will issues a new receipt that is sent to our backend and we are giving them another year, this is easily solved by granting the only the duration given in the receipt
but then users started using the same google account with different accounts for our apps and they give everyone free subscriptions by the generated receipt
I read the API in here and I can't find any field that tells us that this receipt is only a re-activation not a new subscription
https://developers.google.com/android-publisher/api-ref/purchases/subscriptions
when we send a request for google api to get the info this is what we are getting:
status_from_google_play
{
"autoRenewing": true,
"cancelReason": null,
"countryCode": "SA",
"developerPayload": "",
"expiryTimeMillis": "1534073485784",
"kind": "androidpublisher#subscriptionPurchase",
"linkedPurchaseToken": "sometoken",
"orderId": "GPA.xxxx-xxxx-xxxx-xxxxx",
"paymentState": 1,
"priceAmountMicros": "290000",
"priceCurrencyCode": "SAR",
"purchaseType": 0,
"startTimeMillis": "1534071687580",
"userCancellationTimeMillis": null
}
There is no field that gives me this piece of information
am I missing something? is there another way to validate this?
Turns out the linkedPurchaseToken field can be used for this purpose
if the linked token is not empty it means it's a re-activation only or an upgrade/downgrade for the subscription.
It's not a direct usage as you can see but it's how you know.
I currently have a table in a mysql database with all the apn tokens of the users of my iOS app.
Every 24 hours my server runs a script that sends a notification to each user in the table.
I am sure some users are no longer active/have deleted the app and I was wondering if there is a way to identify them/remove them from this table?
The notification involves making a call to a weather api service (which is not free) so I am trying to avoid making unnecessary calls!
The Apple doc says if you get a
400
http code with
BadDeviceToken
as reason then
The specified device token is invalid. Verify that the request
contains a valid token and that the token matches the environment.
I check every response from the APN and invalidate/delete bad tokens from my database.
But you still need to do your weather request one more time for each device until its deleted from your DB.
You could send a silent notification before your request to check if the device token is valid, but that would be two notifications for one.
I am creating the backend for an iOSapp that has inapp purchase products in them.
The backend does the content serving stuff and validations of receipts and what not.
What bothers me is that there is no way to be sure that the product identifier entered in the backend is a valid one.
I know there is an API that can be called from the iOS to get a list of product identifiers for a specific app(was it company?), is there anyway to obtain this information from the backend so true validation of the entered identifier can be done?
Thanks in advance.
Once a user makes a valid purchase, you receive an encoded purchase receipt. To check if the purchase is valid, you need to send this receipt to Apple's validation servers. You'll receive a response indicating if the purchase was valid, and if it's valid - details about the purchase (purchase time, product ID, transaction ID, etc...).
I used the example code here and it was pretty easy and straight forward. Use the sandbox url as an endpoint while testing (including with TestFlight), and the production URL once the app is in the app store.
BTW, I would recommend generating a shared secret for your IAP (you can do it in iTunes Connect), and including it in your validation requests under a field called password:
private function encodeRequest() {
return json_encode(array(
'receipt-data' => $this->getReceipt(),
'password' => $sharedSecret));
}