* 2018년 안드로이드 결제 구현에서 다이얼로그 목록으로 결제를 구현하는 부분때문에 많은 분들이 헷갈려하셨고,
앱도 내 앱이아니라 소스 코드를 완벽하게 공개하기 어려웠는데, 그래서 2019년!
초간단하게 안드로이드 결제를 구현하는 방법을 정리해서 다시 올린다.
(간단명료에 초점을 맞췄으므로 각 메소드에 대한 자세한 설명은 2018년 글을 참조 부탁드립니다.)
- 광고제거 버전 구현
목차
: Android In-app Billing 101~
1. 구글 플레이 콘솔 설정 - 1) 구글 플레이 콘솔에 앱 생성하기 - 2) 라이센스키 얻기 - 3) 인앱상품 등록하기 2. 안드로이드 개발시 설정 - 1) build.gradle에 라이브러리 implement하기 - 2) AndroidManifest.xml에 BILLING 권한 추가하기 - 3) 액티비티에 코드 작성 |
1-1) 구글 플레이 콘솔 설정 > 구글 플레이 콘솔에 앱 생성하기
앱 생성은 너무 쉬우니 생략!
구글 플레이 콘솔에 앱을 생성한 후 앱 버전> 프로덕션 트랙 관리> 출시를 눌러 Google에서 앱 서명 키를 관리 및 보호하도록 허용 > 계속을 눌러준다.
그러면 아래와 같이 "사용 중입니다." 표시가 뜨게된다.
1-2) 구글 플레이 콘솔 설정 > 라이센스키 얻기
위 과정을 끝내면 좌측에 서비스 및 API 탭이 생겼을 것이다.
클릭해서 라이센스키 Base64 코드를 복사해 앱 어딘가에 저장해둔다.
1-3) 인앱상품 등록하기
* 인앱상품 등록을 위해서는 APK 등록이 먼저 필요한데, 이 과정은 안드로이드 코드 구현 후 나중에 해도 된다.
관리되는 제품을 추가한다.
위에 보면 이름 및 ID란에 괄호 안에 있는 코드가 있을텐데 (ex) donate/pro_version/library_slot_10)
이것을 Sku라고 하겠다. 강의에서는 광고 제거 버전을 만들것이고, sku는 no_ads 로 할것이다.
2-1) 안드로이드 개발시 설정 > build.gradle
app 단계의 build.gradle에 결제 라이브러리를 추가한 후 Sync한다.
1 | implementation 'com.anjlab.android.iab.v3:library:1.0.44' | cs |
2-2) 안드로이드 개발시 설정 > AndroidManifest.xml
BILLING 권한을 추가한다.
1 2 3 4 5 6 7 8 9 | <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.geumson.utjjal"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="com.android.vending.BILLING" /> <application ... </application> </manifest> | cs |
2-3) 안드로이드 개발시 설정 > SomeActivity.java
이제 결제를 진행할 Activity에서 설정을 시작할 것이다.
예제에서는 SettingsActivity.java에서 광고 제거를 구매하는 로직을 넣을것이다!
뷰는 아래와 같이 생겼고, 광고 없이 앱 이용하기 버튼의 id는 btn_no_ads이다.
먼저 Activity에 BillingProcessor를 implement한다.
1 | public class SettingsActivity extends AppCompatActivity implements BillingProcessor.IBillingHandler | cs |
그러면 관련 메소드 4종이 나타나게된다.
주석으로 간략하게 설명해두었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Override public void onProductPurchased(@NonNull String productId, @Nullable TransactionDetails details) { // * 구매 완료시 호출 // productId: 구매한 sku (ex) no_ads) // details: 결제 관련 정보 } @Override public void onPurchaseHistoryRestored() { // * 구매 정보가 복원되었을때 호출 // bp.loadOwnedPurchasesFromGoogle() 하면 호출 가능 } @Override public void onBillingError(int errorCode, @Nullable Throwable error) { // * 구매 오류시 호출 // errorCode == Constants.BILLING_RESPONSE_RESULT_USER_CANCELED 일때는 // 사용자가 단순히 구매 창을 닫은것임으로 이것 제외하고 핸들링하기. } @Override public void onBillingInitialized() { // * 처음에 초기화됬을때. } | cs |
그리고 결제를 도와줄 BillingProcessor 객체를 만들어야하는데 코드는 다음과 같다.
onDestroy(), onActivityResult()도 까먹지말고 구현하기~!
Config.GooglePlayLicenseKey에서는 이전에 구한 구글플레이 라이센스키를 넣으면 된다.
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 | public class SettingsActivity extends AppCompatActivity implements BillingProcessor.IBillingHandler, View.OnClickListener { private BillingProcessor bp; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); ButterKnife.bind(this); bp = new BillingProcessor(this, Config.GooglePlayLicenseKey, this); bp.initialize(); } ... @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (!bp.handleActivityResult(requestCode, resultCode, data)) { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onDestroy() { if (bp != null) { bp.release(); } super.onDestroy(); } } | cs |
bp를 통해서 구매정보를 파악하고, 구매를 시킬수도 있지만
모든 Activity에 BillingProcessor를 implement할 수는 없으니(귀찮아서) 나는 구매 정보를 업데이트하고 저장하기 위한 SharedPreferences를 다루는 객체를 사용할것이다. 이 부분은 필수는 아니지만 혹시 코드 진행상 헷갈리실까봐 공개한다.
AppStorage는 사용자의 구매여부를 저장해뒀다가 bp를 불러온 SettingsActivity를 제외한 나머지 Activity에서도 사용자의 광고제거 구매 여부에따라 광고를 보여줄지, 숨길지 결정하는데 도움을 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class AppStorage { private Context context; private SharedPreferences pref; private String PURCHASED_REMOVE_ADS = "remove_ads"; public AppStorage(Context context) { pref = context.getSharedPreferences("app_storage", Context.MODE_PRIVATE); this.context = context; } public boolean purchasedRemoveAds() { return pref.getBoolean(PURCHASED_REMOVE_ADS, false); } public void setPurchasedRemoveAds(boolean flag) { SharedPreferences.Editor editor = pref.edit(); editor.putBoolean(PURCHASED_REMOVE_ADS, flag); editor.apply(); } } | cs |
나머지 구매를 위한 코드는 다음과같다.
앞에서 말했듯 광고 제거 구매버튼을 누를때 구매를 수행한다. (주석을 잘보면 도움이 되실거에요)
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 | public class SettingsActivity extends AppCompatActivity implements BillingProcessor.IBillingHandler, View.OnClickListener { private BillingProcessor bp; private AppStorage storage; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); ButterKnife.bind(this); AppStorage storage = new AppStorage(this); bp = new BillingProcessor(this, Config.GooglePlayLicenseKey, this); bp.initialize(); } // 광고 없이 이용하기 @OnClick(R.id.btn_no_ads) public void clickBtnNoAds(View v) { v.startAnimation(Animator.getClickAnimation(this)); if (storage.purchasedRemoveAds()) { // TODO: 이미 구매하셨습니다. 메세지 띄우기! } else { bp.purchase(this, Config.Sku); } } @Override public void onProductPurchased(@NonNull String productId, @Nullable TransactionDetails details) { // 이 메소드는 구매 '성공'시에만 호출된다. if (productId.equals(Config.Sku)) { // TODO: 구매 해 주셔서 감사합니다! 메세지 보내기 storage.setPurchasedRemoveAds(bp.isPurchased(Config.Sku)); // * 광고 제거는 1번 구매하면 영구적으로 사용하는 것이므로 consume하지 않지만, // 만약 게임 아이템 100개를 주는 것이라면 아래 메소드를 실행시켜 다음번에도 구매할 수 있도록 소비처리를 해줘야한다. // bp.consumePurchase(Config.Sku); } } @Override public void onPurchaseHistoryRestored() { storage.setPurchasedRemoveAds(bp.isPurchased(Config.Sku)); } @Override public void onBillingError(int errorCode, @Nullable Throwable error) { /* TODO: 이런식으로 구매 오류시 오류가 발생했다고 알려주는 것도 좋다. if (errorCode != Constants.BILLING_RESPONSE_RESULT_USER_CANCELED) { Snackbar.make(tvRemoveAds, R.string.unknown_error, Snackbar.LENGTH_SHORT).show(); } */ } @Override public void onBillingInitialized() { // storage에 구매여부 저장 storage.setPurchasedRemoveAds(bp.isPurchased(Config.Sku)); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (!bp.handleActivityResult(requestCode, resultCode, data)) { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onDestroy() { if (bp != null) { bp.release(); } super.onDestroy(); } } | cs |
이제 다음 액티비티에서는 단순히 AppStorage만을 불러와서 purchasedRemoveAds() 메소드를 통해 구매를 했는지 안했는지 가려가며 코딩하면된다.
* 참, 구매 테스트는 안드로이드 스튜디오나 apk파일이 아닌 콘솔에 알파버전 이상에 올린 것으로만 가능하니 참고해주세요!
댓 글 환 영
구독 구현도 궁금하시다면 댓글 달아주시면 5개의 댓글이 쌓이면 준비하도록 하겠습니다!
'프로그래밍 > Android' 카테고리의 다른 글
[안드로이드] Admob 광고 게재가 중단: GOOGLE PLAY 삭제 해결방법 (1) | 2019.03.25 |
---|---|
Fresco 이미지 wrap_content로 로딩하는 법 (0) | 2019.03.10 |
[안드로이드] Admob 배너 광고가 나오지 않을때 (Admob banner not showing) - Admob Failed to load ad: 0 (5) | 2019.02.14 |
[2018년] 안드로이드 인앱 결제 구현 완벽 정리 소스 파일 (1) | 2018.10.13 |
[안드로이드] WebView에서 카카오톡 플러스친구 Intent 실행법 (3) | 2018.09.23 |