Hosted Payment Page

The Hosted Payment Page provides a unified payment UI that presents all supported payment methods in a single flow. It's the recommended integration — one setup covers Google Pay, Instant Bank Transfer, and Pay by Card, and payabl. manages the method selection UI for you.

Order ScreenHosted Payment Page

Flow: user taps Pay → Hosted Payment Page opens → user selects a payment method → result is returned via PBLPaymentResult.

Prerequisites

  • The SDK is added to your project — see Android SDK.
  • Your backend can create a session via /mobile/init — see Setup Your Server.
  • Each payment method you enable in the checkout has its own setup — see below.

Payment method setup

The Hosted Payment Page gives you one integration for the payment UI, but each payment method you enable still requires its own configuration. Complete the setup for every method you plan to offer:

Pay by Card — works out of the box, no additional setup.

Google Pay — requires one-time registration with Google and payabl. before it appears in the checkout:

  • Your app is published on Google Play, your Business profile is complete, and your Google Merchant ID is shared with payabl.
  • The Google Wallet API meta-data is enabled in your AndroidManifest.xml.
  • Your /mobile/init request includes the country parameter.

→ Full setup: Google Pay

Instant Bank Transfer — requires deep link handling so the customer returns to your app after authorizing in their bank:

  • Your /mobile/init request includes the redirect URLs (url_success, url_failed, url_return, notification_url) and app_schema.
  • Your AndroidManifest.xml includes an intent filter matching those redirect URLs.
  • Your Activity forwards the returning deep link to the SDK via handlePaymentCallback — in both onNewIntent and onCreate.

→ Full setup: Instant Bank Transfer

⚠️

If a payment method's setup is incomplete, it may not appear in the checkout or may fail when selected — while the other methods continue to work. If a method is missing or failing for your customers, check its setup page above first.

Integration

1. Fetch session configuration

Request a session from your backend and build a PBLConfiguration. Create a new session for every payment attempt.

private suspend fun fetchSessionConfiguration(): PBLConfiguration {
    val response = apiClient.initPayment()
    return PBLConfiguration(
        sessionId = response.sessionId,
        ephemeralKey = response.ephemeralKey,
        userId = "user_id_from_app",
        environment = PBLEnvironment.SANDBOX // use PBLEnvironment.PRODUCTION when going live
    )
}
💳

Saved cards

The SDK can securely save a customer's card and offer it for reuse on their next payment. To enable this, pass a stable, unique userId for the customer in PBLConfiguration — the same value every time that customer pays.

Saved cards are scoped to the userId, so the value must consistently identify the same person across sessions (for example, your internal customer ID). If you pass a different, random, or empty userId between payments, previously saved cards won't be shown.

Never use a value that changes per session (such as the sessionId) or personally identifying data (such as an email address) as the userId.

2. Launch the Hosted Payment Page

Initialize the SDK only when the user starts a payment, then start the flow.

private var payablSDK: PayablSDK? = null

private fun placeOrderWithSessionInit() {
    lifecycleScope.launch {
        val configuration = fetchSessionConfiguration()
        payablSDK = PayablSDK.init(configuration)
        payablSDK?.start(supportFragmentManager) { result ->
            handlePaymentResult(result)
        }
    }
}
📘

If Instant Bank Transfer is enabled, your Activity must also forward the returning deep link to the SDK — see Handle the deep link return.

3. Handle the result

The SDK returns a PBLPaymentResult. Handle all three cases, and release the SDK reference afterwards.

private fun handlePaymentResult(result: PBLPaymentResult) {
    when (result) {
        is PBLPaymentResult.Completed -> {
            // Payment successful — update order status
        }
        is PBLPaymentResult.Canceled -> {
            // User cancelled the payment
        }
        is PBLPaymentResult.Failed -> {
            // Payment failed — inspect result for the error code
        }
    }
    payablSDK = null
}
📘

For Instant Bank Transfer, the authoritative transaction status is confirmed server-to-server via the notification_url webhook — reconcile against your backend before fulfilling the order rather than relying on the client result alone.

Best practices

Do:

  • Create a new session for each payment attempt.
  • Initialize PayablSDK only when the user starts a payment.
  • Release payablSDK after receiving a result.
  • Pass a stable, unique userId per customer if you want saved cards to work.

Don't:

  • Reuse sessions across multiple payments.
  • Initialize the SDK at app startup.
  • Share sessions between users.
  • Use a changing or shared value for userId — it breaks saved cards and can mix customers' saved cards.

Testing

Use the sandbox environment (PBLEnvironment.SANDBOX) throughout development. To smoke-test a card payment, use this success card (expiry: any future date; CVV: any 3 digits):

Card networkCard numberExpected result
VISA4149011500000147PBLPaymentResult.Completed
📘

For the complete set of test cards, decline scenarios, Google Pay test setup, and bank simulator values across all payment methods, see Testing.

Common issues

IssueCause & resolution
Instant Bank Transfer doesn't return to the appDeep linking isn't configured, the redirect URLs / app_schema are missing from /mobile/init, or the deep link isn't forwarded to the SDK. See Instant Bank Transfer.
Google Pay not shown in the checkoutGoogle Pay registration incomplete (Merchant ID not configured with payabl.), Wallet meta-data missing from the manifest, country missing from /mobile/init, or Google Pay isn't available on the device. See Google Pay.
Saved card not appearing for a returning customerA different, empty, or changing userId was passed. Use the same stable userId for the customer on every payment.
Session error on launchThe session was reused, expired, or belongs to a different user. Create a fresh session per attempt.

When something goes wrong, see Error Handling & Troubleshooting.