Skip to main content

Building In-App Purchases for Apps Built With Bilt

Build In-App Purchases with Bilt: learn StoreKit, App Store Connect, and backend verification for secure purchases, renewals, and restore flows.

·6 min read
Building In-App Purchases for Apps Built With Bilt

In-app purchases look simple from the outside. A user taps a button, Apple shows a purchase sheet, and the app unlocks something. Underneath that flow, though, there is a lot of state to keep: receipts, renewals, refunds, anonymous users, restore flows, test modes, production modes, and support tooling.

When we built our in-app purchase system, we had to connect three worlds: App Store Connect, StoreKit on the device, and the Bilt purchases backend. Apple handles the payment experience, tax, regional pricing, refunds, subscription renewals, and the purchase UI. The app and backend still have to decide what to show, when to start a purchase, how to verify it, and what the user should be allowed to access afterward.

App Store Connect Products

The process starts in App Store Connect. Before an app can sell anything, each digital product has to be created there with a product identifier, type, price, and localization.

Those products can represent different kinds of digital value:

  • Consumables, like credits or coins that can be spent and bought again.
  • Non-consumables, like a lifetime unlock or a remove-ads purchase.
  • Auto-renewable subscriptions, like monthly or yearly premium access.
  • Non-renewing subscriptions, like a limited-time event pass.

The product identifier is the stable handle that connects App Store Connect, StoreKit, and the app. StoreKit uses it to fetch the product, start the purchase, and identify the resulting transaction.

We integrated with the App Store Connect API so products can be created, updated, and deleted from our product setup flow. Products can still be managed directly in App Store Connect too. Either way, the Apple-side product definition and the app-side feature it unlocks stay aligned.

How StoreKit Works

StoreKit is Apple's framework for selling digital goods and services inside Apple platform apps. In plain terms, it is the bridge between the app, the user, Apple's payment system, and the App Store.

For modern apps, StoreKit gives developers a model built around products, transactions, current access, transaction updates, and signed purchase data.

The normal purchase flow looks like this:

  1. The app asks Apple for product information by product identifier.
  2. Apple returns localized product metadata, including price and description.
  3. The app shows its paywall or purchase UI.
  4. The user buys through Apple's payment sheet.
  5. Apple creates a signed transaction.
  6. The app verifies the transaction.
  7. The app unlocks access.
  8. The app finishes the transaction.

StoreKit also has to handle events that do not happen during a button tap. A subscription can renew while the app is closed. A refund can revoke access. A family approval can complete the purchase later. A purchase made on another device may need to appear on this device. StoreKit exposes purchase state and transaction updates so an app can restore access and react to those changes.

For a simple app with a one-time purchase and no account system, on-device StoreKit verification can be enough. But once users have accounts, access spans devices, or the backend needs to know who is subscribed, the server becomes part of the purchase flow.

Bilt Purchases Backend Verification

We built the Bilt purchases backend so purchase state could be tied to a user account, not only to the device where the purchase happened. After StoreKit returns a signed transaction, the client sends that proof to the backend, where we verify the signature and certificate chain, decode the transaction, and store it as a durable record.

This gave us a content guard that is based on current access rather than a single purchase event. When the app asks whether a user can access a feature, the backend computes the answer from verified purchases, subscription state, and the product-to-feature mapping.

That matters because subscriptions are not static. A monthly plan can renew, expire, enter billing retry, recover, or be refunded. Apple server notifications keep that state fresh even when the app is not open.

Restore also becomes more reliable. The client can send device-visible purchases back through verification, and the backend can reconcile known subscription history with Apple when available.

A Client Package for Convenience

To make the app integration easier, we wrapped the StoreKit and Bilt purchases backend steps in a small Expo package. The app still needs to fetch products, show localized prices, start purchases, listen for transaction updates, restore purchases, handle pending states, send signed transactions to the backend, and avoid finishing purchases before the backend has accepted them.

The package keeps that plumbing in one place so app code can stay focused on the paywall and feature checks.

The package handles a few important jobs:

  • Fetching product metadata from the native store when available.
  • Starting the native purchase sheet.
  • Sending signed purchase results to the backend.
  • Waiting for backend acceptance before finishing the transaction.
  • Restoring purchases and refreshing access.
  • Supporting purchase-before-signup with a stable anonymous identity.
  • Retrying purchase verification after temporary network failures.
  • Falling back to read-only access checks when native purchases are not available in the current environment.

For AI generated apps, the ideal surface is small: show the products, start a purchase, restore purchases, and ask whether a feature is currently available. The package gives agents a consistent integration point while the backend keeps the authoritative access state.

The Mental Model

The cleanest way to think about the system is:

  • App Store Connect defines what can be sold.
  • StoreKit lets the user buy through Apple.
  • Apple returns signed proof of the purchase.
  • Bilt purchases backend verifies and stores that proof.
  • Subscription events keep access state up to date over time.
  • The app asks the backend what the user can access.

That division made the implementation easier to test and safer to extend. The app gets Apple's polished purchase experience. The backend gets a durable, account-aware view of access. Users get a flow that can survive renewals, refunds, restores, network failures, and the ordinary messiness of real billing.

We are excited for you to try integrating Bilt payments into your app, and we would love to hear how it goes. If you have any questions or feedback, feel free to reach out to [email protected]