Dart / Flutter server SDK watenga_flutter

Dart 3. Server-side Dart only: never ship a secret key inside an app.

Installation

bash
dart pub add watenga_flutter

Before the first release

The package is release-ready; until it is published, install it from a checkout of the Watenga repository:
bash
# pubspec.yaml
dependencies:
  watenga_flutter:
    path: ../watenga-payment-network/packages/watenga-flutter

Take a test payment

Use a test secret key (sk_test_…) from the developer console. Test keys never move money: use the test cards and wallet numbers.

dart
import 'package:watenga_flutter/watenga.dart';

final client = WatengaClient(Platform.environment['WATENGA_SECRET_KEY']!); // sk_test_..., on your server

final checkout = await client.checkout.create({
  'amount': '25.00',
  'currency': 'USD',
  'merchant_transaction_id': 'ORD-1001',
  'return_url': 'https://shop.example/return',
});
print(checkout['payment_url']);

Receive the webhook

Verify the Watenga-Signature header against the raw request body before you trust an event. While you rotate the endpoint's signing secret, pass both secrets: either one verifies. On your machine, watenga listen --forward-to <your URL> (the CLI) forwards test events signed with a session secret.

dart
import 'package:watenga_flutter/watenga.dart';

try {
  final event = constructEvent(rawBody, request.headers['watenga-signature']!, secret);
  if (event['type'] == 'payment.capture.completed') {
    // fulfil the order for event['data']['object']
  }
} on WebhookSignatureException {
  // answer 400
}

Errors

Every failed call raises one error type with the stable code, the HTTP status, the request_id to quote to support, the field at fault (param) and the code's page in the error reference.

dart
try {
  await client.refunds.create('txn_...', {'amount': '5.00', 'reason': 'customer_request'});
} on WatengaException catch (e) {
  print('${e.code} ${e.statusCode} ${e.param} ${e.requestId} ${e.docUrl}');
}

Retries and idempotency

Network errors, 429 (after Retry-After) and 5xx answers are retried twice with backoff. Every write carries an Idempotency-Key, kept across those retries, so a retry is answered once and never applied twice; checkout.create uses checkout:<merchant_transaction_id>. See Idempotency.

dart
// Every write gets an Idempotency-Key, kept across the SDK's own retries.
await client.refunds.create('txn_...', {'reason': 'duplicate'}, idempotencyKey: 'refund-ORD-1001');

Pagination

Every list answers one page, { object: "list", data, has_more, next_cursor }; the auto-paginating form reads them all. See Pagination.

dart
final page = await client.transactions.list({'status': 'completed', 'limit': 50});

await for (final txn in client.transactions.listAll({'status': 'completed'})) {
  final t = Transaction.fromJson(txn);
  print('${t.id} ${t.amount} ${t.currency}');
}

Resources

The SDK sends Watenga-Version: 2026-10-29; fields use the API's names (snake_case) and money is a decimal string. The same method names in every language.

ResourceMethods
checkoutcreate, getStatus
checkoutSessionscreate, retrieve, expire
transactionslist, listAll, retrieve, getMobileStatus
refundscreate
paymentLinkscreate, list, listAll, retrieve, update, deactivate
invoicescreate, list, listAll, retrieve, update, send, voidInvoice
quotationscreate, list, listAll, retrieve, update, issue, convertToInvoice
planscreate, list, listAll, retrieve, update
subscriptionscreate, list, listAll, retrieve, cancel, pause, resume, upcomingInvoice
customerscreate, list, listAll, retrieve, update
paymentMethodscreate, list, listAll, retrieve, delete, charge
accountgetBalance, getProfile, getApiKey
constructEventwebhook verification (a list of secrets while rotating)

For payments inside a Flutter app, use the native payment sheet (watenga_flutter_ui) with a checkout session's client_secret created by your server.

Typed models (Transaction.fromJson, …) are generated from the OpenAPI document.

Refunds and payouts

A secret key refunds when its app's Refunds switch is on and an Admin has set refund limits; a restricted key needs the refunds scope (Refunds). Payouts to your bank are dashboard-only, with two-step verification: they are not in the API or the SDKs.