Google Play Billing Subscriptions in-app purchase Android


IMPORTANT

* Need google play testing user
*You can create if from the DEVELOPER CONSOLE / SETTINGS / TESTERS

* Products of the app ( Google play developer )
* Products will be available after 15 min
* You need the alpha version installed on your testing device. YOU CAN NOT debug from android studio version.
* price and distribution
* content calification ( app contains virtual purchases )
* APP needs to be uploaded and published in ALPHA VERSION
*Set your testing group and email to the alpha version app
*Send yourself an email with the link of the app for testing
* Loading billing response need some extra time put inside a handler (2 seconds) or click listener else will be null and crash the app

USEFUL LINK 
https://developer.android.com/google/play/billing/billing_integrate.html?hl=es-419#Subs

FIRST STEPS

* YOU NEED THE GOOGLE PLAY BILLING PACKAGE FROM ANDROID STUDIO SDK TOOLS. ( INCLUDED INSIDE DEV KIT LATEST VERSIONS )
* If you don't find it in you computer dowload a file called IInAppBillingService.aidl
* create a new folder inside your project main called aidl
* inside of it create a package called com.android.vending.billing.
* inside of it paste the IInAppBillingService.aidl file.

AIDL FILE IF YOU DON'T FIND IT

/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.android.vending.billing;

import android.os.Bundle;

/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
interface IInAppBillingService {
/**
* Checks support for the requested billing API version, package and in-app type.
* Minimum API version supported by this interface is 3.
* @param apiVersion the billing version which the app is using
* @param packageName the package name of the calling app
* @param type type of the in-app item being purchased "inapp" for one-time purchases
* and "subs" for subscription.
* @return RESULT_OK(0) on success, corresponding result code on failures
*/
int isBillingSupported(int apiVersion, String packageName, String type);

/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the Third-party is using
* @param packageName the package name of the calling app
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
* "title : "Example Title", "description" : "This is an example description" }'
*/
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);

/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type the type of the in-app item ("inapp" for one-time purchases
* and "subs" for subscription).
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
* TODO: change this to app-specific keys.
*/
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
String developerPayload);

/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type the type of the in-app items being requested
* ("inapp" for one-time purchases and "subs" for subscription).
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);

/**
* Consume the last purchase of the given SKU. This will result in this item being removed
* from all subsequent responses to getPurchases() and allow re-purchase of this item.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param purchaseToken token in the purchase information JSON that identifies the purchase
* to be consumed
* @return 0 if consumption succeeded. Appropriate error values for failures.
*/
int consumePurchase(int apiVersion, String packageName, String purchaseToken);
}

WORKING CODE (Landing.class)

public class Landing extends AppCompatActivity {

RelativeLayout splash;

IInAppBillingService mService;
ServiceConnection mServiceConn;

LinearLayout layouttrial;

TextView tvtrial;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);

setContentView(R.layout.activity_landing);

splash = findViewById(R.id.splash);

if(Shared.get(Landing.this,"paid").equals("yes")){

Intent myIntent = new Intent(Landing.this, MainActivity.class);
Landing.this.startActivity(myIntent);
finish();

}else{

}

// HERE BILLING STARTS THE CONNECTION

mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}

@Override
public void onServiceConnected(ComponentName name,
IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
}
};

Intent serviceIntent =
new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);

// HERE YOU NEED TO PUT THE SKU (UNIQUE ID YOU GIVE FOR THE PURCHASE ON THE PLAY GOOGLE DEV DASHBOARD)

ArrayList skuList = new ArrayList ();

skuList.add("monthlyaccess");

final Bundle querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skuList);

Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {

splash.setVisibility(View.INVISIBLE);
try {

// IMPORTANT IS YOU ARE USING SUBSCRIPTIONS PUT BELOW subs else if its a normal purchase or consumable inapp inside brackets

Bundle skuDetails = mService.getSkuDetails(3,
getPackageName(), "subs", querySkus);

int response = skuDetails.getInt("RESPONSE_CODE");
if (response == 0) {
ArrayList responseList
= skuDetails.getStringArrayList("DETAILS_LIST");

for (String thisResponse : responseList) {
JSONObject object = null;
try {
object = new JSONObject(thisResponse);
} catch (JSONException e) {
e.printStackTrace();
}
String sku = null;
try {
sku = object.getString("productId");
} catch (JSONException e) {
e.printStackTrace();
}
String price = null;
try {
price = object.getString("price");

if(sku.equals("monthlyaccess")){
tvmonthly.setText(tvmonthly.getText()+" "+price);
}

} catch (JSONException e) {
e.printStackTrace();
}
}

}else{
// Toast.makeText(Landing.this,"err", Toast.LENGTH_LONG).show();
}
} catch (RemoteException e) {

Log.e("trist",e.toString());
}
}
}, 3000);

startbtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

// HERE IS THE WAY TO CALL THE PURCHASE INTENT

Bundle buyIntentBundle = null;
try {
buyIntentBundle = mService.getBuyIntent(3, getPackageName(),
"monthlyaccess", "subs", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
} catch (RemoteException e) {
e.printStackTrace();
}
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");

try {
startIntentSenderForResult(pendingIntent.getIntentSender(),
1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0));
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
}

// OVERRIDE METHOD REQUIRED TO SAVE MEMORY

@Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
unbindService(mServiceConn);
}
}

// OVERRIDE OF THE ON RESULT IF THE USER CANCEL OR PURCHASE AT THE END THE SUBSCRIPTION OR ITEM

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
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 jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");

// DO SOMTHING OR PURCHASE DONE FOR EXAMPLE SAVE A SHARED AND CALL A NEW ACTIVITY

if(sku.equals("monthlyaccess")){

Shared.set(Landing.this,"paid","yes");
Intent myIntent = new Intent(Landing.this, MainActivity.class);
Landing.this.startActivity(myIntent);
finish();

}

//Toast.makeText(Landing.this,"You have bought the " + sku + ". Excellent choice",Toast.LENGTH_LONG).show();

}
catch (JSONException e) {

// IN CASE OF ERROR OR REJECT...

// Toast.makeText(Landing.this,"Failed to purchase data",Toast.LENGTH_LONG).show();

e.printStackTrace();
}
}
}
}

}

POSSIBLE ERRORS ON ANDROID BILLING RESPONSE

0. BILLING_RESPONSE_RESULT_OK: Success
1. BILLING_RESPONSE_RESULT_USER_CANCELED: User pressed back or canceled a dialog
2. BILLING_RESPONSE_RESULT_SERVICE_UNAVAILABLE: Network connection is down
3. BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE: Billing API version is not supported for the type requested
4. BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE: Requested product is not available for purchase
5. BILLING_RESPONSE_RESULT_DEVELOPER_ERROR: Invalid arguments provided to the API. This error can also indicate that the application was not correctly signed or properly set up for In-app Billing in Google Play, or does not have the necessary permissions in its manifest
6. BILLING_RESPONSE_RESULT_ERROR: Fatal error during the API action
7. BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED: Failure to purchase since item is already owned
8. BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED: Failure to consume since item is not owned

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *