Langsung ke konten

Memulai

  1. Instal paket

    Terminal window
    npm i @capgo/native-purchases
  2. Sinkronkan dengan proyek native

    Terminal window
    npx cap sync
  3. Periksa dukungan billing

    import { NativePurchases } from '@capgo/native-purchases';
    const { isBillingSupported } = await NativePurchases.isBillingSupported();
    if (!isBillingSupported) {
    throw new Error('Billing tidak tersedia di perangkat ini');
    }
  4. Muat produk langsung dari toko

    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, // Gunakan PURCHASE_TYPE.INAPP untuk produk satu kali
    });
    products.forEach((product) => {
    console.log(product.title, product.priceString);
    });
  5. Implementasikan alur pembelian & pemulihan

    import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
    const monthlyPlanId = 'monthly-plan'; // Base Plan ID dari Google Play Console
    const transaction = await NativePurchases.purchaseProduct({
    productIdentifier: 'com.example.premium.monthly',
    planIdentifier: monthlyPlanId, // WAJIB untuk langganan Android, diabaikan di iOS
    productType: PURCHASE_TYPE.SUBS,
    quantity: 1,
    });
    console.log('Transaction ID', transaction.transactionId);
    await NativePurchases.restorePurchases();
    • Buat produk in-app dan langganan di App Store Connect.
    • Gunakan StoreKit Local Testing atau Sandbox tester untuk QA.
    • Tidak perlu edit manifest. Pastikan produk Anda disetujui.
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 saja)
async initialize() {
const { isBillingSupported } = await NativePurchases.isBillingSupported();
if (!isBillingSupported) throw new Error('Billing tidak tersedia');
const { products } = await NativePurchases.getProducts({
productIdentifiers: [this.premiumProduct, this.monthlySubId],
productType: PURCHASE_TYPE.SUBS,
});
console.log('Produk dimuat', 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, // WAJIB untuk langganan Android
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) {
// simpan hak akses secara lokal
console.log('Membuka kunci', productIdentifier);
}
private async refreshEntitlements() {
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
console.log('Pembelian saat ini', purchases);
}
private async handleTransaction(transaction: Transaction) {
console.log('Update transaksi StoreKit:', 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,
}),
});
}
}
OpsiPlatformDeskripsi
productIdentifieriOS + AndroidSKU/Product ID yang dikonfigurasi di App Store Connect / Google Play Console.
productTypeAndroid sajaPURCHASE_TYPE.INAPP atau PURCHASE_TYPE.SUBS. Default ke INAPP. Selalu set ke SUBS untuk langganan.
planIdentifierLangganan AndroidBase Plan ID dari Google Play Console. Diperlukan untuk langganan, diabaikan di iOS dan pembelian in-app.
quantityiOSHanya untuk pembelian in-app, default ke 1. Android selalu membeli satu item.
appAccountTokeniOS + AndroidUUID/string yang menghubungkan pembelian ke pengguna Anda. Harus UUID di iOS; Android menerima string tersamarkan hingga 64 karakter.
isConsumableAndroidSet ke true untuk otomatis mengonsumsi token setelah memberikan hak akses untuk consumable. Default ke false.

Gunakan getPurchases() untuk tampilan lintas platform dari setiap transaksi yang dilaporkan toko:

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('Langganan iOS aktif sampai', purchase.expirationDate);
}
const isAndroidIapValid =
['PURCHASED', '1'].includes(purchase.purchaseState ?? '') && purchase.isAcknowledged;
if (isAndroidIapValid) {
console.log('Berikan hak akses in-app untuk', purchase.productIdentifier);
}
});
  • iOS: Langganan mencakup isActive, expirationDate, willCancel, dan dukungan listener StoreKit 2. Pembelian in-app memerlukan validasi receipt server.
  • Android: isActive/expirationDate tidak diisi; panggil Google Play Developer API dengan purchaseToken untuk status otoritatif. purchaseState harus PURCHASED dan isAcknowledged harus true.
  • isBillingSupported() – periksa ketersediaan StoreKit / Google Play.
  • getProduct() / getProducts() – ambil harga, judul lokal, deskripsi, penawaran intro.
  • purchaseProduct() – mulai alur pembelian StoreKit 2 atau Billing client.
  • restorePurchases() – putar ulang pembelian historis dan sinkronkan ke perangkat saat ini.
  • getPurchases() – daftar semua transaksi iOS atau pembelian Play Billing.
  • manageSubscriptions() – buka UI manajemen langganan native.
  • addListener('transactionUpdated') – tangani transaksi StoreKit 2 yang tertunda saat aplikasi Anda dimulai (iOS saja).
  1. Tampilkan harga toko – Apple mengharuskan menampilkan product.title dan product.priceString; jangan hardcode.
  2. Gunakan appAccountToken – buat UUID secara deterministik (v5) dari ID pengguna Anda untuk menghubungkan pembelian ke akun.
  3. Validasi sisi server – kirim receipt (iOS) / purchaseToken (Android) ke backend Anda untuk verifikasi.
  4. Tangani error dengan baik – periksa pembatalan pengguna, kegagalan jaringan, dan lingkungan billing yang tidak didukung.
  5. Uji secara menyeluruh – ikuti panduan sandbox iOS dan panduan sandbox Android.
  6. Tawarkan pemulihan & manajemen – tambahkan tombol UI yang terhubung ke restorePurchases() dan manageSubscriptions().

Produk tidak dimuat

  • Pastikan bundle ID / application ID cocok dengan konfigurasi toko.
  • Konfirmasikan product ID aktif dan disetujui (App Store) atau diaktifkan (Google Play).
  • Tunggu beberapa jam setelah membuat produk; propagasi toko tidak instan.

Pembelian dibatalkan atau macet

  • Pengguna dapat membatalkan di tengah alur; bungkus panggilan dalam try/catch dan tampilkan pesan error yang ramah.
  • Untuk Android, pastikan akun uji menginstal aplikasi dari Play Store (track internal) agar Billing berfungsi.
  • Periksa logcat/Xcode untuk error billing saat berjalan di perangkat.

Status langganan salah

  • Gunakan getPurchases() untuk membandingkan data toko dengan cache hak akses lokal Anda.
  • Di Android, selalu kueri Google Play Developer API dengan purchaseToken untuk mendapatkan tanggal kedaluwarsa atau status refund.
  • Di iOS, periksa isActive/expirationDate dan validasi receipt untuk mendeteksi refund atau revokasi.