본문 바로가기

시작하기

GitHub

AI-Assisted Setup으로 플러그인을 설치할 수 있습니다. 다음 명령어로 AI 도구에 Capgo 스킬을 추가하세요:

터미널 창
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins

그런 다음 아래 프롬프트를 사용하세요:

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

수동 설정을 선호한다면, 다음 명령어를 실행하여 플러그인을 설치하고 아래의 플랫폼별 지침을 따르세요:

  1. 패키지 설치

    터미널 창
    bun add @capgo/native-purchases
  2. 네이티브 프로젝트와 동기화

    터미널 창
    bunx cap sync
  3. 결제 지원 확인

    import { NativePurchases } from '@capgo/native-purchases';
    const { isBillingSupported } = await NativePurchases.isBillingSupported();
    if (!isBillingSupported) {
    throw new Error('Billing is not available on this device');
    }
  4. 스토어에서 제품 직접 로드

    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);
    });
  5. 구매 및 복원 플로우 구현

    import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
    const monthlyPlanId = 'monthly-plan'; // Base Plan ID from Google Play Console
    const transaction = await NativePurchases.purchaseProduct({
    productIdentifier: 'com.example.premium.monthly',
    planIdentifier: monthlyPlanId, // Android base plan id, required unless offerToken is passed; ignored on iOS
    productType: PURCHASE_TYPE.SUBS,
    quantity: 1,
    });
    console.log('Transaction ID', transaction.transactionId);
    await NativePurchases.restorePurchases();
    • App Store Connect에서 인앱 상품 및 구독을 생성하세요.
    • QA를 위해 StoreKit 로컬 테스트 또는 샌드박스 테스터를 사용하세요.
    • 매니페스트 수정은 필요하지 않습니다. 제품이 승인되었는지 확인하세요.
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, // Android base plan id, required unless offerToken is passed
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,
}),
});
}
}
옵션플랫폼설명
productIdentifieriOS + AndroidApp Store Connect 또는 Google Play Console에 설정된 SKU/Product ID입니다.
productTypeAndroid 전용PURCHASE_TYPE.INAPP 또는 PURCHASE_TYPE.SUBS. 기본값은 INAPP. 항상 다음으로 설정하세요: SUBS 구독 상품용.
planIdentifierAndroid 구독Google Play Console의 Base Plan ID (Product.identifier 구독이 아닌 Product.planIdentifier). Android 구독의 경우 offerToken 이 지정되지 않으면 필수입니다. iOS 및 인앱 구매에서는 무시됩니다.
offerTokenAndroid구매한 getProducts() / getProduct() 호출에서 가져온 특정 구독 또는 일시적 오퍼에 대한 토큰. 구독의 경우 planIdentifier보다 우선합니다. 현재 오퍼와 일치하지 않는 토큰은 거부됩니다.
billingPlanTypeiOS 구독구매할 StoreKit 결제 플랜입니다. 다음을 사용하세요: 'monthly' 12개월 약정으로 월 결제하는 경우 product.pricingTerms 해당 옵션을 노출합니다.
quantityiOS인앱 구매에만 적용되며, 기본값은 1입니다. Android는 항상 항목 하나를 구매합니다.
appAccountTokeniOS + Android구매를 사용자와 연결하는 UUID/문자열입니다. iOS에서는 UUID여야 하며, Android에서는 최대 64자의 난독화된 문자열을 허용합니다.
isConsumableAndroid다음으로 설정하세요 true 소모품 권한 부여 후 토큰을 자동 소모하려면. 기본값은 false.

사용 getPurchases() 스토어가 보고하는 모든 트랜잭션을 한 번에 보려면:

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);
}
});
  • iOS: 구독에는 다음이 포함됩니다: isActive, expirationDate, willCancel, 그리고 StoreKit 2 리스너를 지원합니다. 인앱 결제는 서버 영수증 검증이 필요합니다.
  • Android: isActive/expirationDate 채워지지 않습니다. Google Play Developer API를 호출하여 purchaseToken 정확한 상태를 확인하세요. purchaseState 반드시 PURCHASED 및 isAcknowledged 반드시 true.
  • isBillingSupported() – StoreKit / Google Play 사용 가능 여부 확인.
  • getProduct() / getProducts() – 가격, 현지화된 제목, 설명, 인트로 오퍼, 지원되는 iOS 가격 조건 가져오기.
  • purchaseProduct() – StoreKit 2 또는 Billing 클라이언트 구매 플로우 시작 (iOS 월간 약정 결제 플랜 포함).
  • restorePurchases() – 과거 구매 내역을 재처리하고 현재 기기와 동기화.
  • getPurchases() – 모든 iOS 거래 또는 Play Billing 구매 내역 목록 표시.
  • manageSubscriptions() – 네이티브 구독 관리 UI 열기.
  • addListener('transactionUpdated') – 앱 시작 시 대기 중인 StoreKit 2 거래 처리 (iOS 전용).

베스트 프랙티스

“Best practices” 섹션
  1. 스토어 가격 표시 – Apple은 표시를 요구합니다 product.title 및 product.priceString; 절대 하드코딩하지 마세요.
  2. 사용 appAccountToken – 사용자 ID에서 UUID(v5)를 결정적으로 생성하여 구매를 계정에 연결하세요.
  3. 서버 측에서 검증 – 전송 receipt (iOS) / purchaseToken (Android) 백엔드로 전송하여 검증하세요.
  4. 오류를 적절히 처리 – 사용자 취소, 네트워크 오류, 지원되지 않는 결제 환경을 확인하세요.
  5. 철저한 테스트 – 다음을 따르세요 iOS 샌드박스 가이드 및 Android 샌드박스 가이드.
  6. 오퍼 복원 및 관리 – UI 버튼 추가 restorePurchases() 및 manageSubscriptions().

Breaking (TypeScript 타입 정의)

섹션 제목 “Breaking (TypeScript typings)”

Product.subscriptionPeriod 이제 선택 사항입니다. Android는 가격만 있는 선불(비정기) 오퍼에서 이를 생략합니다. 읽기 product.subscriptionPeriod.unit 검사가 없으면 TypeScript 오류가 발생합니다. 다음을 사용하세요 product.subscriptionPeriod?.unit. 자세한 내용은 Android 구독 오퍼.

수익화 다음 단계

“수익 다음 단계” 섹션

구매 플로우가 작동하면 다음을 사용하세요 수익화 플레이북 첫 유료 퍼널을 계획하려면 제품 범위, ASO, 가격 책정, 페이월 배치, 분석, 이탈 피드백을 고려하세요.

상품이 로드되지 않음

  • Bundle ID / Application ID가 스토어 설정과 일치하는지 확인하세요.
  • Product ID가 App Store에서 활성 및 승인되었는지, Google Play에서 활성화되었는지 확인하세요.
  • 상품 생성 후 몇 시간 기다리세요. 스토어 반영은 즉시 이루어지지 않습니다.

구매가 취소되었거나 멈춤

  • 사용자가 결제 중간에 취소할 수 있으므로, 호출을 try/catch try-catch로 감싸고 사용자 친화적인 에러 메시지를 표시하세요.
  • Android에서는 Billing이 작동하도록 테스트 계정이 Play Store(내부 트랙)에서 앱을 설치했는지 확인하세요.
  • 디바이스에서 실행할 때 logcat/Xcode에서 결제 오류를 확인하세요.

구독 상태가 올바르지 않음

  • 사용 getPurchases() 스토어 데이터와 로컬 entitlement 캐시를 비교하려면.
  • Android에서는 항상 Google Play Developer API을(를) 다음으로 쿼리하세요. purchaseToken 만료일이나 환불 상태를 가져오려면.
  • iOS에서는 isActive/expirationDate 수신증을 검증하여 환불 또는 취소 여부를 확인하세요.

사용 중이라면 시작하기 스토어 승인 및 배포를 계획하려면, 다음 항목과 연결하세요 @capgo/native-purchases 사용하기 @capgo/native-purchases 사용 시 네이티브 기능을 위해, @capgo/capacitor-in-app-review @capgo/capacitor-in-app-review의 구현 세부 사항에 대해서는, @capgo/capacitor-in-app-review 사용하기 @capgo/capacitor-in-app-review 사용하기에서 네이티브 기능은, @capgo/capacitor-native-market @capgo/capacitor-native-market의 구현 세부 사항에 대해서는, 그리고 @capgo/capacitor-native-market 사용하기 @capgo/capacitor-native-market. 사용하기에서 네이티브 기능은