Vezmo App

Mobile

Android SDK

Release candidate: the SDK is published to Maven Central with Vezmo's mobile launch. Until then, use Checkout for apps.

com.vezmo:vezmo-checkout adds the Vezmo payment sheet to a native Android app (Kotlin, Jetpack Compose or Views): cards with 3D Secure, Google Pay, Klarna, Affirm, Afterpay, Amazon Pay, Cash App, UPI and Pix, in the buyer's own currency, in Vezmo's design. Your server keeps your API key; the app only ever gets a session token. The same sheet is in the React Native, iOS and Flutter SDKs.

Requirements

  • Android 7.0 (API 24) or later; compile with SDK 36, Kotlin 2.1 or later, Java 17.
  • Your activity is a ComponentActivity (AppCompatActivity and FragmentActivity are).
  • Your app added under Developers → Mobile apps (see Take payments in your mobile app).

1. Install

build.gradle.kts
// build.gradle.kts (your app module)
dependencies {
    implementation("com.vezmo:vezmo-checkout:0.1.0")
}

Nothing else to set up, and no return link: after a bank app, a pay-later provider or 3D Secure in the browser, the buyer comes back to the open sheet by themselves.

2. Create the session on your server

The same API checkout session as the web. Your server first exchanges your API key and secret for an access token (see Authenticate); sandbox keys are under VezmoPay → Developers → API keys.

Your server
POST https://api.vezmo.com/api/v1/merchant/secure-payments
Authorization: Bearer <access token>

{
  "title": "Order #1042",
  "amount": 49.00,
  "currency": "USD",
  "client": { "name": "Ana Silva", "email": "ana@example.com", "country": "US", "postalCode": "10001" }
}

Send data.securePayment.clientToken to the app. The Android SDK needs no return link; add app.returnUrl only if you also open Checkout for apps.

3. Take the payment

CheckoutActivity.kt
class CheckoutActivity : ComponentActivity() {
    private lateinit var vezmo: VezmoCheckout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Create it in onCreate: it registers for the sheet's result.
        vezmo = VezmoCheckout(this, VezmoConfiguration()) { result ->
            when (result) {
                is CheckoutResult.Succeeded -> showPaid(result.paymentId)
                is CheckoutResult.Processing -> showPaymentReceived() // UPI or Pix still settling
                CheckoutResult.Canceled -> Unit                      // the buyer closed the sheet
                is CheckoutResult.Failed -> showError(result.error.message)
            }
        }
    }

    private fun pay(orderId: String) = lifecycleScope.launch {
        val clientToken = myApi.createVezmoSession(orderId) // your server, your API key
        vezmo.prepare(clientToken)?.let { error -> return@launch showError(error.message) }
        vezmo.present() // the result arrives in the callback above
    }
}

In Compose:

PayButton.kt
@Composable
fun PayButton(orderId: String) {
    val scope = rememberCoroutineScope()
    val vezmo = rememberVezmoCheckout(VezmoConfiguration()) { result ->
        // the same results as above
    }
    Button(onClick = {
        scope.launch {
            val error = vezmo.prepare(myApi.createVezmoSession(orderId))
            if (error == null) vezmo.present() else showError(error.message)
        }
    }) { Text("Pay") }
}

Neither prepare() nor present() throws. Fulfil the order from the webhook (payment.success), not from the result alone.

Subscriptions

Start a subscription in the app: the first payment, or a free trial with the card saved for the renewals. Your server creates it with your API key and sends its checkoutToken to the app; pass it as subscriptionToken.

Subscribe
// checkoutToken from POST /merchant/subscription, via your server
vezmo.prepare(subscriptionToken = checkoutToken)?.let { error -> return@launch showError(error.message) }
vezmo.present()

// In your result callback:
if (result is CheckoutResult.Succeeded && result.subscription != null) {
    // result.subscription → SubscriptionResult(ref = "VS7K2P9Q4FXM", status = Trialing or Active)
    // result.paymentId → the first payment, or null when a free trial started
}

The sheet shows what is due today, the plan after it (“7-day free trial, then $9.99 / month from 23 Oct 2026”), the authorization the buyer gives, and Start free trial or Subscribe. Cards and Google Pay. Vezmo charges the renewals; your server hears about each one from the subscription webhooks. Digital goods used inside the app follow the app store rules.

When the buyer starts a free trial, Vezmo sends subscription.trial_started; the first charge sends subscription.activated (or subscription.payment_failed). See Subscriptions → Webhooks.

Results and errors

SucceededPaid. paymentId and transactionId match the dashboard and the webhook. A subscription also has subscription (ref, status); paymentId is null when a free trial started.
ProcessingPaid, money on its way (UPI or Pix still settling). Wait for the webhook.
CanceledThe buyer closed the sheet or pressed Back. Not an error.
Failederror.code is one of the codes below; error.message is written for the buyer and safe to show.

VezmoError.Code (also returned by prepare() when a session can't be paid):

DeclinedThe bank said no. The buyer can try another card or way to pay.
ExpiredThe session ran out. Create a new one on your server.
AlreadyPaidThe session was already paid. Treat the order as paid.
NotAvailableThis way to pay can't be used for this session.
NetworkNo connection. Let the buyer try again.
ConfigA setup mistake in the app, for example present() before prepare().
UnknownAnything else. Let the buyer try again.

Appearance

The sheet follows Vezmo's checkout design in light and dark. Each color takes one value or a light and a dark one; the keys are accent, onAccent, surface, surface2, field, fieldLine, line, ink, ink2, ink3, good and bad. Your own accent replaces the Vezmo gradient on the Pay button.

Configuration
VezmoConfiguration(
    style = VezmoStyle.Automatic, // Light, Dark
    appearance = VezmoAppearance(
        colors = VezmoColors(
            accent = VezmoColor(0xFF0A84FF.toInt()),
            surface = VezmoColor(light = 0xFFFFFFFF.toInt(), dark = 0xFF1C1C1E.toInt()),
        ),
        cornerRadius = 12f,
        fontResId = R.font.inter, // a font in your app
    ),
)

Google Pay

No code needed: the button shows when your business offers Google Pay and the phone has it, and sandbox sessions use Google's test environment. Before your app takes live Google Pay payments, ask Google for production access in the Google Pay & Wallet Console: add your app, choose the gateway integration when asked how you take payments, and send the screenshots of your checkout that Google asks for.

Testing

Create the session with your sandbox API key and pay with test card 4242 4242 4242 4242, any future expiry and any CVC. 3D Secure shows a test page where you can approve or fail the payment. Card 4000 0000 0000 0002 is always declined; 4000 0000 0000 3220 always asks for 3D Secure. More test cards are in Test mode. The example app (a small shop) pays against Vezmo's sandbox; how to run it is in the SDK's README on GitHub.

Going live

  1. Your server creates sessions with your live API key.
  2. Google Pay: Google approved your app for production.
  3. Live sessions are refused until your account is approved.
  4. The rest of Before you go live, and the app store rules if you sell anything used inside the app.

Troubleshooting

  • "LifecycleOwner is attempting to register while current state is RESUMED": VezmoCheckout was created after onCreate. Create it there, or use rememberVezmoCheckout in Compose.