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