How to validate subscription upgrades on trusted server?

Hi,

I'm working on an app that offers auto-renewable subscriptions on a monthly and yearly basis. The app (or the App Store Server Notification) sends the receipt to our trusted server which validates the receipts against https://buy.itunes.apple.com/verifyReceipt

We are able to validate first purchases and renewals by field latest_receipt_info which contains an array with all the purchase transactions. However, when a user upgrades their monthly subscription to a yearly one, the array latest_receipt_info doesn't contain the new subscription (the yearly one). The yearly subscription appears in the array pending_renewal_info which doesn't contain the expiration_date or other fields required for updating our internal database.

We also tried to restore the purchase and validate the new receipt again but the yearly subscription never appears in latest_receipt_info and always stays in pending_renewal_info.

How can I validate upgrades to yearly subscriptions? why doesn't it appear in latest_receipt_info?

Response from https://buy.itunes.apple.com/verifyReceipt by using the receipt sent by the App Store Server Notifications:

{
    "environment": "Production",
    "receipt": {
        "receipt_type": "Production",
        "adam_id": 987654321,
        "app_item_id": 987654321,
        "bundle_id": "com.company.productcompany",
        "application_version": "1221",
        "download_id": 5000000000000000000,
        "version_external_identifier": 800000000,
        "receipt_creation_date": "2021-11-26 09:39:12 Etc/GMT",
        "receipt_creation_date_ms": "1637919552000",
        "receipt_creation_date_pst": "2021-11-26 01:39:12 America/Los_Angeles",
        "request_date": "2021-11-26 10:11:09 Etc/GMT",
        "request_date_ms": "1637921469566",
        "request_date_pst": "2021-11-26 02:11:09 America/Los_Angeles",
        "original_purchase_date": "2021-11-17 13:50:18 Etc/GMT",
        "original_purchase_date_ms": "1637157018000",
        "original_purchase_date_pst": "2021-11-17 05:50:18 America/Los_Angeles",
        "original_application_version": "1219",
        "in_app": [
            {
                "quantity": "1",
                "product_id": "com.company.product.subs1.onemonth",
                "transaction_id": "150000000000000",
                "original_transaction_id": "150000000000000",
                "purchase_date": "2021-11-25 09:29:50 Etc/GMT",
                "purchase_date_ms": "1637832590000",
                "purchase_date_pst": "2021-11-25 01:29:50 America/Los_Angeles",
                "original_purchase_date": "2021-11-25 09:29:50 Etc/GMT",
                "original_purchase_date_ms": "1637832590000",
                "original_purchase_date_pst": "2021-11-25 01:29:50 America/Los_Angeles",
                "expires_date": "2021-12-25 09:29:50 Etc/GMT",
                "expires_date_ms": "1640424590000",
                "expires_date_pst": "2021-12-25 01:29:50 America/Los_Angeles",
                "web_order_line_item_id": "150000419389034",
                "is_trial_period": "false",
                "is_in_intro_offer_period": "false",
                "in_app_ownership_type": "PURCHASED"
            }
        ]
    },
    "latest_receipt_info": [
        {
            "quantity": "1",
            "product_id": "com.company.product.subs1.onemonth",
            "transaction_id": "150000000000000",
            "original_transaction_id": "150000000000000",
            "purchase_date": "2021-11-25 09:29:50 Etc/GMT",
            "purchase_date_ms": "1637832590000",
            "purchase_date_pst": "2021-11-25 01:29:50 America/Los_Angeles",
            "original_purchase_date": "2021-11-25 09:29:50 Etc/GMT",
            "original_purchase_date_ms": "1637832590000",
            "original_purchase_date_pst": "2021-11-25 01:29:50 America/Los_Angeles",
            "expires_date": "2021-12-25 09:29:50 Etc/GMT",
            "expires_date_ms": "1640424590000",
            "expires_date_pst": "2021-12-25 01:29:50 America/Los_Angeles",
            "web_order_line_item_id": "150000419389034",
            "is_trial_period": "false",
            "is_in_intro_offer_period": "false",
            "in_app_ownership_type": "PURCHASED",
            "subscription_group_identifier": "19900000"
        }
    ],
    "latest_receipt": "MII...Mk6fIA3",
    "pending_renewal_info": [
        {
            "auto_renew_product_id": "com.company.product.subs1.twelvemonths",
            "product_id": "com.company.product.subs1.onemonth",
            "original_transaction_id": "150000000000000",
            "auto_renew_status": "1"
        }
    ],
    "status": 0
}

Replies

I cannot see your specific case, but based on what you are describing, it sounds like you users are not upgrading their subscription, they are performing a crossgrade. This is when the duration of the subscription changes, for example changing from a monthly to yearly subscription, but the underlying product does not change. These types of changes take effect at the next renewal, hence why the pending_renewal_info informs you that the product will change at next renewal, but the renewal has not yet taken place. If they were to switch back to monthly before their renewal date, you would actually see no change in service.

For more information see https://help.apple.com/app-store-connect/#/dev75708c031

Quoted section: "If a customer is currently subscribed to “Yearly Subscription” and wants to change to a monthly duration, they can crossgrade from “Yearly Subscription” to “Monthly Subscription.” The change will take effect on their next renewal date."

Something else to consider - an upgrade in App Store Connect is not a logical concept, it's positional. If the one month subscription identifier is listed higher in the subscription group order than the twelve month identifier, the one month subscription would be considered an upgrade to the twelve month identifier. Which means if the user has purchased a one month subscription then attempts to purchase the twelve month subscription item, the app store would deem this a subscription downgrade and present the user with the alert that the twelve month subscription will take effect once the one month subscription expires. The contents of the pending_renewal_info reflects this situation.

As to whether this can be switched, please contact app store connect.

rich kubota developer technical support CoreOS/Hardware/MFI