How to Implement iOS In-App Purchases (IAP) on the Backend Server

In-app purchases (IAPs) are essential for many apps, allowing users to access premium content or features. In iOS, Apple handles the payment process, but you need to implement backend logic to handle subscriptions and manage real-time notifications from the App Store.

In this blog, we’ll cover the entire process of setting up iOS in-app purchases on the backend server. You’ll learn how to handle real-time notifications, decode signed payloads, verify subscription status, and manage subscription history efficiently.

Setting Up In-App Purchases on the Apple Developer Console

Before handling iOS in-app purchases, you must set up your app in App Store Connect and configure in-app purchases.

Step 1: Create an App in App Store Connect

  1. Log in to App Store Connect.
  2. In the App Dashboard, click My Apps.
  3. Select New App and fill in details like app name, platform, bundle ID, and SKU.
  4. Save your app.

Step 2: Enable In-App Purchases and Add Subscription Plans

  1. Navigate to your app’s dashboard.
  2. Under Features, find In-App Purchases.
  3. Click the + button to create new in-app purchases.
  4. Select Auto-Renewable Subscriptions and define different plans (monthly, yearly, etc.).
  5. Fill in product details, pricing, and localization.
  6. Save the subscriptions.

Step 3: Set Up Real-Time Developer Notifications (RTDN)

  1. In-App Store Connect, go to App Settings.
  2. Under Server Notifications, enter your backend’s URL where Apple will send RTDN.
  3. Ensure your backend can handle POST requests with signed payloads.

Step 4: Managing Sandbox Testing

  1. Before launching the app, you should test subscriptions in a sandbox environment.
  2. Create sandbox test accounts from App Store Connect.
  3. Test various subscription scenarios like renewals, cancellations, and upgrades using the sandbox environment.

What We Get in RTDN Callbacks and How to Decode the Signed Payload

Here’s a high-level process for decoding:

  1. Verify the Signature: Use Apple’s app-store-server-library for Python (or equivalent in other languages) to verify the signature.
  2. Decode the Payload: Once verified, decode the SignedPayload to extract transactionInfo and renewalInfo.

Supported Libraries:

Example of a Decoded SignedPayload:

{
    "notificationType": "SUBSCRIBED",
    "subtype": "INITIAL_BUY",
    "notificationUUID": "550ffbbb-e6a8-482c-ba57-2d5a3b2265d8",
    "data": {
      "appAppleId": 1234567890,
      "bundleId": "com.app.dev",
      "bundleVersion": "13",
      "environment": "Sandbox",
      "signedTransactionInfo": "eyJhbGciOiJFUzI1NiIsIng1Y...",
      "signedRenewalInfo": "sJnTlZCQXNNSTBGd2NHe..."
    }
}

Decoding and Understanding TransactionInfo and RenewalInfo

Both transactionInfo and renewalInfo are also signed JWTs and need to be decoded similarly. They contain important data about the subscription transaction and the auto-renewal status.

Example of Decoded TransactionInfo:

{
  "transactionId": "2000000671303537",
  "originalTransactionId": "2000000671303537",
  "webOrderLineItemId": "2000000069147956",
  "bundleId": "com.app.dev",
  "productId": "45_monthly",
  "subscriptionGroupIdentifier": "21513905",
  "purchaseDate": 1722335111000,
  "originalPurchaseDate": 1722335112000,
  "expiresDate": 1722337271000,
  "quantity": 1,
  "type": "Auto-Renewable Subscription",
  "inAppOwnershipType": "PURCHASED",
  "signedDate": 1722335124753,
  "environment": "Sandbox",
  "transactionReason": "PURCHASE",
  "storefront": "IND",
  "storefrontId": "143467",
  "price": 450,
  "currency": "INR"
}

Key Fields in TransactionInfo:

  • transactionId: Unique identifier for the transaction.
  • originalTransactionId: The original transaction ID for this subscription (remains the same across renewals).
  • productId: The product ID representing the subscription plan.
  • purchaseDate: Date and time of the subscription purchase.
  • expiresDate: Expiry date of the subscription.
  • price: Cost of the plan.

Example of Decoded RenewalInfo:

{
  "originalTransactionId": "2000000671303537",
  "autoRenewProductId": "45_monthly",
  "productId": "45_monthly",
  "autoRenewStatus": 1,
  "renewalPrice": 450,
  "currency": "INR",
  "signedDate": 1722335124753,
  "environment": "Sandbox",
  "recentSubscriptionStartDate": 1722335111000,
  "renewalDate": 1722337271000
}

Key Fields in RenewalInfo:

  • autoRenewProductId: The product ID that will be renewed.
  • autoRenewStatus: Indicates whether the subscription is set to auto-renew (1 for active, 0 for disabled).
  • signedDate: Date of the current transaction.
  • renewalDate: Date when this plan will auto-renew.

Get Started with iOS In-App Purchases Today!

Understanding Different Scenarios of Subscription

When handling subscription callbacks, you will encounter various notificationType and subType combinations. These represent the event that has occurred and the specifics of that event. The table below will help you handle various edge cases.

Events that enable service for subscriptions, including initial subscriptions, resubscribing, and successful auto-renewals:

Understanding-Different-Scenarios-of-Subscription

Customers changing their subscription options, including upgrading, downgrading, or canceling:

Customers-changing-their-subscription-options-including-upgrading-downgrading-or-canceling

Billing events, including billing retries, entering and exiting the Billing Grace Period, and expiring subscriptions:

Billing-events-including-billing-retries-entering-and-exiting-Billing-Grace-Period-and-expiring-subscriptions

There are a few more such combinations that you can explore in Apple’s documentation.

Handling Subscription Callbacks on the Backend and Identifying Users

When you receive a real-time developer notification (RTDN) from Apple, the backend must handle the subscription event based on the notificationType and subType, and correctly identify the user associated with the subscription. Here’s how to do it:

  1. Verify the Signature:
    🔺Use Apple’s provided library to verify the authenticity of the signed payload to ensure it has not been tampered with.
  2. Decode TransactionInfo and RenewalInfo:
    🔺Once the signature is verified, decode the signedTransactionInfo and signedRenewalInfo. These contain critical information about the subscription, such as the originalTransactionId, productId, expiresDate, autoRenewStatus, and more.
  3. Identify the User:
    🔺Use the originalTransactionId to map the subscription to the correct user. When a user first subscribes, you store this originalTransactionId in your database alongside the user’s information. This ID remains constant for the lifetime of the subscription, including renewals, upgrades, or downgrades.
    🔺On receiving a renewal or other event, use the originalTransactionId from the transactionInfo to look up the user in your database and retrieve their associated subscription data.
  4. Check notificationType and subType:
    🔺The notificationType and subType help you identify what kind of event has occurred. For example, an event could indicate a successful renewal (DID_RENEW), a cancellation (DID_FAIL_TO_RENEW), or a change in auto-renew status (DID_CHANGE_RENEWAL_STATUS).
  5. Update the Database:
    🔺Based on the event type, update the subscription details in your database.
    For instance:
    On Renewal: Update the expiresDate, autoRenewStatus, and other relevant fields.
    On Cancellation or Expiration: Set the subscription as expired or disabled in your database.
  6. Notify the User (Optional):
    🔺Optionally, notify the user about the subscription event (e.g., renewal, cancellation, or expiration) via email or push notification, depending on your app’s requirements.
coma

Conclusion

Handling iOS in-app purchases and subscriptions on the backend is critical to providing a seamless user experience. By understanding the notificationType and subType combinations, decoding the signedPayload, transactionInfo, and renewalInfo, and efficiently managing these events in your database, you ensure accurate subscription management.

Implementing best practices like secure payload verification and maintaining a detailed subscription history will allow you to provide a reliable service for your users, enhancing their trust in your application.

Keep Reading

Keep Reading

  • Service
  • Career
  • Let's create something together!

  • We’re looking for the best. Are you in?