Microsoft Azure PlayFab logo
    • Multiplayer
    • LiveOps
    • Data & Analytics
    • Add-ons
    • For Any Role

      • Engineer
      • Designer
      • Executive
      • Marketer
    • For Any Stage

      • Build
      • Improve
      • Grow
    • For Any Size

      • Solo
      • Indie
      • AAA
  • Runs on PlayFab
  • Pricing
    • Blog
    • Forums
    • Contact us
  • Sign up
  • Sign in
  • Ask a question
  • Spaces
    • PlayStream
    • Feature Requests
    • Add-on Marketplace
    • Bugs
    • API and SDK Questions
    • General Discussion
    • LiveOps
    • Topics
    • Questions
    • Articles
    • Ideas
    • Users
    • Badges
  • Home /
  • Bugs /
avatar image
Question by Niall · Jul 23, 2021 at 08:07 AM · In-Game EconomyPlayer Inventorysupport

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:

  • https://community.playfab.com/questions/27917/ios-restore-purchase-receipt-using-unity-iap.html
  • https://community.playfab.com/questions/20664/how-to-restore-non-consumable-items-properly-using.html
  • https://community.playfab.com/questions/9053/is-there-any-way-to-restore-purchases-without-usin.html
  • https://community.playfab.com/questions/5815/receipt-validation-iosandroid-after-restore-purcha.html

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

Comment

People who like this

0 Show 0
10 |1200 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
Best Answer

Answer by Jay Zuo · Jul 26, 2021 at 09:28 AM

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.

Comment

People who like this

0 Show 1 · Share
10 |1200 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Niall · Sep 27, 2021 at 07:39 PM 0
Share

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

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Navigation

Spaces
  • General Discussion
  • API and SDK Questions
  • Feature Requests
  • PlayStream
  • Bugs
  • Add-on Marketplace
  • LiveOps
  • Follow this Question

    Answers Answers and Comments

    5 People are following this question.

    avatar image avatar image avatar image avatar image avatar image

    Related Questions

    Unable to fetch player virtual currency? 1 Answer

    Steam microtransactions bug. 0 Answers

    Noticed Odd Inventory behaviour 1 Answer

    Item provides not happening even after using Playfab payment API 0 Answers

    Cannot add Items to a Bundle in Playfab. 2 Answers

    PlayFab

    • Multiplayer
    • LiveOps
    • Data & Analytics
    • Runs on PlayFab
    • Pricing

    Solutions

    • For Any Role

      • Engineer
      • Designer
      • Executive
      • Marketer
    • For Any Stage

      • Build
      • Improve
      • Grow
    • For Any Size

      • Solo
      • Indie
      • AAA

    Engineers

    • Documentation
    • Quickstarts
    • API Reference
    • SDKs
    • Usage Limits

    Resources

    • Forums
    • Contact us
    • Blog
    • Service Health
    • Terms of Service
    • Attribution

    Follow us

    • Facebook
    • Twitter
    • LinkedIn
    • YouTube
    • Sitemap
    • Contact Microsoft
    • Privacy & cookies
    • Terms of use
    • Trademarks
    • Safety & eco
    • About our ads
    • © Microsoft 2020
    • Anonymous
    • Sign in
    • Create
    • Ask a question
    • Create an article
    • Post an idea
    • Spaces
    • PlayStream
    • Feature Requests
    • Add-on Marketplace
    • Bugs
    • API and SDK Questions
    • General Discussion
    • LiveOps
    • Explore
    • Topics
    • Questions
    • Articles
    • Ideas
    • Users
    • Badges