Mobile SDK — In-app Payment

Complete in-app payment flow for Android (Kotlin) and iOS (Swift) — create, redirect to hosted page, poll status, verify, with full state & error handling.

Payment lifecycle (state machine)
init() → createPayment() → PENDING ──► (user pays in hosted page)
                              │              │
                              │              ▼
                              │     AWAITING_CONFIRMATION
                              │              │
                              │              ▼
                              │           ┌──┴──┐
                              │         PAID  FAILED
                              │
                              ├──► EXPIRED  (after 15 min)
                              └──► CANCELLED (user closes WebView)
PENDING
AWAITING_CONFIRMATION
PAID
FAILED
EXPIRED
CANCELLED
1. Install

Drop fastpayglobal-1.0.0.aar into app/libs/:

// app/build.gradle
dependencies {
  implementation files('libs/fastpayglobal-1.0.0.aar')
  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0'
  implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0'
  implementation 'androidx.browser:browser:1.7.0'
}
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<activity android:name="com.fastpayglobal.FastPayGlobalCheckoutActivity" />
2. Quick start (one-liner)
FastPayGlobal.init(publicKey = "pk_live_xxx")

FastPayGlobal.startPayment(
    activity = this,
    req = FastPayGlobal.CreateRequest(
        orderId = "ORDER-123",
        amount = 250.0,
        currency = "BDT",
        customer = FastPayGlobal.Customer(
            name = "Rahim", phone = "01700000000"
        )
    ),
    scope = lifecycleScope,
) { result ->
    when (result) {
        is FastPayGlobal.Result.Success -> {
            // result.payment.trxId, result.payment.amount
            toast("✅ Paid: ${result.payment.trxId}")
        }
        is FastPayGlobal.Result.Failure -> {
            // result.status: EXPIRED / FAILED
            toast("❌ ${result.message}")
        }
        FastPayGlobal.Result.Cancelled -> toast("Cancelled")
    }
}
3. Manual flow (custom UI)

For full control over WebView, progress UI, retry — use the low-level methods:

lifecycleScope.launch {
    try {
        // Step A — create payment
        val res = FastPayGlobal.createPayment(
            FastPayGlobal.CreateRequest(orderId = "ORD1", amount = 100.0)
        )
        // res.paymentId, res.paymentUrl, res.expiresAt

        // Step B — open WebView with res.paymentUrl
        myWebView.loadUrl(res.paymentUrl)

        // Step C — poll until terminal state, with live UI updates
        val final = FastPayGlobal.pollStatus(
            paymentId = res.paymentId,
            intervalMs = 3000,
            timeoutMs = 15 * 60_000L
        ) { p ->
            // Called on every poll — update UI
            runOnUiThread {
                statusText.text = when (p.status) {
                    "pending" -> "Waiting for payment…"
                    "awaiting_confirmation" -> "Verifying transaction…"
                    "paid" -> "✅ Paid"
                    else -> p.status
                }
            }
        }
        onSuccess(final)
    } catch (e: FastPayGlobalException) {
        when (e.code) {
            "expired" -> showExpiredDialog()
            "failed" -> showError("Payment failed")
            "cancelled" -> Unit
            "timeout" -> showRetry()
            else -> showError(e.message ?: "Unknown")
        }
    }
}
4. Manual verification (user paid via wallet app)
// User completed bKash payment in another app, copied trxId
val payment = FastPayGlobal.verify(
    paymentId = "uuid…",
    method = "bkash",
    trxId = "BC123456789",
    senderNumber = "01700000000"
)
// payment.status == "paid" if matched
Error codes
All SDK methods throw FastPayGlobalException (Android) / FastPayGlobalError (iOS) with these codes:
CodeMeaningRecommended action
create_failedBad orderId / invalid pk_live_Show error, log to crash reporter
status_failedNetwork error during pollingSDK auto-retries; surface only after timeout
verify_failedtrxId not matchedAsk user to re-enter trxId
expired15 min timer expiredOffer "Try again" button
failedBackend marked failedShow generic failure
cancelledUser dismissed WebViewSilent — no UI change
timeoutPolling exceeded timeoutMsOffer "Refresh status" button
Production checklist
  • Use pk_live_ only in production builds (use pk_test_ in debug)
  • Add domain whitelist in dashboard → API Keys
  • Always verify with backend webhook before fulfilling order — SDK callback is convenience only
  • Set timeoutMs to match your UX (default 15 min = backend expiry)
  • Handle Android process death — store paymentId in SavedStateHandle and re-poll on resume