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:
dependencies:
zevpay_checkout: ^1.0.0Then run:
flutter pub getRequires Flutter 3.10+ and Dart 3.0+.
Quick start
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
ZevPayCheckout.open()calls the initialize endpoint to create a session- Opens the ZevPay standard checkout page in a WebView
- Customer selects a payment method and completes payment
- 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
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
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | String | Yes | — | Public API key (pk_live_* or pk_test_*) |
email | String | Yes | — | Customer's email address |
amount | int | Yes | — | Amount in kobo (min 10000 = ₦100) |
currency | String | No | 'NGN' | Currency code |
reference | String? | No | Auto | Your transaction reference |
firstName | String? | No | — | Customer's first name |
lastName | String? | No | — | Customer's last name |
metadata | Map? | No | — | Custom metadata |
paymentMethods | List<String>? | No | All | ['bank_transfer', 'payid'] |
theme | CheckoutTheme? | No | Default | Visual customization |
CheckoutResult
| Property | Type | Description |
|---|---|---|
status | CheckoutStatus | success, cancelled, expired, or error |
reference | String? | Transaction reference (success only) |
sessionId | String | Session ID |
error | CheckoutError? | Error details (error only) |
isSuccess | bool | Convenience getter |
isCancelled | bool | Convenience getter |
Customize the checkout screen
Control the appearance of the WebView screen:
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
)| Property | Type | Default | Description |
|---|---|---|---|
appBarColor | Color? | ZevPay teal | Background color |
appBarForegroundColor | Color? | White | Title and icon color |
appBarTitle | String? | "ZevPay Checkout" | Title text |
showAppBar | bool | true | Show/hide the app bar |
showLoadingIndicator | bool | true | Show progress bar while loading |
loadingWidget | Widget? | Default spinner | Custom loading widget |
Verify payment
After a successful checkout, verify the payment on your server using a secret key:
// 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:
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.
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 code | When |
|---|---|
MISSING_API_KEY | No API key provided |
INVALID_API_KEY | Secret key used instead of public key |
MISSING_EMAIL | No email provided |
INVALID_AMOUNT | Amount below 10,000 kobo |
INITIALIZATION_FAILED | API returned an error |
NETWORK_ERROR | Network or timeout error |
API_ERROR | Server returned non-2xx status |
Full example
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
| Platform | Support |
|---|---|
| Android | Supported |
| iOS | Supported |
| Web | Not supported — use Inline Checkout or React |
| Desktop | Not supported |
Resources
- API Reference — Full endpoint documentation
- Webhook Events — All event types
- pub.dev — Package registry