Skip to content

Flutter SDK ​

Official Flutter SDK for integrating ZevPay Checkout in mobile apps. Opens the standard checkout page in a WebView and returns the result to your app.

Installation ​

Add to your pubspec.yaml:

yaml
dependencies:
  zevpay_checkout: ^1.0.0

Then run:

bash
flutter pub get

Requires Flutter 3.10+ and Dart 3.0+.

Quick start ​

dart
import 'package:zevpay_checkout/zevpay_checkout.dart';

final result = await ZevPayCheckout.open(
  context: context,
  options: CheckoutOptions(
    apiKey: 'pk_test_your_public_key',
    email: 'customer@example.com',
    amount: 500000, // ₦5,000 in kobo
  ),
);

if (result.isSuccess) {
  print('Payment successful! Ref: ${result.reference}');
}

How it works ​

  1. ZevPayCheckout.open() calls the initialize endpoint to create a session
  2. Opens the ZevPay standard checkout page in a WebView
  3. Customer selects a payment method and completes payment
  4. SDK intercepts the callback redirect and returns a CheckoutResult

Public keys only

The Flutter SDK only accepts public keys (pk_live_* or pk_test_*). Secret keys must not be used in mobile apps.

Open checkout ​

dart
try {
  final result = await ZevPayCheckout.open(
    context: context,
    options: CheckoutOptions(
      apiKey: 'pk_test_your_public_key',
      email: 'customer@example.com',
      amount: 500000,
      currency: 'NGN',
      reference: 'ORDER-123',
      firstName: 'John',
      lastName: 'Doe',
      metadata: {'order_id': '123'},
      paymentMethods: ['bank_transfer', 'payid'],
      theme: CheckoutTheme(
        appBarColor: Colors.teal,
        appBarTitle: 'Pay Now',
      ),
    ),
  );

  switch (result.status) {
    case CheckoutStatus.success:
      // Verify on your server, then fulfill the order
      await verifyOnServer(result.reference!);
      break;
    case CheckoutStatus.cancelled:
      print('User cancelled');
      break;
    case CheckoutStatus.expired:
      print('Session expired');
      break;
    case CheckoutStatus.error:
      print('Error: ${result.error?.message}');
      break;
  }
} on CheckoutError catch (e) {
  print('Initialization failed: ${e.message}');
}

CheckoutOptions ​

ParameterTypeRequiredDefaultDescription
apiKeyStringYesPublic API key (pk_live_* or pk_test_*)
emailStringYesCustomer's email address
amountintYesAmount in kobo (min 10000 = ₦100)
currencyStringNo'NGN'Currency code
referenceString?NoAutoYour transaction reference
firstNameString?NoCustomer's first name
lastNameString?NoCustomer's last name
metadataMap?NoCustom metadata
paymentMethodsList<String>?NoAll['bank_transfer', 'payid']
themeCheckoutTheme?NoDefaultVisual customization

CheckoutResult ​

PropertyTypeDescription
statusCheckoutStatussuccess, cancelled, expired, or error
referenceString?Transaction reference (success only)
sessionIdStringSession ID
errorCheckoutError?Error details (error only)
isSuccessboolConvenience getter
isCancelledboolConvenience getter

Customize the checkout screen ​

Control the appearance of the WebView screen:

dart
CheckoutTheme(
  appBarColor: Colors.indigo,           // App bar background
  appBarForegroundColor: Colors.white,  // Title and icon color
  appBarTitle: 'Complete Payment',       // Title text
  showAppBar: true,                      // Show/hide app bar
  showLoadingIndicator: true,            // Progress bar in app bar
  loadingWidget: MyCustomSpinner(),      // Custom loading widget
)
PropertyTypeDefaultDescription
appBarColorColor?ZevPay tealBackground color
appBarForegroundColorColor?WhiteTitle and icon color
appBarTitleString?"ZevPay Checkout"Title text
showAppBarbooltrueShow/hide the app bar
showLoadingIndicatorbooltrueShow progress bar while loading
loadingWidgetWidget?Default spinnerCustom loading widget

Verify payment ​

After a successful checkout, verify the payment on your server using a secret key:

dart
// Dart backend only — never use secret keys in mobile apps
final verified = await ZevPayCheckout.verify(
  secretKey: 'sk_test_your_secret_key',
  sessionId: result.sessionId,
);

if (verified.isCompleted) {
  print('Confirmed! Paid at: ${verified.paidAt}');
  print('Method: ${verified.paymentMethod}');
}

Server-side only

verify() requires a secret key and should only be called from your Dart backend (dart_frog, shelf, etc.). For mobile apps, call your own backend API to verify the payment.

Webhook verification ​

Verify incoming webhook signatures on your Dart backend:

dart
import 'dart:io';
import 'package:zevpay_checkout/zevpay_checkout.dart';

final isValid = ZevPayWebhook.verifySignature(
  payload: requestBody,
  signature: request.headers['x-zevpay-signature']!,
  webhookSecret: Platform.environment['ZEVPAY_WEBHOOK_SECRET']!,
);

if (isValid) {
  final event = jsonDecode(requestBody);
  switch (event['event']) {
    case 'charge.success':
      // Handle successful payment
      break;
    case 'transfer.success':
      // Handle successful transfer
      break;
  }
}

Always verify

Never process webhook events without verifying the signature. See Verifying Signatures for details.

Error handling ​

ZevPayCheckout.open() throws CheckoutError for initialization failures. Cancellation and expiry are returned in the CheckoutResult, not as exceptions.

dart
try {
  final result = await ZevPayCheckout.open(...);
  // result.status is success, cancelled, expired, or error
} on CheckoutError catch (e) {
  // Initialization failed
  print('${e.code}: ${e.message}');
}
Error codeWhen
MISSING_API_KEYNo API key provided
INVALID_API_KEYSecret key used instead of public key
MISSING_EMAILNo email provided
INVALID_AMOUNTAmount below 10,000 kobo
INITIALIZATION_FAILEDAPI returned an error
NETWORK_ERRORNetwork or timeout error
API_ERRORServer returned non-2xx status

Full example ​

dart
import 'package:flutter/material.dart';
import 'package:zevpay_checkout/zevpay_checkout.dart';

class PaymentPage extends StatefulWidget {
  const PaymentPage({super.key});

  @override
  State<PaymentPage> createState() => _PaymentPageState();
}

class _PaymentPageState extends State<PaymentPage> {
  Future<void> _pay() async {
    try {
      final result = await ZevPayCheckout.open(
        context: context,
        options: CheckoutOptions(
          apiKey: 'pk_test_your_public_key',
          email: 'customer@example.com',
          amount: 500000,
          reference: 'order_${DateTime.now().millisecondsSinceEpoch}',
          theme: const CheckoutTheme(
            appBarTitle: 'Complete Payment',
          ),
        ),
      );

      if (!mounted) return;

      if (result.isSuccess) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Paid! Ref: ${result.reference}')),
        );
        // Call your backend to verify and fulfill the order
      } else if (result.isCancelled) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Payment cancelled')),
        );
      }
    } on CheckoutError catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error: ${e.message}')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Checkout')),
      body: Center(
        child: ElevatedButton(
          onPressed: _pay,
          child: const Text('Pay ₦5,000'),
        ),
      ),
    );
  }
}

Platform support ​

PlatformSupport
AndroidSupported
iOSSupported
WebNot supported. Use Inline Checkout or React
DesktopNot supported

Resources ​

ZevPay Checkout Developer Documentation