11

I want to know if and when the app receipt is automagically refreshed when an IAP auto-renew subscription auto renews. The documentation implies that the app receipt is updated when a purchase is made (renewal?) but I'm not seeing this behavior in the IAP sandbox:

Information about consumable products and non-renewing subscriptions is added to the receipt when they’re paid for and remains in the receipt until you finish the transaction. After you finish the transaction, this information is removed the next time the receipt is updated—for example, the next time the user makes a purchase.

Information about all other kinds of purchases is added to the receipt when they’re paid for and remains in the receipt indefinitely.

Furthermore, the docs state:

After a subscription is successfully renewed, Store Kit adds a transaction for the renewal to the transaction queue. Your app checks the transaction queue on launch and handles the renewal the same way as any other transaction. Note that if your app is already running when the subscription renews, the transaction observer is not called; your app finds out about the renewal the next time it’s launched.

To me this implies that I can monitor the SKPaymentQueue for completed transactions then check the app receipt to find record of them. But I'm not seeing this in practice in the IAP sandbox. In the IAP sandbox I have an auto-renew subscription which is auto-renewing (6 times per user/purchase, normal sandbox behavior) but to discover the renewal I need to manually refresh the app receipt.

Assuming this all does work the way I'm expecting it to, are there best practices for testing in the IAP sandbox to trigger this behavior?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
TomSwift
  • 39,369
  • 12
  • 121
  • 149
  • Are you directly validating the receipt or relying on 3rd-party (opaque) code? I have a couple of apps with auto renewing subs and don't have any issues with the items showing in the receipt. Of course, I do validate the receipt at each app launch... – Brad Brighton May 27 '15 at 20:14
  • I'm decoding the receipt client side using RMStore and validating it server-side with Apple. That all seems to work. – TomSwift May 27 '15 at 20:20
  • Ok, cool. I don't know the details of RMStore; are you walking the entire list of entries or does it do it for you? You should have a subscription entry for each renewal. – Brad Brighton May 27 '15 at 20:22
  • Yes, and it finds the renewal transactions once the receipt has been manually refreshed. Just not before. – TomSwift May 27 '15 at 20:36
  • Speculating: Is it possible that the app isn't terminating between your renewal sessions? From the docs, "your app finds out about the renewal the next time it’s launched"... if you're just coming back to foreground, I'm pretty sure that doesn't count. (FWIW, I'm not _just_ guessing here, I'm trying to see if I can identify what's different in your case from mine. Strictly speaking, I refresh whenever the validation fails, using code basically from https://github.com/rmaddy/VerifyStoreReceiptiOS - ohai @rmaddy) – Brad Brighton May 27 '15 at 20:42
  • Definitely terminating and restarting. That said I'm keen to understand the definition of "launch" in this context. – TomSwift May 27 '15 at 20:44
  • @TomSwift I have a query what will happen to the main receipt when he cancels and activates the subscription again? Whether new receipt will be generated or what will happen? – Yohan Sep 19 '16 at 07:04

1 Answers1

6

As a side note, the documentation is inconsistent on the types of purchases and their persistence in the receipt -- see my answer to this question.

The receipt is updated on the server-side when the auto-renew ticks over -- you can confirm this by checking with a call to the server-side validateReceipt method.

UPDATE: Having seen that you're using RMStore, I mocked something up so that I could look at the behavior.

It looks to me like the client-side receipt is being updated. My scenario: one month AR subscription (so 5 minute renew in the sandbox). I put some diagnostic code in viewDidLoad:

RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];
if (receipt != nil) {
    NSDateFormatter* localDateTime = [[NSDateFormatter alloc] init];
    [localDateTime setTimeZone:[NSTimeZone timeZoneWithName:@"PST"]];
    [localDateTime setDateFormat:@"yyyy.MM.dd HH:mm:ss zzz"];

    for (RMAppReceiptIAP* purchase in receipt.inAppPurchases) {
        NSString* cancellationDate = nil;
        if (purchase.cancellationDate) {
            cancellationDate = [localDateTime stringFromDate:purchase.cancellationDate];
        }
        NSLog(@"Transaction: %@: product %@, original purchase date: %@, expiration date: %@, cancellation date: %@",
              purchase.originalTransactionIdentifier,
              purchase.productIdentifier,
              [localDateTime stringFromDate:purchase.originalPurchaseDate],
              [localDateTime stringFromDate:purchase.subscriptionExpirationDate],
              cancellationDate);
    }

I also put a breakpoint in RMStore's paymentQueue:updatedTransactions: to see if the queue is updated with subsequent AR purchases.

I then purchased one month of my test product, verified the transaction and then quit the application.

On subsequent re-invocations of the application at 5 minute intervals, I then saw the breakpoint in the SKPaymentTransactionObserver method being hit with transactionSate SKPaymentTransactionStatePurchased. The log showed successive additions of the purchases (only last version shown):

2015-05-27 14:27:32.828 StoreKitSandbox[5803:1054853] Transaction: 1000000156919353: product com.foo.StoreKitSandbox.1_month_autorenew_foo, original purchase date: 2015.05.27 14:02:59 GMT-7, expiration date: 2015.05.27 14:07:58 GMT-7, cancellation date: (null)
2015-05-27 14:27:33.350 StoreKitSandbox[5803:1054853] Transaction: 1000000156919353: product com.foo.StoreKitSandbox.1_month_autorenew_foo, original purchase date: 2015.05.27 14:06:02 GMT-7, expiration date: 2015.05.27 14:12:58 GMT-7, cancellation date: (null)
2015-05-27 14:27:33.774 StoreKitSandbox[5803:1054853] Transaction: 1000000156919353: product com.foo.StoreKitSandbox.1_month_autorenew_foo, original purchase date: 2015.05.27 14:11:07 GMT-7, expiration date: 2015.05.27 14:17:58 GMT-7, cancellation date: (null)
2015-05-27 14:27:34.174 StoreKitSandbox[5803:1054853] Transaction: 1000000156919353: product com.foo.StoreKitSandbox.1_month_autorenew_foo, original purchase date: 2015.05.27 14:16:00 GMT-7, expiration date: 2015.05.27 14:22:58 GMT-7, cancellation date: (null)
2015-05-27 14:27:34.637 StoreKitSandbox[5803:1054853] Transaction: 1000000156919353: product com.foo.StoreKitSandbox.1_month_autorenew_foo, original purchase date: 2015.05.27 14:21:04 GMT-7, expiration date: 2015.05.27 14:27:58 GMT-7, cancellation date: (null)
2015-05-27 14:27:35.069 StoreKitSandbox[5803:1054853] Transaction: 1000000156919353: product com.foo.StoreKitSandbox.1_month_autorenew_foo, original purchase date: 2015.05.27 14:26:15 GMT-7, expiration date: 2015.05.27 14:32:58 GMT-7, cancellation date: (null)

Can you repro using this diagnostic approach?

Community
  • 1
  • 1
jstevenco
  • 2,913
  • 2
  • 25
  • 36
  • I understand that the receipt is updated server side - if I manually refresh the receipt (prompts the user) it is updated in the app too. I want to know if the app bundle receipt will be updated on auto renew (as it is at first purchase). As for monitoring completed transactions, this is via addTransactionObserver: to the default SKPaymentQueue. Calling restoreCompletedTransactions or refreshReceipt should only be done in response to user input - which I want to avoid. – TomSwift May 27 '15 at 20:11
  • Thanks for all the effort - I really appreciate it. Low and behold, it started working for me too. I don't know if I should chalk this up to sandbox gremlins or gremlins on my end - I was pretty tired last night when it wasn't working. – TomSwift May 28 '15 at 02:51