PWA가 Google Play에 등록되어 있고 인앱 상품 또는 정기 결제를 판매하여 수익을 창출하려는 경우 Play 정책에 따라 Play 결제를 구현해야 합니다. PWA에서 구현해야 하는 API는 Digital Goods API와 Payment Request API 두 가지입니다.
Digital Goods API
Digital Goods API는 앱과 Google Play 간의 인터페이스입니다. 이를 통해 Play Console에서 인앱 상품 및 정기 결제에 대해 입력한 디지털 제품과 세부정보를 가져올 수 있으며, 사용자가 기존에 구매한 내역을 가져올 수도 있습니다. Play Console에 아직 인앱 상품 또는 정기 결제를 추가하지 않은 경우 Play 결제 설정을 위한 Play Console 설정을 따르세요.
2021년 11월 30일에 디지털 상품 API 2.0 구현과 함께 ChromeOS 96이 출시되었습니다.
디지털 상품 API의 첫 번째 버전의 오리진 트라이얼이 2022년 1월 30일에 종료되었습니다. 따라서 이제 지원 중단되었으며 API의 v2만 사용할 수 있습니다.
2022년 6월 23일에 디지털 상품 API 2.1 구현이 포함된 ChromeOS 103이 출시되었습니다. 이 출시에는 브레이킹 체인지가 없으며 listPurchaseHistory() 및 itemType이라는 새로운 메서드와 추가 필드만 포함되어 있습니다.
오리진 트라이얼 등록
참고: 디지털 상품 API는 현재 개발자가 새로운 웹 API에 조기 액세스할 수 있는 메커니즘인 오리진 트라이얼을 통해 제공됩니다. Digital Goods API v2 오리진 트라이얼에 등록하고 토큰을 요청해야 합니다. 이 토큰은 오리진의 모든 페이지에서 제공해야 합니다.
오리진 트라이얼에 등록하면 토큰이 작동하는 기간을 보장하는 '유효 기간'이 표시됩니다. 무료 체험에 계속 참여하려면 해당 날짜가 다가오면 토큰을 갱신하세요. 오리진 트라이얼로 제공되는 API는 변경될 수 있으므로 참여 중인 오리진 트라이얼의 최신 변경사항을 확인하세요. 문제가 있는 경우 디지털 상품 API 문서를 참고하세요.
Payment Request API
Payment Request API는 구매가 이루어질 때 실제 결제 거래를 처리합니다. 디지털 상품 API가 제공하는 상품 세부정보를 활용하여 적절한 결제 수단(이 경우 Google Play 결제)을 사용하여 인앱 구매를 진행합니다.
디지털 상품 API 기능 감지
window 객체에서 getDigitalGoodsService 메서드를 확인하여 웹사이트에서 오리진 트라이얼을 통해 API를 올바르게 사용 설정했는지 감지할 수 있습니다.
if ('getDigitalGoodsService' in window) { // Digital Goods API is supported! } else { console.log('DigitalGoodsService is not available.'); // Use another payment method }
Google Play 결제 서비스에 연결
Digital Goods API는 Payment Request API가 브라우저에 구애받지 않고 다양한 결제 서비스 제공업체와 함께 사용할 수 있는 것과 마찬가지로 다양한 브라우저 및 디지털 스토어와 호환되도록 설계되었습니다. Google Play 결제와 연결된 서비스 인스턴스를 가져오려면 "https://play.google.com/billing" 문자열을 결제 수단으로 getDigitalGoodsService()에 전달하세요.
메서드에서 오류가 발생하면 Google Play 결제 수단을 사용할 수 없습니다 (예: 사용자가 브라우저를 통해 PWA에 액세스하는 경우). 대신 거래에 다른 결제 수단을 제공해야 합니다.
if ('getDigitalGoodsService' in window) { // Digital Goods API is supported! try { const service = await window.getDigitalGoodsService('https://play.google.com/billing'); // Google Play Billing service is available } catch (error) { // Google Play Billing service is not available. Use another payment flow. } }
상품 세부정보 가져오기
디지털 상품 서비스가 Google Play에 연결되면 API를 사용하여 제품 및 구매에 관한 정보를 가져올 수 있습니다.
getDetails() 메서드를 사용하면 Play Console에서 설정한 항목에 관한 정보를 가져올 수 있습니다. 제품명, 설명, 가격과 같은 정보는 사용자가 구매 가능한 제품과 가격을 알 수 있도록 앱 UI에 표시해야 합니다.
getDetails() 메서드에는 Play Console에서 생성한 인앱 상품 및 정기 결제의 제품 ID에 해당하는 항목 ID 목록이 필요합니다.
const itemDetails = await service.getDetails(['product_1', 'product_2', 'product_3']); for (const item of itemDetails) { // Display item information to user displayItem(item.title, item.description, item.price); }
사용자의 언어에 맞는 가격을 가져오려면 다음과 같이 추가 형식을 지정해야 합니다.
const localePrice = new Intl.NumberFormat(navigator.language, { style: 'currency', currency: item.price.currency, }).format(item.price.value);
참고: Digital Goods API는 항목 ID 목록을 가져오는 방법을 제공하지 않습니다. 대신 클라이언트에 하드 코딩하거나 백엔드 서버에서 가져와야 합니다. Google Play Developer API를 사용하면 백엔드에서 상품 ID 목록을 쿼리할 수 있습니다. 백엔드 서버에서 주요 Play 결제 구성요소를 구현하는 방법을 자세히 알아보세요. 어떤 솔루션을 선택하든 항목 ID가 Play Console의 항목 ID와 일치해야 합니다.
API v2.1에서 getDetails()에 의해 반환되는 필드 중 하나는 itemType입니다. 해당 항목이 인앱 상품인지 정기 결제인지 각각 나타내는 값이 ”product” 또는 ”subscription”인 enum입니다. 각 제품 유형에 서로 다른 처리를 적용해야 하는 경우 두 유형의 제품을 구분할 수 있으면 유용합니다. 예를 들어 사용자가 구독할 수 있는 특정 페이지와 구독 외 다른 제품을 위한 페이지가 있을 수 있습니다. 또한 백엔드에서 사용할 적절한 Google Play Developer API REST 리소스 (purchases.products 또는 purchases.subscriptions)를 파악하는 데도 유용합니다.
상품 구매
제품과 세부정보가 사용자에게 표시되면 Payment Request API를 사용하여 구매 흐름을 빌드할 수 있습니다. Digital Goods API와 함께 사용하는 경우 하나의 입력 매개변수(methodData)만 필요합니다.
Play 결제에서는 한 번에 하나의 상품만 구매할 수 있습니다. 상품의 가격과 세부정보는 이미 Play 서버에서 알고 있으므로 details 매개변수가 필요하지 않습니다. 자세한 내용은 설명을 참고하세요.
PaymentRequest에서 methodData 매개변수의 supportedMethods 멤버를 사용하여 Google Play 결제를 "https://play.google.com/billing" 문자열이 있는 결제 수단으로 식별합니다. 그런 다음 data 멤버에서 항목 ID를 sku로 전달합니다.
const paymentMethodData = [ { supportedMethods: 'https://play.google.com/billing', data: { sku: item.itemId, }, }, ];
그런 다음 결제 요청을 만들고 show()를 호출하여 결제 흐름을 시작합니다.
const request = new PaymentRequest(paymentMethodData); const paymentResponse = await request.show();
그러면 사용자에게 Play 구매 UI가 표시되고, 여기에서 구매하려는 제품에 관한 세부정보를 확인할 수 있습니다. 거래를 포기하거나 결제를 진행할 수 있습니다. 사용자가 결제를 취소하면 show()에서 반환된 프로미스가 오류와 함께 거부됩니다. 사용자가 결제를 완료하고 구매를 완료하면 프로미스가 PaymentResponse로 처리됩니다. 결제 응답의 details 속성에서 구매 토큰이 반환됩니다.
사기를 방지하려면 백엔드 서버에서 구매 및 구매 토큰을 인증해야 합니다. 사용자와 연결된 구매 토큰을 추적하는 것도 좋습니다. 백엔드 서버에서 인증을 구현하는 방법을 알아보세요.
구매를 검증한 후 결제 응답에서 complete()를 호출하여 결제 흐름을 완료하고 결제 UI를 닫습니다. 선택사항인 result 문자열을 전달하여 결제 프로세스의 상태를 나타낼 수도 있습니다. 이 결과를 사용자에게 표시할지 여부는 브라우저에 달려 있습니다. Chrome은 사용자에게 표시되는 단서를 만들지 않으므로 PWA에 자체 오류 또는 성공 메시지를 표시하는 것이 좋습니다.
/* Changes were recently made so that the PaymentResponse `details` property returns the purchase token as `purchaseToken` instead of `token`. Note that `token` will be deprecated at some point in the future. To ensure that your app won't be affected by this, make the change to `purchaseToken` in your client code and use the latest version of Bubblewrap (v1.13.5 and later) to update and generate a new app package to upload to the Play Console. */ const { purchaseToken } = paymentResponse.details; let paymentComplete; if (validatePurchaseOnBackend(purchaseToken)) { paymentComplete = await paymentResponse.complete('success'); // Let user know their purchase transaction has successfully completed and been verified } else { paymentComplete = await paymentResponse.complete('fail'); // Let user know their purchase transaction failed to verify }
구독 업그레이드 및 다운그레이드
이 구매 흐름은 인앱 상품과 정기 결제 구매 모두에 동일합니다. 하지만 정기 결제의 경우 Google Play에는 업그레이드 및 다운그레이드와 같은 추가 구매 옵션이 있습니다. 결제 수단의 data를 빌드할 때 업그레이드 또는 다운그레이드 흐름을 시작하려면 다음을 전달해야 합니다.
sku: 업그레이드 또는 다운그레이드할 새 정기 결제의 상품 ID입니다.oldSku: 사용자의 현재 정기 결제의 상품 ID입니다.purchaseToken: 사용자의 현재 정기 결제의 구매 토큰입니다. 앞서 언급한 것처럼 백엔드에서 구매 토큰을 추적하는 것이 좋습니다. 이 시나리오와 기타 시나리오에서는 사용자를 현재 구매 및 구매 토큰과 연결해야 합니다.prorationMode: 사용자의 현재 정기 결제를 대체할 때 새 정기 결제에 요금이 청구되는 방식입니다.
| 비례 배분 모드 | 설명 |
|---|---|
immediateAndChargeProratedPrice |
정기 결제가 즉시 업그레이드되며 결제 주기는 동일하게 유지됩니다. 남은 기간의 가격 차이는 사용자에게 청구됩니다. |
immediateAndChargeFullPrice |
정기 결제가 업그레이드되거나 다운그레이드되고 새 사용 권한과 관련된 전체 가격이 사용자에게 즉시 청구됩니다. 이전 정기 결제의 남은 값은 새 정기 결제에 대해 시간에 따라 비례 배분됩니다. 이 비례 배분 모드는 최근 Google Play 결제 라이브러리 4.0 출시에서 추가되었습니다. 이제 버전 1.13.5부터 Bubblewrap을 통해 사용할 수 있습니다. |
immediateWithoutProration |
일시적으로 사용 중지됨 이 일할 계산 모드에는 사용자가 한 청구 주기에 추가 결제 없이 업그레이드된 구독을 이용할 수 있는 잠재적인 사기 경로가 있습니다. 문제 해결을 위해 노력하는 동안 이 모드는 일시적으로 사용 중지됩니다. |
immediateWithTimeProration |
정기 결제가 즉시 업그레이드 또는 다운그레이드됩니다. 남은 기간은 가격 차이를 기반으로 조정되며 다음 결제일을 앞당겨 새 정기 결제에 적립됩니다. 이것이 기본 동작입니다. |
deferred |
정기 결제가 갱신될 때만 업그레이드 또는 다운그레이드됩니다. 이는 특히 다운그레이드에 유용합니다. |
unknownSubscriptionUpgradeDowngradePolicy |
설정된 정책이 없습니다. 권장되지 않습니다. |
Google Play Billing Library 참조 문서에서 다양한 일할 계산 모드에 대해 자세히 알아보세요. 정기 결제 업그레이드 및 다운그레이드와 일할 계산 모드 추천에 관한 자세한 내용은 Android 개발자 문서를 참고하세요.
이러한 추가 필드의 사용은 다음과 같습니다.
const paymentMethod = [ { supportedMethods: 'https://play.google.com/billing', data: { sku: item.itemId, oldSku: oldPurchase.itemId, purchaseToken: oldPurchase.purchaseToken, prorationMode: 'immediateAndChargeProratedPrice', }, }, ];
여기서 item는 사용자가 업그레이드 또는 다운그레이드하려는 새 정기 결제의 ItemDetails이고 oldPurchase는 사용자의 현재 정기 결제의 PurchaseDetails입니다.
구매 확인
사용자가 상품을 구매하면 적절한 권한 (방금 구매한 상품 또는 콘텐츠에 대한 액세스)을 부여해야 합니다. 그런 다음 구매를 확인합니다. 구매를 확인하면 구매를 적절하게 수신하고 처리했음을 Google Play에 알릴 수 있습니다.
참고: 구매 후 72시간 이내에 구매를 확인하지 않으면 사용자에게 결제가 환불되고 구매가 취소됩니다. 구매 토큰이 더 이상 유효하지 않으므로 기존 구매를 쿼리할 때 취소된 구매가 반환되지 않습니다. 이렇게 하면 네트워크 오류로 인해 사용자에게 상품에 대한 권한이 부여되지 않는 경우 부적절하게 요금이 청구되지 않습니다.
Google Play Developer API를 사용하여 백엔드 서버에서 구매를 확인해야 합니다. 백엔드 서버에서 사용 권한을 부여한 후 구매를 함께 확인하는 것이 좋습니다.
- 사용자가 클라이언트 측에서 구매한 후 구매 토큰과 상품 ID를 백엔드 서버에 대한 요청으로 전송합니다.
- 백엔드에서 구매를 확인하기 위해 구매에 관한 세부정보를 가져오려면 다음을 호출하세요.
- 인앱 상품의 경우 purchases.products.get
- purchases.subscriptions.get을 사용합니다.
- 백엔드 데이터베이스에서 적절한 권한을 부여합니다.
- 그런 다음 다음을 호출하여 구매를 확인합니다.
- 인앱 상품의 경우 purchases.products.acknowledge
- 구독의 경우 purchases.subscriptions.acknowledge를 사용합니다.
구매 소비
구매를 확인하면 사용자가 이제 상품을 소유하고 있으며 다시 구매할 수 없음을 Google Play에 알립니다. 사용자가 한 번만 구매하면 영구적으로 소유할 수 있는 항목 (예: 게임 캐릭터 스킨)인 경우 소비성 항목이 아닙니다.
또는 사용자가 한 번에 하나만 사용할 수 있는 항목일 수도 있습니다. 그런 다음 사용자가 다른 항목을 구매하려면 먼저 해당 항목을 사용해야 합니다. 사용자가 상품을 '사용'하여 상품을 소비했음을 Google Play에 알리려면 consume() 메서드를 호출해야 합니다. 그러면 Google Play에서 사용자가 다시 구매할 수 있도록 항목을 제공합니다.
사용자가 여러 개를 소유할 수 있는 상품의 경우 먼저 사용하지 않고도 반복적으로 구매할 수 있어야 합니다 (이를 반복 가능한 상품이라고 함). 마찬가지로 Google Play에서 사용자가 다시 구매할 수 있도록 하려면 이러한 항목을 '소비'해야 합니다. 따라서 사용자가 아직 상품을 사용하지 않았더라도 consume() 메서드를 호출하여 상품을 소비됨으로 표시해야 합니다.
// After the user purchases the item, send the purchase token and item ID to your backend to grant the entitlement and acknowledge it right away . . . // When the user uses the item or if it is a repeatable item, consume it so it’s available for purchase again. service.consume(purchaseToken); }
기존 구매 확인
마지막 주요 사용자 흐름은 기존 구매 (아직 소비되지 않은 인앱 상품 및 진행 중인 정기 결제)를 확인하여 사용자에게 현재 소유한 정기 결제 또는 상품을 알리는 것입니다. 이러한 기존 구매는 인앱 또는 Play 스토어에서 이루어진 모든 기기의 이전 Google Play 구매에서 비롯됩니다. Play 스토어에서 앱 외부에서 이루어진 구매를 앱 외부 구매라고 합니다.
기존 구매를 가져올 때 확인 상태도 확인하고 이전에 이루어졌지만 제대로 확인되지 않은 구매를 확인해야 합니다. 사용자의 권한이 최신 상태로 유지되고 앱에 올바르게 반영되도록 구매를 최대한 빨리 확인하는 것이 좋습니다.
Digital Goods API listPurchases() 메서드는 각 구매의 itemId 및 purchaseToken이 포함된 PurchaseDetails 목록을 반환합니다. 백엔드 서버에서 Google Play Developer API를 사용하여 구매 상태를 확인하고 적절하게 확인해야 합니다. 다음을 준수해야 합니다.
- 디지털 상품 API
listPurchases()메서드를 클라이언트 측에서 호출하여 사용자의 구매 목록을 가져옵니다. - 각 구매에 대해
purchaseToken및itemId를 백엔드에 전달합니다. - 적절한 경우 백엔드 데이터베이스에서 권한을 부여합니다.
- 그런 다음 다음을 호출하고
acknowledgementState를 확인합니다.- 인앱 상품의 경우 purchases.products.get
- purchases.subscriptions.get을 사용합니다.
- 값이 0 (아직 확인되지 않음)인 경우 다음을 호출합니다.
- 인앱 상품의 경우 purchases.products.acknowledge
- 구독의 경우 purchases.subscriptions.acknowledge를 사용합니다.
자격을 부여하기 전에 백엔드 서버에서 구매를 확인하는 방법을 자세히 알아보세요.
구매 내역
listPurchases는 사용자의 기존 구매에 관한 정보를 반환하지만 listPurchaseHistory() 메서드 (API v2.1)는 구매가 만료, 취소 또는 소비되었는지와 관계없이 사용자가 각 항목에 대해 가장 최근에 완료한 구매를 반환합니다. listPurchaseHistory() 메서드는 각 구매의 itemId 및 purchaseToken가 포함된 PurchaseDetails 목록을 반환하며, 이 목록을 백엔드 서버에서 Google Play Developer API와 함께 사용하여 자세한 정보를 가져와야 합니다.
앱 외부 구매
앱 외부 구매는 일반적인 인앱 구매 흐름에서 이루어지지 않은 구매입니다. 이러한 구매는 일반적으로 앱이 아닌 Play 스토어에서 발생합니다. 사용자가 앱 외부 구매를 할 수 있는 두 가지 주요 방법은 다음과 같습니다.
- 프로모션 코드 사용: Play 스토어 사용자 메뉴의 '혜택 및 알림' -> '프로모션 코드 사용' 또는 '결제 및 정기 결제' -> '기프트 코드 사용'
- 재구독: Play 스토어 사용자 메뉴의 '결제 및 구독' -> '구독'에서 재구독할 수 있습니다. 여기에서 사용자는 여러 앱의 모든 구독을 관리할 수 있습니다. 만료되거나 취소된 정기 결제의 경우 사용자에게 '정기 결제 재신청' 옵션이 표시됩니다.
사용자가 Play 스토어에서 정기 결제를 재신청하면 구매가 자동으로 확인되지 않아 환불이 발생할 수 있습니다. 사용자가 앱을 열어 사용하는 경우에만 정기 결제 요금이 청구되어야 하므로 이 동작은 의도된 것입니다. 사용자에게 다음과 같은 '정기 결제 확인'이 표시되어 앱을 열도록 알릴 수 있습니다.
사용자가 앱을 실행하면 개발자가 이러한 구매를 확인해야 합니다. 따라서 기존 구매를 확인 (일반적으로 앱이 처음 실행될 때)하고 아직 확인되지 않은 구매를 확인하는 것이 좋습니다.
사용자가 구독을 관리하도록 허용
좋은 사용자 환경을 위해서는 사용자가 인앱에서 정기 결제를 관리하고 취소할 수 있는 방법을 제공하는 것이 중요합니다. 설정 페이지나 메뉴에서 사용자를 앱의 Play 스토어 정기 결제 관리 페이지로 리디렉션하는 딥 링크를 만드는 것이 좋습니다. 다음 URL을 적절한 'sub-product-id' 및 'app-package-name'으로 바꿉니다.
https://play.google.com/store/account/subscriptions?sku=sub-product-id&package=app-package-name다음 단계
이 사용자 흐름과 코드 스니펫은 PWA에서 Digital Goods API와 Payment Request API를 사용하여 Play Billing을 구현하는 방법을 보여주는 기본 구현입니다. 앱의 컨텍스트와 사용 사례에 적합한 API를 활용해야 합니다. 엔드 투 엔드 구현의 예는 오픈소스 샘플을 참고하세요.
그런 다음 백엔드 서버에서 중요한 Play 결제 구성요소를 구현하여 앱을 안전하게 유지하고 항상 사용자의 권한으로 업데이트하는 방법을 살펴보세요.