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

Upload repository / Android library to jitpack.io via github

REFERENCE LINK

https://medium.com/@ome450901/publish-an-android-library-by-jitpack-a0342684cbd0

1) In the project

buildscript {
dependencies {
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' // Add this line


2) In the library gradle

apply plugin: 'com.github.dcendents.android-maven'

group='com.github.YourUsername'

3)in the android studio console project path

./gradlew install

git tag 1.0
git push --tags

4) go to your github account , your project and go to releases and create a new one with the same tag you push before

5) go to https://jitpack.io/ search your github path Fespuna/AndroidEasyRate and choose your version in this case 1.0

6) use your repository / library in any project using the methods below

IMPORTANT PUT THIS LINES ON ALLPROJECTS INSIDE THRE PROJECT GRADLE

allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}

dependencies {
implementation 'com.github.Fespuna:AndroidEasyRate:1.0'
}

followed tutorial:
https://medium.com/@ome450901/publish-an-android-library-by-jitpack-a0342684cbd0

Volley invalidate SSL request error Android

try {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] myTrustedAnchors = new X509Certificate[0];
return myTrustedAnchors;
}

@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {}

@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};

SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
} catch (Exception e) {
}

Upload Android to Bitbucket -GIT – BITBUCKET – RESPOSITORY – ANDROID STUDIO

1- Create the repository on your Bitbucket account

2- Create your project in Android Studio

3- In Android Studio, Go to VCS

4- Choose 'Enable version control'

5- Choose Git and press OK

6- Right click on your project, choose Git then click Add

7- Open Terminal in Android studio

8- Go to your Bitbucket repository Overview

9- Click on 'I have an existing Project'

10- Copy the 'git remote add origin ... etc' line to your terminal and press enter

11- Click on 'Commit Changes', write your comment then press Commit and push

IF error: src refspec master does not match any ...

git init
git add .
git commit -m 'Initial Commit'
git push -u origin master

How to build aar – Android

How to build aar library release

1- In Android Studio go to the top right window called gradle like in the image below.

2- Find your library and press the build button, wait till the process ends.

3- Now your aar release will be generated in the output folder like in the picture below

 

4- Then you just have to copy this aar to your project libararies to use it.