question

Niall Muldoon avatar image
Niall Muldoon asked

Restoring Purchases via PlayFab's Receipt Verification Does Not Work - Apple's App Receipt (iOS7+)

Hey @Citrus Yan, @Andy, @Brendan, @SethDu (tagging you all as you've all been active on these threads over the years),

Sorry to bring this issue back from the dead, but it's important and I don't think it's been resolved yet:

Threads I could find where this issue has been reported over the years:

While the best practice is of course to try and restore the user's PlayFab account, Apple requires that purchases can be restored so this is an important issue that needs to be addressed.

Are you aware of the longstanding (5 years+ now!) Apple bug where the transaction_id and original_transaction_id are always the same? This issue exists for the iOS7+ "app receipt" format rather than the older and deprecated iOS6 "transactionReceipts".

It's been discussed on numerous Apple developer forum posts, but this one probably has the most info. I've added bold type to highlight what I feel are important points:

"In the earlier receipts the transaction_id was, in fact, the transactionIdentifier. It was unique for all transactions and could be used to detect copied receipts and to differentiate repurchase-for-free and restore transactions from original paid purchases. It was included in the receipt. Now that transaction.transactionReceipt is deprecated we must use the new receipts. The transaction_id in post iOS6 receipts is now the same as original_transaction_id. There is no separate field for the transactionIdentifier for the latest transaction, just for each idividual IAP - and they all reflect the original purchase transaction. This means transaction_id can no longer be used to identifiy a copied receipt nor can it be used to tell whether the user has restored a transaction or repurchased an item for free. What you can do is detect the value of "receipt_creation_date" aka "creation_date" and compare that to the value for "original_purchase_date". If they are within a few seconds of each other then the purchase is a new paid purchase not a restore and not a repurchase-for-free. Also, if "creation_date" is close to [[NSDate alloc] init] then the receipt is not copied - you have to get the NSDate format correct."

This post from the same thread recommends:


"here is a solution that is just as good as before. Use the combination of transaction_id and creation_date in your server to identify a receipt that is a duplicate of another receipt that you have received. Reject any duplicates.

This does not protect you from someone doing a restore and grabbing the receipt before it is sent to your server and using that receipt to allow someone steal the IAP. But that scam could also have been used even when the receipt had unique transaction_id's.

If you want full protection then either decode the receipt yourself relying on the uniqueness of the device's identifierForVendor or check to be sure that the creation_date comes very shortly after the date of the actual purchaseRequest (except for an ask-to-buy purchase - so it's not fool-proof)."

I think "creation_date" refers to "receipt_creation_date" that exists outside of the in_app array but inside the secure Base64/ANS.1 string that must be decoded using the Apple Cert / ReceiptVerification endpoint.

Can you shed any light of what you are comparing in the PlayFab receipt validation code, please? If you are just looking at the transactionIDs from the in_app array then I think that will be the cause of all these "Receipt already used." errors that have been reported over the years!

Thanks for reading, I know it's a long one!

All the best,

Niall

In-Game EconomyPlayer Inventorysupport
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

1 Answer

·
JayZuo avatar image
JayZuo answered

PlayFab is not only looking at transaction_id but also purchase_date is used. I'm not familiar with Restore Transactions, not sure if purchase_date will be changed after restoring. If it's not, then you will also get "Receipt already used." error.

Besides, as Brendan answered here, a safer route would be to only sell virtual currencies (a consumable good) in the iTunes store, and then use that VC to make purchases of digital goods via your game's Catalog (using PurchaseItem). And, of course, the best route is always to make sure you can reliably get the player back to their original account on any device by incorporating alternate authentication systems, like Facebook, Twitch, and Google whenever possible.

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Niall Muldoon avatar image Niall Muldoon commented ·

Hi @Jay Zuo, @Brendan, @SethDu, @Citrus Yan, @Andy,

Selling everything via Virtual Currency is not an option.

  • Apple won't fix this - It's been an issue for at least 5 years, apps likely rely on this behaviour at this point.
  • Unity cannot fix this as they are just passing Apple's data.

The only viable option is for PlayFab to modify it's receipt verification logic to accommodate the reality of what is sent via an App Receipt.

Does no-one at PlayFab have any desire to fix this? It's really disappointing to see this issue ignored for so long :(

As an aside, the workaround kindly provided by Dreadwolf here that forces UnityIAP to return the legacy Transaction Receipt is not a viable solution anymore. Unity IAP is distributed via the Package Manager so edits do not persist between Unity sessions and cannot be stored on source control. It would also mean taking a step back and using a deprecated receipt format which could be pulled at any time :/

Come on guys, please invest some time into solving this! :)

All the best,

Niall

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.