SlideShare ist ein Scribd-Unternehmen logo
1 von 13
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
2017/07/25
Shigehiro Soejima
Play Billing Library
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
Play Billing Library
 https://developer.android.com/google/play/billing/billing_library.html
 https://www.youtube.com/watch?v=9chvh1WYCvw
 https://developer.android.com/training/play-billing-library/index.html
 https://android-developers.googleblog.com/2017/06/money-made-easily-
with-new-google-play.html
 https://github.com/googlesamples/android-play-
billing/tree/master/TrivialDrive_v2
1
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
下準備
 パーミッションを追加
<uses-permission android:name="com.android.vending.BILLING" />
 dependenciesを追加
dependencies {
compile 'com.android.billingclient:billing:dp-1'
}
2
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
Play Billingサービス利用までの基本実装手順
1. PurhcasesUpdatedListenerを実装
2. BillingClientStateListenerを実装
3. BillingClientの作成
4. IABサービスに接続
5. コールバックでセットアップ完了を確認
5まで正常終了した段階でクエリ、購入等のアプリ別の実装が可能となる。
3
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
public class MainActivity extends AppCompatActivity implements BillingClientStateListener, PurchasesUpdatedListener {
private static final String TAG = "PBL Sample";
private BillingClient mBillingClient;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
launchPurchase();
}
});
mTextView = (TextView) findViewById(R.id.textView);
mBillingClient = new BillingClient.Builder(this).setListener(this).build();
mBillingClient.startConnection(this);
}
@Override
public void onBillingSetupFinished(int resultCode) {
if (resultCode == BillingClient.BillingResponse.OK) {
Log.d(TAG, "Billing setup successful");
queryPurchases();
} else {
Log.d(TAG, "Billing setup failed");
}
}
@Override
public void onBillingServiceDisconnected() {
Log.d(TAG, "Billing setup failed");
}
@Override
public void onPurchasesUpdated(int responseCode, List<Purchase> purchases) {
switch (responseCode) {
case BillingClient.BillingResponse.OK: {
Log.d(TAG, "onPurchasesUpdated: OK");
break;
}
case BillingClient.BillingResponse.USER_CANCELED: {
Log.d(TAG, "onPurchasesUpdated: User canceled");
break;
}
default: {
Log.d(TAG, "onPurchasesUpdated: responseCode=" + responseCode);
}
}
}
private void queryPurchases() {
List<String> skuList = new ArrayList<>();
skuList.add("premium");
skuList.add("gas");
skuList.add("dummy"); // non-existing id
mBillingClient.querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuList, new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(SkuDetails.SkuDetailsResult result) {
if (result.getResponseCode() == BillingClient.BillingResponse.OK) {
List<SkuDetails> list = result.getSkuDetailsList();
if (list.size() != 0) {
for (SkuDetails sd : list) {
mTextView.append(sd.toString() + "nn");
}
} else {
Toast.makeText(MainActivity.this, "No purchases yet", Toast.LENGTH_LONG).show();
}
} else {
Log.d(TAG, "Query failed: (response code=" + result.getResponseCode() + ")");
}
}
});
}
private void launchPurchase() {
BillingFlowParams params = new BillingFlowParams.Builder()
.setSku("gas")
.setType(BillingClient.SkuType.INAPP)
.build();
mBillingClient.launchBillingFlow(this, params);
}
}
4
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
1. PurchasesUpdatedListenerを実装
購入処理にかかわるコールバックの実装を行う。
PurchasesUpdatedListener#
onPurchasesUpdated(@BillingClient.BillingResponse responseCode: Int, purchases: List<Purchase>?)
 BillingClientのインスタンス作成に必要なので先に(空)実装する。
 セットアップ、購入時等の非同期処理のresponseCodeは正常終了の
BillingClient.BillingResponse.OKの他に、
BillingClient.BillingResponse.BILLING_UNAVAILABLEなど全部で11種類
あるので、それぞれアプリの仕様に従って処理を実装。
5
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
2. BillingClientStateListenerを実装
BillingClientのセットアップ処理のコールバック。
BillingClientStateListener#
onBillingSetupFinished(@BillingClient.BillingResponse resultCode: Int)
onBillingServiceDisconnected()
 アプリを起動して最初に必要な処理を行う部分。
 裏でマーケットアプリが更新されていたりすると接続が切られることも。
6
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
3. BillingClientの作成
実際にクエリや購入の操作を行うクラス。
private lateinit var billingClient: BillingClient
…
billingClient = BillingClient.Builder(this).setListener(this).build()
 ビルダーを使ってインスタンス作成。setListenerで1.で実装した
PurchasesUpdateListenerを渡す。
7
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
4. IABサービスに接続
billingClient.startConnection(this)
 2.で作成したBillingClientStateListenerのインスタンスを渡す。
 非同期で接続処理が開始される。
8
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
5.コールバックでセットアップ完了を確認
override fun onBillingSetupFinished(@BillingClient.BillingResponse resultCode: Int) {
if (resultCode == BillingClient.BillingResponse.OK) {
// 接続完了、購入状態の確認等
} else {
// 接続失敗、リトライ・エラーメッセージの表示等
}
}
 BillingClient.BillingResponse.OKでセットアップが正しく完了。ここまでがすべての
アプリ共通で必要な処理。
 正しく完了したら、プロダクトの購入状態をチェックしてUIを更新する等のアプリ独
自の処理を実装する。
9
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
プロダクト購入状態の確認
BillingClientのquerySkuDetailsAsync等のqueryXXXで取得する。
billingClient.querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuList) {
// SkuDetailsResponseListener#onSkuDetailsResponse(SkuDetailsResult)
if (it.responseCode == BillingClient.BillingResponse.OK) {
if (it.skuDetailsList.size != 0) {
for (skuDetails in it.skuDetailsList) {
// UIを更新等のアプリの処理
}
} else {
// まだ何も購入していない
}
…
10
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
プロダクト購入の開始
val params = BillingFlowParams.Builder()
.setSku(“gas”)
.setType(BillingClient.SkuType.INAPP)
.build()
billingClient.launchBillingFlow(this, params)
11
Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved.
サンプルコード
https://github.com/mightyfrog/Play-Billing-Library-Sample
12

Weitere ähnliche Inhalte

Ähnlich wie Play Billing Libary

【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へDevelopers Summit
 
AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版Junichiro Tasaki
 
初めての Data api cms どうでしょう - 大阪夏の陣
初めての Data api   cms どうでしょう - 大阪夏の陣初めての Data api   cms どうでしょう - 大阪夏の陣
初めての Data api cms どうでしょう - 大阪夏の陣Yuji Takayama
 
PostgreSQL 10 新機能 @オープンセミナー香川 2017
PostgreSQL 10 新機能 @オープンセミナー香川 2017PostgreSQL 10 新機能 @オープンセミナー香川 2017
PostgreSQL 10 新機能 @オープンセミナー香川 2017Shigeru Hanada
 
How Smalltalker Works
How Smalltalker WorksHow Smalltalker Works
How Smalltalker WorksSho Yoshida
 
Microsoft Graph APIを活用した社内アプリケーション開発
Microsoft Graph APIを活用した社内アプリケーション開発Microsoft Graph APIを活用した社内アプリケーション開発
Microsoft Graph APIを活用した社内アプリケーション開発Yuki Hattori
 
ノンコーディング・超高速のApi 開発・運用基盤「cdata api server」のご紹介
ノンコーディング・超高速のApi 開発・運用基盤「cdata api server」のご紹介ノンコーディング・超高速のApi 開発・運用基盤「cdata api server」のご紹介
ノンコーディング・超高速のApi 開発・運用基盤「cdata api server」のご紹介CData Software Japan
 
Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.Yuki Higuchi
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回Naoyuki Yamada
 
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要 第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要 Daiyu Hatakeyama
 
Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界Yuji Takayama
 
D3.js と SVG によるデータビジュアライゼーション
D3.js と SVG によるデータビジュアライゼーションD3.js と SVG によるデータビジュアライゼーション
D3.js と SVG によるデータビジュアライゼーションKohei Kadowaki
 
今更聞けない!?Microsoft Graph で始める Office 365 データ活用と事例の紹介
今更聞けない!?Microsoft Graph で始める Office 365 データ活用と事例の紹介今更聞けない!?Microsoft Graph で始める Office 365 データ活用と事例の紹介
今更聞けない!?Microsoft Graph で始める Office 365 データ活用と事例の紹介Kenichiro Nakamura
 
Spring data-rest-and-spring-cloud-contract
Spring data-rest-and-spring-cloud-contractSpring data-rest-and-spring-cloud-contract
Spring data-rest-and-spring-cloud-contractTakeshi Ogawa
 
初めての Data api
初めての Data api初めての Data api
初めての Data apiYuji Takayama
 
うちのRedmineの使い方(2)
うちのRedmineの使い方(2)うちのRedmineの使い方(2)
うちのRedmineの使い方(2)Tomohisa Kusukawa
 
「Entity Framework Coreを使ってみる」 公開用
「Entity Framework Coreを使ってみる」 公開用「Entity Framework Coreを使ってみる」 公開用
「Entity Framework Coreを使ってみる」 公開用ESM SEC
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用Yatabe Terumasa
 
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401Shota Onishi
 

Ähnlich wie Play Billing Libary (20)

【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ【18-C-4】Google App Engine - 無限の彼方へ
【18-C-4】Google App Engine - 無限の彼方へ
 
AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版
 
初めての Data api cms どうでしょう - 大阪夏の陣
初めての Data api   cms どうでしょう - 大阪夏の陣初めての Data api   cms どうでしょう - 大阪夏の陣
初めての Data api cms どうでしょう - 大阪夏の陣
 
PostgreSQL 10 新機能 @オープンセミナー香川 2017
PostgreSQL 10 新機能 @オープンセミナー香川 2017PostgreSQL 10 新機能 @オープンセミナー香川 2017
PostgreSQL 10 新機能 @オープンセミナー香川 2017
 
Apexデザインパターン
ApexデザインパターンApexデザインパターン
Apexデザインパターン
 
How Smalltalker Works
How Smalltalker WorksHow Smalltalker Works
How Smalltalker Works
 
Microsoft Graph APIを活用した社内アプリケーション開発
Microsoft Graph APIを活用した社内アプリケーション開発Microsoft Graph APIを活用した社内アプリケーション開発
Microsoft Graph APIを活用した社内アプリケーション開発
 
ノンコーディング・超高速のApi 開発・運用基盤「cdata api server」のご紹介
ノンコーディング・超高速のApi 開発・運用基盤「cdata api server」のご紹介ノンコーディング・超高速のApi 開発・運用基盤「cdata api server」のご紹介
ノンコーディング・超高速のApi 開発・運用基盤「cdata api server」のご紹介
 
Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
 
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要 第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要
 
Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界
 
D3.js と SVG によるデータビジュアライゼーション
D3.js と SVG によるデータビジュアライゼーションD3.js と SVG によるデータビジュアライゼーション
D3.js と SVG によるデータビジュアライゼーション
 
今更聞けない!?Microsoft Graph で始める Office 365 データ活用と事例の紹介
今更聞けない!?Microsoft Graph で始める Office 365 データ活用と事例の紹介今更聞けない!?Microsoft Graph で始める Office 365 データ活用と事例の紹介
今更聞けない!?Microsoft Graph で始める Office 365 データ活用と事例の紹介
 
Spring data-rest-and-spring-cloud-contract
Spring data-rest-and-spring-cloud-contractSpring data-rest-and-spring-cloud-contract
Spring data-rest-and-spring-cloud-contract
 
初めての Data api
初めての Data api初めての Data api
初めての Data api
 
うちのRedmineの使い方(2)
うちのRedmineの使い方(2)うちのRedmineの使い方(2)
うちのRedmineの使い方(2)
 
「Entity Framework Coreを使ってみる」 公開用
「Entity Framework Coreを使ってみる」 公開用「Entity Framework Coreを使ってみる」 公開用
「Entity Framework Coreを使ってみる」 公開用
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用
 
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
 

Play Billing Libary

  • 1. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. 2017/07/25 Shigehiro Soejima Play Billing Library
  • 2. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. Play Billing Library  https://developer.android.com/google/play/billing/billing_library.html  https://www.youtube.com/watch?v=9chvh1WYCvw  https://developer.android.com/training/play-billing-library/index.html  https://android-developers.googleblog.com/2017/06/money-made-easily- with-new-google-play.html  https://github.com/googlesamples/android-play- billing/tree/master/TrivialDrive_v2 1
  • 3. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. 下準備  パーミッションを追加 <uses-permission android:name="com.android.vending.BILLING" />  dependenciesを追加 dependencies { compile 'com.android.billingclient:billing:dp-1' } 2
  • 4. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. Play Billingサービス利用までの基本実装手順 1. PurhcasesUpdatedListenerを実装 2. BillingClientStateListenerを実装 3. BillingClientの作成 4. IABサービスに接続 5. コールバックでセットアップ完了を確認 5まで正常終了した段階でクエリ、購入等のアプリ別の実装が可能となる。 3
  • 5. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. public class MainActivity extends AppCompatActivity implements BillingClientStateListener, PurchasesUpdatedListener { private static final String TAG = "PBL Sample"; private BillingClient mBillingClient; private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { launchPurchase(); } }); mTextView = (TextView) findViewById(R.id.textView); mBillingClient = new BillingClient.Builder(this).setListener(this).build(); mBillingClient.startConnection(this); } @Override public void onBillingSetupFinished(int resultCode) { if (resultCode == BillingClient.BillingResponse.OK) { Log.d(TAG, "Billing setup successful"); queryPurchases(); } else { Log.d(TAG, "Billing setup failed"); } } @Override public void onBillingServiceDisconnected() { Log.d(TAG, "Billing setup failed"); } @Override public void onPurchasesUpdated(int responseCode, List<Purchase> purchases) { switch (responseCode) { case BillingClient.BillingResponse.OK: { Log.d(TAG, "onPurchasesUpdated: OK"); break; } case BillingClient.BillingResponse.USER_CANCELED: { Log.d(TAG, "onPurchasesUpdated: User canceled"); break; } default: { Log.d(TAG, "onPurchasesUpdated: responseCode=" + responseCode); } } } private void queryPurchases() { List<String> skuList = new ArrayList<>(); skuList.add("premium"); skuList.add("gas"); skuList.add("dummy"); // non-existing id mBillingClient.querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuList, new SkuDetailsResponseListener() { @Override public void onSkuDetailsResponse(SkuDetails.SkuDetailsResult result) { if (result.getResponseCode() == BillingClient.BillingResponse.OK) { List<SkuDetails> list = result.getSkuDetailsList(); if (list.size() != 0) { for (SkuDetails sd : list) { mTextView.append(sd.toString() + "nn"); } } else { Toast.makeText(MainActivity.this, "No purchases yet", Toast.LENGTH_LONG).show(); } } else { Log.d(TAG, "Query failed: (response code=" + result.getResponseCode() + ")"); } } }); } private void launchPurchase() { BillingFlowParams params = new BillingFlowParams.Builder() .setSku("gas") .setType(BillingClient.SkuType.INAPP) .build(); mBillingClient.launchBillingFlow(this, params); } } 4
  • 6. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. 1. PurchasesUpdatedListenerを実装 購入処理にかかわるコールバックの実装を行う。 PurchasesUpdatedListener# onPurchasesUpdated(@BillingClient.BillingResponse responseCode: Int, purchases: List<Purchase>?)  BillingClientのインスタンス作成に必要なので先に(空)実装する。  セットアップ、購入時等の非同期処理のresponseCodeは正常終了の BillingClient.BillingResponse.OKの他に、 BillingClient.BillingResponse.BILLING_UNAVAILABLEなど全部で11種類 あるので、それぞれアプリの仕様に従って処理を実装。 5
  • 7. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. 2. BillingClientStateListenerを実装 BillingClientのセットアップ処理のコールバック。 BillingClientStateListener# onBillingSetupFinished(@BillingClient.BillingResponse resultCode: Int) onBillingServiceDisconnected()  アプリを起動して最初に必要な処理を行う部分。  裏でマーケットアプリが更新されていたりすると接続が切られることも。 6
  • 8. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. 3. BillingClientの作成 実際にクエリや購入の操作を行うクラス。 private lateinit var billingClient: BillingClient … billingClient = BillingClient.Builder(this).setListener(this).build()  ビルダーを使ってインスタンス作成。setListenerで1.で実装した PurchasesUpdateListenerを渡す。 7
  • 9. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. 4. IABサービスに接続 billingClient.startConnection(this)  2.で作成したBillingClientStateListenerのインスタンスを渡す。  非同期で接続処理が開始される。 8
  • 10. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. 5.コールバックでセットアップ完了を確認 override fun onBillingSetupFinished(@BillingClient.BillingResponse resultCode: Int) { if (resultCode == BillingClient.BillingResponse.OK) { // 接続完了、購入状態の確認等 } else { // 接続失敗、リトライ・エラーメッセージの表示等 } }  BillingClient.BillingResponse.OKでセットアップが正しく完了。ここまでがすべての アプリ共通で必要な処理。  正しく完了したら、プロダクトの購入状態をチェックしてUIを更新する等のアプリ独 自の処理を実装する。 9
  • 11. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. プロダクト購入状態の確認 BillingClientのquerySkuDetailsAsync等のqueryXXXで取得する。 billingClient.querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuList) { // SkuDetailsResponseListener#onSkuDetailsResponse(SkuDetailsResult) if (it.responseCode == BillingClient.BillingResponse.OK) { if (it.skuDetailsList.size != 0) { for (skuDetails in it.skuDetailsList) { // UIを更新等のアプリの処理 } } else { // まだ何も購入していない } … 10
  • 12. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. プロダクト購入の開始 val params = BillingFlowParams.Builder() .setSku(“gas”) .setType(BillingClient.SkuType.INAPP) .build() billingClient.launchBillingFlow(this, params) 11
  • 13. Copyright (C) 2017 DeNA Co.,Ltd. All Rights Reserved. サンプルコード https://github.com/mightyfrog/Play-Billing-Library-Sample 12