dart_payway
Dart client for the ABA PayWay Ecommerce Checkout API.
| API | Method |
|---|---|
| Purchase (KHQR / ABA Mobile deep link) | purchase |
| Purchase on PayWay's hosted page (cards, Alipay, WeChat, ...) | checkoutHtml / checkoutUri |
| Check transaction | checkTransaction |
| Get a transaction details | getTransactionDetail |
| Close transaction | closeTransaction |
| Get transaction list | getTransactionList |
| Refund | refund |
| Exchange rate | getExchangeRates |
Verify the payment callback on your return_url |
verifyCallback + parseCallback |
Setup
import 'package:dart_payway/dart_payway.dart';
final payway = PaywayService(
merchant: PaywayMerchant(
merchantId: 'your merchant id',
apiKey: 'your API key',
rsaPublicKey: '-----BEGIN PUBLIC KEY-----...', // only needed for refunds
referer: 'https://your-whitelisted-domain.com',
baseApiUrl: PaywayMerchant.sandboxBaseUrl, // or PaywayMerchant.productionBaseUrl
),
);
Call PayWay from your server: the API key is a secret, and PayWay only accepts requests from whitelisted domains or IPs.
Take a payment
KHQR / ABA Mobile returns JSON you can show in your own UI:
final response = await payway.purchase(PaywayPurchase(
tranId: 'order-1001', // unique, max 20 characters
amount: 12.5,
currency: PaywayCurrency.usd,
paymentOption: PaywayPaymentOption.abapayKhqrDeeplink,
items: const [PaywayItem(name: 'Coffee', quantity: 1, price: 12.5)],
returnUrl: 'https://your-domain.com/payway/callback',
returnDeeplink: const PaywayReturnDeeplink(
iosScheme: 'myapp://paid', androidScheme: 'myapp://paid'),
));
if (response.isSuccess) {
// render response.qrString as a QR code, or open response.abapayDeeplink
}
Every other payment option opens PayWay's hosted page. checkoutHtml
returns a page that POSTs the signed purchase to PayWay; load
checkoutUri(purchase) in a web view, or serve the HTML from your site:
final uri = payway.checkoutUri(PaywayPurchase(
tranId: 'order-1002',
amount: 12.5,
paymentOption: PaywayPaymentOption.cards,
));
Values PayWay wants Base64-encoded (items, return URL, deep link, custom fields, payout, additional params) are given as plain Dart values: the SDK encodes and signs them.
Confirm the payment
final status = await payway.checkTransaction(tranId: 'order-1001');
if (status.isPaid) {
// deliver the order
}
checkTransaction covers the last 7 days; use getTransactionDetail for
older transactions and for the list of payment and refund operations.
Callback on your return_url
PayWay POSTs the result as JSON with an X-PayWay-HMAC-SHA512 signature
header. Verify it before trusting it:
if (!payway.verifyCallback(body: rawBody, signature: headerValue)) {
return 401;
}
final callback = payway.parseCallback(rawBody);
if (callback.isSuccess) {
// mark callback.tranId as paid
}
Other operations
await payway.closeTransaction(tranId: 'order-1001'); // cancel an unpaid payment
await payway.refund(tranId: 'order-1001', amount: 2.5); // needs rsaPublicKey
final page = await payway.getTransactionList(
query: PaywayTransactionListQuery(
fromDate: DateTime(2026, 1, 1),
statuses: [PaywayPaymentStatus.approved],
pagination: 100,
),
);
final rates = await payway.getExchangeRates(); // riel per unit
print(rates.rates['usd']?.sell);
Errors
- PayWay business errors (wrong hash, transaction not found, ...) are
returned in
response.status; checkresponse.isSuccessandresponse.status.code. PaywayExceptionis thrown when PayWay could not be reached or did not answer with a status, and for a missing RSA key on refunds. Branch ontype(connection,timeout,cancelled,badCertificate,unexpectedResponse,encryption,invalidCallback,unknown);isRetryabletells whether retrying later may help.
Every call accepts a CancelToken (re-exported from dio).
Dependency injection
final payway = PaywayService(
merchant: merchant,
dio: myDio, // your HTTP client, used as is and reused
clock: () => DateTime.now(), // source of req_time (sent in UTC)
crypto: myCrypto, // hashing and RSA
logger: (line) => log(line), // nothing is logged without it
);
Tests
dart test -x integration
dart test -t integration
PAYWAY_ENV_FILE=.env.production dart test -t integration
The first runs the offline tests. Their expected hashes and callback
signatures come from ABA's own PHP samples
(spec/test-vectors/php_known_answers.php at the repository root).
The others call PayWay with the credentials in .env (copy .env.example)
or in the file named by PAYWAY_ENV_FILE. They create a 0.10 USD
transaction and close it, so a file pointing at production is refused
unless you also set PAYWAY_ALLOW_PRODUCTION=true.
See the example folder for a Flutter demo.
Libraries
- dart_payway
- ABA PayWay Ecommerce Checkout: purchase, check, close, refund and list transactions, get exchange rates, and verify payment callbacks.