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

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: CredoWebview does not require configure() — 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'),
    );
  }
}

Libraries

credo_flutter