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 matchedError codes
All SDK methods throw
FastPayGlobalException (Android) / FastPayGlobalError (iOS) with these codes:| Code | Meaning | Recommended action |
|---|---|---|
| create_failed | Bad orderId / invalid pk_live_ | Show error, log to crash reporter |
| status_failed | Network error during polling | SDK auto-retries; surface only after timeout |
| verify_failed | trxId not matched | Ask user to re-enter trxId |
| expired | 15 min timer expired | Offer "Try again" button |
| failed | Backend marked failed | Show generic failure |
| cancelled | User dismissed WebView | Silent — no UI change |
| timeout | Polling exceeded timeoutMs | Offer "Refresh status" button |
Production checklist
- Use
pk_live_only in production builds (usepk_test_in debug) - Add domain whitelist in dashboard → API Keys
- Always verify with backend webhook before fulfilling order — SDK callback is convenience only
- Set
timeoutMsto match your UX (default 15 min = backend expiry) - Handle Android process death — store paymentId in SavedStateHandle and re-poll on resume