Flutter SDK
Accept payments in your Flutter app with the Credo Flutter SDK.
The official Flutter SDK for integrating Credo payments into your mobile app. It wraps Credo's checkout in a WebView and returns structured results you can act on directly.
Installation
Add the package to your pubspec.yaml:
dependencies:
credo_flutter: ^1.0.0Then install:
flutter pub getPlatform requirements
Add internet permission to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>No additional setup required.
Quick start
There are two ways to accept payments. Choose the approach that fits your architecture.
Your backend initializes the transaction via the Credo API and passes the checkout URL to the app. No SDK configuration needed.
import 'package:credo_flutter/credo_flutter.dart';
final checkoutUrl = await yourBackend.initializePayment();
final result = await CredoCheckout.open(
context,
checkoutUrl: checkoutUrl,
);
if (result.isSuccess) {
print('Paid: ${result.reference}');
} else if (result.isCancelled) {
print('User cancelled');
} else {
print('Failed: ${result.transaction?.message}');
}This is the recommended approach. Your secret key stays on the server and the app never handles payment initialization directly.
The SDK initializes the transaction itself. Simpler to set up, but your public key is embedded in the app.
First, configure the SDK in main():
import 'package:credo_flutter/credo_flutter.dart';
void main() {
CredoCheckout.configure(
publicKey: 'your_public_key',
environment: CredoEnvironment.demo, // .live for production
);
runApp(MyApp());
}Then call pay():
final result = await CredoCheckout.pay(
context,
PaymentInitConfig(
amount: 10000, // NGN 100.00 in kobo
email: 'customer@example.com',
currency: Currency.ngn,
bearer: FeeBearer.customer,
),
);
if (result.isSuccess) {
// Grant value to the customer
}Configuration
Only required when using the SDK-initiated approach (CredoCheckout.pay()).
| Option | Type | Default | Description |
|---|---|---|---|
publicKey | String | - | Your Credo public key (required) |
environment | CredoEnvironment | .live | .demo for sandbox, .live for production |
debug | bool | false | Log HTTP requests, WebView events, and errors |
Payment options
Parameters accepted by 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 | Your unique transaction reference |
channels | List<PaymentChannel>? | No | Restrict available payment methods |
callbackUrl | String? | No | Redirect URL after payment |
customerFirstName | String? | No | Customer first name |
customerLastName | String? | No | Customer last name |
customerPhoneNumber | String? | No | Customer phone number |
metadata | TransactionMetadata? | No | Custom key-value data attached to the transaction |
narration | String? | No | Transaction description |
Available channels
channels: [
PaymentChannel.card,
PaymentChannel.bank,
PaymentChannel.ussd,
PaymentChannel.wallet,
PaymentChannel.opay,
PaymentChannel.payoutlet,
]If omitted, all channels are presented to the customer.
Handling results
Every call to CredoCheckout.pay() or CredoCheckout.open() returns a CheckoutResult:
switch (result.status) {
case CheckoutStatus.success:
final txn = result.transaction!;
print('Paid: ${txn.amount} ${txn.currency}');
print('Ref: ${txn.reference}');
break;
case CheckoutStatus.failed:
print('Error: ${result.transaction?.message}');
if (result.hasFieldErrors) {
result.fieldErrors!.forEach((field, error) {
print('$field: $error');
});
}
break;
case CheckoutStatus.cancelled:
// User closed the checkout
break;
case CheckoutStatus.unknown:
// Unexpected state - treat as failure
break;
}CheckoutResult
| Property | Type | Description |
|---|---|---|
status | CheckoutStatus | success, failed, cancelled, or unknown |
transaction | TransactionDetails? | Full transaction details from the gateway |
fieldErrors | Map<String, String>? | Validation errors keyed by field name |
reference | String? | Transaction reference (convenience getter) |
isSuccess | bool | true if payment succeeded |
isCancelled | bool | true if user cancelled |
hasFieldErrors | bool | true if validation errors exist |
TransactionDetails
| Property | Type | Description |
|---|---|---|
reference | String? | Your transaction reference |
transRef | String? | Credo's internal transaction reference |
amount | double? | Transaction amount |
currency | String? | Currency code |
processorFee | double? | Fee charged |
message | String? | Status message from the gateway |
Split payments
Distribute transaction proceeds across multiple accounts:
PaymentInitConfig(
amount: 100000,
email: 'customer@example.com',
currency: Currency.ngn,
bearer: FeeBearer.merchant,
splitConfiguration: [
SplitEntry(
accountId: '0441234567890', // Bank code + account number
splitType: SplitType.percentage,
splitValue: 80,
isDefault: true,
),
SplitEntry(
accountId: '0449876543210',
splitType: SplitType.flat,
splitValue: 500,
isDefault: false,
),
],
)See Settlement System for split configuration rules.
Custom metadata
Attach custom data to transactions. This data appears in your Credo dashboard and webhook payloads.
PaymentInitConfig(
amount: 10000,
email: 'customer@example.com',
currency: Currency.ngn,
bearer: FeeBearer.customer,
metadata: TransactionMetadata(
customFields: [
CustomField(
variableName: 'orderId',
value: 'ORD-12345',
displayName: 'Order ID',
),
],
),
)Full 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(const MaterialApp(home: PaymentPage()));
}
class PaymentPage extends StatelessWidget {
const PaymentPage({super.key});
Future<void> _pay(BuildContext context) async {
final result = await CredoCheckout.pay(
context,
PaymentInitConfig(
amount: 50000, // NGN 500.00
email: 'customer@example.com',
currency: Currency.ngn,
bearer: FeeBearer.customer,
),
);
if (!context.mounted) return;
if (result.isSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Payment successful!')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => _pay(context),
child: const Text('Pay NGN 500'),
),
),
);
}
}Next steps
Was this page helpful?
Last updated on
