Xtopay
Payment APIs

Checkout SDK

Simplify payment collection across Web, iOS, Android, and Flutter applications using Xtopay's Checkout SDKs.

Checkout SDK

The Checkout SDK provides a simplified way for developers to accept online payments within web, mobile, and cross-platform applications. Instead of redirecting customers away from your application, the Checkout SDK opens a native overlay interface to process card, mobile money, and wallet payments securely in-context.


JavaScript Web SDK

The Xtopay JavaScript SDK supports three primary methods of integration:

  1. Redirect Integration: Opens the hosted payment page in a new browser tab or window.
  2. Iframe Integration: Embeds the checkout experience inside a secure iframe container on your webpage.
  3. Modal Integration: Loads the checkout flow inside a lightbox popup overlay on top of your webpage.

Installation

To integrate the JavaScript SDK into your web project, install the NPM package or include the CDN script tag.

npm install @xtopay/checkout

Pre-Checkout Flow

[!IMPORTANT] Secure Session Initialization Before invoking the frontend SDK, your backend server must create a checkout session via POST /v1/payments. Never initialize checkout with your Secret API Key on the client side.

Backend Session Creation

On your server, initiate the session and return the payload to your client app:

{
  "success": true,
  "data": {
    "amount": 10000,
    "currency": "GHS",
    "description": "Premium Plan Purchase",
    "clientReference": "f03dbdcf8d4040dd88d0a82794b0229f",
    "customer": {
      "name": "Kwame Mensah",
      "email": "kwame@example.com",
      "phone": "+233241234567"
    }
  }
}

Web Integration Methods

1. Redirect Integration

Opens the payment page in a new window or tab.

import XtopayCheckout from "@xtopay/checkout";

const checkout = new XtopayCheckout();

// Purchase information returned from your pre-checkout backend
const purchaseInfo = {
  amount: 10000, // GHS 100.00
  purchaseDescription: "Premium Plan Purchase",
  customerPhoneNumber: "+233241234567",
  clientReference: "f03dbdcf8d4040dd88d0a82794b0229f"
};

const config = {
  publicKey: "xtpk_live_xxxxxxxxxxxxxxxxxxxx",
  branding: "enabled",
  callbackUrl: "https://merchant.com/webhooks/xtopay"
};

// Redirect customer to checkout
const initiatePayment = () => {
  checkout.redirect({
    purchaseInfo,
    config
  });
};

2. Iframe Integration

Embeds the checkout flow inside a local container element.

Add a designated container element in your HTML:

<div id="xtopay-checkout-iframe"></div>

Initialize the inline iframe using the SDK:

import XtopayCheckout from "@xtopay/checkout";

const checkout = new XtopayCheckout();

const purchaseInfo = {
  amount: 10000,
  purchaseDescription: "Premium Plan Purchase",
  customerPhoneNumber: "+233241234567",
  clientReference: "f03dbdcf8d4040dd88d0a82794b0229f"
};

const config = {
  publicKey: "xtpk_live_xxxxxxxxxxxxxxxxxxxx",
  branding: "enabled",
  callbackUrl: "https://merchant.com/webhooks/xtopay"
};

const iframeStyle = {
  width: "100%",
  height: "550px",
  border: "none"
};

const openEmbeddedCheckout = () => {
  checkout.initIframe({
    purchaseInfo,
    config,
    iframeStyle,
    callBacks: {
      onInit: () => console.log("Iframe initialized"),
      onLoad: () => console.log("Iframe finished loading"),
      onPaymentSuccess: (data) => console.log("Payment successful", data),
      onPaymentFailure: (error) => console.error("Payment failed", error),
      onFeesChanged: (fees) => console.log("Applicable fees changed", fees),
      onResize: (size) => console.log("Iframe resized to:", size.height)
    }
  });
};

3. Modal Integration

Renders the checkout process inside a dismissible screen overlay popup.

import XtopayCheckout from "@xtopay/checkout";

const checkout = new XtopayCheckout();

const purchaseInfo = {
  amount: 10000,
  purchaseDescription: "Premium Plan Purchase",
  customerPhoneNumber: "+233241234567",
  clientReference: "f03dbdcf8d4040dd88d0a82794b0229f"
};

const config = {
  publicKey: "xtpk_live_xxxxxxxxxxxxxxxxxxxx",
  branding: "enabled",
  callbackUrl: "https://merchant.com/webhooks/xtopay"
};

const openCheckoutPopup = () => {
  checkout.openModal({
    purchaseInfo,
    config,
    callBacks: {
      onInit: () => console.log("Modal initialized"),
      onLoad: () => console.log("Modal content loaded"),
      onPaymentSuccess: (data) => {
        console.log("Payment succeeded:", data);
        checkout.closePopUp(); // Dismiss modal overlay
      },
      onPaymentFailure: (error) => console.error("Payment failed:", error),
      onClose: () => console.log("Checkout modal dismissed by user")
    }
  });
};

JS SDK Configurations

ParameterTypeRequiredDescription
purchaseInfoObjectYesTransaction details.
purchaseInfo.amountNumberYesCharge amount in minor units (e.g. 10000 = GHS 100.00).
purchaseInfo.purchaseDescriptionStringYesItem purchase summary.
purchaseInfo.customerPhoneNumberStringYesMobile wallet or contact phone number.
purchaseInfo.clientReferenceStringYesUnique string reference for order mapping.
configObjectYesSession configurations.
config.publicKeyStringYesClient-side API key.
config.brandingStringNo"enabled" to show merchant headers/logos; "disabled" to hide them.
config.callbackUrlStringYesTarget url to receive payment notification updates.

Android SDK

The Xtopay Android SDK simplifies checkout integration inside native Kotlin or Java applications.

Installation

Add the maven coordinates into your module's build.gradle dependencies:

dependencies {
    implementation "com.xtopay:merchant-checkout-sdk:1.0.0"
}

Creating Checkout Intent

To initialize the checkout controller UI, configure the intent variables using the CheckoutIntent.Builder utility:

import com.xtopay.checkout.CheckoutIntent
import com.xtopay.checkout.ThemeConfig
import java.util.UUID

val themeConfig = ThemeConfig().apply {
    primaryColor = "#7C3AED" // Custom styling theme color
}

val checkoutIntent = CheckoutIntent.Builder(this)
    .setAmount(100.0) // Transaction amount in base units
    .setApiKey("xtpk_live_xxxxxxxxxxxxxxxxxxxx")
    .setMerchantId("11684") // Collection account ID
    .setClientReference(UUID.randomUUID().toString())
    .setCustomerPhoneNumber("+233241234567")
    .setDescription("Order #99283")
    .setCallbackUrl("https://merchant.com/webhooks/xtopay")
    .setTheme(themeConfig)
    .build()

startActivityForResult(checkoutIntent, CHECKOUT_REQUEST_CODE)

Request Parameters

ParameterTypeRequiredDescription
amountDoubleYesPayment amount.
apiKeyStringYesBase64 or plain client Public Key (xtpk_...).
merchantIdStringYesCollection merchant ID.
clientReferenceStringYesUnique transaction ID (max 36 chars).
customerPhoneNumberStringYesCustomer phone number.
callbackUrlStringYesMerchant webhook callback URL.

Handling Results

Override your Activity's or Fragment's onActivityResult block to process payment completion results:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == CHECKOUT_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        val status = data?.getParcelableExtra<CheckoutStatus?>(CheckoutStatus.CHECKOUT_RESULT)
        
        status?.let {
            val isSuccess = it.isPaymentSuccessful
            val transactionId = it.transactionId
            val methodUsed = it.paymentMethod
            val isCanceled = it.isCanceled
            
            if (isSuccess) {
                // Payment succeeded
            }
        }
    }
}

iOS SDK

The Xtopay iOS SDK allows integration inside Swift or Objective-C iOS applications.

Installation

Include the SDK pod identifier inside your Podfile:

pod 'XtopayMerchantCheckoutSDK', '~> 1.0.0'

Run pod install from your command line to sync project settings.

Initialization & View Presentation

Create configuration and purchase information profiles before calling the presentation controller:

import xtopay_merchant_checkout_sdk

// Step 1: Initialize Configuration Profile
let configuration = XtopayCheckoutConfiguration(
    merchantId: "11684",
    callbackUrl: "https://merchant.com/webhooks/xtopay",
    merchantApiKey: "xtpk_live_xxxxxxxxxxxxxxxxxxxx"
)

// Step 2: Initialize Purchase Specifications
let purchaseInfo = PurchaseInfo(
    amount: 100.0,
    customerPhoneNumber: "+233241234567",
    purchaseDescription: "Invoice Item #10023",
    clientReference: UUID().uuidString
)

// Step 3: Launch Checkout View Overlay
CheckoutViewController.presentCheckout(
    from: self,
    with: configuration,
    and: purchaseInfo,
    delegate: self,
    tintColor: UIColor.purple
)

Delegate Callbacks

To check the checkout status result, implement the PaymentFinishedDelegate protocol inside your view controller:

extension MyViewController: PaymentFinishedDelegate {
    func checkStatus(value: PaymentStatus, transactionId: String) {
        switch value {
        case .paymentSuccessful:
            print("Payment completed successfully. Txn: \(transactionId)")
        case .paymentFailed:
            print("Payment authorization failed")
        case .userCancelledPayment:
            print("Customer closed the checkout interface without paying")
        case .unknown:
            print("Payment state undefined. Query backend status API")
        @unknown default:
            break
        }
    }
}

Flutter SDK

Accept payments inside cross-platform Flutter applications on iOS and Android.

Installation

Open your project's pubspec.yaml file and add the git dependency reference:

dependencies:
  xtopay_merchant_checkout_sdk:
    git:
      url: https://github.com/xtopay/xtopay-flutter-merchant-checkout-sdk.git
      ref: main

Integration

Load the widgets and push the screen view configuration onto your navigation stack:

import 'package:xtopay_merchant_checkout_sdk/xtopay_merchant_checkout_sdk.dart';

// Create checkout settings
final configuration = XtopayCheckoutConfiguration(
  merchantId: "11684",
  callbackUrl: "https://merchant.com/webhooks/xtopay",
  merchantApiKey: "xtpk_live_xxxxxxxxxxxxxxxxxxxx"
);

// Define purchase parameters
final purchaseInfo = PurchaseInfo(
  amount: 100.0,
  customerPhoneNumber: "+233241234567",
  purchaseDescription: "Store Checkout Item #8812",
  clientReference: "txn_8812_unique"
);

// Trigger payment screen
final result = await Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) {
      return CheckoutScreen(
        purchaseInfo: purchaseInfo,
        configuration: configuration,
        themeConfig: ThemeConfig(primaryColor: Colors.deepPurple),
      );
    },
  ),
);

if (result is CheckoutCompletionStatus) {
  if (result == CheckoutCompletionStatus.paymentSuccessful) {
    // Payment success
  } else if (result == CheckoutCompletionStatus.userCancelledPayment) {
    // Customer dismissed checkout
  }
}

Webhook Callbacks

When using the Checkout SDK, Xtopay registers the customer actions and performs asynchronous postback webhooks to the callback URL configured in the payment object parameters.

Sample Webhook (Successful Payment)

{
  "ResponseCode": "0000",
  "Message": "success",
  "Data": {
    "Amount": 100.00,
    "Charges": 1.50,
    "AmountAfterCharges": 98.50,
    "Description": "The MTN Mobile Money payment has been approved and processed successfully",
    "ClientReference": "f03dbdcf8d4040dd88d0a82794b0229f",
    "TransactionId": "4a5f113ceeec419399753dc7d704454d",
    "ExternalTransactionId": "0000007375832466",
    "AmountCharged": 100.00,
    "OrderId": "4a5f113ceeec419399753dc7d704454d",
    "PaymentDate": "2026-06-06T17:18:58Z"
  }
}

Sample Webhook (Failed Payment)

{
  "ResponseCode": "2001",
  "Message": "failed",
  "Data": {
    "Amount": 100.00,
    "Charges": 0.00,
    "AmountAfterCharges": 100.00,
    "Description": "Transaction failed: Insufficient wallet balance",
    "ClientReference": "f03dbdcf8d4040dd88d0a82794b0229f",
    "TransactionId": "478e03ec641b4a4287ca7dd0ec0e199b",
    "ExternalTransactionId": "42042068855",
    "AmountCharged": 100.00,
    "OrderId": "478e03ec641b4a4287ca7dd0ec0e199b",
    "PaymentDate": "2026-06-06T17:00:33Z"
  }
}

How is this guide?

Edit this page on GitHub
Last updated on June 6, 2026

On this page