Getting Started
此内容尚不支持你的语言。
-
Install the package
Terminal window npm i @capgo/native-purchasesTerminal window pnpm add @capgo/native-purchasesTerminal window yarn add @capgo/native-purchasesTerminal window bun add @capgo/native-purchases -
Sync with native projects
Terminal window npx cap syncTerminal window pnpm cap syncTerminal window yarn cap syncTerminal window bunx cap sync -
Check billing support
import { NativePurchases } from '@capgo/native-purchases';const { isBillingSupported } = await NativePurchases.isBillingSupported();if (!isBillingSupported) {throw new Error('Billing is not available on this device');} -
Load products directly from the stores
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';const { products } = await NativePurchases.getProducts({productIdentifiers: ['com.example.premium.monthly','com.example.premium.yearly','com.example.one_time_unlock'],productType: PURCHASE_TYPE.SUBS, // Use PURCHASE_TYPE.INAPP for one‑time products});products.forEach((product) => {console.log(product.title, product.priceString);}); -
Implement purchase & restore flows
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';const monthlyPlanId = 'monthly-plan'; // Base Plan ID from Google Play Consoleconst transaction = await NativePurchases.purchaseProduct({productIdentifier: 'com.example.premium.monthly',planIdentifier: monthlyPlanId, // REQUIRED for Android subscriptions, ignored on iOSproductType: PURCHASE_TYPE.SUBS,quantity: 1,});console.log('Transaction ID', transaction.transactionId);await NativePurchases.restorePurchases();- Create in-app products and subscriptions in App Store Connect.
- Use StoreKit Local Testing or Sandbox testers for QA.
- No manifest edits required. Make sure your products are approved.
- Create in-app products and subscriptions in Google Play Console.
- Upload at least an internal test build and add license testers.
- Add the billing permission to
AndroidManifest.xml:
<uses-permission android:name="com.android.vending.BILLING" /> - Create in-app products and subscriptions in App Store Connect.
Purchase service example
import { NativePurchases, PURCHASE_TYPE, Transaction } from '@capgo/native-purchases';import { Capacitor } from '@capacitor/core';
class PurchaseService { private premiumProduct = 'com.example.premium.unlock'; private monthlySubId = 'com.example.premium.monthly'; private monthlyPlanId = 'monthly-plan'; // Base Plan ID (Android only)
async initialize() { const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) throw new Error('Billing unavailable');
const { products } = await NativePurchases.getProducts({ productIdentifiers: [this.premiumProduct, this.monthlySubId], productType: PURCHASE_TYPE.SUBS, });
console.log('Loaded products', products);
if (Capacitor.getPlatform() === 'ios') { NativePurchases.addListener('transactionUpdated', (transaction) => { this.handleTransaction(transaction); }); } }
async buyPremium(appAccountToken?: string) { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: this.premiumProduct, productType: PURCHASE_TYPE.INAPP, appAccountToken, });
await this.processTransaction(transaction); }
async buyMonthly(appAccountToken?: string) { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: this.monthlySubId, planIdentifier: this.monthlyPlanId, // REQUIRED for Android subscriptions productType: PURCHASE_TYPE.SUBS, appAccountToken, });
await this.processTransaction(transaction); }
async restore() { await NativePurchases.restorePurchases(); await this.refreshEntitlements(); }
async openManageSubscriptions() { await NativePurchases.manageSubscriptions(); }
private async processTransaction(transaction: Transaction) { this.unlockContent(transaction.productIdentifier); this.validateOnServer(transaction).catch(console.error); }
private unlockContent(productIdentifier: string) { // persist entitlement locally console.log('Unlocked', productIdentifier); }
private async refreshEntitlements() { const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); console.log('Current purchases', purchases); }
private async handleTransaction(transaction: Transaction) { console.log('StoreKit transaction update:', transaction); await this.processTransaction(transaction); }
private async validateOnServer(transaction: Transaction) { await fetch('/api/validate-purchase', { method: 'POST', body: JSON.stringify({ transactionId: transaction.transactionId, receipt: transaction.receipt, purchaseToken: transaction.purchaseToken, }), }); }}Required purchase options
| Option | Platform | Description |
|---|---|---|
productIdentifier | iOS + Android | SKU/Product ID configured in App Store Connect / Google Play Console. |
productType | Android only | PURCHASE_TYPE.INAPP or PURCHASE_TYPE.SUBS. Defaults to INAPP. Always set to SUBS for subscriptions. |
planIdentifier | Android subscriptions | Base Plan ID from Google Play Console. Required for subscriptions, ignored on iOS and in-app purchases. |
quantity | iOS | Only for in-app purchases, defaults to 1. Android always purchases one item. |
appAccountToken | iOS + Android | UUID/string linking the purchase to your user. Required to be UUID on iOS; Android accepts any obfuscated string up to 64 chars. |
isConsumable | Android | Set to true to auto-consume tokens after granting entitlement for consumables. Defaults to false. |
Checking entitlement status
Use getPurchases() for a cross-platform view of every transaction the stores report:
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS,});
purchases.forEach((purchase) => { if (purchase.isActive && purchase.expirationDate) { console.log('iOS sub active until', purchase.expirationDate); }
const isAndroidIapValid = ['PURCHASED', '1'].includes(purchase.purchaseState ?? '') && purchase.isAcknowledged;
if (isAndroidIapValid) { console.log('Grant in-app entitlement for', purchase.productIdentifier); }});Platform behavior
- iOS: Subscriptions include
isActive,expirationDate,willCancel, and StoreKit 2 listener support. In-app purchases require server receipt validation. - Android:
isActive/expirationDateare not populated; call the Google Play Developer API with thepurchaseTokenfor authoritative status.purchaseStatemust bePURCHASEDandisAcknowledgedmust betrue.
API quick reference
isBillingSupported()– check for StoreKit / Google Play availability.getProduct()/getProducts()– fetch price, localized title, description, intro offers.purchaseProduct()– initiate StoreKit 2 or Billing client purchase flow.restorePurchases()– replay historical purchases and sync to current device.getPurchases()– list all iOS transactions or Play Billing purchases.manageSubscriptions()– open the native subscription management UI.addListener('transactionUpdated')– handle pending StoreKit 2 transactions when your app starts (iOS only).
Best practices
- Show store pricing – Apple requires displaying
product.titleandproduct.priceString; never hardcode. - Use
appAccountToken– deterministically generate a UUID (v5) from your user ID to link purchases to accounts. - Validate server-side – send
receipt(iOS) /purchaseToken(Android) to your backend for verification. - Handle errors gracefully – check for user cancellations, network failures, and unsupported billing environments.
- Test thoroughly – follow the iOS sandbox guide and Android sandbox guide.
- Offer restore & management – add UI buttons wired to
restorePurchases()andmanageSubscriptions().
Troubleshooting
Products not loading
- Make sure the bundle ID / application ID matches store configuration.
- Confirm the product IDs are active and approved (App Store) or activated (Google Play).
- Wait several hours after creating products; store propagation is not instant.
Purchase cancelled or stuck
- Users can cancel mid-flow; wrap calls in
try/catchand surface friendly error messages. - For Android, ensure test accounts install the app from Play Store (internal track) so Billing works.
- Check logcat/Xcode for billing errors when running on device.
Subscription state incorrect
- Use
getPurchases()to compare store data with your local entitlement cache. - On Android, always query the Google Play Developer API with the
purchaseTokento obtain expiration dates or refund status. - On iOS, check
isActive/expirationDateand validate receipts to detect refunds or revocations.