* 해당 글을 참고하기 전에 해당 앱에 '관리되는 제품' 등록되어 있어야 한다.
참고 https://puch-android.tistory.com/4
* 아래 글을 참고하면서, 구글에서 제공하는 문서도 참고하면 이해하는데 도움이 된다.
참고 https://developer.android.com/google/play/billing/billing_library_overview?hl=ko
안드로이드 인앱결제 - 관리되는 제품 코드 (이하 아이템)
public class BillingEntireManager implements PurchasesUpdatedListener {
선언부
private BillingClient mBillingClient;
// 아이템 상세정보 리스트
private List<SkuDetails> mSkuDetailsList_item;
// 아이템 소비 리스너
private ConsumeResponseListener mConsumeListener;
초기화
public BillingEntireManager(Context ctx) {
this.ctx = ctx;
Log.d(TAG, "구글 결제 매니저를 초기화 하고 있습니다.");
mBillingClient = BillingClient.newBuilder(ctx).setListener(this).enablePendingPurchases().build();
mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Log.d(TAG, "구글 결제 서버에 접속을 성공하였습니다.");
getSkuDetailList();
// 아이템 결제 히스토리 가져옴
Purchase.PurchasesResult result_item = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
// 소모가 안된 상품 존재시 - 리스트에 아이템이 존재하게 된다
List<Purchase> list_item = result_item.getPurchasesList();
for (int i = 0; i < list_item.size(); i++) {
Purchase purchase = list_item.get(i);
// 계속 보류중일때
if (purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) {
// 카드사 승인중인 결제 또는 결제 보류중
} else {
// 결제 승인된 경우
handlePurchase(list_item.get(i));
}
}
} else {
Log.d(TAG, "구글 결제 서버 접속에 실패하였습니다.\n오류코드: " + billingResult.getResponseCode());
// case 구글 플레이스토어 계정 정보 인식 안될 때
}
}
@Override
public void onBillingServiceDisconnected() {
Log.d(TAG, "구글 결제 서버와 접속이 끊어졌습니다.");
}
});
// 상품 소모결과 리스너
mConsumeListener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Log.d(TAG, "상품을 성공적으로 소모하였습니다. 소모된 상품 => " + purchaseToken);
return;
} else {
Log.d(TAG, "상품 소모에 실패하였습니다. 오류코드 (" + billingResult.getResponseCode() + "), 대상 상품 코드: " + purchaseToken);
return;
}
}
};
}
상품 리스트 가져오는 메소드
- Sku 리스트에 추가되는 아이템 ID 는 본인이 콘솔에서 아이템 추가할때, 기재한 ID를 넣어야 한다.
// 구입 가능한 상품의 리스트를 받아 오는 메소드
private void getSkuDetailList() {
// 구글 상품 정보들의 ID를 만들어 줌
List<String> Sku_ID_List_INAPP = new ArrayList<>();
Sku_ID_List_INAPP.add(POINT_01_CODE);
Sku_ID_List_INAPP.add(POINT_02_CODE);
Sku_ID_List_INAPP.add(POINT_03_CODE);
Sku_ID_List_INAPP.add(POINT_04_CODE);
Sku_ID_List_INAPP.add(POINT_05_CODE);
Sku_ID_List_INAPP.add(POINT_06_CODE);
// SkuDetailsList 객체를 만듬
SkuDetailsParams.Builder params_item = SkuDetailsParams.newBuilder();
params_item.setSkusList(Sku_ID_List_INAPP).setType(BillingClient.SkuType.INAPP);
// 비동기 상태로 앱의 정보를 가지고 옴
mBillingClient.querySkuDetailsAsync(params_item.build(), new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
// 상품 정보를 가지고 오지 못한 경우
if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
Log.d(TAG, "(인앱) 상품 정보를 가지고 오던 중 오류가 발생했습니다.\n오류코드: " + billingResult.getResponseCode());
return;
}
if (skuDetailsList == null) {
Log.d(TAG, "(인앱) 상품 정보가 존재하지 않습니다.");
return;
}
//응답 받은 데이터들의 숫자를 출력
Log.d(TAG, "(인앱) 응답 받은 데이터 숫자: " + skuDetailsList.size());
//받아온 상품 정보를 차례로 호출
for (int sku_idx = 0; sku_idx < skuDetailsList.size(); sku_idx++) {
//해당 인덱스의 객체를 가지고 옴
SkuDetails _skuDetail = skuDetailsList.get(sku_idx);
//해당 인덱스의 상품 정보를 출력
Log.d(TAG, _skuDetail.getSku() + ": " + _skuDetail.getTitle() + ", " + _skuDetail.getPrice());
Log.d(TAG, _skuDetail.getOriginalJson());
}
//받은 값을 멤버 변수로 저장
mSkuDetailsList_item = skuDetailsList;
}
});
}
실제 구입요청하는 메소드
//실제 구입 처리를 하는 메소드
public void purchase(String item, Activity act) {
SkuDetails skuDetails = null;
if (null != mSkuDetailsList_item) {
for (int i = 0; i < mSkuDetailsList_item.size(); i++) {
SkuDetails details = mSkuDetailsList_item.get(i);
if (details.getSku().equals(item)) {
skuDetails = details;
break;
}
}
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
mBillingClient.launchBillingFlow(act, flowParams);
}
}
결제에 대해 소비시켜주는 함수
// 결제 요청 후 상품에대해 소비시켜주는 함수
void handlePurchase(Purchase purchase) {
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
// 인앱 소비
// TODO 인앱 구매 결과전송 함수 호출
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
mBillingClient.consumeAsync(consumeParams, mConsumeListener);
} else if (purchase.getPurchaseState() == Purchase.PurchaseState.PENDING) {
// Here you can confirm to the user that they've started the pending
// purchase, and to complete it, they should follow instructions that
// are given to them. You can also choose to remind the user in the
// future to complete the purchase if you detect that it is still
// pending.
// ex 해당 아이템에 대해 소모되지 않은 결제가 있을시
}
}
결제 성공여부 리스너
@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
//결제에 성공한 경우
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
Log.d(TAG, "결제에 성공했으며, 아래에 구매한 상품들이 나열됨");
for (Purchase _pur : purchases) {
Log.e(TAG, "purchases: " + purchases);
handlePurchase(_pur);
}
}
// 사용자가 결제를 취소한 경우
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
Log.d(TAG, "사용자에 의해 결제취소");
}
// 그 외에 다른 결제 실패 이유
else {
Log.d(TAG, "결제가 취소 되었습니다. 종료코드: " + billingResult.getResponseCode());
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
// ex 해당 아이템에 대해 소모되지 않은 결제가 있을시
}
}
}
'안드로이드' 카테고리의 다른 글
[Android] 안드로이드 Referrer 추출 (0) | 2020.08.14 |
---|---|
[Android] SNS 로그인 키해시(key hash) 추출 (0) | 2020.05.21 |
[Android] 인앱결제 예제 (BillingClient 사용) - 판매자 계정 설정 , 아이템 등록 (0) | 2020.04.13 |
[Android] 상태바 색상 변경 (api 21 이상) (0) | 2020.04.09 |
[Android] JSONObject["value"] not a string. 예외 (0) | 2020.04.08 |