Skip to main content

Android

Integrate the Aghanim to start accepting payments for your game items online through a prebuilt checkout page. The Checkout on Android uses our Android SDK. For it to work properly, you need:

  • Android Studio 4.1 or higher.
  • Minimum Android API level 24 (Android 7.0) or higher.

The Checkout integration mode that works in the player default browser. Use when you want to redirect the players outside your game.

Android SDK. Default browser
Android SDK. Default browser

Register with Aghanim and link your game

First, register for an Aghanim account. At the end of registration, add the link to your mobile game. It should be published in Apple App Store or Google Play Store.

Set up environment

If you want to make real payments, you are all set as the live mode is used as default. Otherwise, use a sandbox, an isolated test environment, to simulate the Aghanim events to test payments without real money movement. To turn on the sandbox mode, set the Sandbox toggle to the active position.

While integrating, you will need an SDK key to authenticate requests to the Aghanim. Keep in mind that the sandbox and live modes have different keys. Find the SDK key in Integration → API keys.

Configure game client-side

Configure your game client to work with the Checkout by setting up the SDK and implementing the necessary code to process its methods.

Install SDK

To install the SDK, add android-sdk to the repositories’ block of your build system so it will know where to find the SDK dependencies.

repositories {
maven {
url = uri("https://us-central1-maven.pkg.dev/ag-registry/android-sdk")
}
}

Configure dependencies

Add android-sdk to the dependencies’ block of your build system as well.

dependencies {
implementation("com.aghanim.android:checkout-web:1.11.0")
}

Initialize SDK

Initialize the SDK manually in the app class.

import android.app.Application
import com.aghanim.android.sdk.common.api.Aghanim

class MyApplication : Application() {
lateinit var aghanim: Aghanim

override fun onCreate() {
super.onCreate()
aghanim = Aghanim(
context = this,
apiKey = "<YOUR_SDK_KEY>"
)
}
}

To let Android SDK know that it should use your app class as an Application, add the app class to the manifest.

<application
android:name=".MyApplication"
>
</application>

OptionalAdjust Logger

With the SDK, you can read its logs from one of the supported levels. The SDK writes all log messages into Android logcat, the default tool for logging in Android.

The simple usage of the SDK log messages means setting the log level you are interested in the most:

  • DEBUG — detailed debug information on almost every event.
  • INFO — general information on the SDK instance state and its events.
  • WARNING — warnings and recoverable errors.
  • ERROR — critical and fatal errors.
  • NONE — no logging. Used by default.
import android.app.Application
import com.aghanim.android.sdk.common.api.Aghanim
import com.aghanim.android.sdk.logging.api.LogLevel

class MyApplication : Application() {
lateinit var aghanim: Aghanim

override fun onCreate() {
super.onCreate()
aghanim = Aghanim(
context = this,
apiKey = "<YOUR_SDK_KEY>"
) {
logLevel = LogLevel.DEBUG
}
}
}

Configure player ID

Since a mobile game has one instance per device, the SDK allows to set the player ID once to use it in all following method calls.

When your game client has the player ID, set it for the current SDK instance.

aghanim.setPlayerId(playerId)

Create item

The integration needs the items to be added to the Dashboard. When creating items, each should have its SKU, a unique identifier for the item within your game backend. You can add their prices, currency, sale configuration, and more.

To add an item to the Dashboard:

  1. Go to SKU Management → Items.
  2. Click Add Item. The site will open the Add Item page.
  3. Enter the item name New item.
  4. Enter the item SKU items.new.ba68a028-2d51-46b4-a854-68fc16af328a.
  5. In the Price block:
    1. Select the Fiat price type for a real money item.
    2. Enter the price 1.99.
  6. Click Add item.

For integration purposes, we have shortened an item setup. Before going live, use every suitable feature while adding items to the Dashboard.

Get items with localized prices

The SDK retrieves items created in the Dashboard with localized prices based on the player's region. Use this to display accurate prices in your in-game store before the player proceeds to checkout.

import com.aghanim.android.sdk.common.api.result.ApiResult
import android.util.Log

when (val result = aghanim.items.get(
skus = listOf("items.new.ba68a028-2d51-46b4-a854-68fc16af328a"),
)) {
is ApiResult.Success -> {
val items = result.value
items.forEach { item ->
// Use item.name, item.price.display, item.imageUrl to populate your store
Log.d("Items", "${item.name}: ${item.price.display}")
}
}

is ApiResult.Failure -> {
// Log debug information for troubleshooting
Log.e("Items", "Failed to get items: ${result.error}")
// TODO: Handle error
}
}

Create Checkout item

It is time to create a variable that represents the items to be purchased.

import com.aghanim.android.sdk.checkout.core.api.models.CheckoutItem

val checkoutItem = CheckoutItem(
sku = "items.new.ba68a028-2d51-46b4-a854-68fc16af328a"
)

As the SDK launches the Checkout in the browser, the player needs to be back to your app once they complete the payment. To return the player to, the SDK needs you to specify deep links for the app.

With Android App Links, the player goes directly to the app without any additional clicks. App Links use standard HTTPS URLs and the Android system verifies their domain. It makes this approach secure and more suitable for the production environment.

Now, create a variable for the deep link URL. We will use it later.

val backToGameUrl = "https://<YOUR_DOMAIN>/checkout-complete"

The Android SDK can trust the deep links and their domain only when the domain is hosted on a server. The server should have the assetlinks.json file containing this domain information. Host the file at https://<YOUR_DOMAIN>/.well-known/assetlinks.json. The file acts as a bridge to verify the authenticity of the links’ domain and your app by the Android SDK.

[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.<YOUR_COMPANY>.<YOUR_APP>",
"sha256_cert_fingerprints": ["<YOUR_APP_FINGERPRINT>"]
}
}]

The Android SDK needs to know that the deep links lead to your app. To create this connection, add an intent filter in the manifest.

<activity android:name=".MainActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<!-- URL parts from backToGameUrl -->
<data
android:scheme="https"
android:host="<YOUR_DOMAIN>"
android:pathPrefix="/checkout"
/>
</intent-filter>
</activity>

Since configuring the intent filter doesn’t perform any app logic, handle the deep link in the Activity.

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.data?.let { uri ->
// TODO: Handle player returning from Checkout
if (uri.host == "<YOUR_DOMAIN>" && uri.path?.startsWith("/checkout") == true) {
// Player has returned from Checkout
// TODO: Check unconsumed orders and consume order if needed
}
}
}

Create Checkout params

When all data variables are ready, create another one that represents Checkout params. Checkout params are the programmatic representation of what the player sees when they are on the payment form. Checkout params are associated with a player and items, they are crucial for the Checkout to work. You can use the existing player from your game or create them at runtime. At this point, you should have the Price template ID.

import com.aghanim.android.sdk.checkout.core.api.models.CheckoutParams
import com.aghanim.android.sdk.common.api.models.common.Locale

val checkoutParams = CheckoutParams(
items = listOf(checkoutItem),
// Optional. Custom message in Checkout UI
customMessage = "Holiday Sale!",
backToGameUrl = backToGameUrl,
// Optional. Locale for texts’ localization. Default is system locale
locale = Locale.EN
)

OptionalUse metadata

You can attach custom metadata to the Checkout for item tracking purposes. You can access it through webhooks and in API responses from the Aghanim. Metadata has a structure of “key-value” pairs.

import com.aghanim.android.sdk.checkout.core.api.models.CheckoutParams

val metadata = mapOf(
"campaign" to "winter_sale",
"source" to "mobile_app",
"user_segment" to "premium",
"ab_test_variant" to "variant_a",
"player_level" to "42"
)

val checkoutParams = CheckoutParams(
items = listOf(checkoutItem),
backToGameUrl = backToGameUrl,
metadata = metadata
)

OptionalSet post-payment redirect behavior

You can choose the behavior of redirecting the player after they have completed the payment successfully. The difference in the provided by the SDK modes is a delay before redirecting or absence of redirecting.

When the player has completed the payment, the SDK redirects them immediately to the deep link from backToGameUrl.

import com.aghanim.android.sdk.common.api.models.order.RedirectSettings
import com.aghanim.android.sdk.common.api.models.order.RedirectMode
import com.aghanim.android.sdk.checkout.core.api.models.CheckoutParams

val redirectSettings = RedirectSettings(
mode = RedirectMode.IMMEDIATE
)

val checkoutParams = CheckoutParams(
items = listOf(checkoutItem),
backToGameUrl = backToGameUrl,
redirectSettings = redirectSettings
)

OptionalSet checkout appearance

You can set the appearance mode for the Checkout UI. The SDK supports automatic detection based on the system setting, or you can force a specific mode.

The SDK automatically detects and applies the appropriate appearance mode based on the system setting.

import com.aghanim.android.sdk.checkout.core.api.models.UiSettings
import com.aghanim.android.sdk.checkout.core.api.models.UiMode
import com.aghanim.android.sdk.checkout.core.api.models.CheckoutParams

val uiSettings = UiSettings(
mode = UiMode.AUTO
)

val checkoutParams = CheckoutParams(
items = listOf(checkoutItem),
uiSettings = uiSettings
)

Launch Checkout

Add a checkout button to your game client that launches the payment form. To display the form, the SDK uses the current activity context. The SDK creates an order from the provided checkout params and opens the Checkout UI. On success, you receive the Order ID to track the order. On failure, you receive an error with debug information for troubleshooting.

import com.aghanim.android.sdk.checkout.web.api.startWebCheckout
import com.aghanim.android.sdk.checkout.web.api.models.LaunchMode
import com.aghanim.android.sdk.common.api.result.ApiResult
import android.util.Log

when (val result = aghanim.startWebCheckout(
context = context,
checkoutParams = checkoutParams,
launchMode = LaunchMode.DefaultBrowser,
)) {
is ApiResult.Success -> {
// Order is created and checkout has launched successfully
val orderId = result.value
// TODO: Save order ID for further granting or tracking
}

is ApiResult.Failure -> {
// Log debug information for troubleshooting
Log.e("Checkout", "Failed to launch Checkout: ${result.error}")
// TODO: Show user-friendly error message to player
}
}

Check unconsumed Orders

After the Checkout page has launched and the Order ID is ready, the player can leave the page and come back, and pay for the items in the Order or not pay for them. The main goal of the Checkout launch result is to tell whether the player has opened it successfully, closed it, or the launch has failed. So after, you need to know which Orders have been paid for and should be granted to the player. The SDK has functionality to get the list of the player paid Orders. When you know their IDs, you should mark them as consumed and grant to the player.

import com.aghanim.android.sdk.common.api.result.ApiResult
import android.util.Log

when (val unconsumedResult = aghanim.orders.getUnconsumed()) {
is ApiResult.Success -> {
// Player has paid but not granted items from orders
val unconsumedOrderIds = unconsumedResult.value
// TODO: Save order IDs for further consuming and granting
}

is ApiResult.Failure -> {
// Log debug information for troubleshooting
Log.e("Orders", "Failed to get unconsumed orders: ${unconsumedResult.error}")
// TODO: Handle error
}
}

Consume paid Orders

When you know the Order IDs that the player has paid for, you should mark them as consumed and grant to the player withing your game logic.

import com.aghanim.android.sdk.common.api.result.ApiResult
import android.util.Log

when (val consumeResult = aghanim.orders.consume(orderId)) {
is ApiResult.Success -> {
// Paid orders are marked as consumed
Log.d("Orders", "Order $orderId is successfully consumed")
// TODO: Grant items in order to player
}

is ApiResult.Failure -> {
// Log debug information for troubleshooting
Log.e("Orders", "Failed to consume order: ${consumeResult.error}")
// TODO: Handle error
}
}

Full implementation code

import com.aghanim.android.sdk.checkout.core.api.models.CheckoutItem
import com.aghanim.android.sdk.checkout.core.api.models.CheckoutParams
import com.aghanim.android.sdk.checkout.web.api.startWebCheckout
import com.aghanim.android.sdk.checkout.web.api.models.LaunchMode
import com.aghanim.android.sdk.common.api.result.ApiResult
import android.util.Log

val checkoutItem = CheckoutItem(
sku = "items.new.ba68a028-2d51-46b4-a854-68fc16af328a"
)

val checkoutParams = CheckoutParams(
items = listOf(checkoutItem),
backToGameUrl = backToGameUrl
)

when (val result = aghanim.startWebCheckout(
context = context,
checkoutParams = checkoutParams,
launchMode = LaunchMode.DefaultBrowser,
)) {
is ApiResult.Success -> {
// Order is created and checkout has launched successfully
val orderId = result.value
// TODO: Save order ID for further granting or tracking
}

is ApiResult.Failure -> {
// Log debug information for troubleshooting
Log.e("Checkout", "Failed to launch Checkout: ${result.error}")
// TODO: Show user-friendly error message to player
}
}

Make payment

Make a payment. If you have set the sandbox mode, use the test card below. In the sandbox, you can make payments only with the test cards. They accept any digits as CVV and any future date as expiry date. Don’t forget to fill in an email address to check the receipt is sent and any postal code as a billing address.

Successful payments

After you complete the payment, you will receive a receipt sent to the specified email address and a transaction record in Aghanim Dashboard → Transactions.

Card BrandCard NumberCVVExpiry dateCountry
VISA (credit)
4242 4242 4242 4242
Any 3 digitsAny future dateGB

Unsuccessful payments

Make unsuccessful payment just in case you are curious. You will see the transaction in Aghanim Dashboard → Transactions as well.

NumberCVVExpiry dateResponse codeDescription
4832 2850 6160 9015
Any 3 digitsAny future date16Payment declined

OptionalAll payment methods

For the live mode, you can find all supported payment methods in Company settings → Payment methods. Turn on or off those you see suitable. Some payment methods are available globally by default. You can’t disable Credit cards, Apple Pay, Google Pay, and PayPal.

In Checkout, the Aghanim evaluates the currency and any restrictions, then dynamically presents only the payment methods available to the player based on evaluation.

OptionalSaving payment method

When you use the live mode, the payment form shows to the player a setting to save their payment method so they can make a one-click payment in the future.

Handle post-payment events on game server-side

To complete the Checkout, handle items’ granting and chargebacks on your game backend. To do so, implement a webhook system that accepts the item.add and item.remove webhooks. See the code example with the implementation.

Comply with the Aghanim requirements for these webhooks:

  • Use HTTPS schema for the single POST webhook endpoint.
  • Check that webhooks are generated and signed by the Aghanim.
  • Handle the idempotency_key field in the webhook payload to prevent processing duplicate webhooks.
  • Respond with the HTTP status codes:
    • 2xx for successfully processed webhooks.
    • 4xx and 5xx for errors.

Grant items to player

The Aghanim sends the item.add webhook to let you know about the purchased items and ask for your permission to grant them to the player.

When the Aghanim has your 2xx answer, it can complete the checkout logic and redirect the player to a deep link if provided.

Support refunds and chargebacks

The Aghanim sends the item.remove webhook when a bank or payment system reverses the transaction, or you have requested refund in Aghanim Dashboard → Transactions. Partial refunds are not supported.

OptionalUse suggested implementation

The suggested implementation handles the webhooks mentioned before:

  • item.add for granting items. You need it for integration.
  • item.remove for refunds and chargebacks. You might need it for integration.
# Use this sample code to handle webhook events in your integration.
#
# 1. Paste this code into a new file `server.py`.
#
# 2. Install dependencies:
# python -m pip install fastapi[all]
#
# 3. Run the server on http://localhost:8000
# python server.py

import fastapi, hashlib, hmac, json, typing

app = fastapi.FastAPI()

@app.post("/webhook")
async def webhook(request: fastapi.Request) -> dict[str, typing.Any]:
secret_key = "<YOUR_S2S_KEY>" # Replace with your actual webhook secret key

raw_payload = await request.body()
payload = raw_payload.decode()
timestamp = request.headers["x-aghanim-signature-timestamp"]
received_signature = request.headers["x-aghanim-signature"]

if not verify_signature(secret_key, payload, timestamp, received_signature):
raise fastapi.HTTPException(status_code=403, detail="Invalid signature")

data = json.loads(payload)
event_type = data["event_type"]
event_data = data["event_data"]

if event_type == "item.add":
add_item(event_data)
return {"status": "ok"}

if event_type == "item.remove":
remove_item(event_data)
return {"status": "ok"}

raise fastapi.HTTPException(status_code=400, detail="Unknown event type")

def verify_signature(secret_key: str, payload: str, timestamp: str, received_signature: str) -> bool:
signature_data = f"{timestamp}.{payload}"
computed_hash = hmac.new(secret_key.encode(), signature_data.encode(), hashlib.sha256)
computed_signature = computed_hash.hexdigest()
return hmac.compare_digest(computed_signature, received_signature)

def add_item(event_data: dict[str, typing.Any]) -> None:
# Placeholder logic for processing the event and adding item.
# In a real application, this function would interact with your database or inventory system.
player_id = event_data["player_id"]
for item in event_data["items"]:
sku = event_data["sku"]
print(f"Item {sku} have been credited to player's {player_id} account.")

def remove_item(event_data: dict[str, typing.Any]) -> None:
# Placeholder logic for processing the event and removing item.
# In a real application, this function would interact with your database or inventory system.
player_id = event_data["player_id"]
for item in event_data["items"]:
sku = event_data["sku"]
print(f"Item {sku} have been removed from player's {player_id} account.")

if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

Add webhook endpoint to Aghanim

When the webhook handling is ready, add the endpoint to the account so the Aghanim could start sending the events.

  1. Go to Integration → Webhooks.
  2. Click New Webhook. The site will open the Create new webhook window.
  3. Cope and paste the URL https://<YOUR_DOMAIN>/webhook.
  4. Click Select events. The site will open the Select events to send window.
  5. Expand the Main class and select the Item add, Item remove checkboxes.
  6. Click Apply.
  7. Click Add. The site will redirect you to the webhook page.
  8. Click Back.

Test your integration

After you have handled the webhooks, check that the purchased items are in your inventory. That’s all.

Next steps

FAQ

What platforms does your SDK support?

The SDK is currently available for Android only.

Which module to choose: checkout-ui or checkout-web?

checkout-ui supports the Checkout for the Native UI integration mode with maximum control over appearance and behavior.

checkout-web supports the Checkout for the In-app browser or Default browser integration modes, providing quick integration and automatic UI updating.

You can use the modules independently or together according to your needs.

What are the code size implications of your SDK?

The size impact depends on your build setup and which SDK modules you integrate. In comparison, checkout-ui has higher impact than checkout-web. The SDK itself is already minified, and we strive to keep the footprint as small as possible.

Key factors that affect the final binary size:

  • R8/ProGuard configuration.
  • Code shrinking.
  • Feature usage.

What are the startup time implications, if any, of your SDK?

The SDK should have minimal impact on app startup:

  • No operations are performed automatically when the app launches.
  • Initialization can be triggered either manually or at app startup, but even then it has little effect since all components use lazy initialization.
  • All operations are completed when the payment UI is closed.

As a result, the SDK has no measurable impact on app startup time.

Your app performance may still depend on the app configuration:

  • Initialization order.
  • Baseline profiles.
  • And build optimizations.

Does your SDK work with EdgeToEdge mode?

The SDK works optimally with apps that use EdgeToEdge mode. The modern Android UI approach provides a seamless, immersive experience by extending your app content behind the system bars.

Do I need to add extra rules when using R8 or ProGuard?

The SDK supports R8, the default code shrinker and obfuscator for modern Android projects. It has all required rules for proper operation of the Android SDK already bundled in the artifact and will be applied automatically during the build process. So if you use R8 or ProGuard, you don’t need to add extra rules.

Need help?
Contact our integration team at integration@aghanim.com

On this page