web-dev-qa-db-ja.com

Androidアプリケーションでアプリ内課金を実装するには?

アプリ内課金をAndroidアプリに実装するのは非常に複雑なようです。どうすればよいでしょうか?SDKのサンプルアプリにはアクティビティが1つしかありません。複数のアクティビティがある私のようなアプリケーション向けです。

69
SZH

さて、私は私が経験したことを説明しようとします。私は自分をこれに関する専門家とは考えていませんが、数日頭を骨折しました。

まず第一に、例とアプリケーションのワークフローを理解しようとして非常に苦労しました。単純な例から始める方が良いと思いましたが、コードを小さな断片に分けることは非常に難しく、何かを壊しているかどうかはわかりません。私が持っているものと、それを機能させるために例から変更したものを説明します。

私はすべての購入が由来する単一のアクティビティを持っています。 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を使用してみてください。これまでのところこれをテストしましたが、動作しますが、返金された状態などのすべてをカバーする必要があります。この場合、ユーザーに機能を保持させていますが、変更する前に完全に機能することを確認したいと思います。

それがあなたや他の人たちに役立つことを願っています。

41
sfratini

V3:クイックスタートのためのチュートリアルです。彼はgoogleの例(Trivial Drive)のヘルパークラスを使用しています...良い最初の「こんにちは請求」として..

http://www.techotopia.com/index.php/Integrating_Google_Play_In-app_Billing_into_an_Android_Application_%E2%80%93_A_Tutorial

アプリ内課金v3の場合、これは非常に役立ちました。
http://blog.blundellapps.com/simple-inapp-billing-payment-v3/

9
unlimit

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ファイルに権限を追加します

AIDLファイルをプロジェクトに追加する

アプリケーションをビルドします。プロジェクトの/ genディレクトリにIInAppBillingService.Javaという名前の生成されたファイルが表示されます。

Build.gradleファイルの依存関係を更新する

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();
    }
}

アプリ内課金の購入のテスト

  1. Google+アカウントを作成します(メインアカウントは使用しないでください)
  2. グループまたはコミュニティでアプリをテストするユーザーを追加します。

アプリ内購入テスト中に発生する可能性のあるエラー

リクエストしたアイテムは購入できません

ソリューション- StackoverflowのAndreiBogdanによる

すべてのクレジットは Inducesmile の彼の tutorial

Android開発者ブログでは、アプリ内製品の販売に関するトレーニングクラスも推奨しています。完全な実装を確認し、アプリケーションをテストする方法を学習するには、このチュートリアルを確認してください。 アプリ内製品の販売

6
SkyWalker

簡単なライブラリを使用してGoogle PlayとAmazon Appstore全体に公開する場合は、 RoboBillingLibrary を使用します。両方の詳細を1つの使いやすいライブラリに抽象化します。詳細な手順はGithubページにあります。

5

さて、これはオンラインで入手できるドキュメントがあまりないものの1つなので、すべてを段階的に説明するために最善を尽くします。これのより詳細なバージョン(スクリーンショット付き)である私のブログ投稿から取られた Millibit にあります。難しい話は抜きにして、

ステップ1:パーミッションこれは最も簡単なステップです。 manifest.xmlファイルに移動し、タグの下に次の行を追加します。

<uses-permission Android:name="com.Android.vending.BILLING" />

これにより、アプリにアプリ内課金にアクセスする権限が付与されます。 API 22より上のバージョンをターゲットにしている場合、実行時にこの権限が付与されていることを確認する必要があります。

ステップ2:Play Console次に、アプリをGoogle Play Consoleにアップロードする必要があります。アプリはまだ一般公開されていません(心配しないでください)。ベータリリースセクションにアップロードするだけで、アプリ内購入をテストできます。これを行う必要があるのは、請求プロセスが実際に機能するために、Googleが何らかのバージョンのAPKをアップロードする必要があるためです。

  1. https://play.google.com/apps/publish/ にアクセスします

  2. アプリケーションを作成する

  3. 手順に従ってアプリをセットアップします

  4. アプリのリリースに移動

  5. ベータ版に移動します

  6. Android studioでアプリのAPKを作成し、Play Consoleのベータ版プロダクションにアップロードします

(リリースする前に、ストアの掲載、コンテンツのレーティング、価格設定、および配布に入力済みであることを確認してください)

  1. 魔法のボタンを押してください(公開!)

ステップ3:プロジェクトのセットアップこれは、ファイルの束をコピーして貼り付ける必要がある部分です。

まず、 this ファイルを取得し、ダウンロードして、src/mainItの下に配置します。次に、フォルダーに自分自身をビルドする必要があります次に、 this whole utilフォルダーを取得し、それをsrc/Java folder.に貼り付けてから、プロジェクトを再構築してエラーを解決します。 Utilフォルダには次のクラスが含まれています。

  • IabBroadcastReceiver
  • IabException
  • IabHelper
  • IabResult
  • 在庫
  • 購入
  • セキュリティ
  • SkuDetails

ステップ4:製品の作成

  1. 管理下の製品を作成する

  2. [保存]をクリックして、「価格設定テンプレート」を作成します

ここで、この製品の価格を選択します。さまざまな国の価格を選択することも、価格の下にあるすべての国を選択した場合に自動的に調整することもできます。

  1. アプリ内製品がアクティブ化され、最後にもう一度コンソールで正しいアプリケーションにリンクされていることを確認してください。

最後に、製品のIDをメモします。次のいくつかの手順でこのIDを使用します。

  1. Base64EncodedStringを取得します

「サービスと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();
    }
}

それでおしまい!これでお金を稼ぐことができます。本当に簡単です!

繰り返しますが、スクリーンショットと写真を含むこのチュートリアルのより詳細なバージョンが必要な場合は、 元の投稿をご覧ください 。他にご質問がある場合は、コメントでお知らせください。

4
Ruchir Baronia