How to implement In-App Billing in an Android application?

前端 未结 7 763
广开言路
广开言路 2020-12-02 04:41

It seems that it is quite complicated to implement In-App Billing in an Android app. How could I do this? The sample app from the SDK only has one Activity, which kind of ov

7条回答
  •  生来不讨喜
    2020-12-02 04:54

    There is a full example of Android In-App Billing v3 step by step is given here with screenshot. Please check the tutorial: Android In-App Billing v3 using ServiceConnection Class

    Hope it will help.

    For more clarification, go through this tutorial: Implementing In-app Billing in Version 3 API

    Steps to follow to Integrate In-app Billing library in our project

    Update your AndroidManifest.xml file.

    Create a ServiceConnection and bind it to IInAppBillingService.

    Send In-app Billing requests from your application to IInAppBillingService.

    Handle In-app Billing responses from Google Play.

    Update AndroidManifest.xml

    
    

    Add the permissions in Manifest.xml file

    Adding the AIDL file to your project

    Build your application. You should see a generated file named IInAppBillingService.java in the /gen directory of your project.

    Update Dependencies in build.gradle file

    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 and activity_in_app_purchase.xml

    This is where will offer our app users the opportunity to to make in-app purchase. In the layout file, we will give the user the opportunity to make purchase in different denominations.

    InAppPurchaseActivity.java

    Note: getAllUserPurchase() and itemPurchaseAvailability() methods should be called in non UI Thread to avoid app crashing.

    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 {
            String packageName;
            public AvailablePurchaseAsyncTask(String packageName){
                this.packageName = packageName;
            }
            @Override
            protected Bundle doInBackground(Void... voids) {
                ArrayList skuList = new ArrayList();
                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 canPurchase = new ArrayList();
                int response = skuDetails.getInt("RESPONSE_CODE");
                if (response == 0) {
                    ArrayList 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 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 extractAllUserPurchase(Bundle ownedItems){
            List mUserItems = new ArrayList();
            int response = ownedItems.getInt("RESPONSE_CODE");
            if (response == 0) {
                ArrayList ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
                ArrayList  purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
                ArrayList  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);
            }
        }
    }
    

    Create Helper Package Directory

    Create a new package folder and name it helpers. Inside the package, create a new java file 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();
        }
    }
    

    Testing In-App Billing Purchase

    1. Create a Google+ account(don't use main account)
    2. Add the users that will test the app in your group or community.

    Errors You might encounter during In-App purchase testing

    the item you requested is not available for purchase

    Solution – According to AndreiBogdan in Stackoverflow,

    All credit goes to Inducesmile for his tutorial

    Android Developer Blog also recommends a training class on Selling In-app Products. To see a complete implementation and learn how to test the application, Please check this tutorial: Selling In-app Products

提交回复
热议问题