Reference : https://developer.android.com/google/play/billing/billing_library_overview
1. app 수준 build.gradle 에 dependencies 추가
1
2
3
4
|
dependencies {
...
implementation 'com.android.billingclient:billing:1.2' }
|
cs |
2. 소스코드
결제기능을 구현하고자 하는 화면에 아래 구문 추가.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
public class BuyPointActivity extends AppCompatActivity implements PurchasesUpdatedListener {
Context mContext;
// create new Person
private BillingClient mBillingClient;
SkuDetails skuDetails700, skuDetails2100;
String skuID700 = "point_700", skuID2100 = "point_2100"; //제품 ID
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buy_point);
mContext = BuyPointActivity.this;
...
mBillingClient = BillingClient.newBuilder(mContext).setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@BillingResponse int billingResponseCode) {
if (billingResponseCode == BillingResponse.OK) {
// The billing client is ready. You can query purchases here.
List<String> skuList = new ArrayList<> ();
skuList.add(skuID700);
skuList.add(skuID2100);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
mBillingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
// Process the result.
if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null) {
for (SkuDetails skuDetails : skuDetailsList) {
String sku = skuDetails.getSku();
String price = skuDetails.getPrice();
if(skuID700.equals(sku)) {
skuDetails700 = skuDetails;
} else if(skuID2100.equals(sku)) {
skuDetails2100 = skuDetails;
}
}
}
}});
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
/**
* Handle a callback that purchases were updated from the Billing library
*/
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {
if (responseCode == BillingClient.BillingResponse.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
} else if (responseCode == BillingClient.BillingResponse.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
} else {
// Handle any other error codes.
}
}
private void doBillingFlow(SkuDetails skuDetails) {
BillingFlowParams flowParams;
int responseCode;
// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
flowParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build();
responseCode = mBillingClient.launchBillingFlow(BuyPointActivity.this, flowParams);
/*if(responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED) {
Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
onPurchasesUpdated(BillingClient.BillingResponse.OK, purchasesResult.getPurchasesList());
}*/
}
private void handlePurchase(Purchase purchase) {
String purchaseToken;
purchaseToken = purchase.getPurchaseToken();
mBillingClient.consumeAsync(purchaseToken, consumeListener);
}
ConsumeResponseListener consumeListener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(@BillingClient.BillingResponse int responseCode, String outToken) {
if (responseCode == BillingClient.BillingResponse.OK) {
// Handle the success of the consume operation.
// For example, increase the number of coins inside the user's basket.
}
}
};
Button.OnClickListener mClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.btn700:
doBillingFlow(skuDetails700);
break;
case R.id.btn2100:
doBillingFlow(skuDetails2100);
break;
...
}
}
};
...
}
|
cs |
doBillingFlow 안에 if(responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED)
코드는 처음에 테스트 구매 하다가 구매성공 후에 consume 처리를 안해서 다시 구매가 안 된 경우에 consume 시키려고 놔둔 코드 입니다. 구매 완료후 consume 를 제대로 하지 못한 경우 등에 이 코드를 쓰면 될 것 같습니다.
TrivialDrive v2 sample app 은 너무 복잡해서 잘 이해가 안 되고, anjlab 의 android-inapp-billing-v3 는 왠지 안내켜서 Google Play 결제 라이브러리 사용하여 구현해보았습니다. 인앱 결제 구현 구글 문서는 최신 내용이 아닌 듯 합니다. 업무에 참고 하시기 바랍니다.
ps.
-. 이 방법을 사용하면 Google 마켓 개발자 콘솔 설정에 있는 '서비스 및 API' 의 Base64 인코딩 RSA 공개 키 인 애플리케이션용 라이선스 키 를 입력하지 않아도 됩니다.
-. Manifest 에 <uses-permission android:name="com.android.vending.BILLING" /> 도 추가하지 않아도 됩니다. 다만 앱 등록시 인앱상품 생성을 위해 이 권한을 요구하긴 합니다.
-. 인앱 결제 테스트 및 디버깅(참고 : https://developer.android.com/google/play/billing/billing_testing?hl=ko)*인앱결제를 디버깅 모드에서도 테스트 하려면 알파 또는 베타 채널에 앱을 게시해야 하고, 게시하면 생기는 테스트 주소 https://play.google.com/apps/testing/com.android.test 와 같은 주소를 인터넷 앱등을 통해 접속한 후
테스트 참여 대상 관리 에 등록된
계정 주소를 입력해야 합니다. 이렇게 하고 나면 개발하는 앱의 디버깅 모드에서도 결제 테스트가 가능합니다. 테스트 참여 대상 관리 에 등록된 사용자만 테스트 주소(https://play.google.com/apps/testing/com.android.test)에 등록되는지는 확인해보지 못했습니다.
1. Google Play Console 접속2. 좌측 하단 '설정' 클릭3. 라이선스 테스트 추가테스트 하고자 하는 계정을 추가합니다. (구글 플레이에 설정된 계정 등)