12 Replies
      Latest reply on Dec 1, 2019 3:11 AM by digitalheaven
      digitalheaven Level 1 Level 1 (0 points)

        I'm selling consumable products and using Apple's servers for receipt validation via a call from the app to my server.

         

        Twice in past 24 hours, a situation has occurred where the transaction state has updated to SKPaymentTransactionStatePurchased causing the receipt to be sent for validation. However when the response comes back from Apple's server, "body.receipt.in_app" is empty and therefore the purchased product is not added to the user's account.

         

        One of these customers told me that the transaction was showing as "Pending" and a few hours later they received an invoice by email. However the app did not fire another purchased transaction state, so I had to add the purchase manually.

         

        So some questions:

        1. Was this unexpected behaviour and likely to be a rare and temporary IAP processing problem?

        2. If not, why is the state changing to SKPaymentTransactionStatePurchased if the payment didn't actually complete and is pending?

        3. Currently the app is calling finishTransaction when a response is received from my server, but this is NOT dependent on the presence of any inapp purchases in the decoded receipt. I'm thinking that this could be a problem. If the transaction state has not actually completed, is calling finishTransaction going to block any further state changes from being sent to the app, even when that state change is a critically important one when the payment HAS actually been processed and the purchased product should be added to the user's account?

        • Re: Handling pending consumable transaction
          KMT Level 9 Level 9 (15,215 points)

               >One of these customers told me that the transaction was showing as "Pending" - I had to add the purchase manually.

           

          You might want to read Rich's comment in this recent thread:

           

          https://forums.developer.apple.com/message/395609#395609

           

          ...sounds like you're being scammed.

            • Re: Handling pending consumable transaction
              digitalheaven Level 1 Level 1 (0 points)

              Not necessarily. Rich's comment is clearly about iOS because he talks about Settings app. This isn't iOS. It's macOS and the customer who I had communication with said that the purchase was showing as "pending" in iTunes. I have no reason not to believe him, since he then said that many hours later the payment went through, and he got an emailed invoice which he forwarded to me.

                • Re: Handling pending consumable transaction
                  KMT Level 9 Level 9 (15,215 points)

                  Either way, I hate to see the dev stick their neck out in these situations where all parties involved may be better off letting Apple take a shot at dealing with it, instead of the dev pulling levers manually based on conversations strictly w/users.

                   

                  Do you think the payment went thru striclty because of your actions, or was it just stuck in a queue?

              • Re: Handling pending consumable transaction
                PBK Level 7 Level 7 (3,345 points)

                did the customer send you a copy of the email invoice?.........why do you write that the state changed to 'purchased'?......does your app maintain an observer after a call to finishTransaction so it can receive any future transactions?   (Edit-  and when you call finishTransaction you must provide a transaction - what transaction did you provide?)

                  • Re: Handling pending consumable transaction
                    digitalheaven Level 1 Level 1 (0 points)

                    Yes the one customer I had communications with did send me the invoice that he received when the purchase was no longer "pending".

                     

                    Only the state changing to "purchased" causes the receipt to be sent to our server, that's why I know the state changed to "purchased" even though payment had not actually completed and customer said it was "pending".

                     

                    Yes the app observes the payment queue all the time.

                     

                    finishTransaction is passed the transaction that it received on that notification, in the usual way.

                      • Re: Handling pending consumable transaction
                        PBK Level 7 Level 7 (3,345 points)

                        So let me try to understand what you are saying happened:

                         

                        >  One of these customers told me that the transaction was showing as "Pending" and a few hours later they received an invoice by email. However the app did not fire another purchased transaction state, so I had to add the purchase manually.

                         

                        and then you wrote:

                         

                        >Only the state changing to "purchased" causes the receipt to be sent to our server, that's why I know the state changed to "purchased" even though payment had not actually completed and customer said it was "pending".

                         

                        So it seems that the pending transaction went through and the app received a call to updatedTransactions with a state of 'purchased' - is that correct????

                         

                         

                        And this is the problem:

                         

                        > ....the transaction state has updated to SKPaymentTransactionStatePurchased causing the receipt to be sent for validation.  However when the response comes back from Apple's server, "body.receipt.in_app" is empty

                         

                        This is what you would expect for future receipts if you had called finishTransaction on a receipt that had a consumable IAP reported in it.  I don't think you should call finishTransaction on a transaction when the state is SKPaymentTransactionStatePurchasing or SKPaymentTransactionStateDeferred.  Is it possible you call finishTransaction on a transaction that actually had the consumable IAP in it thereby wiping out the consumable IAP portion?

                          • Re: Handling pending consumable transaction
                            digitalheaven Level 1 Level 1 (0 points)

                            Not quite. There was only one change of state to "purchased" and that happened soon after the customer chose to purchase. But the point is that at that moment the purchase was not actually confirmed, it was only pending. It was 9 hours later when the transaction changed from pending to confirmed. That is the moment when it would be reasonable to expect the app payment queue to receive the notification rather than when it did.

                             

                            As per recommended practice, I'm calling finishTransaction only for purchased/failed/restored states (although restored isn't applicable anyway for consumables). I'm aware that the inapp data only contains "unfinished" purchases (which is why it is useful for adding the purchase to an online account) but I can see there is a timing risk if Apple's server doesn't respond instantly. Since the app finishes the transaction when it receives the 200 OK response from my server, that could possibly cause that purchase to be removed from the receipt by Apple before the decoded receipt is sent back to my server.

                             

                            Or am I misunderstanding things and the decoded receipt sent from Apple server to my server is simply the receipt uploaded from the app without any modification? In that case, that receipt will always be before the transaction has been finished, so there is no timing risk.

                             

                            However I feel that scenario isn't applicable here because surely the inapp data should have been empty anyway if the purchase wasn't going to be confirmed for another 9 hours? This is what I'm trying to get to the bottom of.

                              • Re: Handling pending consumable transaction
                                PBK Level 7 Level 7 (3,345 points)

                                Ignore whatever you mean by "pending" in "(b)ut the point is that at that moment the purchase was not actually confirmed, it was only pending."

                                 

                                Go solely with the calls to updatedTransactions.

                                 

                                If the state in updatedTransactions is 'purchased' then grab the receipt from  [[NSBundle mainBundle] appStoreReceiptURL] at that moment and examine that receipt.  "The rest are details."

                                  • Re: Handling pending consumable transaction
                                    digitalheaven Level 1 Level 1 (0 points)

                                    That's exactly what I am doing!

                                     

                                    I will raise a support request to confirm my suspicions but I think that what's going on here is that Apple is incorrectly setting the state to "purchased" when it should still be "purchasing" if the transaction has not actually completed. That feels like a bug.

                                     

                                    Even though I'm finishing the transaction on the first state change to "purchased", it doesn't really make any sense that the app should get another notification from "purchased" to "really purchased" does it.

                                      • Re: Handling pending consumable transaction
                                        PBK Level 7 Level 7 (3,345 points)

                                        So to make it very simple....

                                         

                                        You have a situation in which two users claim that a call was made to

                                               paymentQueue:updatedTransactions:(NSArray *)transactions

                                        and there was a transaction object in transactions that had the value

                                              SKPaymentTransactionStatePurchased for transaction.transactionState

                                        and at that moment, before calling finishTransaction, you grabbed the receiptData in this:

                                            NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];

                                            NSData *receiptData=[NSData dataWithContentsOfURL:receiptURL];

                                        and sent it to the Apple servers for decoding.  Apple responded with an empty in_app field.

                                         

                                        If this is what happened then either:

                                         

                                        1) you are being scammed by the user who is slipping a fake receipt into your app or otherwise manipulating your code so it appears like this is the case - but it's not

                                         

                                        or

                                         

                                        2) this is a bug in the system.