digetpay_plugin 0.1.0
digetpay_plugin: ^0.1.0 copied to clipboard
Pure-Dart DigetPay payment plugin: hosted (PCI-safe) checkout via WebView plus transaction management over the DigetPay REST API.
digetpay_plugin #
Pure-Dart Flutter plugin for the DigetPay payment gateway. Card entry uses a
hosted, PCI-safe WebView checkout — raw card data never passes through your
app. Built on http (REST) and webview_flutter.
Getting started #
dependencies:
digetpay_plugin:
path: ../ # or a pub/git reference
Initialize once at startup:
import 'package:digetpay_plugin/digetpay_plugin.dart';
DigetPaySdk.initialize(
apiKey: '<your-api-key>',
baseUrl: kDigetPayDefaultBaseUrl, // https://fin-api.digetpay.com/v1
);
Every call before initialize() throws DigetPaySdkIsNotInitializedException.
Platform setup #
This is a pure-Dart plugin — you do not write or register any native
code. It relies on webview_flutter (federated: Android WebView, iOS
WKWebView) for the hosted checkout and http for the REST calls, so your app
only needs the standard network/WebView configuration below.
Android #
-
Minimum SDK. This plugin requires
minSdkVersion24 (Android 7.0). Set it inandroid/app/build.gradle:android { defaultConfig { minSdkVersion 24 } } -
Internet permission. REST calls and the checkout WebView need the INTERNET permission. Add it to
android/app/src/main/AndroidManifest.xml(a direct child of<manifest>, before<application>):<uses-permission android:name="android.permission.INTERNET"/>Flutter's debug/profile manifests include this automatically, but the release manifest does not — add it explicitly so production builds can reach the gateway.
No Hybrid Composition or manual PlatformView registration is required —
webview_flutter 4.x handles that internally.
iOS #
-
Deployment target. Set the platform to iOS 13.0 or higher in
ios/Podfile, and match it in the Runner target's Minimum Deployments in Xcode:platform :ios, '13.0' -
WKWebView is used automatically; no
Info.plistentry is required to enable it. -
App Transport Security. DigetPay endpoints and the hosted checkout page are served over HTTPS, so the default ATS policy works with no changes. You would only add an
NSAppTransportSecurityexception toios/Runner/Info.plistif you deliberately pointbaseUrlor your success/failure URLs at a non-HTTPS host (e.g. a local test server). Avoid a blanketNSAllowsArbitraryLoadsin production.
Completion detection & result URLs (optional) #
The SDK detects the end of checkout when the WebView reaches a terminal
page — DigetPay's own hosted result pages (…/pay/checkout/success and
…/pay/checkout/failure) — and closes the WebView. The definitive
success-vs-failure decision is not taken from that URL (the gateway may
briefly route to /success before correcting to /failure); instead the SDK
fetches the authoritative checkout status and decides from paymentStatus.
Because of that, setResultUrls(...) is optional and rarely needed. The SDK
ships HTTPS defaults (kDigetPayDefaultSuccessUrl / kDigetPayDefaultFailureUrl)
and also recognizes the gateway's own result pages by path. Call
setResultUrls(successUrl:, failureUrl:) only if your account is configured to
redirect to specific custom return URLs; if you do, use distinct HTTPS paths.
Supported platforms #
webview_flutter targets Android and iOS. Hosted checkout is not supported
on Flutter web or desktop in this version.
Security requirement — mobile API key #
DigetPayConfig.apiKey is embedded in your app binary, and anything shipped in
a mobile app can be extracted. Always initialize the SDK with a
publishable / restricted mobile key scoped only to the client operations
this SDK performs — never a full server secret. Rotate the key if it is ever
exposed, and keep privileged operations behind your own server. This is why the
plugin implements only the hosted (PCI-safe) card flow: raw card data never
touches the device.
Hosted checkout (recommended card flow) #
await DigetPaySdk.cardPay()
.setOrder(DigetPaySaleOrder(
id: orderId, description: 'Coffee', currency: 'SAR', amount: 15))
.setPayer(DigetPayPayer(
firstName: 'Demo', lastName: 'User', address: 'Riyadh',
country: 'SA', city: 'Riyadh', zip: '00000',
email: 'demo@example.com', phone: '+966500000000'))
// .setResultUrls(...) is optional — omit to use the SDK defaults.
.onTransactionSuccess((res) { /* res.status, res.transactionId ... */ })
.onTransactionFailure((res) { /* res.message, res.errorCode */ })
.onDismiss(() { /* user backed out */ })
.start(context);
start() calls POST /payment/checkout/intiate, opens the returned redirectUrl
in a WebView, and — once the hosted page reaches a result page — fetches
GET /sdk/status?sessionId= and dispatches success or failure from the
transaction's paymentStatus.
Hosted Checkout flow #
Flutter App
│
│ DigetPaySdk.cardPay()...start(context)
▼
POST /payment/checkout/intiate (order + payer → flat body)
│
▼
{ id, redirectUrl }
│
│ push CheckoutPage → load redirectUrl
▼
WebView (hosted page: card form + 3-D Secure)
│
├─ reaches a result page (…/pay/checkout/success | /failure)
│ │
│ ▼
│ GET /sdk/status?sessionId=<id> (authoritative outcome)
│ │
│ ├─ paymentStatus == APPROVED ─▶ onTransactionSuccess(res) + pop
│ └─ otherwise ─▶ onTransactionFailure(res) + pop
│
└─ user backs out (no result page) ───▶ onDismiss()
Notes:
intiateis spelled that way on the backend (kept verbatim so the docs match the wire call).- The terminal callback fires exactly once;
onDismissonly fires when the page is popped without reaching a result page. - The status response is authoritative — the SDK does not trust the result
URL alone, because the gateway can route to
/successbriefly before settling on/failure.
Other operations #
await DigetPaySdk.capture(transactionId: id, amount: 10); // /payment/s2s/capture
await DigetPaySdk.voidd(id); // /payment/s2s/void
await DigetPaySdk.refund(transactionId: id, amount: 10); // /payment/refund
final bySession = await DigetPaySdk.getCheckoutStatus(sessionId); // /sdk/status?sessionId=
final byTxn = await DigetPaySdk.getTransactionById(id); // /payment/checkout/status?id=
capture/voidd/refund return a typed DigetPayResponse (isSuccess,
status, message, transactionId, needs3ds, …). HTTP errors and declines
are returned as an unsuccessful response — they do not throw. The status lookups
return a typed Transaction? (a DECLINED transaction is still returned — check
paymentStatus).
Handling the transaction data (sensitive fields) #
The status response carries the merchant's own transaction, which includes some sensitive fields — the SDK delivers them to you but never logs them:
pan— already masked by the backend (4323 2** **** 0853); safe to show.cardHolderName— PII; display it, but don't log/persist broadly.recurringToken— sensitive: it can initiate future charges. Store it server-side, never log it, and don't surface it in the UI.
Access them via the typed model: res.transaction?.recurringToken, .rrn,
.pan, etc. The built-in logger only ever emits a redacted summary
(code/status/txn/rrn/…) and never these values.
Debug logging #
Pass an optional logger to initialize for a redacted, step-by-step trace
of the flow — HTTP method + path + status, checkout milestones, WebView
navigation paths, and the terminal outcome. It never receives the API key, card
data, request/response bodies, or URL query values.
import 'dart:developer' as developer;
DigetPaySdk.initialize(
apiKey: '<key>',
baseUrl: kDigetPayDefaultBaseUrl,
logger: (message) => developer.log(message, name: 'DigetPay'),
);
A successful checkout traces roughly like this (icons come from the example's
digetPayLog):
💳 checkout: creating session for order 6f… (15.0 SAR)
➡️ → POST /payment/checkout/intiate
⬅️ ← 201 OK POST /payment/checkout/intiate {code=201, session=ab02…, redirect=https://fin-admin.digetpay.com/pay/checkout}
💳 checkout: session ab02… created → opening WebView
🌐 webview → https://fin-admin.digetpay.com/pay/checkout
🌐 webview → https://fin-admin.digetpay.com/pay/checkout/success
🌐 webview: ✓ terminal page reached
💳 checkout: terminal page → confirming via status
➡️ → GET /sdk/status?sessionId
⬅️ ← 200 OK GET /sdk/status {code=200, status=APPROVED, txn=070ae…, msg=Success}
✅ checkout: APPROVED (txnStatus=SUCCESS, payStatus=APPROVED, rrn=616510265041) → onTransactionSuccess
Each ← line carries a redacted response summary (code, result,
status, txn/paymentId, session, redirect, err, msg) — the safe
typed fields, never the raw body (which can hold a masked PAN, cardholder name,
or tokens). Need the full payload? Read DigetPayResponse.data inside your
callback.
Leave logger unset (the default) for zero logging in production. The
webview → lines are especially useful for confirming where the hosted page
redirects on completion — that redirect is what triggers the success/failure
callbacks.
Not yet available #
sale (raw-card S2S — disabled to keep card data off-device), applePay
(needs native wallet support), externalPayment, recurring,
getTransactionByOrderId, and getTransactionByRrn return a guarded failure /
null until the backend exposes the corresponding endpoints. See the TODO
markers in the source.