credo_flutter 1.0.0
credo_flutter: ^1.0.0 copied to clipboard
Official Credo checkout SDK for Flutter.
Credo Flutter SDK #
A Flutter SDK for integrating Credo payment gateway into your mobile app.
Installation #
Add to your pubspec.yaml:
dependencies:
credo_flutter: ^1.0.0
Quick Start #
Option A: Use a checkout URL from your backend (Recommended) #
If your backend initializes transactions and provides a checkout URL, you can use open() directly — no SDK configuration required:
import 'package:credo_flutter/credo_flutter.dart';
// Your backend initializes the transaction and returns a checkout URL
final checkoutUrl = await yourBackend.initializePayment();
final result = await CredoCheckout.open(
context,
checkoutUrl: checkoutUrl,
);
if (result.isSuccess) {
print('Payment successful: ${result.transaction?.reference}');
} else if (result.isCancelled) {
print('User cancelled');
} else {
print('Payment failed: ${result.transaction?.message}');
}
Option B: Initialize and pay in one call #
If you want the SDK to initialize transactions (simpler but exposes your public key in the app), configure the SDK first:
import 'package:credo_flutter/credo_flutter.dart';
void main() {
// Required only for CredoCheckout.pay()
CredoCheckout.configure(
publicKey: 'your_public_key',
environment: CredoEnvironment.demo, // or .live for production
);
runApp(MyApp());
}
Then use pay():
final result = await CredoCheckout.pay(
context,
PaymentInitConfig(
amount: 10000, // Amount in lowest unit (kobo for NGN)
email: 'customer@example.com',
currency: Currency.ngn,
bearer: FeeBearer.customer,
reference: 'unique_transaction_ref', // Optional
),
);
if (result.isSuccess) {
print('Payment successful: ${result.transaction?.reference}');
} else if (result.isCancelled) {
print('User cancelled');
} else {
print('Payment failed: ${result.transaction?.message}');
}
Configuration Options #
CredoCheckout.configure() #
| Parameter | Type | Required | Description |
|---|---|---|---|
publicKey |
String |
Yes | Your Credo public key |
environment |
CredoEnvironment |
No | .demo or .live (default: .live) |
debug |
bool |
No | Enable debug logging (default: false) |
PaymentInitConfig #
| Parameter | Type | Required | Description |
|---|---|---|---|
amount |
int |
Yes | Amount in lowest currency unit (kobo/cents) |
email |
String |
Yes | Customer email address |
currency |
Currency |
Yes | .ngn or .usd |
bearer |
FeeBearer |
Yes | .customer or .merchant |
reference |
String? |
No | Unique transaction reference |
channels |
List<PaymentChannel>? |
No | Allowed payment channels |
customerPhoneNumber |
String? |
No | Customer phone number |
customerFirstName |
String? |
No | Customer first name |
customerLastName |
String? |
No | Customer last name |
callbackUrl |
String? |
No | URL for redirect after payment |
metadata |
TransactionMetadata? |
No | Additional transaction data |
narration |
String? |
No | Transaction description |
Payment Channels #
PaymentInitConfig(
// ...
channels: [
PaymentChannel.card,
PaymentChannel.bank,
PaymentChannel.ussd,
PaymentChannel.wallet,
PaymentChannel.opay,
PaymentChannel.payoutlet,
],
)
Handling Results #
final result = await CredoCheckout.pay(context, config);
switch (result.status) {
case CheckoutStatus.success:
// Payment successful
final txn = result.transaction!;
print('Amount: ${txn.amount} ${txn.currency}');
print('Reference: ${txn.reference}');
break;
case CheckoutStatus.failed:
// Payment failed
print('Error: ${result.transaction?.message}');
// Check for field-specific validation errors
if (result.hasFieldErrors) {
result.fieldErrors!.forEach((field, error) {
print('$field: $error');
});
}
break;
case CheckoutStatus.cancelled:
// User cancelled the checkout
break;
case CheckoutStatus.unknown:
// Unknown state (e.g., timeout)
break;
}
CheckoutResult Properties #
| Property | Type | Description |
|---|---|---|
status |
CheckoutStatus |
success, failed, cancelled, unknown |
transaction |
TransactionDetails? |
Transaction details from gateway |
fieldErrors |
Map<String, String>? |
Field-specific validation errors |
reference |
String? |
Transaction reference (convenience getter) |
isSuccess |
bool |
Whether payment succeeded |
isFailure |
bool |
Whether payment failed |
isCancelled |
bool |
Whether user cancelled |
hasFieldErrors |
bool |
Whether validation errors exist |
TransactionDetails Properties #
| Property | Type | Description |
|---|---|---|
reference |
String? |
Merchant transaction reference |
transRef |
String? |
Credo transaction reference |
amount |
double? |
Transaction amount |
currency |
String? |
Currency code |
processorFee |
double? |
Fee charged |
message |
String? |
Status message |
CredoWebview Widget #
For full control over how the checkout is presented, use CredoWebview directly. Embed it in a modal bottom sheet, a custom page with your own AppBar, or anywhere in your widget tree.
In a Modal Bottom Sheet #
showModalBottomSheet(
context: context,
isScrollControlled: true,
useSafeArea: true,
builder: (sheetContext) => SizedBox(
height: MediaQuery.of(sheetContext).size.height * 0.9,
child: Column(
children: [
AppBar(
title: const Text('Checkout'),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(sheetContext),
),
),
Expanded(
child: CredoWebview(
checkoutUrl: checkoutUrl,
onResult: (result) {
Navigator.pop(sheetContext);
// Handle result
},
),
),
],
),
),
);
In a Custom Page #
Scaffold(
appBar: AppBar(title: const Text('Pay')),
body: CredoWebview(
checkoutUrl: checkoutUrl,
onResult: (result) {
Navigator.pop(context);
// Handle result
},
onLoadingChanged: (isLoading) {
// Show/hide your own loading indicator
},
timeout: const Duration(minutes: 5),
),
)
CredoWebview Properties #
| Parameter | Type | Required | Description |
|---|---|---|---|
checkoutUrl |
String |
Yes | The checkout URL to load |
onResult |
ValueChanged<CheckoutResult> |
Yes | Called exactly once when checkout completes |
onLoadingChanged |
ValueChanged<bool>? |
No | Called when loading state changes |
timeout |
Duration? |
No | Auto-complete with unknown after timeout |
Note:
CredoWebviewdoes not requireconfigure()— it works with any valid checkout URL.
Advanced Features #
Split Payments #
Configure split settlements for marketplace scenarios:
PaymentInitConfig(
// ...
splitConfiguration: [
SplitEntry(
accountId: '0441234567890', // Bank code + account number
splitType: SplitType.percentage,
splitValue: 10,
isDefault: true,
),
SplitEntry(
accountId: '0449876543210',
splitType: SplitType.flat,
splitValue: 500, // Flat amount
isDefault: false,
),
],
)
Custom Metadata #
PaymentInitConfig(
// ...
metadata: TransactionMetadata(
customFields: [
CustomField(
variableName: 'orderId',
value: 'ORD-12345',
displayName: 'Order ID',
),
],
),
)
Timeout #
Set a timeout for the checkout session:
final result = await CredoCheckout.pay(
context,
config,
timeout: const Duration(minutes: 5),
);
Debug Logging #
Enable debug mode to see detailed logs:
CredoCheckout.configure(
publicKey: 'your_key',
environment: CredoEnvironment.demo,
debug: true, // Enable logging
);
This logs:
- HTTP requests and responses
- WebView navigation events
- Gateway messages
- Errors with details
View logs in Flutter DevTools or your IDE's debug console.
Error Handling #
Network Errors #
final result = await CredoCheckout.pay(context, config);
if (result.isFailure) {
final message = result.transaction?.message;
if (message?.contains('Network error') == true) {
// Handle network issues
}
}
Validation Errors #
The API may return field-specific errors:
if (result.hasFieldErrors) {
// Example: {"reference": "Reference must be unique"}
result.fieldErrors!.forEach((field, error) {
showFieldError(field, error);
});
}
Platform Setup #
Android #
Add internet permission to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
iOS #
No additional setup required.
Example App #
See the example directory for a complete working example.
import 'package:flutter/material.dart';
import 'package:credo_flutter/credo_flutter.dart';
void main() {
CredoCheckout.configure(
publicKey: 'your_public_key',
environment: CredoEnvironment.demo,
);
runApp(MyApp());
}
class PaymentPage extends StatelessWidget {
Future<void> _pay(BuildContext context) async {
final result = await CredoCheckout.pay(
context,
PaymentInitConfig(
amount: 50000, // 500 NGN
email: 'customer@example.com',
currency: Currency.ngn,
bearer: FeeBearer.customer,
),
);
if (result.isSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Payment successful!')),
);
}
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => _pay(context),
child: Text('Pay Now'),
);
}
}