Google Play 결제 구현하기
Google Play에서 소모성 상품을 판매하고 사용자에게 지급하려면 아래 절차를 순서대로 완료하세요.
시작하기 전에 공통 사전 준비를 마치세요.
전체 흐름
영수증 검증과 상품 지급은 9단계에서 앱 서버가 Hive Axyl 서버 API로 처리합니다. 각 단계에서 무엇을 조작하고 무엇을 호출하는지는 아래와 같습니다.
| 순서 | 구분 | 하는 일 |
|---|---|---|
| 1 | 스토어 콘솔 | Google Play 콘솔에서 라이선스 키와 서비스 계정 키 발급 |
| 2 | Hive 콘솔 | 인증 정보 등록과 상품 등록 |
| 3 | Hive Axyl SDK, 스토어 플러그인 | SDK 모듈과 Google 결제 플러그인 설치 및 등록 |
| 4 | 레시피 코드 | 레시피 폴더 복사 |
| 5 | Hive Axyl SDK, 스토어 플러그인 | 상품 목록 조회 |
| 6 | 레시피 코드 | PurchaseOrder 생성 |
| 7 | 레시피 코드 | 구매 시작 |
| 8 | 레시피 코드 | 구매 정보 준비 |
| 9 | 앱 코드, 앱 서버, Hive Axyl 서버 API | 영수증 검증과 상품 지급 |
| 10 | 레시피 코드 | 거래 종료 |
| 11 | Hive Axyl SDK | 지급 결과 기록 |
| 12 | Hive 콘솔, Hive Axyl 서버 API | 구매 내역 확인 |
거래 종료는 상품 지급이 끝난 뒤에 호출하세요
10단계의 거래 종료는 이 구매가 끝났다고 표시하는 동작입니다. 미지급 구매 복구는 종료되지 않은 구매만 찾으므로, 지급보다 종료를 먼저 처리하면 도중에 앱이 중단되었을 때 해당 구매를 다시 찾을 수 없습니다.
1. Google Play 콘솔 설정
-
스토어 콘솔 Google Play 콘솔에서 설정합니다.
상세 절차: Google Play 콘솔 결제 연동
Google Play 콘솔에서 라이선스 키와 서비스 계정 키 JSON 파일을 발급받으세요. Hive Axyl 서버가 이 값으로 Google Play에 영수증 검증을 요청합니다.
Google Play 콘솔에 판매할 인앱 상품도 함께 등록하세요. 여기에 등록한 상품 ID를 2단계에서 그대로 사용합니다.
2. Hive 콘솔 설정
- Hive 콘솔 Hive 콘솔에서 설정하거나 확인합니다.
1단계에서 받은 값을 Hive 콘솔에 등록하고 판매할 상품을 등록하세요.
| 설정 항목 | 필수 여부 | 확인할 곳 |
|---|---|---|
| 라이선스 키와 서비스 계정 키 등록 | 필수 | Google Play 인증 정보 |
| 상품 등록과 가격 입력 | 필수 | 상품 정보 등록 |
상품 ID를 두 콘솔에서 일치시키세요
Google Play 콘솔에 등록한 상품 ID와 Hive 콘솔에 등록한 상품 ID가 같아야 합니다. 값이 다르면 영수증 검증이 실패합니다.
Google Play 결제에서 실제 청구 금액은 Google Play가 관리합니다. Hive 콘솔의 상품 등록 화면에도 가격을 입력하지만, 이 값은 콘솔에서 금액을 확인하는 용도로만 사용합니다.
3. SDK 모듈과 플러그인 준비
-
Hive Axyl SDK Hive Axyl SDK를 앱에서 호출합니다.
스토어 플러그인 스토어 결제 플러그인을 앱에서 호출합니다.
상세 절차: 모듈 설치, 초기화, 로그인, 플러그인 등록 및 획득
필요한 SDK 모듈과 Google 결제 플러그인을 설치한 뒤 SDK를 초기화할 때 등록하세요. 결제는 로그인한 사용자를 기준으로 동작하므로 결제 모듈과 함께 인증 모듈 AddAuth()와 토큰 모듈 AddToken()도 등록해야 합니다. 결제 모듈을 등록하지 않으면 레시피가 FailedPrecondition 오류로 실패합니다.
- 설치할 패키지
com.com2usplatform.hiveaxyl.core: SDK 초기화와 공통 기능com.com2usplatform.hiveaxyl.auth: 결제 주체인 사용자의 로그인 세션과 토큰 발급com.com2usplatform.hiveaxyl.payments: 결제 서버 호출com.com2usplatform.hiveaxyl.payments.addon.google: Google Play 결제창 호출
아래는 SDK를 초기화할 때 인증, 토큰, 결제 모듈과 Google 결제 플러그인을 등록하는 예제 코드입니다. 예제는 개발과 테스트를 위해 결제 모듈을 샌드박스 서버에 연결하므로, 실제 서비스 빌드에서는 sandbox: true 없이 AddPayments()로 등록하세요.
using Hive.Axyl.Core;
using Hive.Axyl.Core.Unity;
using Hive.Axyl.Auth;
using Hive.Axyl.Payments;
using Hive.Axyl.Payments.Addon.Google;
var config = CoreConfig.CreateBuilder("{appId}").Build();
HiveBootstrap.Initialize(config, builder =>
{
builder.AddAuth();
builder.AddToken();
builder.AddPayments(sandbox: true);
builder.AddPlayBilling();
});
4. 레시피 코드 설치
- 레시피 코드 레시피 코드를 프로젝트에 복사합니다.
레시피는 패키지가 아니라 프로젝트에 복사해서 사용하는 소스 코드입니다. axyl-samples-unity 저장소에서 아래 항목을 Unity 프로젝트의 Assets/Recipes/ 아래로 복사하세요.
- 복사 대상
- Recipes.asmdef: 공통 어셈블리 정의
- AssemblyInfo.cs: 내부 헬퍼를 다른 레시피 어셈블리에 공개하는 설정
- Helper/: 여러 레시피가 함께 사용하는 공통 코드
- Payments/: 레시피 본체와 공통 타입
- Payments.Google/: Google Play 담당 코드
앱 코드에서 레시피를 호출하려면 앱 쪽 어셈블리 정의의 references에 Hive.Axyl.Samples.Recipes, Hive.Axyl.Samples.Recipes.Payments, Hive.Axyl.Samples.Recipes.Payments.Google을 추가하세요.
Google 결제 플러그인이 없으면 빌드에서 제외됩니다
Payments.Google/의 어셈블리 정의는 Google 결제 플러그인 패키지가 설치되어 있을 때만 컴파일됩니다. 플러그인을 설치하지 않아도 컴파일 오류는 발생하지 않으며, 해당 어셈블리만 빌드에서 제외됩니다.
5. 상품 목록 조회
-
Hive Axyl SDK Hive Axyl SDK를 앱에서 호출합니다.
스토어 플러그인 스토어 결제 플러그인을 앱에서 호출합니다.
상품 목록 조회에는 레시피가 없습니다. Hive 콘솔에 등록한 상품 ID와 Google Play가 가진 가격 정보를 합쳐야 하므로 아래 순서대로 직접 호출하세요.
| 순서 | 구분 | 호출 | 확인할 곳 |
|---|---|---|---|
| 1 | Hive Axyl SDK | ListStoreProductIdsAsync()로 서버에 등록된 상품 ID 조회 | 상품 ID 목록 조회 |
| 2 | 스토어 플러그인 | StartConnectionAsync()로 BillingClient 연결 | BillingClient 연결 |
| 3 | 스토어 플러그인 | QueryProductDetailsAsync()로 Play에서 가격 조회 | Google Play 상품 상세 정보 조회 |
| 4 | Hive Axyl SDK | FetchGoogleProductsAsync()로 서버가 두 결과를 병합 | Hive Axyl 서버 상품 상세 정보 조회 |
병합 결과의 ProductId, Price, Currency를 그대로 6단계의 PurchaseOrder에 넣으세요. 화면에 표시한 문자열을 다시 파싱하거나 값을 하드코딩하면 사용자가 본 가격과 결제 요청 금액이 달라집니다. Price가 비어 있는 상품은 가격을 확인하지 못한 상품이므로 판매하지 마세요.
6. PurchaseOrder 생성
- 레시피 코드 레시피 코드를 앱에서 호출합니다.
PurchaseOrder는 사용자가 어떤 상품을 얼마에 구매하려는지를 담는 레시피 타입이며, 모든 결제 레시피의 시작점입니다.
using Hive.Axyl.Samples.Recipes;
// 앱이 한 번 정하고 절대 바꾸지 않는 네임스페이스 UUID
const string k_Namespace = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
AccountUuid.TryCompute(k_Namespace, playerId, out var accountUuid);
if (product.Price == null)
{
// 가격을 확인하지 못한 상품은 판매하지 않습니다.
return;
}
var order = new PurchaseOrder(
productId: product.ProductId, // 5단계에서 조회한 값
price: product.Price.Value, // 화면에 표시한 가격
currency: product.Currency,
country: "KR",
language: "ko",
storePlayerId: 0L, // Google Play는 사용하지 않습니다
serverId: "server01",
iapPayload: "{\"character\":\"A\"}",
appVersion: Application.version,
accountUuid: accountUuid);
AccountUuid.TryCompute()는 로그인한 사용자의 playerId로 UUIDv5를 만드는 레시피 헬퍼입니다. 레시피는 이 값을 Google Play의 ObfuscatedAccountId로 전달하며, Hive Axyl 서버는 영수증을 검증할 때 두 값을 대조합니다. 생성 규칙은 AccountUuid 생성을 참조하세요.
iapPayload에는 지급 대상을 특정할 정보를 넣으세요. 이 값은 검증 응답에 그대로 돌아오므로, 나중에 구매를 복구할 때 누구에게 무엇을 지급해야 하는지 판단하는 근거가 됩니다.
7. 구매 시작
- 레시피 코드 레시피 코드를 앱에서 호출합니다.
사용자가 결제 버튼을 누르면 레시피의 InitiatePurchaseAsync()를 호출하세요. 이 메서드 하나가 구매 의사를 서버에 기록하고, Play 결제창을 열어 결과를 받는 것까지 처리합니다.
구매를 시작하기 전후로 아래 두 가지를 앱이 직접 기록하세요. 예제 코드의 RecordOrderAsync()와 pendingStore는 앱이 만드는 기록 메서드와 저장소입니다. pendingStore의 저장과 삭제는 사용 예제의 IPurchaseApplication에서 SavePendingAsync()와 MarkClosedAsync()가 맡는 역할입니다. 영수증과 토큰 값은 로그에 남기지 마세요.
| 기록 | 기록 시점 | 키 | 용도 |
|---|---|---|---|
| 주문 기록 | InitiatePurchaseAsync() 호출 전 | 계정과 상품 ID | 편의점 충전 같은 지연 결제는 started.Pending 없이 끝났다가 나중에 완료되므로, 이런 구매를 복구할 때 구매 당시 금액을 채웁니다. 앱을 다시 설치해도 남도록 앱 서버에 기록하세요. |
| 구매 정보 | Pending을 받을 때마다, 결과 상태를 확인하기 전 | StoreTransactionId | 검증이나 종료가 실패해도 이 구매의 영수증과 주문 정보를 잃지 않습니다. 앱을 다시 실행해도 남아 있어야 하며, 거래 종료에 성공한 뒤에만 지웁니다. |
var recipe = new ConsumablePurchaseRecipe(new GooglePurchaseSource());
// 구매를 시작하기 전에 구매 당시의 주문 정보를 기록합니다.
await RecordOrderAsync(order);
InitiatePurchaseOutcome started = await recipe.InitiatePurchaseAsync(order, token);
if (started.Pending != null)
{
// 결과 상태와 관계없이 먼저 저장합니다.
await pendingStore.SaveAsync(started.Pending);
}
switch (started.Status)
{
case InitiatePurchaseStatus.Success:
// 8단계로 진행합니다.
break;
case InitiatePurchaseStatus.UserCanceled:
// 결제창만 닫혔습니다. 아무것도 청구되지 않았습니다.
break;
case InitiatePurchaseStatus.BusinessOutcome
when started.BusinessOutcome == PurchaseBusinessOutcome.StorePurchasePending:
// 결제가 아직 완료되지 않았습니다. 지급하지 마세요.
// 결제가 완료되면 미지급 구매 복구에서 처리합니다.
break;
case InitiatePurchaseStatus.BusinessOutcome
when started.BusinessOutcome == PurchaseBusinessOutcome.StoreItemAlreadyOwned:
// 이전 구매가 소비되지 않았습니다. 미지급 구매 복구를 먼저 실행하세요.
break;
default:
Debug.LogError($"{started.FailedStep}: {started.BusinessOutcome} {started.Error?.Message}");
break;
}
레시피가 내부에서 수행하는 작업은 아래와 같습니다.
| 구분 | 호출 | 확인할 곳 |
|---|---|---|
| Hive Axyl SDK | CreatePrePurchaseAsync()로 구매 의사를 서버에 기록 | 구매 사전 정보 저장 |
| 스토어 플러그인 | StartConnectionAsync()로 BillingClient 연결 | BillingClient 연결 |
| 스토어 플러그인 | QueryProductDetailsAsync()로 결제할 상품을 다시 조회 | Google Play 상품 상세 정보 조회 |
| 스토어 플러그인 | PurchasesUpdated 이벤트 구독 | 구매 결과 이벤트 구독 |
| 스토어 플러그인 | LaunchBillingFlowAsync()로 결제창 호출과 결과 수신 | 결제창 호출 |
결제창이 열리는 순간 구매 결과가 도착할 수 있으므로 레시피는 이벤트를 먼저 구독한 뒤에 결제창을 엽니다.
Play 결제 라이브러리는 이번 연결에서 조회한 상품만 결제창에 표시합니다. 그래서 레시피는 결제창을 열기 전에 해당 상품을 다시 조회하며, 5단계에서 상품 목록을 조회했더라도 이 호출은 생략되지 않습니다.
성공하면 started.Pending에 PendingPurchase가 담깁니다. 이 객체를 그대로 다음 단계에 전달하세요. 영수증 문자열만 따로 꺼내어 전달하면 거래를 종료할 때 필요한 구매 토큰이 사라집니다.
8. 구매 정보 준비
- 레시피 코드 레시피 코드를 앱에서 호출합니다.
PreparePurchaseAsync()를 호출해 구매 결과를 Hive Axyl 서버에 기록하고, 앱 서버에 보낼 구매 정보를 준비하세요. 레시피는 이 단계에서 영수증 검증을 요청하지 않으므로, 성공 결과를 받아도 아직 상품을 지급하면 안 됩니다.
PreparePurchaseOutcome prepared = await recipe.PreparePurchaseAsync(started.Pending, token);
if (prepared.Pending != null)
{
// 준비 단계가 돌려준 값으로 저장한 구매 정보를 교체합니다.
await pendingStore.SaveAsync(prepared.Pending);
}
if (prepared.Status != PreparePurchaseStatus.Success)
{
// 저장한 구매 정보는 지우지 않습니다. 미지급 구매 복구에서 마무리합니다.
return;
}
// 9단계에서 prepared.Pending을 앱 서버에 보냅니다.
레시피가 내부에서 수행하는 작업은 아래와 같습니다.
| 구분 | 호출 | 확인할 곳 |
|---|---|---|
| Hive Axyl SDK | RecordStorePurchaseAsync()로 구매 결과 저장 | 결제 결과 데이터 저장 |
9. 영수증 검증과 상품 지급
-
앱 코드 앱에서 직접 구현합니다.
앱 서버 앱 서버에서 직접 구현합니다.
Hive Axyl 서버 API 앱 서버에서 Hive Axyl 서버 API를 호출합니다.
상세 절차: Hive Axyl Server API로 영수증 검증
영수증 검증과 상품 지급은 앱 서버가 처리하며, 레시피는 이 단계에 관여하지 않습니다. 앱이 8단계의 prepared.Pending을 앱 서버에 보내면, 앱 서버는 Hive Axyl 서버 API로 영수증을 검증하고 상품을 한 번만 지급한 뒤 그 결과를 앱에 응답합니다.
9.1. 앱 서버에 검증과 지급 요청
앱에서 앱 서버를 호출해 영수증 검증과 상품 지급을 요청하세요. 앱 서버에는 prepared.Pending의 Market, AxylReceipt, VerifyToken, OrderId, StoreTransactionId와 Order에 담긴 주문 정보를 보내세요. 앱 서버를 호출하는 방법과 응답 형식은 앱이 정합니다.
예제 코드의 VerifyAndGrantOnceAsync()는 앱 서버를 호출하는 앱의 메서드입니다. 사용 예제에서는 IPurchaseApplication.VerifyAndGrantOnceAsync()가 같은 역할을 합니다. 이 메서드는 아래와 같이 구현하세요.
- 두 번째 인자
requestType: 신규 구매인지 구매 복구인지를 나타내는 값. 인자 형식은 앱이 정함- 예제 코드: 영수증 검증 요청의
requestType에 넣을 값1을 그대로 전달 - 사용 예제:
PurchaseVerificationKind.NewPurchase또는PurchaseVerificationKind.Recovery를 전달하고, 앱 서버 연동 코드가 이 값을 각각1과2로 바꿈
- 예제 코드: 영수증 검증 요청의
- 반환값: 앱 서버가 지급 완료로 응답하면
new PurchaseDeliveryConfirmation(), 거절이나 실패로 지급을 확인하지 못하면null hiveAxylTransactionId보관:PurchaseDeliveryConfirmation에는 이 값을 담는 필드가 없으므로, 앱 서버 응답에서 따로 보관해 11단계의 지급 결과 기록에 사용
// 앱 서버에 영수증 검증과 상품 지급을 요청합니다.
PurchaseDeliveryConfirmation delivery = await VerifyAndGrantOnceAsync(
prepared.Pending,
requestType: 1, // 1: 신규 구매. 사용 예제의 PurchaseVerificationKind.NewPurchase
token);
if (delivery == null)
{
// 앱 서버가 지급을 확인하지 않았습니다.
// 저장한 구매 정보를 그대로 두고 거래를 종료하지 않습니다.
return;
}
// 앱 서버 응답의 hiveAxylTransactionId는 VerifyAndGrantOnceAsync()가 따로 보관합니다.
// 11단계에서 axylTransactionId로 사용합니다.
9.2. Hive Axyl 서버 API로 영수증 검증
앱 서버는 Hive Axyl 서버 API의 POST /payment/v1/purchase/verify를 호출해 영수증을 검증하세요. 요청 헤더 X-App-Id에는 Hive 콘솔에 등록한 App ID를 넣고, 요청을 보낼 주소는 기본 URL을 참조하세요. 호출에 필요한 앱 서버용 액세스 토큰은 토큰 발급으로 받습니다. 요청과 응답의 모든 필드는 소모성 상품 영수증 검증을 참조하세요.
요청 필드에는 앱이 보낸 PendingPurchase 값을 아래와 같이 넣습니다.
providerId:GOOGLEaxylReceipt:VerifyToken에 담긴 Google Play 구매 토큰productId,price,currency,country,language,serverId,iapPayload,accountUuid,appVersion:Order의 같은 이름 값orderId:OrderIdstoreTransactionId:StoreTransactionIdrequestType: 신규 구매를 나타내는1
9.3. 상품 지급과 지급 완료 응답
앱 서버는 검증 응답을 확인한 뒤 상품을 한 번만 지급하고 그 결과를 앱에 응답하세요. 앱 서버가 해야 할 일은 아래와 같습니다.
- 검증 응답의
hiveAxylProductId,hiveAxylPrice,hiveAxylCurrency가 사용자에게 판매한 상품, 금액과 같은지 확인합니다.hiveAxylPurchaseCancelState가1이면 취소된 결제이므로 지급하지 않습니다. - 앱 서버의 지급 기록에서 이미 지급한 구매인지 확인합니다.
hiveAxylDuplicated가true이면 반드시 확인해야 합니다. 이미 지급한 구매라면 3번을 건너뜁니다. - 상품을 지급하고 지급 완료를 기록합니다.
- 앱에 지급 완료로 응답합니다. 이미 지급한 구매도 지급 완료로 응답해야 앱이 10단계에서 거래를 종료합니다. 11단계의 지급 결과 기록에 쓰도록 검증 응답의
hiveAxylTransactionId도 함께 돌려줍니다.
지급을 판단할 때 함께 확인하는 응답 값은 아래와 같습니다.
hiveAxylAccountUuidCompare: 결제한 계정과 요청한 계정의 대조 결과.1은 일치,2는 불일치,9는 대조할 수 없음hiveAxylPayload: 6단계에서 전달한iapPayload가 그대로 돌아오는 값
앱이 보낸 값만 믿고 지급하지 마세요
앱 서버는 요청한 사용자의 계정을 직접 인증하고, 상품과 금액은 검증 응답으로 확인하세요. 앱이 만드는 PurchaseDeliveryConfirmation은 결제를 증명하는 값이 아닙니다. 지급 여부는 언제나 앱 서버가 판단합니다.
10. 거래 종료
- 레시피 코드 레시피 코드를 앱에서 호출합니다.
앱 서버가 지급 완료로 응답한 뒤에 ClosePurchaseAsync()를 호출하세요. 9단계에서 만든 delivery가 없으면 레시피는 거래를 종료하지 않고 Failure를 돌려줍니다.
ClosePurchaseOutcome closed = await recipe.ClosePurchaseAsync(
prepared.Pending, // 8단계가 돌려준 값
delivery, // 9단계에서 앱 서버의 지급 완료 응답으로 만든 값
token);
if (closed.Status == ClosePurchaseStatus.Success)
{
// 구매가 끝났으므로 저장한 구매 정보를 지웁니다.
await pendingStore.DeleteAsync(prepared.Pending);
}
else
{
// 검증과 지급은 이미 끝났고 구매만 열려 있는 상태입니다.
// 저장한 구매 정보를 그대로 두고 다시 호출하거나 미지급 구매 복구에서 마무리합니다.
}
레시피가 내부에서 수행하는 작업은 아래와 같습니다.
| 구분 | 호출 | 확인할 곳 |
|---|---|---|
| Hive Axyl SDK | RequestPurchaseAsync()로 결제 확정 | 결제 확정 요청 |
| 스토어 플러그인 | ConsumeAsync()로 Play에서 소비 처리 | 소모성 상품 거래 완료 처리 |
소비 처리는 선택이 아닙니다
소비 처리하지 않은 소모성 상품은 다시 구매할 수 없습니다. Play가 ITEM_ALREADY_OWNED로 거절하며, 확인 기간이 지나면 Google이 해당 구매를 자동으로 환불합니다.
11. 지급 결과 기록
-
Hive Axyl SDK Hive Axyl SDK를 앱에서 호출합니다.
상세 절차: 상품 지급 결과 저장
지급이 끝나면 그 결과를 Hive Axyl 서버에 기록하세요. 이 호출은 결제를 확정하거나 지급 여부를 결정하지 않으며, 운영용 기록만 남깁니다.
AxylTransactionId에는 9.1의 VerifyAndGrantOnceAsync()가 앱 서버 응답에서 따로 보관한 hiveAxylTransactionId를 넣으세요.
using Hive.Axyl.Payments;
var payments = HiveCore.Resolve<IPaymentsService>();
await payments.ItemResultAsync(new ItemResultBody
{
AxylTransactionId = axylTransactionId, // 9.1에서 앱 서버 응답으로 받아 따로 보관한 hiveAxylTransactionId
Status = 1, // 1: 지급 성공
Assets = new[]
{
new ItemResultAsset { AssetId = "gold", AssetName = "골드", Quantity = 100 },
},
});
12. 구매 내역 확인
-
Hive 콘솔 Hive 콘솔에서 설정하거나 확인합니다.
Hive Axyl 서버 API 앱 서버에서 Hive Axyl 서버 API를 호출합니다.
운영 중에는 구매 내역을 조회해 결제 상태와 지급 상태를 확인하세요.
| 구분 | 방법 | 확인할 곳 |
|---|---|---|
| Hive 콘솔 | 콘솔에서 조회 조건을 지정해 확인 | 조회 조건 지정 |
| Hive Axyl 서버 API | 앱 서버에서 구매 내역 조회 API 호출 | 구매 내역 조회 |
실패 처리
레시피 메서드는 예외를 던지지 않고 결과 객체를 돌려줍니다. 언제나 Status부터 확인하세요.
Status | 의미 | 앱이 할 일 |
|---|---|---|
Success | 해당 단계가 정상적으로 끝났습니다. | 다음 단계로 진행합니다. |
BusinessOutcome | Hive Axyl 서버나 마켓이 요청을 거절했습니다. | BusinessOutcome 값에 따라 분기합니다. 값별 대응 방법은 결제 레시피의 거절 사유를 참조하세요. |
Failure | 네트워크 오류나 타임아웃 같은 기술적 실패입니다. | Error를 기록하고 재시도 여부를 판단합니다. |
UserCanceled | 사용자가 결제창을 닫았습니다. | 오류가 아니므로 상점으로 돌아갑니다. |
UserCanceled는 구매를 시작하는 단계에만 있습니다. 준비와 종료 단계의 결과 타입에는 없으므로 그 값으로 분기하면 컴파일되지 않습니다.
실패해도 PendingPurchase를 버리지 마세요
영수증을 받은 시점에는 이미 사용자에게 청구가 끝난 상태입니다. PreparePurchaseOutcome과 ClosePurchaseOutcome은 실패할 때도 전달받은 Pending을 그대로 돌려줍니다. 돌아온 Pending으로 7단계에서 저장한 구매 정보를 교체해 두어야 나중에 그 구매를 마무리할 수 있습니다. 9단계에서 앱 서버가 지급을 확인하지 못한 경우에도 구매 정보를 그대로 두세요. 저장한 구매 정보는 거래 종료에 성공한 뒤에만 지우세요.
다음 단계
결제는 끝났지만 지급이 마무리되지 않은 구매를 처리하려면 Google Play 미지급 구매 복구를 참조하세요.