MCPcopy Index your code
hub / github.com/Cap-go/capacitor-native-purchases

github.com/Cap-go/capacitor-native-purchases @8.6.3

Chat with this repo
repository ↗ · DeepWiki ↗ · release 8.6.3 ↗ · + Follow
155 symbols 306 edges 21 files 21 documented · 14%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

native-purchases

Capgo - Instant updates for Capacitor

➡️ Get Instant updates for your App with Capgo

Missing a feature? We’ll build the plugin for you 💪

In-app Purchases Made Easy

This plugin allows you to implement in-app purchases and subscriptions in your Capacitor app using native APIs.

Why Native Purchases?

The only free, battle-tested in-app purchase plugin for Capacitor with full feature parity:

  • StoreKit 2 (iOS) - Uses Apple's latest purchase APIs for iOS 15+
  • iOS subscription commitment plans - Merchandises and purchases monthly subscriptions with 12-month commitments when StoreKit supports them
  • Google Play Billing 7.x (Android) - Implements the newest billing library
  • Complete feature set - In-app products AND subscriptions with base plans
  • Same JavaScript API - Compatible interface with paid alternatives
  • Comprehensive validation - Built-in receipt/token validation examples
  • Modern package management - Supports both Swift Package Manager (SPM) and CocoaPods (SPM-ready for Capacitor 8)
  • Production-ready - Extensive documentation, testing guides, refund handling

Perfect for apps monetizing through one-time purchases or recurring subscriptions.

Documentation

The most complete doc is available here: https://capgo.app/docs/plugins/native-purchases/

Compatibility

Plugin version Capacitor compatibility Maintained
v8.*.* v8.*.*
v7.*.* v7.*.* On demand
v6.*.* v6.*.*
v5.*.* v5.*.*

Note: The major version of this plugin follows the major version of Capacitor. Use the version that matches your Capacitor installation (e.g., plugin v8 for Capacitor 8). Only the latest major version is actively maintained.

Install

You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:

npx skills add https://github.com/cap-go/capacitor-skills --skill capacitor-plugins

Then use the following prompt:

Use the `capacitor-plugins` skill from `cap-go/capacitor-skills` to install the `@capgo/native-purchases` plugin in my project.

If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:

npm install @capgo/native-purchases
npx cap sync

📚 Testing Guides

Complete visual testing guides for both platforms:

Platform Guide Content
🍎 iOS iOS Testing Guide StoreKit Local Testing, Sandbox Testing, Developer Mode setup
🤖 Android Android Testing Guide Internal Testing, License Testing, Internal App Sharing

💡 Quick Start: Choose StoreKit Local Testing for iOS or Internal Testing for Android for the fastest development experience.

Android

Add this to manifest

<uses-permission android:name="com.android.vending.BILLING" />

Testing with Google Play Console

📖 Complete Android Testing Guide - Comprehensive guide covering Internal Testing, License Testing, and Internal App Sharing methods with step-by-step instructions, troubleshooting, and best practices.

For testing in-app purchases on Android:

  1. Upload your app to Google Play Console (internal testing track is sufficient)
  2. Create test accounts in Google Play Console:
  3. Go to Google Play Console
  4. Navigate to "Setup" > "License testing"
  5. Add Gmail accounts to "License testers" list
  6. Install the app from Google Play Store on a device signed in with a test account
  7. Test purchases will be free and won't charge real money

iOS

Add the "In-App Purchase" capability to your Xcode project:

  1. Open your project in Xcode
  2. Select your app target
  3. Go to "Signing & Capabilities" tab
  4. Click the "+" button to add a capability
  5. Search for and add "In-App Purchase"

⚠️ App Store Requirement: You MUST display product names and prices using data from the plugin (product.title, product.priceString). Hardcoded values will cause App Store rejection.

📖 Complete iOS Testing Guide - Comprehensive guide covering both Sandbox and StoreKit local testing methods with step-by-step instructions, troubleshooting, and best practices.

StoreKit Monthly Commitment Subscriptions

On supported iOS versions, the plugin exposes Apple's monthly subscription with 12-month commitment billing plan configuration from StoreKit:

  • Show product.pricingTerms so users can compare the standard up-front plan and the monthly commitment plan before purchasing.
  • Pass billingPlanType: 'monthly' to purchaseProduct() when the user chooses the monthly commitment plan.
  • Read transaction.billingPlanType, transaction.commitmentInfo, and transaction.renewalInfo.commitmentInfo from purchases, restored purchases, and entitlement checks.
const { products } = await NativePurchases.getProducts({
  productIdentifiers: ['com.yourapp.premium.yearly'],
  productType: PURCHASE_TYPE.SUBS,
});

const premiumYearly = products[0];
const monthlyCommitment = premiumYearly.pricingTerms?.find(
  (term) => term.billingPlanType === 'monthly'
);

if (monthlyCommitment) {
  // Display both billingDisplayPrice and commitmentInfo.priceString before purchase.
  await NativePurchases.purchaseProduct({
    productIdentifier: premiumYearly.identifier,
    productType: PURCHASE_TYPE.SUBS,
    billingPlanType: 'monthly',
  });
}

Testing with Sandbox

For testing in-app purchases on iOS:

  1. Create a sandbox test user in App Store Connect:
  2. Go to App Store Connect
  3. Navigate to "Users and Access" > "Sandbox Testers"
  4. Create a new sandbox tester account
  5. On your iOS device, sign out of your regular Apple ID in Settings > App Store
  6. Install and run your app
  7. When prompted for Apple ID during purchase testing, use your sandbox account credentials

Usage

Import the plugin in your TypeScript file:

import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';

⚠️ Important: In-App vs Subscription Purchases

There are two types of purchases with different requirements:

Purchase Type productType planIdentifier Use Case
In-App Purchase PURCHASE_TYPE.INAPP ❌ Not needed One-time purchases (premium features, remove ads, etc.)
Subscription PURCHASE_TYPE.SUBS REQUIRED (Android only) Recurring purchases (monthly/yearly subscriptions)

Key Rules: - ✅ In-App Products: Use productType: PURCHASE_TYPE.INAPP, no planIdentifier needed on any platform - ✅ Subscriptions on Android: Must use productType: PURCHASE_TYPE.SUBS AND planIdentifier: "your-plan-id" (the Base Plan ID from Google Play Console) - ✅ Subscriptions on iOS: Use productType: PURCHASE_TYPE.SUBS, planIdentifier is optional and ignored - ❌ Missing planIdentifier for Android subscriptions will cause purchase failures

About planIdentifier (Android-specific): The planIdentifier parameter is only required for Android subscriptions. It should be set to the Base Plan ID that you configure in the Google Play Console when creating your subscription product. For example, if you create a monthly subscription with base plan ID "monthly-plan" in Google Play Console, you would use planIdentifier: "monthly-plan" when purchasing that subscription.

iOS does not use this parameter - subscriptions on iOS only require the product identifier.

Complete Example: Get Product Info and Purchase

Here's a complete example showing how to get product information and make purchases for both in-app products and subscriptions:

```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';

class PurchaseManager { // In-app product (one-time purchase) private premiumProductId = 'com.yourapp.premium_features';

// Subscription products (require planIdentifier on Android) private monthlySubId = 'com.yourapp.premium.monthly'; private monthlyPlanId = 'monthly-plan'; // Base plan ID from Google Play Console (Android only)

private yearlySubId = 'com.yourapp.premium.yearly'; private yearlyPlanId = 'yearly-plan'; // Base plan ID from Google Play Console (Android only)

async initializeStore() { try { // 1. Check if billing is supported const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) { throw new Error('Billing not supported on this device'); }

  // 2. Get product information (REQUIRED by Apple - no hardcoded prices!)
  await this.loadProducts();

} catch (error) {
  console.error('Store initialization failed:', error);
}

}

async loadProducts() { try { // Load in-app products const { product: premiumProduct } = await NativePurchases.getProduct({ productIdentifier: this.premiumProductId, productType: PURCHASE_TYPE.INAPP });

  // Load subscription products  
  const { products: subscriptions } = await NativePurchases.getProducts({
    productIdentifiers: [this.monthlySubId, this.yearlySubId],
    productType: PURCHASE_TYPE.SUBS
  });
  // Android note: subscriptions can include multiple entries per product (one per offer/base plan).
  // Use `identifier` (base plan), `offerToken`, and optional `offerId` to pick a specific offer.

  console.log('Products loaded:', {
    premium: premiumProduct,
    subscriptions: subscriptions
  });

  // Display products with dynamic info from store
  this.displayProducts(premiumProduct, subscriptions);

} catch (error) {
  console.error('Failed to load products:', error);
  throw error;
}

}

displayProducts(premiumProduct: any, subscriptions: any[]) { // ✅ CORRECT: Use dynamic product info (required by Apple)

// Display one-time purchase
document.getElementById('premium-title')!.textContent = premiumProduct.title;
document.getElementById('premium-price')!.textContent = premiumProduct.priceString;

// Display subscriptions
subscriptions.forEach(sub => {
  const element = document.getElementById(`sub-${sub.identifier}`);
  if (element) {
    element.textContent = `${sub.title} - ${sub.priceString}`;
  }
});

// ❌ WRONG: Never hardcode prices - Apple will reject your app
// document.getElementById('premium-price')!.textContent = '$9.99';

}

// Purchase one-time product (no planIdentifier needed) async purchaseInAppProduct() { try { console.log('Starting in-app purchase...');

  const result = await NativePurchases.purchaseProduct({
    productIdentifier: this.premiumProductId,
    productType: PURCHASE_TYPE.INAPP,
    quantity: 1
  });

  console.log('In-app purchase successful!', result.transactionId);
  await this.handleSuccessfulPurchase(result.transactionId, 'premium');

} catch (error) {
  console.error('In-app purchase failed:', error);
  this.handlePurchaseError(error);
}

}

// Purchase subscription (planIdentifier REQUIRED for Android) async purchaseMonthlySubscription() { try { console.log('Starting subscription purchase...');

  const result = await NativePurchases.purchaseProduct({
    productIdentifier: this.monthlySubId,
    planIdentifier: this.monthlyPlanId,    // REQUIRED for Android subscriptions, ignored on iOS
    productType: PURCHASE_TYPE.SUBS,       // REQUIRED for subscriptions
    quantity: 1
  });

  console.log('Subscription purchase successful!', result.transactionId);
  await this.handleSuccessfulPurchase(result.transactionId, 'monthly');

} catch (error) {
  console.error('Subscription purchase failed:', error);
  this.handlePurchaseError(error);
}

}

// Purchase yearly subscription (planIdentifier REQUIRED for Android) async purchaseYearlySubscription() { try { console.log('Starting yearly subscription purchase...');

  const result = await NativePurchases.purchaseProduct({
    productIdentifier: this.yearlySubId,
    planIdentifier: this.yearlyPlanId,     // REQUIRED for Android subscriptions, ignored on iOS
    productType: PURCHASE_TYPE.SUBS,       // REQUIRED for subscriptions
    quantity: 1
  });

  console.log('Yearly subscription successful!', result.transactionId);
  await this.handleSuccessfulPurchase(result.transactionId, 'yearly');

} catch (error) {
  console.error('Yearly subscription failed:', error);
  this.handlePurchaseError(error);
}

}

async handleSuccessfulPurchase(transactionId: string, purchaseType: string) { // 1. Grant access to premium features localStorage.setItem('premium_active', 'true'); localStorage.setItem('purchase_type', purchaseType);

// 2. Update UI
const statusText = purchaseType === 'premium' ? 'Premium Unlocked' : `${purchaseType} Subscription Active`;
document.getElementById('subscription-status')!.textContent = statusText;

// 3. Optional: Verify purchase on your server
await this.verifyPurchaseOnServer(transactionId);

}

handlePurchaseError(error: any) {

Extension points exported contracts — how you extend this code

NativePurchasesPlugin (Interface)
(no doc) [1 implementers]
src/definitions.ts
Env (Interface)
(no doc)
validator/index.ts
VerifyResponse (Interface)
(no doc)
validator/apple.ts
Transaction (Interface)
(no doc)
src/definitions.ts
TransactionVerificationFailedEvent (Interface)
(no doc)
src/definitions.ts
AppTransaction (Interface)
(no doc)
src/definitions.ts
SubscriptionPeriod (Interface)
(no doc)
src/definitions.ts

Core symbols most depended-on inside this repo

closeBillingClient
called by 23
android/src/main/java/ee/forgr/nativepurchases/NativePurchasesPlugin.java
getPurchaseState
called by 14
android/src/main/java/ee/forgr/nativepurchases/PurchaseActionDecider.java
decide
called by 7
android/src/main/java/ee/forgr/nativepurchases/PurchaseActionDecider.java
withBillingClient
called by 7
android/src/main/java/ee/forgr/nativepurchases/NativePurchasesPlugin.java
closeBillingClientLocked
called by 7
android/src/main/java/ee/forgr/nativepurchases/NativePurchasesPlugin.java
getProducts
called by 7
src/definitions.ts
isAcknowledged
called by 6
android/src/main/java/ee/forgr/nativepurchases/PurchaseActionDecider.java
readText
called by 6
scripts/check-capacitor-plugin-wiring.mjs

Shape

Method 102
Interface 17
Function 16
Class 13
Enum 7

Languages

Java55%
TypeScript45%

Modules by API surface

android/src/main/java/ee/forgr/nativepurchases/NativePurchasesPlugin.java41 symbols
src/definitions.ts35 symbols
src/web.ts16 symbols
android/src/main/java/ee/forgr/nativepurchases/ProductPayloadMapper.java13 symbols
android/src/main/java/ee/forgr/nativepurchases/PurchaseActionDecider.java11 symbols
android/src/test/java/ee/forgr/nativepurchases/PurchaseActionDeciderTest.java9 symbols
scripts/check-capacitor-plugin-wiring.mjs7 symbols
example-app/src/main.js6 symbols
android/src/test/java/ee/forgr/nativepurchases/ProductPayloadMapperTest.java3 symbols
validator/googl.ts2 symbols
validator/apple.ts2 symbols
example-app/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java2 symbols

For agents

$ claude mcp add capacitor-native-purchases \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page