Make purchases in mobile app in Capacitor SDK

Displaying paywalls within your mobile app is an essential step in offering users access to premium content or services. However, simply presenting these paywalls is enough to support purchases only if you use Paywall Builder to customize your paywalls.

If you don’t use the Paywall Builder, you must use a separate method called .makePurchase() to complete a purchase and unlock the desired content. This method serves as the gateway for users to engage with the paywalls and proceed with their desired transactions.

If your paywall has an active promotional offer for the product a user is trying to buy, Adapty will automatically apply it at the time of purchase.

Make sure you’ve done the initial configuration without skipping a single step. Without it, we can’t validate purchases.

Make purchase

Using Paywall Builder? Purchases are processed automatically—you can skip this step.

Looking for step-by-step guidance? Check out the quickstart guide for end-to-end implementation instructions with full context.

import { adapty } from '@adapty/capacitor';

try {
  const result = await adapty.makePurchase({ product });
  
  if (result.type === 'success') {
    const isSubscribed = result.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive;
    
    if (isSubscribed) {
      // Grant access to the paid features
      console.log('User is now subscribed!');
    }
  } else if (result.type === 'user_cancelled') {
    console.log('Purchase cancelled by user');
  } else if (result.type === 'pending') {
    console.log('Purchase is pending');
  }
} catch (error) {
  console.error('Purchase failed:', error);
}

Request parameters:

ParameterPresenceDescription
productrequiredAn AdaptyPaywallProduct object retrieved from the paywall.

Response parameters:

ParameterDescription
resultAn AdaptyPurchaseResult object with a type field indicating the purchase outcome ('success', 'user_cancelled', or 'pending') and a profile field containing the updated AdaptyProfile on successful purchases.

Change subscription when making a purchase

When a user opts for a new subscription instead of renewing the current one, the way it works depends on the app store:

  • For the App Store, the subscription is automatically updated within the subscription group. If a user purchases a subscription from one group while already having a subscription from another, both subscriptions will be active at the same time.
  • For Google Play, the subscription isn’t automatically updated. You’ll need to manage the switch in your mobile app code as described below.

To replace the subscription with another one in Android, call .makePurchase() method with the additional parameter:

import { adapty } from '@adapty/capacitor';

try {
  const result = await adapty.makePurchase({ 
    product,
    params: {
      android: {
        subscriptionUpdateParams: {
          oldSubVendorProductId: 'old_product_id',
          prorationMode: 'charge_prorated_price'
        },
        isOfferPersonalized: true
      }
    }
  });
  
  if (result.type === 'success') {
    const isSubscribed = result.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive;
    
    if (isSubscribed) {
      // Grant access to the paid features
      console.log('Subscription updated successfully!');
    }
  } else if (result.type === 'user_cancelled') {
    console.log('Purchase cancelled by user');
  } else if (result.type === 'pending') {
    console.log('Purchase is pending');
  }
} catch (error) {
  console.error('Purchase failed:', error);
}

Additional request parameter:

ParameterPresenceDescription
paramsoptionalAn object of the MakePurchaseParamsInput type containing platform-specific purchase parameters.

The MakePurchaseParamsInput structure includes:

{
  android: {
    subscriptionUpdateParams: {
      oldSubVendorProductId: 'old_product_id',
      prorationMode: 'charge_prorated_price'
    },
    isOfferPersonalized: true
  }
}

You can read more about subscriptions and replacement modes in the Google Developer documentation:

Manage prepaid plans (Android)

If your app users can purchase prepaid plans (e.g., buy a non-renewable subscription for several months), you can enable pending transactions for prepaid plans.

await adapty.activate({
  apiKey: 'YOUR_PUBLIC_SDK_KEY',
  params: {
    android: {
        enablePendingPrepaidPlans: true,      
    },
  }
});

Redeem offer codes in iOS

About offer codes

Offer codes allow you to give discounts or free trials to specific users. Unlike regular offers that are applied automatically, offer codes are distributed outside the app — through email campaigns, social media, or printed materials. Users redeem them by entering the code in the App Store, following a redemption URL, or through an in-app dialog.

To set up offer codes, open a subscription in App Store Connect and go to its Offer Codes section. You can create three kinds of offer codes:

  • Free — the subscription is free for a set duration, and the next renewal is at full price.
  • Pay as you go — the user pays a discounted price each billing cycle for a set duration, then the subscription renews at full price.
  • Pay up front — the user pays a single discounted price for the entire offer duration, then the subscription renews at full price.

You don’t need to add offer codes to Adapty. Apple tags every transaction during the offer period with the offer code category. This includes the initial redemption and all subsequent discounted renewals. Adapty detects the tag and records each transaction with the offer category offer_code. Once the offer period ends and the subscription renews at full price, the tag is no longer present. You can filter analytics by the Offer Code offer type in the Adapty Dashboard.

Revenue discrepancy troubleshooting

If you notice that an offer code transaction appears in Adapty at the full product price instead of the discounted offer price, verify the following in App Store Connect:

  • The offer code has the correct pricing configured for all regions where users can redeem it.
  • The offer price is set for the specific country or region of the user. Apple sends the regional price in the transaction. If no regional price is configured for the offer, Apple may send the full product price instead.

You can filter and verify offer code transactions in the Adapty Dashboard by the Offer Code offer type and Offer Discount Type filters.

Legacy promo codes (deprecated)

Apple deprecated promo codes for in-app purchases in March 2026. Offer codes replace them with more capabilities: configurable eligibility, expiration dates, and up to 1 million codes per quarter. If you previously used promo codes for in-app purchases, transition to offer codes in App Store Connect.

Legacy promo codes (limited to 100 per app per version) granted free access to a subscription. Unlike offer codes, Apple did not include discount information in promo code transactions — it sent the full product price in the receipt. As a result, Adapty recorded these transactions at the full price, which caused revenue discrepancies between Adapty analytics and App Store Connect.

If you see historical transactions at full price that should have been free, they are likely from legacy promo codes. Since these codes are now deprecated, transition to offer codes for accurate revenue tracking.

To display the code redemption sheet in your app:

import { adapty } from '@adapty/capacitor';

try {
  await adapty.presentCodeRedemptionSheet();
} catch (error) {
  console.error('Failed to present code redemption sheet:', error);
}

Based on our observations, the Offer Code Redemption sheet in some apps may not work reliably. We recommend redirecting the user directly to the App Store.

In order to do this, you need to open the url of the following format: https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}