Skip to main content

Android

Xsolla Mobile SDK requires very few steps to get a working integration, which normally consists of:

  • Configuration and initialization
  • Launching a purchasing flow
  • Awarding the purchased content to the user (i.e. consumption)

Here is a fully-fledged example implementation that achieves the aforementioned functionality in just under 150 lines of code:

package com.example.yoursdkintegration;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.xsolla.android.mobile.*;
import com.xsolla.android.mobile.common.LogLevel;

import java.util.Arrays;
import java.util.List;

public class YourSDKIntegrationActivity extends Activity {
private static final String TAG = "YourSDKIntegration";

@Nullable
private BillingClient mBillingClient = null;

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

final Config.Common configCommon = Config.Common.getDefault()
.withDebugEnabled(true)
.withLogLevel(LogLevel.VERBOSE)
.withSandboxEnabled(true);

final Config.Integration configIntegration = Config.Integration.forXsolla(
Config.Integration.Xsolla.Authentication.forAutoJWT(
ProjectId.parse(77640).getRight(),
LoginUUID.parse("026201e3-7e40-11ea-a85b-42010aa80004").getRight()
)
);

final Config.Payments configPayments = Config.Payments.getDefault();

final Config config = new Config(
configCommon,
configIntegration,
configPayments
);

mBillingClient = BillingClient.newBuilder(this)
.setConfig(config)
.setListener(new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(
@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases
) {
if (billingResult.isSuccess() && purchases != null) {
for (int i = 0; i < purchases.size(); ++i) {
final ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchases.get(i).getPurchaseToken())
.build();

mBillingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
@Override
public void onConsumeResponse(
@NonNull BillingResult billingResult, String purchaseToken
) {
Log.d(TAG,
"Purchase consumption response: " +
billingResult.getResponseCode()
);
}
});
}
} else {
Log.e(TAG,
"Purchase consumption response: " +
billingResult.getResponseCode()
);
}
}
})
.build();

mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingServiceDisconnected() {
Log.d(TAG, "Disconnected.");
}

@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if (billingResult.isSuccess()) {
final QueryProductDetailsParams queryProductDetailsParams =
QueryProductDetailsParams.newBuilder()
.setProductList(Arrays.asList(
QueryProductDetailsParams.Product.newBuilder()
.setProductId("key_1")
.setProductType(BillingClient.ProductType.INAPP)
.build()
))
.build();

mBillingClient.queryProductDetailsAsync(
queryProductDetailsParams, new ProductDetailsResponseListener() {
@Override
public void onProductDetailsResponse(
@NonNull BillingResult billingResult,
@Nullable List<ProductDetails> productDetailsList
) {
if (billingResult.isSuccess() && productDetailsList != null &&
!productDetailsList.isEmpty()) {
// We'll just use the very first element on the list, but you'd normally
// want to handle the rest of the product details as well.
final ProductDetails productDetails = productDetailsList.get(0);

// Create parameters for the purchasing flow..
final BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(Arrays.asList(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build()
))
.build();

// Initiate a purchasing flow.
mBillingClient.launchBillingFlow(
YourSDKIntegrationActivity.this, billingFlowParams
);
} else {
Log.e(TAG,
"Received an invalid product details response: " +
billingResult.getResponseCode()
);
}
}
}
);
} else {
Log.e(TAG, "Failed to connect.");
}
}
});
}
}