9 Replies
      Latest reply on Sep 27, 2019 7:16 AM by renat6
      ksimka Level 1 Level 1 (0 points)

        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.