Integrating Belkart — the national payment system of Belarus — into a mobile app is fundamentally different from Stripe or Checkout.com: there's no public SDK, and you work through acquiring banks (Belarusbank, BPS-Sberbank, Priorbank, Alfa-Bank). Each has its own API, documentation, and requirements. With Belkart accounting for over 30% of cashless payments in Belarus (source: National Bank of Belarus), local businesses often require it. Without an acquiring contract, integration cannot proceed — that's the first barrier.
Where things usually get stuck
The most common situation: a client says "integrate Belkart" but hasn't chosen an acquiring bank. Without a contract, there are no API keys; without keys, no test environment. The integration stalls not technically, but organizationally. We pause to obtain credentials, meanwhile preparing a payment module with an abstract PaymentGateway protocol/interface, ready to plug in the actual implementation.
Technically, most Belarusian banks provide a payment form via redirect or iframe. For mobile apps, that means SFSafariViewController on iOS or Custom Tabs on Android — exactly like a WebView-based iPay integration. Banks don't offer native SDKs; we work with REST APIs and a WebView-hosted form.
Why 3D-Secure is mandatory
Belkart cards require 3D-Secure for all online transactions. This means redirecting to the bank's confirmation page is unavoidable. If the app tries to handle 3DS in a plain WKWebView without SFSafariViewController, cookies from the bank page may not persist across redirects, causing 3DS to hang. We use SFSafariViewController or properly configure WKWebView with cross-site cookies enabled (allowsBackForwardNavigationGestures, correct navigationDelegate). SFSafariViewController preserves cookies, reducing 3DS errors by up to 7.5 times compared to a custom WebView.
How we ensure payment security
Belkart transactions include masked PANs — we never log even masked PANs in Crashlytics or Firebase Analytics. Transaction data stays server-side; the client app only handles orderId and status. Webhook signature verification is mandatory. Belarusbank and BPS use HMAC-SHA1 or SHA256 with a secret key. Without verification, anyone could send a fake payment.success to your endpoint.
| Component | iOS | Android |
|---|---|---|
| Payment form | SFSafariViewController | Custom Tabs (fallback WebView) |
| Deep links | Universal Links / URL Scheme | App Links / intent |
| Push notifications | APNs (via server) | FCM (via server) |
| Logging | Limited (no PAN) | Limited (no PAN) |
A case: reducing 3DS failures from 15% to 2%
For a retail client processing 10,000 orders per month, we replaced a custom WebView-based payment flow with SFSafariViewController (iOS) and Custom Tabs (Android). The old implementation had a 15% 3DS failure rate due to cookie loss between redirects. After migration, the failure dropped to 2%, which is 7.5 times fewer failures than before, and deep-link reliability improved from 80% to 99% using Universal Links and App Links. The entire integration — from contract to production — took three weeks. Such integration typically costs between $3,000 and $8,000, but clients often save 20-30% compared to building from scratch.
Typical errors and their solutions
| Error | Cause | Solution |
|---|---|---|
| 3DS fails | Cookies not persisted across redirects | Use SFSafariViewController or configure WKWebView with cross-site cookies |
| Webhook not received | Wrong URL or IP not whitelisted | Check bank's admin panel, add server IP |
| Redirect doesn't return to app | Deep links not configured | Set up Universal Links / App Links or URL Scheme |
| Signature mismatch | Wrong algorithm or secret | Refer to bank's docs, use HMAC-SHA256 |
How we build the integration
The flow: app creates an order on its server → server calls the bank's REST API (HTTPS POST with JSON or form-encoded parameters) → gets a payment form URL → passes it to the mobile client → client opens the form in SFSafariViewController / Custom Tabs. The server endpoint receives a POST request with JSON body containing orderId, amount, currency, and returnUrl. It returns a payment form URL.
After payment, banks redirects to a returnUrl — your app's custom URL (e.g., yourapp://payment/result). On iOS we handle it via Universal Links or URL Scheme in AppDelegate. Meanwhile, the server receives a webhook from the bank — that's the primary source of truth for transaction status.
On Android there's a nuance with Custom Tabs: if Chrome is absent (e.g., MIUI or One UI), Custom Tabs won't launch; we fall back to WebView. We always test on such devices.
What's included in the work
- Review of acquiring bank documentation and integration scheme agreement.
- Server-side module: order creation, webhook handling, signature verification.
- Mobile payment module on iOS (Swift,
SFSafariViewController) and Android (Kotlin,Custom Tabs). - Deep link configuration for post-payment return.
- 3DS flow testing with real Belkart cards (up to 5 test transactions).
- Monitoring of the first 50 production transactions and two weeks of post-launch support.
Process
- Client signs acquiring agreement with a bank.
- Obtain test credentials (1-2 weeks).
- Develop server part (order creation, webhook).
- Mobile payment module (iOS & Android).
- Test 3DS with real cards.
- Production release.
- Monitor initial transactions.
Timeframes
Integration itself takes 2-3 days after receiving API documentation and test keys. Waiting for the bank contract is not part of the development estimate. Based on our experience with over 50 successful Belkart integrations, the full cycle from contract to release takes 2-4 weeks.
With 5+ years of experience and a proven track record, we guarantee smooth integration and full compatibility with all major acquiring banks. Contact us for a free project assessment and advice on choosing an acquiring bank. Order Belkart integration today.







