アプリ内課金をAndroidアプリに実装するのは非常に複雑なようです。どうすればよいでしょうか?SDKのサンプルアプリにはアクティビティが1つしかありません。複数のアクティビティがある私のようなアプリケーション向けです。
さて、私は私が経験したことを説明しようとします。私は自分をこれに関する専門家とは考えていませんが、数日頭を骨折しました。
まず第一に、例とアプリケーションのワークフローを理解しようとして非常に苦労しました。単純な例から始める方が良いと思いましたが、コードを小さな断片に分けることは非常に難しく、何かを壊しているかどうかはわかりません。私が持っているものと、それを機能させるために例から変更したものを説明します。
私はすべての購入が由来する単一のアクティビティを持っています。 Proと呼ばれます。
まず、Securityクラスの変数base64EncodedPublicKeyを公開マーケット開発者キーで更新する必要があります。そうしないと、Nice Exceptionが表示されます。
さて、私は次のようにアクティビティをBillingServiceにバインドします。
public class Pro extends TrackedActivity implements OnItemClickListener {
private BillingService mBillingService;
private BillingPurchaseObserver mBillingPurchaseObserver;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pro);
//Do my stuff
mBillingService = new BillingService();
mBillingService.setContext(getApplicationContext());
mHandler = new Handler();
mBillingPurchaseObserver = new BillingPurchaseObserver(mHandler);
}
}
@Override
protected void onStart() {
//Register the observer to the service
super.onStart();
ResponseHandler.register(mBillingPurchaseObserver);
}
@Override
protected void onStop() {
//Unregister the observer since you dont need anymore
super.onStop();
ResponseHandler.unregister(mBillingPurchaseObserver);
}
@Override
protected void onDestroy() {
//Unbind the service
super.onDestroy();
mBillingService.unbind();
}
そのようにして、すべての購入がこのサービスと通信し、JSONリクエストを市場に送信します。購入は同じ瞬間に行われますが、そうではないと思うかもしれません。リクエストを送信すると、購入が数分または数時間後に行われる場合があります。これは主にサーバーの過負荷とクレジットカードの承認のためだと思います。
次に、アイテムのリストビューを作成し、各アイテムでAlertDialogを開いて、アイテムを購入するよう招待します。彼らがアイテムをクリックすると、私はこれを行います:
private class BuyButton implements DialogInterface.OnClickListener {
private BillingItem item = null;
private String developerPayload;
public BuyButton(BillingItem item, String developerPayload) {
this.item = item;
this.developerPayload = developerPayload;
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (GeneralHelper.isOnline(getApplicationContext())){
//I track the buy here with GA SDK.
mBillingService.requestPurchase(this.item.getSku(), this.developerPayload);
} else {
Toast.makeText(getApplicationContext(), R.string.msg_not_online, Toast.LENGTH_SHORT).show();
}
}
}
申し分なく、マーケットが開き、ユーザーが購入を完了するかキャンセルすることがわかります。
次に重要なのは、私のPurChaseObserverです。これは、市場が送信するすべてのイベントを処理します。これは削除されたバージョンですが、ポイントを取得する必要があります(コード全体で私のコメントを参照してください)。
private class BillingPurchaseObserver extends PurchaseObserver {
public BillingPurchaseObserver(Handler handler) {
super(Pro.this, handler);
}
@Override
public void onBillingSupported(boolean supported) {
if (supported) {
//Enable buy functions. Not required, but you can do stuff here. The market first checks if billing is supported. Maybe your country is not supported, for example.
} else {
Toast.makeText(getApplicationContext(), R.string.billing_not_supported, Toast.LENGTH_LONG).show();
}
}
@Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload) {
//This is the method that is called when the buy is completed or refunded I believe.
// Here you can do something with the developerPayload. Its basically a Tag you can use to follow your transactions. i dont use it.
BillingItem item = BillingItem.getBySku(getApplicationContext(), itemId);
if (purchaseState == PurchaseState.PURCHASED) {
if (item != null){
//This is my own implementation that sets the item purchased in my database. BillingHelper is a class with methods I use to check if the user bought an option and update the UI. You should also check for refunded. You can see the Consts class to find what you need to check for.
boolean resu = item.makePurchased(getApplicationContext());
if (resu){
Toast.makeText(getApplicationContext(), R.string.billing_item_purchased, Toast.LENGTH_LONG).show();
}
}
}
}
private void trackPurchase(BillingItem item, long purchaseTime) {
//My code to track the purchase in GA
}
@Override
public void onRequestPurchaseResponse(RequestPurchase request,
ResponseCode responseCode) {
//This is the callback that happens when you sent the request. It doesnt mean you bought something. Just that the Market received it.
if (responseCode == ResponseCode.RESULT_OK) {
Toast.makeText(getApplicationContext(), R.string.billing_item_request_sent, Toast.LENGTH_SHORT).show();
} else if (responseCode == ResponseCode.RESULT_USER_CANCELED) {
//The user canceled the item.
} else {
//If it got here, the Market had an unexpected problem.
}
}
@Override
public void onRestoreTransactionsResponse(RestoreTransactions request,
ResponseCode responseCode) {
if (responseCode == ResponseCode.RESULT_OK) {
//Restore transactions should only be run once in the lifecycle of your application unless you reinstalled the app or wipe the data.
SharedPreferences.Editor edit = PreferencesHelper.getInstance().getDefaultSettings(getApplicationContext()).edit();
edit.putBoolean(Consts.DB_INITIALIZED, true);
edit.commit();
} else {
//Something went wrong
}
}
}
そして、他に何も編集する必要はないはずです。残りのコードは「機能」します。最初に独自のアイテム「Android.test.purchased」でサンプルSKUを使用してみてください。これまでのところこれをテストしましたが、動作しますが、返金された状態などのすべてをカバーする必要があります。この場合、ユーザーに機能を保持させていますが、変更する前に完全に機能することを確認したいと思います。
それがあなたや他の人たちに役立つことを願っています。
V3:クイックスタートのためのチュートリアルです。彼はgoogleの例(Trivial Drive)のヘルパークラスを使用しています...良い最初の「こんにちは請求」として..
アプリ内課金v3の場合、これは非常に役立ちました。
http://blog.blundellapps.com/simple-inapp-billing-payment-v3/
Androidのアプリ内課金v3の完全な例が、スクリーンショットとともに段階的に示されています。チュートリアルを確認してください: ServiceConnectionクラスを使用したAndroidアプリ内課金v
それが役立つことを願っています。
詳細については、次のチュートリアルをご覧ください。 バージョン3 APIでのアプリ内課金の実装
プロジェクトにアプリ内課金ライブラリを統合する手順
AndroidManifest.xmlファイルを更新します。
ServiceConnectionを作成し、それをIInAppBillingServiceにバインドします。
アプリ内課金リクエストをアプリケーションからIInAppBillingServiceに送信します。
Google Playからのアプリ内課金の応答を処理します。
AndroidManifest.xmlを更新する
<uses-permission Android:name="com.Android.vending.BILLING" />
Manifest.xmlファイルに権限を追加します
アプリケーションをビルドします。プロジェクトの/ genディレクトリにIInAppBillingService.Javaという名前の生成されたファイルが表示されます。
apply plugin: 'com.Android.application'
Android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.inducesmile.androidinapppurchase"
minSdkVersion 14
targetSdkVersion 24
versionCode 2
versionName "1.1"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.Android.support:appcompat-v7:24.1.1'
compile 'com.intuit.sdp:sdp-Android:1.0.3'
compile 'com.Android.support:support-annotations:24.1.1'
compile 'org.jetbrains:annotations-Java5:15.0'
}
InAppPurchaseActivity.Javaおよびactivity_in_app_purchase.xml
これは、アプリユーザーにアプリ内購入を行う機会を提供する場所です。レイアウトファイルでは、ユーザーにさまざまな額面で購入する機会を与えます。
InAppPurchaseActivity.Java
注:getAllUserPurchase()およびitemPurchaseAvailability()メソッドは、アプリのクラッシュを防ぐために非UIスレッドで呼び出す必要があります。
public class InAppPurchaseActivity extends AppCompatActivity {
private static final String TAG = InAppPurchaseActivity.class.getSimpleName();
private IInAppBillingService mService;
private CustomSharedPreference customSharedPreference;
String[] productIds = new String[]{Helper.ITEM_ONE_ID, Helper.ITEM_TWO_ID, Helper.ITEM_THREE_ID};
private ImageView buyOneButton, buyTwoButton, buyThreeButton;
private static final char[] symbols = new char[36];
static {
for (int idx = 0; idx < 10; ++idx)
symbols[idx] = (char) ('0' + idx);
for (int idx = 10; idx < 36; ++idx)
symbols[idx] = (char) ('a' + idx - 10);
}
private String appPackageName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_in_app_purchase);
appPackageName = this.getPackageName();
Intent serviceIntent = new Intent("com.Android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.Android.vending");
bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
customSharedPreference = new CustomSharedPreference(InAppPurchaseActivity.this);
buyOneButton = (ImageView)findViewById(R.id.buy_one);
buyOneButton.setVisibility(View.GONE);
assert buyOneButton != null;
buyOneButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!isBillingSupported()){
Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support));
return;
}
purchaseItem(Helper.ITEM_ONE_ID);
}
});
buyTwoButton = (ImageView)findViewById(R.id.buy_two);
buyTwoButton.setVisibility(View.GONE);
assert buyTwoButton != null;
buyTwoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!isBillingSupported()){
Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support));
return;
}
purchaseItem(Helper.ITEM_TWO_ID);
}
});
buyThreeButton = (ImageView)findViewById(R.id.buy_three);
buyThreeButton.setVisibility(View.GONE);
assert buyThreeButton != null;
buyThreeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!isBillingSupported()){
Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support));
return;
}
purchaseItem(Helper.ITEM_THREE_ID);
}
});
}
ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
AvailablePurchaseAsyncTask mAsyncTask = new AvailablePurchaseAsyncTask(appPackageName);
mAsyncTask.execute();
}
};
private void purchaseItem(String sku){
String generatedPayload = getPayLoad();
customSharedPreference.setDeveloperPayLoad(generatedPayload);
try {
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, "inapp", generatedPayload);
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
try {
startIntentSenderForResult(pendingIntent.getIntentSender(), Helper.RESPONSE_CODE, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Helper.RESPONSE_CODE) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
try {
JSONObject purchaseJsonObject = new JSONObject(purchaseData);
String sku = purchaseJsonObject.getString("productId");
String developerPayload = purchaseJsonObject.getString("developerPayload");
String purchaseToken = purchaseJsonObject.getString("purchaseToken");
//the developerPayload value is better stored in remote database but in this tutorial
//we will use a shared preference
for(int i = 0; i < productIds.length; i++){
if(productIds[i].equals(sku) && developerPayload.equals(customSharedPreference.getDeveloperPayload())){
customSharedPreference.setPurchaseToken(purchaseToken);
//access to private content
Intent contentIntent = new Intent(InAppPurchaseActivity.this, PrivateContentActivity.class);
startActivity(contentIntent);
}
}
}
catch (JSONException e) {
e.printStackTrace();
}
}
}
}
private String getPayLoad(){
RandomString randomString = new RandomString(36);
String payload = randomString.nextString();
return payload;
}
public class RandomString {
private final Random random = new Random();
private final char[] buf;
public RandomString(int length) {
if (length < 1)
throw new IllegalArgumentException("length < 1: " + length);
buf = new char[length];
}
public String nextString() {
for (int idx = 0; idx < buf.length; ++idx)
buf[idx] = symbols[random.nextInt(symbols.length)];
return new String(buf);
}
}
public final class SessionIdentifierGenerator {
private SecureRandom random = new SecureRandom();
public String nextSessionId() {
return new BigInteger(130, random).toString(32);
}
}
private class AvailablePurchaseAsyncTask extends AsyncTask<Void, Void, Bundle> {
String packageName;
public AvailablePurchaseAsyncTask(String packageName){
this.packageName = packageName;
}
@Override
protected Bundle doInBackground(Void... voids) {
ArrayList<String> skuList = new ArrayList<String>();
skuList.add(Helper.ITEM_ONE_ID);
skuList.add(Helper.ITEM_TWO_ID);
skuList.add(Helper.ITEM_THREE_ID);
Bundle query = new Bundle();
query.putStringArrayList(Helper.ITEM_ID_LIST, skuList);
Bundle skuDetails = null;
try {
skuDetails = mService.getSkuDetails(3, packageName, "inapp", query);
} catch (RemoteException e) {
e.printStackTrace();
}
return skuDetails;
}
@Override
protected void onPostExecute(Bundle skuDetails) {
List<AvailablePurchase> canPurchase = new ArrayList<AvailablePurchase>();
int response = skuDetails.getInt("RESPONSE_CODE");
if (response == 0) {
ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
if(responseList != null){
for (String thisResponse : responseList) {
JSONObject object = null;
try {
object = new JSONObject(thisResponse);
String sku = object.getString("productId");
String price = object.getString("price");
canPurchase.add(new AvailablePurchase(sku, price));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
if(checkIfPurchaseIsAvailable(canPurchase, productIds[0])){
buyOneButton.setVisibility(View.VISIBLE);
}else{
buyOneButton.setVisibility(View.GONE);
}
if(checkIfPurchaseIsAvailable(canPurchase, productIds[1])){
buyTwoButton.setVisibility(View.VISIBLE);
}else{
buyTwoButton.setVisibility(View.GONE);
}
if(checkIfPurchaseIsAvailable(canPurchase, productIds[2])){
buyThreeButton.setVisibility(View.VISIBLE);
}else{
buyThreeButton.setVisibility(View.GONE);
}
}
}
@org.jetbrains.annotations.Contract("null, _ -> false")
private boolean checkIfPurchaseIsAvailable(List<AvailablePurchase> all, String productId){
if(all == null){ return false;}
for(int i = 0; i < all.size(); i++){
if(all.get(i).getSku().equals(productId)){
return true;
}
}
return false;
}
public boolean isBillingSupported(){
int response = 1;
try {
response = mService.isBillingSupported(3, getPackageName(), "inapp");
} catch (RemoteException e) {
e.printStackTrace();
}
if(response > 0){
return false;
}
return true;
}
public void consumePurchaseItem(String purchaseToken){
try {
int response = mService.consumePurchase(3, getPackageName(), purchaseToken);
if(response != 0){
return;
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Bundle getAllUserPurchase(){
Bundle ownedItems = null;
try {
ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
} catch (RemoteException e) {
e.printStackTrace();
}
return ownedItems;
}
public List<UserPurchaseItems> extractAllUserPurchase(Bundle ownedItems){
List<UserPurchaseItems> mUserItems = new ArrayList<UserPurchaseItems>();
int response = ownedItems.getInt("RESPONSE_CODE");
if (response == 0) {
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN");
if(purchaseDataList != null){
for (int i = 0; i < purchaseDataList.size(); ++i) {
String purchaseData = purchaseDataList.get(i);
assert signatureList != null;
String signature = signatureList.get(i);
assert ownedSkus != null;
String sku = ownedSkus.get(i);
UserPurchaseItems allItems = new UserPurchaseItems(sku, purchaseData, signature);
mUserItems.add(allItems);
}
}
}
return mUserItems;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
unbindService(mServiceConn);
}
}
}
ヘルパーパッケージディレクトリの作成
新しいパッケージフォルダーを作成し、helpersという名前を付けます。パッケージ内に、新しいJavaファイルHelper.Javaを作成します。
Helper.Java
public class Helper {
public static final String ITEM_ID_LIST = "ITEM_ID_LIST";
public static final String ITEM_ONE_ID = "productone";
public static final String ITEM_TWO_ID = "producttwo";
public static final String ITEM_THREE_ID = "productthree";
public static final int RESPONSE_CODE = 1001;
public static final String SHARED_PREF = "shared_pref";
public static final String DEVELOPER_PAYLOAD = "developer_payload";
public static final String PURCHASE_TOKEN = "purchase_token";
public static void displayMessage(Context context, String message){
Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
}
アプリ内課金の購入のテスト
アプリ内購入テスト中に発生する可能性のあるエラー
リクエストしたアイテムは購入できません
ソリューション- StackoverflowのAndreiBogdanによる 、
すべてのクレジットは Inducesmile の彼の tutorial
Android開発者ブログでは、アプリ内製品の販売に関するトレーニングクラスも推奨しています。完全な実装を確認し、アプリケーションをテストする方法を学習するには、このチュートリアルを確認してください。 アプリ内製品の販売
簡単なライブラリを使用してGoogle PlayとAmazon Appstore全体に公開する場合は、 RoboBillingLibrary を使用します。両方の詳細を1つの使いやすいライブラリに抽象化します。詳細な手順はGithubページにあります。
さて、これはオンラインで入手できるドキュメントがあまりないものの1つなので、すべてを段階的に説明するために最善を尽くします。これのより詳細なバージョン(スクリーンショット付き)である私のブログ投稿から取られた Millibit にあります。難しい話は抜きにして、
ステップ1:パーミッションこれは最も簡単なステップです。 manifest.xmlファイルに移動し、タグの下に次の行を追加します。
<uses-permission Android:name="com.Android.vending.BILLING" />
これにより、アプリにアプリ内課金にアクセスする権限が付与されます。 API 22より上のバージョンをターゲットにしている場合、実行時にこの権限が付与されていることを確認する必要があります。
ステップ2:Play Console次に、アプリをGoogle Play Consoleにアップロードする必要があります。アプリはまだ一般公開されていません(心配しないでください)。ベータリリースセクションにアップロードするだけで、アプリ内購入をテストできます。これを行う必要があるのは、請求プロセスが実際に機能するために、Googleが何らかのバージョンのAPKをアップロードする必要があるためです。
アプリケーションを作成する
手順に従ってアプリをセットアップします
アプリのリリースに移動
ベータ版に移動します
Android studioでアプリのAPKを作成し、Play Consoleのベータ版プロダクションにアップロードします
(リリースする前に、ストアの掲載、コンテンツのレーティング、価格設定、および配布に入力済みであることを確認してください)
ステップ3:プロジェクトのセットアップこれは、ファイルの束をコピーして貼り付ける必要がある部分です。
まず、 this ファイルを取得し、ダウンロードして、src/main
Itの下に配置します。次に、フォルダーに自分自身をビルドする必要があります次に、 this whole utilフォルダーを取得し、それをsrc/Java folder.
に貼り付けてから、プロジェクトを再構築してエラーを解決します。 Utilフォルダには次のクラスが含まれています。
ステップ4:製品の作成
管理下の製品を作成する
[保存]をクリックして、「価格設定テンプレート」を作成します
ここで、この製品の価格を選択します。さまざまな国の価格を選択することも、価格の下にあるすべての国を選択した場合に自動的に調整することもできます。
最後に、製品のIDをメモします。次のいくつかの手順でこのIDを使用します。
「サービスとAPI」に進み、Base64EncodedStringを取得します。これをコピーして、どこかにあるメモ帳に貼り付けて、アクセスできるようにします。これを誰かと共有しないでください。彼らは悪意のあることをすることができます。
ステップ5:最後に!コーディングを開始できます。最初にアプリ内課金ライブラリにバインドし、ユーザーが購入したものと購入していないものを照会します。次に、以前にセットアップした製品を購入します。
最初に、以前に設定したすべてをインポートします。
import util.*;
ここで、mHelperというIabHelperオブジェクトを使用し、これですべてを行います。
base64EncodedPublicKey = ""; //PUT YOUR BASE64KEY HERE
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.enableDebugLogging(false); //set to false in real app
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh no, there was a problem.
if (result.getResponse() == 3) {
new AlertDialog.Builder(MainActivity.this)
.setTitle("In app billing")
.setMessage("This device is not compatible with In App Billing, so" +
" you may not be able to buy the premium version on your phone. ")
.setPositiveButton("Okay", null)
.show();
}
Log.v(TAG, "Problem setting up In-app Billing: " + result);
} else {
Log.v(TAG, "YAY, in app billing set up! " + result);
try {
mHelper.queryInventoryAsync(mGotInventoryListener); //Getting inventory of purchases and assigning listener
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
}
}
});
さて、ここで何が起こっているのかを説明しましょう。基本的に、「startSetup」を呼び出して「IabHelper」を初期化します。セットアップが成功した場合、ユーザーが既に購入しているものを照会し、応答をmGotInventoryListener
に保存します。これは次にコーディングします。
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
i = inventory;
if (result.isFailure()) {
// handle error here
Log.v(TAG, "failure in checking if user has purchases");
} else {
// does the user have the premium upgrade?
if (inventory.hasPurchase("premium_version")) {
premiumEditor.putBoolean("hasPremium", true);
premiumEditor.commit();
Log.v(TAG, "Has purchase, saving in storage");
} else {
premiumEditor.putBoolean("hasPremium", false);
premiumEditor.commit();
Log.v(TAG, "Doesn't have purchase, saving in storage");
}
}
}
};
上記のコードは一目瞭然です。基本的に、ユーザーが既に購入したものをチェックするだけです。ユーザーが既に製品を購入したかどうかがわかったので、アイテムの購入を依頼するかどうかがわかりました!以前に製品を購入したことがない場合は、購入リクエストを開始しましょう。
public void buyPremium() {
try {
mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually
mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener
} catch (Exception e) {
e.printStackTrace();
mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually
new AlertDialog.Builder(MainActivity.this)
.setTitle("Error")
.setMessage("An error occurred in buying the premium version. Please try again.")
.setPositiveButton("Okay", null)
.show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
}
else
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.v(TAG, "purchase finished");
if (purchase != null) {
if (purchase.getSku().equals("premium_version")) {
Toast.makeText(MainActivity.this, "Purchase successful!", Toast.LENGTH_SHORT).show();
premiumEditor.putBoolean("hasPremium", true);
premiumEditor.commit();
}
} else {
return;
}
if (result.isFailure()) {
return;
}
}
};
ここでは、次のようにアイテムを購入します(以前にPlayコンソールで生成したIDを使用)。
mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener
mPurchaseFinishedListener
をパラメーターに渡したことに注意してください。これは、購入の結果がこのリスナーに返されることを意味します。次に、購入がnullであるかどうかを確認し、nullでない場合は、購入した機能をユーザーに付与します。
リスナーに漏れさせないでください!アプリが破壊された場合、それらを破壊する必要があります。
@Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null)
try {
mHelper.dispose();
mHelper = null;
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
}
最後に、購入したアイテムを消費して、再び購入できるようにしたい場合は、簡単に行うことができます。これの例は、ユーザーがバーチャルカー用にガソリンを購入し、それがなくなった場合です。彼らは同じ製品を再度購入する必要があり、それを消費することで2回目の購入が可能になります:
public void consume(){
//MAKING A QUERY TO GET AN ACCURATE INVENTORY
try {
mHelper.flagEndAsync(); //If any async is going, make sure we have it stop eventually
mHelper.queryInventoryAsync(mGotInventoryListener); //Getting inventory of purchases and assigning listener
if(i.getPurchase("gas")==null){
Toast.makeText(this, "Already consumed!", Toast.LENGTH_SHORT).show();
}
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
Toast.makeText(this, "Error, try again", Toast.LENGTH_SHORT).show();
mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually
}
//ACTUALLY CONSUMING
try {
mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually
this.mHelper.consumeAsync(this.i.getPurchase("gas"), new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase paramAnonymousPurchase, IabResult paramAnonymousIabResult) {
//resell the gas to them
}
});
return;
} catch (IabHelper.IabAsyncInProgressException localIabAsyncInProgressException) {
localIabAsyncInProgressException.printStackTrace();
Toast.makeText(this, "ASYNC IN PROGRESS ALREADY!!!!" +localIabAsyncInProgressException, Toast.LENGTH_LONG).show();
Log.v("myTag", "ASYNC IN PROGRESS ALREADY!!!");
mHelper.flagEndAsync();
}
}
それでおしまい!これでお金を稼ぐことができます。本当に簡単です!
繰り返しますが、スクリーンショットと写真を含むこのチュートリアルのより詳細なバージョンが必要な場合は、 元の投稿をご覧ください 。他にご質問がある場合は、コメントでお知らせください。