0

I am implementing iOS 7+ in app purchases (consumables, like gold coins) with server validation. The workflow is the standard one:

  1. Make purchase:

    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    SKPayment* payment = [SKPayment paymentWithProduct:productToBuy];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
    
  2. Listen for the purchase success event:

    -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
    {
        for (SKPaymentTransaction *transaction in transactions)
        {
            if (transaction.transactionState == SKPaymentTransactionStatePurchased || transaction.transactionState == SKPaymentTransactionStateRestored)
            {
                NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
    
                if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptUrl path]])
                {
                    NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
                    NSString *receiptStr = [receiptData base64EncodedStringWithOptions:0];
                    // send receipt to our server for validation
                    // MY QUESTION IS WHAT TO DO ON NEXT STARTUP IF CANNOT CONNECT TO OUR SERVER AT THIS POINT
                    ...
    
  3. At this point we send the purchase receipt to our server. If the server finds it valid we consume the purchase:

    for (SKPaymentTransaction* transaction in transactions)
    {
        if ([[transaction transactionIdentifier] isEqualToString:transactionId])
        {
            currentTransaction = transaction;
            break;
        }
    }
    
    if (([currentTransaction transactionState] == SKPaymentTransactionStatePurchased || [currentTransaction transactionState] == SKPaymentTransactionStateRestored))
    {
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        [[SKPaymentQueue defaultQueue] finishTransaction:currentTransaction];
        ...
    

My problem is what should I do if our server is down/user's phone loses signal at the server validation (at (3))? In this case the receipt will never will validated thus the user will have a purchase in a stuck state. (Also in my tests I see my app asking to log in into iTunes in this case; but logging in has no effect.)

I tried to add an observer to the payment queue at every startup to check for incomplete transactions, but this asks the user to log in to iTunes, thus its not a good solution.

sydd
  • 1,824
  • 2
  • 30
  • 54
  • Can't you just have the client app contact the server in your control to see if there are any pending receipts to be validated? – ArtOfWarfare Nov 16 '15 at 17:50
  • @ArtOfWarfare The problem is that the server might not know if there is a pending purchase. For example the user successfully makes a purchase but the server is down. – sydd Nov 16 '15 at 17:52
  • Okay... so your scenario would be the client app sends a request to Apple's server, but the client app never hears back from Apple's server because it loses its connection? That seems like an incredibly unlikely scenario. Can't you just store in the client app that you were waiting for a response from Apple's servers? If on launch it's discovered that it was waiting, it can pop up a message to the user explaining the situation and then pop up asking that they log in to iTunes. – ArtOfWarfare Nov 16 '15 at 17:59
  • No. as I wrote I am implementing server validation for receipts. The flow is: 1. User makes purchase. 2. I sent the purchase receipt returned by Apple to out server to validate it. 3. If the server returns that the purchase is valid I consume the purchase. My question is what should I do if (3) does not happen for some reason (for example our server is down). – sydd Nov 16 '15 at 18:10
  • Then store the receipt until your server is back online and able to validate it? – ArtOfWarfare Nov 16 '15 at 18:14
  • Yeah, thats one solution... but since this is a common problem I've thought that Apple would have implemented something for this case.. – sydd Nov 16 '15 at 18:19
  • No... having all of your servers down all at the same time isn't particularly common. And storing the receipt for later is a rather easy thing to do should that ever happen. – ArtOfWarfare Nov 16 '15 at 18:28
  • Storing locally does not solve the problem if the user deletes + reinstalls the app. – sydd Nov 16 '15 at 18:33
  • True. Apple recommends a "Restore Purchases" button that requires an iTunes login and queries their server for that scenario. – ArtOfWarfare Nov 16 '15 at 19:17
  • Restore purchases is a different thing - its for purchases that are permanent, not consumable. (in this case its OK to ask the user to login once after reinstall) My question is about consumable purchases. – sydd Nov 17 '15 at 10:44
  • Okay, so the scenario you're worried about is the user purchases a consumable in your app, your server is down, then they uninstall your app, and then they reinstall your app. I would bet money that this will never happen. Something you could do, though, is before you contact the iTunes Store, contact your own server, just to verify it's up. If your server is down, just tell the user the server is down and to try again later. Now we have the even less likely scenario that in a matter of seconds, your server goes from up to down. Nothing you can do. Just focus your efforts on keeping it up. – ArtOfWarfare Nov 17 '15 at 13:03
  • @sydd take a look at [this](http://stackoverflow.com/a/34708022/2521214) (Have re-asked your last deleted question). – Spektre Jan 10 '16 at 18:36

0 Answers0