QattaPay Flutter SDK

Official QattaPay SDK for Flutter — add group contribution checkout to your app.

dependencies:
  qattapay_flutter: ^1.0.0

QattaPay lets groups of people split the cost of a purchase. This package handles:

  • Server-side (Dart): creating checkout intents and managing orders with your merchant API key
  • Webhooks: verifying and parsing events when a session is funded
  • Flutter UI: official branded QattaPayButton + hosted checkout opener

Parity with @hadawi/sdk and qattapay/laravel.


Requirements

  • Dart ^3.5 / Flutter ≥ 3.24
  • A QattaPay merchant account (qatta.sa)

Architecture (important)

Flutter app                         Your backend                      QattaPay
───────────                         ────────────                      ────────
QattaPayButton ──POST /intent──►  QattaPayClient.intents.create ──► POST /intents
     │                                  │
     │◄────────── intentId ─────────────┘
     │
     └── opens hosted checkout ──────────────────────────────────► /checkout/{id}

Never put your merchant API key or webhook secret in the mobile app. Create intents on your server (Node, Laravel, Dart Frog, etc.), then pass only the intentId to Flutter.

Checkout is the hosted web flow. Do not embed it in an HTML iframe — framing is blocked. Loading checkout as a full-page in-app WebView (or external browser / Custom Tabs / SFSafariViewController) is supported.


Installation

dependencies:
  qattapay_flutter: ^1.0.0
flutter pub get

Platform setup for url_launcher follows the official docs (Android queries / iOS LSApplicationQueriesSchemes as needed).


Quick start

1 — Create an intent (server)

Using this package on a Dart backend:

import 'package:qattapay_flutter/qattapay_flutter.dart';

final qattapay = QattaPayClient(
  apiKey: Platform.environment['QATTAPAY_API_KEY']!,
  mode: QattaPayMode.dev, // or live
  webhookSecret: Platform.environment['QATTAPAY_WEBHOOK_SECRET'],
);

final result = await qattapay.intents.create(
  CreateIntentParams(
    itemSnapshot: [
      ItemSnapshot(
        name: 'Luxury Watch',
        price: 150000, // 1500.00 SAR in halalas
        reference: 'watch-001',
      ),
    ],
    totalAmount: 150000,
    currency: 'SAR',
    metadata: {'cart_id': 'abc'},
  ),
);

return {'intentId': result.intent.id};

Or use Node (@hadawi/sdk) / Laravel (qattapay/laravel) — same API.

Amounts are integers in the smallest currency unit (halalas for SAR — e.g. 15000 = 150.00 SAR).

2 — Mount the branded button (Flutter)

import 'package:qattapay_flutter/qattapay_flutter.dart';

QattaPayButton(
  mode: QattaPayMode.live,
  variant: QattaPayButtonVariant.primary,
  label: QattaPayButtonLabel.split,
  locale: QattaPayLocale.en,
  openMode: CheckoutOpenMode.inAppWebView, // or externalBrowser
  getIntentId: () async {
    final res = await http.post(Uri.parse('https://api.my-store.com/qattapay/intent'));
    final data = jsonDecode(res.body) as Map<String, dynamic>;
    return data['intentId'] as String;
  },
  returnUrl: Uri.parse('myapp://thank-you'),
  onSuccess: (data) => debugPrint('done ${data.intentId}'),
  onError: (err) => debugPrint('QattaPay error: $err'),
)

3 — Verify webhooks (server)

final event = qattapay.webhooks.constructEvent(
  requestBodyBytes,
  request.headers['x-qattapay-signature']!,
);

if (event.type == WebhookEventType.orderFunded) {
  final orderId = event.payload.orderId;
  if (orderId != null) {
    await qattapay.orders.fulfill(orderId);
  }
}

Event types: order.funded, order.partially_funded, order.cancelled, order.expired.


API surface

QattaPayClient (server)

API Methods
intents create, get
orders list, get, fulfill, deliver
webhooks verifySignature, constructEvent

Config: apiKey, mode (dev | live), optional baseUrl (disables host fallback), optional webhookSecret.

Host fallback when using mode: qatta.sahadawi.sa (same as the Node / Laravel SDKs).

QattaPayButton / QattaPayCheckout (mobile)

Option Values
variant primary, dark, light, outline
size sm, md, lg
label split, splitCart, pay (or labelText)
locale en, ar
showBadge / showIcon booleans
openMode inAppWebView (default) or externalBrowser
returnUrl deep link / https URL appended to hosted checkout

Imperative open:

final checkout = QattaPayCheckout(mode: QattaPayMode.live);
await checkout.open(
  intentId,
  context: context, // required for inAppWebView
  mode: CheckoutOpenMode.inAppWebView,
  returnUrl: Uri.parse('myapp://thank-you'),
  onSuccess: (data) {},
);

Exceptions

  • QattaPayApiException — HTTP / API errors (status, code)
  • QattaPayWebhookException — invalid signature or payload

License

MIT © Hadawi Engineering

Libraries

qattapay_flutter
Official QattaPay Flutter SDK.