flutter_bkash
An open-source Flutter package making bKash payment integration a single function call — serving 65M+ account holders across Bangladesh with token management, WebView lifecycle handling, and JS bridge fallback for OEM quirks.
Overview
bKash is Bangladesh's largest mobile financial service with over 65 million registered accounts. For Flutter developers building apps that need payment functionality, integrating bKash meant navigating undocumented flows, managing token refresh manually, and re-implementing the same WebView handshake on every project.
flutter_bkash removes all of that. It's a BSD-licensed pub.dev package that reduces a multi-day bKash integration to a single function call, supporting all three bKash payment modes out of the box.
The Problem
At Codeboxr, I was building Flutter apps that needed to accept payments via bKash and other Bangladeshi gateways. Every project meant re-implementing the same bKash integration from scratch — the token handshake, the WebView payment flow, the callback handling, the error states.
There was no community package for this. Every Flutter developer in Bangladesh was solving the same problem independently, usually with copy-pasted code that wasn't generalised or maintained. We built the integration for a client project and decided to extract and publish it so the whole ecosystem could benefit.
My Role
I built flutter_bkash with co-maintainer ABDULLAH AL MASUM while working at Codeboxr. I designed the public API, wrote the core implementation, and have maintained it since. The goal was to make bKash integration as simple as a single await call.
Architecture
Public API
The entire package surface is three methods — the goal was to reduce a multi-day integration to a single await:
// One-time payment
final result = await flutterBkash.pay(context, amount: 100.0);
// Tokenize a customer for future charges
final result = await flutterBkash.createAgreement(context);
// Charge a previously tokenized customer
final result = await flutterBkash.payWithAgreement(context, amount: 100.0, agreementId: "agr_123");Each method returns a typed response or throws BkashFailure — no ambiguous status codes, no parsing required.
WebView Lifecycle Management
bKash's payment UI is web-based. The package launches a managed WebView, intercepts the OAuth-style redirect callbacks, extracts the payment result, and disposes the WebView cleanly — regardless of how the user exits (success, cancel, or back button). A JavaScript bridge listens for payment completion events at the DOM level as a fallback, catching results even when OEM browser layers swallow the redirect URL.
Token Management
bKash issues short-lived tokens that expire roughly hourly. The package caches the token, tracks its expiry, and refreshes automatically — consumers never write token refresh logic:
Future<String> _getToken() async {
if (_token != null && !_tokenExpired()) return _token!;
_token = await _fetchFreshToken();
_tokenExpiry = DateTime.now().add(const Duration(hours: 1));
return _token!;
}Environment Abstraction
A single isSandbox flag switches base URLs, token endpoints, and credential validation across all three payment modes. All known behavioural differences between sandbox and production — redirect URL formats, error code vocabularies, token expiry timing — are documented in the README and handled transparently by the flag.
Technical Decisions
WebView Payment Flow
bKash's payment UI is web-based. The package launches a managed WebView, intercepts the redirect callbacks, extracts the payment result, and disposes the WebView cleanly — with no memory leaks regardless of how the user exits (success, cancel, or back button).
Token Management
bKash uses short-lived tokens that must be refreshed. The package handles this transparently — consumers never write token refresh logic:
Future<String> _getToken() async {
if (_token != null && !_tokenExpired()) return _token!;
_token = await _fetchFreshToken();
_tokenExpiry = DateTime.now().add(const Duration(hours: 1));
return _token!;
}Sandbox vs Production
A single isSandbox flag switches base URLs, token endpoints, and credential validation. Switching environments is a one-line change, and all known behavioural differences between sandbox and production are documented in the README.
Challenges & Solutions
Challenge: bKash's sandbox and production environments behave differently in subtle ways — redirect URLs, error codes, and token expiry timing all differ between environments.
Solution: Built a complete environment abstraction behind the isSandbox flag. Documented every known difference between environments in the README so integrators know what to expect before going live.
Challenge: bKash's WebView doesn't always fire the success redirect cleanly on all Android WebView versions. Some OEM browsers intercept the redirect before the package can read it.
Solution: Added a JavaScript bridge that listens for payment completion events at the DOM level as a fallback, catching the result even when the URL redirect is swallowed by the browser layer.
Outcome
- Reduced a multi-day bKash integration — token handshake, WebView flow, callback handling, error states — to a single
awaitcall across all three payment modes - The JS-bridge fallback for OEM WebView quirks and the sandbox/production abstraction have held up across dozens of downstream integrations without needing a breaking change
- Adopted by Flutter developers across Bangladesh in e-commerce, fintech, and marketplace apps, saving each integrating team an estimated 2–5 days of development and debugging time
- Shared the integration patterns at GDG Sonargaon community meetups with 50+ local developers
- Published on pub.dev and GitHub (BSD 3-Clause, ★50+ stars); also a contributor to
expirydbandbangla-apisin the Bangladeshi open source ecosystem
Contributing
Contributions are welcome. Follow Effective Dart: Style and read the bKash API documentation before submitting a pull request. Contact bKash directly for API documentation and sandbox access.
Core Maintainers: Md Riadul Islam · ABDULLAH AL MASUM