Since the version of the library has increased from 1.0 to 2.0, I rewrote the contents. Below is the code for the test trial.
I'll show you an example of code that works somehow, but at your own risk. It is a story that involves money, so it is strongly recommended to check the information of the head family. https://developer.android.com/google/play/billing/billing_library_overview?hl=ja
Searching for samples of the in-app purchase process on Android, many were old and complex. It's relatively easy to do with the new Google Play Billing Library, so I'll remember it.
This procedure does not require any settings such as "IInAppBillingService.aidl" or "AndroidManifest.xml". You can also test it before uploading the app to Google Play.
Here, I selected a simple "Emply Activity" as the Activity and wrote all the code in the Main Activity. The project name is "Billing Sample".
build.gradle
dependencies {
....
implementation 'com.android.billingclient:billing:2.0.1'
}
MainActivity.java
package xx.xx.xx.xx.billingsample;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.android.billingclient.api.AcknowledgePurchaseParams;
import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchaseHistoryRecord;
import com.android.billingclient.api.PurchaseHistoryResponseListener;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity
implements View.OnClickListener, PurchasesUpdatedListener, AcknowledgePurchaseResponseListener {
TextView textView1;
private BillingClient billingClient;
List<SkuDetails> mySkuDetailsList;
//Called when the app starts
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Prepare operation buttons and result output field
textView1 = findViewById(R.id.text_view1);
findViewById(R.id.button_get_skus).setOnClickListener(this);
findViewById(R.id.button_query_owned).setOnClickListener(this);
findViewById(R.id.button_purchase).setOnClickListener(this);
findViewById(R.id.button_purchase_history).setOnClickListener(this);
//Prepare Billing Client
billingClient = BillingClient.newBuilder(this)
.setListener(this).enablePendingPurchases().build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
int responseCode = billingResult.getResponseCode();
if (responseCode == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
textView1.setText("Billing Setup OK");
} else {
showResponseCode(responseCode);
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
textView1.setText("Billing Servise Disconnected. Retry");
}
});
}
//Called when the button is clicked
@Override
public void onClick(View v) {
if (v != null) {
switch (v.getId()) {
case R.id.button_get_skus:
querySkuList();
break;
case R.id.button_query_owned:
queryOwned();
break;
case R.id.button_purchase:
startPurchase("android.test.purchased");
break;
case R.id.button_purchase_history:
queryPurchaseHistory();
break;
default:
break;
}
}
}
//Called when the app is closed
@Override
protected void onDestroy() {
billingClient.endConnection();
super.onDestroy();
}
//Inquire about the item you want to buy
void querySkuList() {
List skuList = new ArrayList<>();
skuList.add("android.test.purchased"); // prepared by Google
skuList.add("android.test.canceled");
skuList.add("android.test.refunded");
skuList.add("android.test.item_unavailable");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
// Process the result.
StringBuffer resultStr = new StringBuffer("");
int responseCode = billingResult.getResponseCode();
if (responseCode == BillingClient.BillingResponseCode.OK) {
//Keep Sku details for later purchase process
mySkuDetailsList = skuDetailsList;
//Show list
if (skuDetailsList != null) {
for (Object item : skuDetailsList) {
SkuDetails skuDetails = (SkuDetails) item;
String sku = skuDetails.getSku();
String price = skuDetails.getPrice();
resultStr.append("Sku=" + sku + " Price=" + price + "\n");
}
} else {
resultStr.append("No Sku");
}
textView1.setText(resultStr);
} else {
showResponseCode(responseCode);
}
}
});
}
//Start the purchase process
void startPurchase(String sku) {
SkuDetails skuDetails = getSkuDetails(sku);
if (skuDetails != null) {
BillingFlowParams params = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
BillingResult billingResult = billingClient.launchBillingFlow(this, params);
showResponseCode(billingResult.getResponseCode());
}
}
//Get the details of the specified SKU from within the list
SkuDetails getSkuDetails(String sku) {
SkuDetails skuDetails = null;
if(mySkuDetailsList==null){
textView1.setText("Exec [Get Skus] first");
}else {
for (SkuDetails sd : mySkuDetailsList) {
if (sd.getSku().equals(sku)) skuDetails = sd;
}
if (skuDetails == null) {
textView1.setText(sku + " is not found");
}
}
return skuDetails;
}
//Called when updating purchase results
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
StringBuffer resultStr = new StringBuffer("");
int billingResultCode = billingResult.getResponseCode();
if (billingResultCode == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
//Approve the purchase
String state = handlePurchase(purchase);
//Display the purchased Sku string and approval result
String sku = purchase.getSku();
resultStr.append(sku).append("\n");
resultStr.append(" State=").append(state).append("\n");
}
textView1.setText(resultStr);
} else {
// Handle error codes.
showResponseCode(billingResultCode);
}
}
//Approve the purchase
String handlePurchase(Purchase purchase) {
String stateStr = "error";
int purchaseState = purchase.getPurchaseState();
if (purchaseState == Purchase.PurchaseState.PURCHASED) {
// Grant entitlement to the user.
stateStr = "purchased";
// Acknowledge the purchase if it hasn't already been acknowledged.
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, this);
}
}else if(purchaseState == Purchase.PurchaseState.PENDING){
stateStr = "pending";
}else if(purchaseState == Purchase.PurchaseState.UNSPECIFIED_STATE){
stateStr = "unspecified state";
}
return stateStr;
}
//Purchase approval results returned
@Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
int responseCode = billingResult.getResponseCode();
if(responseCode != BillingClient.BillingResponseCode.OK) {
showResponseCode(responseCode);
}
}
//Inquire about purchased items (cash processing)
void queryOwned(){
StringBuffer resultStr = new StringBuffer("");
Purchase.PurchasesResult purchasesResult
= billingClient.queryPurchases(BillingClient.SkuType.INAPP);
int responseCode = purchasesResult.getResponseCode ();
if(responseCode== BillingClient.BillingResponseCode.OK){
resultStr.append("Query Success\n");
List<Purchase> purchases = purchasesResult.getPurchasesList();
if(purchases.isEmpty()){
resultStr.append("Owned Nothing");
} else {
for (Purchase purchase : purchases) {
resultStr.append(purchase.getSku()).append("\n");
}
}
textView1.setText(resultStr);
}else{
showResponseCode(responseCode);
}
}
//Inquire about purchase history (network access processing)
void queryPurchaseHistory() {
billingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP,
new PurchaseHistoryResponseListener() {
@Override
public void onPurchaseHistoryResponse(BillingResult billingResult,
List<PurchaseHistoryRecord> purchasesList) {
int responseCode = billingResult.getResponseCode();
if (responseCode == BillingClient.BillingResponseCode.OK) {
if (purchasesList == null || purchasesList.size() == 0) {
textView1.setText("No History");
} else {
for (PurchaseHistoryRecord purchase : purchasesList) {
// Process the result.
textView1.setText("Purchase History="
+ purchase.toString() + "\n");
}
}
} else {
showResponseCode(responseCode);
}
}
});
}
//View server response
void showResponseCode(int responseCode){
switch(responseCode){
case BillingClient.BillingResponseCode.OK:
textView1.setText("OK");break;
case BillingClient.BillingResponseCode.USER_CANCELED:
textView1.setText("USER_CANCELED");break;
case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE:
textView1.setText("SERVICE_UNAVAILABLE");break;
case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE:
textView1.setText("BILLING_UNAVAILABLE");break;
case BillingClient.BillingResponseCode.ITEM_UNAVAILABLE:
textView1.setText("ITEM_UNAVAILABLE");break;
case BillingClient.BillingResponseCode.DEVELOPER_ERROR:
textView1.setText("DEVELOPER_ERROR");break;
case BillingClient.BillingResponseCode.ERROR:
textView1.setText("ERROR");break;
case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED:
textView1.setText("ITEM_ALREADY_OWNED");break;
case BillingClient.BillingResponseCode.ITEM_NOT_OWNED:
textView1.setText("ITEM_NOT_OWNED");break;
case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED:
textView1.setText("SERVICE_DISCONNECTED");break;
case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED:
textView1.setText("FEATURE_NOT_SUPPORTED");break;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/button_get_skus"
android:text="Get Skus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/button_query_owned"
android:text="Query Owned"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/button_purchase"
android:text="Purchase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/button_purchase_history"
android:text="Purchase History"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/text_view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
Open Build Valiants from the leftmost tab of Android Studio and change Build Valiant from Debug to Release.
Error: The apk for your currently selected variant (app-release-unsigned.apk) is not signed.
Click "Fix" displayed on the right side of the error display and enter the key registered in Google Play. If you have not registered, you need to register. The key input procedure is as follows.