Skip to main content

Unity

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

  • Unity 2022.3.62f2 LTS or later.
  • Android Min API Level 24.
  • EDM4U Min 1.2.184.
  • Gradle 8.11.1 and AGP 8.10.0 with the com.android.tools.build:gradle classpath.
  • Build tools 35.0.0 or higher.
  • JDK version 17.
  • NewtonJson 3.2.1 or higher.
Android. Default browser
Android. Default browser

Android. 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.

Add SDK scoped registries

Additionally to the Aghanim resources, the SDK uses EDM (External Dependency Manager) for Unity to update and resolve Android (Gradle and Maven) и iOS (CocoaPods) dependencies. Add the registries to Unity settings so it will know where to find their dependencies.

In the Unity Editor:

  1. Select Edit → Project Settings.
  2. Go to Package Manager.
  3. Expand the Scoped Registries pane.
  4. Add the Aghanim registry:
    1. In the pane for a scoped registry name, click +.
    2. Copy and paste the registry details:
      • For the Name filed, use Aghanim Registry.
      • For the URL filed, use https://us-central1-npm.pkg.dev/ag-registry/aghanim.
      • For the Scopes filed, use com.aghanim.
    3. Click Save.
  5. Add the EDM registry:
    1. In the pane for a scoped registry name, click +.
    2. Copy and paste the registry details:
      • For the Name filed, use package.openupm.com.
      • For the URL filed, use https://package.openupm.com.
      • For the Scopes filed, use com.google.external-dependency-manager.
    3. Click Save.

Set Android Minimum API Level

To comply with the SDK software requirements, set Android Minimum API Level 24.

In the Unity Editor:

  1. Select Edit → Project Settings.
  2. Go to Player.
  3. Click the Android settings tab.
  4. Expand the Other Settings pane.
  5. Scroll to the Identification group.
  6. In the Minimum API Level field, select API level 24.

Enable Custom Gradle templates

On Android, Unity handles the project builds by Gradle. To give the SDK full access to Gradle builds, enable some templates to customize the Gradle configuration.

In the Unity Editor:

  1. Select Edit → Project Settings.
  2. Go to Player.
  3. Click the Android settings tab.
  4. Expand the Publishing settings pane.
  5. Scroll to the Build group.
  6. Select the checkboxes:
    • Custom Main Gradle Template.
    • Custom Base Gradle Template.
    • Custom Gradle Properties Template.

Install SDK

Additionally to the Aghanim resources, the SDK uses EDM (External Dependency Manager) for Unity to update and resolve Android (Gradle and Maven) и iOS (CocoaPods) dependencies. Add the registries to Unity settings so it will know where to find their dependencies.

In the Unity Editor:

  1. Select Window → Package Manager.
  2. In the window, select + → Add package by name.
  3. Copy and paste the SDK details:
    • For the Name filed, use com.aghanim.sdk.
    • For the Version field, use 1.13.2.
  4. Click Add.
  5. Wait for the Aghanim SDK and EDM to be installed.
  6. Close the window.

For Android, to verify that the SDK is installed in the Unity Editor:

  1. Select Assets → External Dependency Manager → Android Resolver → Force Resolve.
  2. Get the Android Dependencies window with the Resolution Succeeded message.
  3. Close the window.

Configure EDM

To let EDM declare dependencies in the mainTemplate.gradle file rather than download dependency artifacts into the Unity project, configure EDM in the Unity Editor:

  1. Select Assets → External Dependency Manager → Android Resolver → Settings.
  2. In the window:
    1. Clear the Enable Auto-Resolution checkbox.
    2. Select the Patch mainTemplate.gradle checkbox.
    3. Click OK.

Initialize SDK

To use the SDK, initialize it once in the lifetime of the game process:

  1. Copy the SDK key from Integration → API keys.
  2. Create AghanimConfig with the SDK key and pass it to Aghanim.Initialize:
using Aghanim;

private void Awake()
{
var config = new AghanimConfig("YOUR_SDK_API_KEY");
Aghanim.Initialize(config);
}

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, iOS Xcode console, and Unity Editor → Unity Console, the default tools for logging.

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.
using Aghanim;

private void Awake()
{
var config = new AghanimConfig("YOUR_SDK_API_KEY")
{
MinLogLevel = AghanimLogLevel.DEBUG
};
Aghanim.Initialize(config);
}

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.

Aghanim.GetItems(
skus: new List<string> { "items.new.ba68a028-2d51-46b4-a854-68fc16af328a" },
onSuccess: (items) =>
{
foreach (var item in items)
{
// Use item.Name, item.Price.Display, item.ImageUrl to populate your store
Debug.Log($"{item.Name}: {item.Price.Display}");
}
},
onError: (error) =>
{
// Log debug information for troubleshooting
Debug.LogError($"Failed to get items: {error}");
// TODO: Handle error
}
);

Create Checkout item

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

var items = new List<CheckoutItem>
{
new CheckoutItem("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 and iOS Universal Links, the player goes directly to the app without any additional clicks. Links use standard HTTPS URLs and the operating system verifies their domain. It makes this approach secure and more suitable for the production environment. We recommend to use it for better players’ experience.

Let’s configure App Links first. Create a variable for the deep link URL. We will use it later.

string 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 Unity has the intent filter configuration, no need to handle the deep link in the Activity.

Now, it is Universal Links turn. After building the Xcode project from Unity, open it in Xcode to add the Associated Domains capability:

  1. Select your app target in the project editor.
  2. Go to the Signing & Capabilities tab.
  3. Click + Capability and select Associated Domains.
  4. Click the Add (+) button at the bottom of the Domains table and add applinks:<YOUR_DOMAIN>. Include only the desired subdomain and the top-level domain. Don't include path and query components or a trailing slash.

To avoid repeating this on every rebuild, you can automate it with a PostProcessBuild script using ProjectCapabilityManager.AddAssociatedDomains():

using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;

public class Sample_AddAssociatedDomains
{
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuiltProject)
{
// Stop processing if build target isn't iOS

if (buildTarget != BuildTarget.iOS)
return;

// Initialize PBXProject
var projectPath = PBXProject.GetPBXProjectPath(pathToBuiltProject);
PBXProject pbxProject = new PBXProject();
pbxProject.ReadFromFile(projectPath);

// Get Main target GUID
string mainTargetGuid = pbxProject.GetUnityMainTargetGuid();

// Check if there's already an entitlements file created and use it. If not, create a new file called Example.entitlements
string entitlementsFile = pbxProject.GetBuildPropertyForAnyConfig(mainTargetGuid, "CODE_SIGN_ENTITLEMENTS");
if (entitlementsFile == null)
{
entitlementsFile = string.Format("Example.entitlements");
}

// Initialize ProjectCapabilityManager
ProjectCapabilityManager capabilityManager = new ProjectCapabilityManager(projectPath, entitlementsFile, targetGuid: mainTargetGuid);

// Add 'Associated Domains' capability and pass the chosen domains as a string array
string[] domains = new string[] { "applinks:<YOUR_DOMAIN>" };
capabilityManager.AddAssociatedDomains(domains);

// Call WriteToFile to save the changes to project file
capabilityManager.WriteToFile();
}

}

iOS also requires domain verification. Create a file named apple-app-site-association (without an extension) and host it at https://<YOUR_DOMAIN>/.well-known/apple-app-site-association. The appIDs value uses the format <Application Identifier Prefix>.<Bundle Identifier> as documented in Supporting Associated Domains.

{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.mygame"],
"components": [
{
"/": "/checkout*"
}
]
}
]
}
}
Hosting requirements
  • You must host the file using HTTPS with a valid certificate and with no redirects.
  • If your site uses multiple subdomains, each requires its own entry in the Associated Domains Entitlement, and each must serve its own apple-app-site-association file.
  • Since iOS 14, apps send requests for the file to an Apple-managed CDN instead of directly to your web server. The CDN requests the file within 24 hours. Devices check for updates approximately once per week after app installation. During development, if your server is unreachable from the public internet, use the alternate mode to bypass the CDN: applinks:<YOUR_DOMAIN>?mode=developer.

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.

var checkoutParams = new CheckoutParams(
items: items,
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.

var metadata = new Dictionary<string, string>
{
{ "campaign", "winter_sale" },
{ "source", "mobile_app" },
{ "user_segment", "premium" },
{ "ab_test_variant", "variant_a" },
{ "player_level", "42" }
};

var checkoutParams = new CheckoutParams(
items: items,
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.

using Aghanim.Scripts.Models.Orders;

var redirectSettings = new RedirectSettings(
mode: RedirectMode.Immediate
);

var checkoutParams = new CheckoutParams(
items: items,
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.

var uiSettings = new UiSettings(
mode: UiMode.Auto
);

var checkoutParams = new CheckoutParams(
items: items,
uiSettings: uiSettings
);

Launch Checkout

Add a checkout button to your game client that launches the payment form. 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.

For Android and iOS, the Default browser launch mode works in the player default browser. Use the mode when you want to redirect the player outside your app.

Aghanim.StartCheckout(
checkoutParams,
LaunchMode.DefaultBrowser,
onSuccess: (orderId) =>
{
// Order is created and checkout has launched successfully
// TODO: Save order ID for further granting or tracking
},
onError: (error) =>
{
// Log debug information for troubleshooting
Debug.LogError($"Failed to launch Checkout: {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 Add webhook. The site will open the Create 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

Troubleshooting

Failed to initialize Aghanim Checkout

Verify that the specified SDK key is correct.

Having dependency issues

  1. Make sure the External Dependency Manager is installed.
  2. Check if all dependencies are resolved in the CheckoutSdkDependencies.xml file.
  1. Verify that the deep link scheme for your app is properly configured in Android Manifest.
  2. Check that the deep link handler is set up in Unity build settings.

FAQ

What platforms does your Unity SDK support?

The Unity SDK is available for both iOS and Android.

Need help?
Contact our integration team at [email protected]