"transaction_id" value is changed on 1 or 2 from the value of the same transaction

This sounds weird, I know, but let me tell you what we have about this issue for now.

Actually we have two different issues relative to "transaction_id". Note: all the following is about auto-renewable subscriptions for services which must be available both on our native mobile app and our web app.



1. Transaction id from a client not found in an app receipt.

a. user purchases an auto-renewable subscription;

b. client gets a new corresponding transaction and sends its id to the server alongside with an app receipt (base64 encoded);

c. server sends this app receipt to the Apple's verification server (buy.itunes.apple.com/verifyReceipt) in order to verify it and to get "latest_receipt_info";

d. server tries to find a transaction id from client in "latest_receipt_info" (matches it with "transaction_id" field), finds it and delivers a corresponding product for user.


The problem is that sometimes there is no given transaction id in "latest_receipt_info"! And no, we don't need to refresh the receipt, it's "latest_receipt_info" from Apple's server, remember? And the most interesting thing is that needed transaction is there but with different transaction id! More often these transaction ids differ on 1, less often - on 2.


Example (data is real):


Request from client:

- transactionId=240000321191999

- receipt=MIJfygYJKo...c3DXml2Q==


Server:

- transaction id was not found in app receipt


We're looking to logs:

- {"quantity":"1", "product_id":"***", "transaction_id":"240000321191998", "original_transaction_id":"240000237838384", "purchase_date":"2017-03-29 23:37:27 Etc/GMT", "purchase_date_ms":"1490830647000", "purchase_date_pst":"2017-03-29 16:37:27 America/Los_Angeles", "original_purchase_date":"2016-06-03 08:49:13 Etc/GMT", "original_purchase_date_ms":"1464943753000", "original_purchase_date_pst":"2016-06-03 01:49:13 America/Los_Angeles", "expires_date":"2017-04-05 23:37:27 Etc/GMT", "expires_date_ms":"1491435447000", "expires_date_pst":"2017-04-05 16:37:27 America/Los_Angeles", "web_order_line_item_id":"240000065809161", "is_trial_period":"false"}


Client gets 240000321191999, but on the server we only have 240000321191998.


Please let's not discuss here if using transaction id is a right way to process purchases. Now after WWDC 2017 we all are finally said to use original transaction id, but who guarantees there will not be the same issue?


We're observing this issue for 3-4 years, but earlier we found it only on test accounts and only on long receipts (with dozens of transactions). We were like "ah, it's a sandbox, who cares, move on". Now it occures in production, and this is a real problem.



2. "transaction_id" from "latest_receipt_info" differs from "transaction_id" for the same transaction in the same app receipt after some time.


This is the most mindblowing thing we found recently.


We have some legacy code for processing subscriptions renews. It takes all transactions from "latest_receipt_info" and tries to find those that are not in our DB yet (we store all processed transactions). If a transaction is not found in our table - it's considered new (despite "expires_date"). It's totally wrong, I know, but nevermind, we noticed sometimes after receipt processing user gets way more days of subscription than normally.


After some investigation it turns out that after the next request to Apple's verification server some transactions in "latest_receipt_info" changed their ids! And again, the difference is 1 or 2 like in the previous example.


Example, what it looks like:


- user purchases AR subscription, we deliver product and save app receipt;


1. {"quantity":"1", "product_id":"***", "transaction_id":"560000170967711", "original_transaction_id":"560000170967711", "purchase_date":"2017-02-15 13:25:53 Etc/GMT", "purchase_date_ms":"1487165153000", "purchase_date_pst":"2017-02-15 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-02-22 13:25:53 Etc/GMT", "expires_date_ms":"1487769953000", "expires_date_pst":"2017-02-22 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000027871292", "is_trial_period":"false"}


- near to "expires_date" we ask verification server if subscription has been prolonged; if so, we get "latest_receipt_info" with one more transaction, deliver product and go to sleep until the next "expires_date";


1. {"quantity":"1", "product_id":"***", "transaction_id":"560000170967711", "original_transaction_id":"560000170967711", "purchase_date":"2017-02-15 13:25:53 Etc/GMT", "purchase_date_ms":"1487165153000", "purchase_date_pst":"2017-02-15 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-02-22 13:25:53 Etc/GMT", "expires_date_ms":"1487769953000", "expires_date_pst":"2017-02-22 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000027871292", "is_trial_period":"false"}

2. {"quantity":"1", "product_id":"***", "transaction_id":"560000172091757", "original_transaction_id":"560000170967711", "purchase_date":"2017-02-22 13:25:53 Etc/GMT", "purchase_date_ms":"1487769953000", "purchase_date_pst":"2017-02-22 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-03-01 13:25:53 Etc/GMT", "expires_date_ms":"1488374753000", "expires_date_pst":"2017-03-01 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000027871293", "is_trial_period":"false"}


- this is repeated several times, and all is OK;


1. {"quantity":"1", "product_id":"***", "transaction_id":"560000170967711", "original_transaction_id":"560000170967711", "purchase_date":"2017-02-15 13:25:53 Etc/GMT", "purchase_date_ms":"1487165153000", "purchase_date_pst":"2017-02-15 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-02-22 13:25:53 Etc/GMT", "expires_date_ms":"1487769953000", "expires_date_pst":"2017-02-22 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000027871292", "is_trial_period":"false"}

2. {"quantity":"1", "product_id":"***", "transaction_id":"560000172091757", "original_transaction_id":"560000170967711", "purchase_date":"2017-02-22 13:25:53 Etc/GMT", "purchase_date_ms":"1487769953000", "purchase_date_pst":"2017-02-22 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-03-01 13:25:53 Etc/GMT", "expires_date_ms":"1488374753000", "expires_date_pst":"2017-03-01 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000027871293", "is_trial_period":"false"}


...


20. {"quantity":"1", "product_id":"***", "transaction_id":"560000194566860", "original_transaction_id":"560000170967711", "purchase_date":"2017-06-28 12:25:53 Etc/GMT", "purchase_date_ms":"1498652753000", "purchase_date_pst":"2017-06-28 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-07-05 12:25:53 Etc/GMT", "expires_date_ms":"1499257553000", "expires_date_pst":"2017-07-05 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000034328261", "is_trial_period":"false"}

21. {"quantity":"1", "product_id":"***", "transaction_id":"560000195977476", "original_transaction_id":"560000170967711", "purchase_date":"2017-07-05 12:25:53 Etc/GMT", "purchase_date_ms":"1499257553000", "purchase_date_pst":"2017-07-05 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-07-12 12:25:53 Etc/GMT", "expires_date_ms":"1499862353000", "expires_date_pst":"2017-07-12 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000034733824", "is_trial_period":"false"}

22. {"quantity":"1", "product_id":"***", "transaction_id":"560000197301507", "original_transaction_id":"560000170967711", "purchase_date":"2017-07-12 12:25:53 Etc/GMT", "purchase_date_ms":"1499862353000", "purchase_date_pst":"2017-07-12 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-07-19 12:25:53 Etc/GMT", "expires_date_ms":"1500467153000", "expires_date_pst":"2017-07-19 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000035181430", "is_trial_period":"false"}

23. {"quantity":"1", "product_id":"***", "transaction_id":"560000198640399", "original_transaction_id":"560000170967711", "purchase_date":"2017-07-19 12:25:53 Etc/GMT", "purchase_date_ms":"1500467153000", "purchase_date_pst":"2017-07-19 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-07-26 12:25:53 Etc/GMT", "expires_date_ms":"1501071953000", "expires_date_pst":"2017-07-26 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000035598540", "is_trial_period":"false"}


- until one day happens THIS


20. {"quantity":"1", "product_id":"***", "transaction_id":"560000194566860", "original_transaction_id":"560000170967711", "purchase_date":"2017-06-28 12:25:53 Etc/GMT", "purchase_date_ms":"1498652753000", "purchase_date_pst":"2017-06-28 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-07-05 12:25:53 Etc/GMT", "expires_date_ms":"1499257553000", "expires_date_pst":"2017-07-05 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000034328261", "is_trial_period":"false"}

21. {"quantity":"1", "product_id":"***", "transaction_id":"560000195977475", "original_transaction_id":"560000170967711", "purchase_date":"2017-07-05 12:25:53 Etc/GMT", "purchase_date_ms":"1499257553000", "purchase_date_pst":"2017-07-05 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-07-12 12:25:53 Etc/GMT", "expires_date_ms":"1499862353000", "expires_date_pst":"2017-07-12 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000034733824", "is_trial_period":"false"}

22. {"quantity":"1", "product_id":"***", "transaction_id":"560000197301507", "original_transaction_id":"560000170967711", "purchase_date":"2017-07-12 12:25:53 Etc/GMT", "purchase_date_ms":"1499862353000", "purchase_date_pst":"2017-07-12 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-07-19 12:25:53 Etc/GMT", "expires_date_ms":"1500467153000", "expires_date_pst":"2017-07-19 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000035181430", "is_trial_period":"false"}

23. {"quantity":"1", "product_id":"***", "transaction_id":"560000198640398", "original_transaction_id":"560000170967711", "purchase_date":"2017-07-19 12:25:53 Etc/GMT", "purchase_date_ms":"1500467153000", "purchase_date_pst":"2017-07-19 05:25:53 America/Los_Angeles", "original_purchase_date":"2017-02-15 13:25:56 Etc/GMT", "original_purchase_date_ms":"1487165156000", "original_purchase_date_pst":"2017-02-15 05:25:56 America/Los_Angeles", "expires_date":"2017-07-26 12:25:53 Etc/GMT", "expires_date_ms":"1501071953000", "expires_date_pst":"2017-07-26 05:25:53 America/Los_Angeles", "web_order_line_item_id":"560000035598540", "is_trial_period":"false"}


560000195977476 (21.) becomes 560000195977475, 560000198640399 (23.) becomes 560000198640398. Our (******) code thinks it's something new and delivers product again, for the second time per each transaction with changed id.


So, my question is - ***?


If "transaction_id" is something we can not rely on for any kind of job, why do we need it at all? Why it's in app receipts? If not - what's wrong with it? Or with us.

Replies

Looks like we're not alone — https://forums.developer.apple.com/thread/68944


Any reaction on this?

Today we've got a receipt with transaction that changed its id from 180000339396116 to 180000339396123 (diff is 7 !!! )


was (and we can still see this in "in_app" section)

{"quantity":"1", "product_id":"***", "transaction_id":"180000339396116", "original_transaction_id":"180000249186236", "purchase_date":"2017-08-24 10:57:58 Etc/GMT", "purchase_date_ms":"1503572278000", "purchase_date_pst":"2017-08-24 03:57:58 America/Los_Angeles", "original_purchase_date":"2016-08-17 11:44:52 Etc/GMT", "original_purchase_date_ms":"1471434292000", "original_purchase_date_pst":"2016-08-17 04:44:52 America/Los_Angeles", "expires_date":"2017-08-31 10:57:58 Etc/GMT", "expires_date_ms":"1504177078000", "expires_date_pst":"2017-08-31 03:57:58 America/Los_Angeles", "web_order_line_item_id":"180000071076493", "is_trial_period":"false"}


now (in the "latest_receipt_info")

{"quantity":"1", "product_id":"***", "transaction_id":"180000339396123", "original_transaction_id":"180000249186236", "purchase_date":"2017-08-24 10:57:58 Etc/GMT", "purchase_date_ms":"1503572278000", "purchase_date_pst":"2017-08-24 03:57:58 America/Los_Angeles", "original_purchase_date":"2016-08-17 11:44:52 Etc/GMT", "original_purchase_date_ms":"1471434292000", "original_purchase_date_pst":"2016-08-17 04:44:52 America/Los_Angeles", "expires_date":"2017-08-31 10:57:58 Etc/GMT", "expires_date_ms":"1504177078000", "expires_date_pst":"2017-08-31 03:57:58 America/Los_Angeles", "web_order_line_item_id":"180000071076493", "is_trial_period":"false"}

>why do we need it at all


The transaction identifier used to be useful to make sure that a single receipt is not being hacked, duplicated, installed in many devices and used to get multiple IAPs. This protective system fell apart when Apple used the same transaction id for an original purchase, a repurchase-for-free and a restoreCompletedTransaction.

> The transaction identifier used to be useful to make sure that a single receipt is not being hacked, duplicated, installed in many devices and used to get multiple IAPs. This protective system fell apart when Apple used the same transaction id for an original purchase, a repurchase-for-free and a restoreCompletedTransaction.


That's a good sentence to be put in a documentation. Will save many hours for some developers.

We also happen to have this issue, is there a solution to this problem ?


Regards

We are seeing this affecting us as well. We'll file a bug with Apple but wondering if anyone's figured anything out?


To us it seems like if there is a transaction_id for the same user for the same product within a range of +-10 we should somehow mark this as extra data on the first one we found... Seems annoying at best.

Some simple observations for those of you using transaction_id:


A purchase, refresh receipt, restoreCompletedTransactions and a repurchase-for-free (i.e. by the same device) will all have the same transaction_id so you can't use transaction_id to assure the receipt is not a hacked copy inserted into the device because you would be rejecting legit restoring.


The original_transaction_id is unique to the device and subscription

Hi! We found the same issue! Did you find a solution?

Hi! Did you find any better solution? Or the reason why this happens?

Hi! Did you find any better solution? Or the reason why this happens?
@ksimka Hi, We also happen to have this issue, did you find the solution? or what did Apple say?

thank you

so! one year later. any solutions or bug reports?

We have the same issue, did you find any better solution? Or the reason why this happens?