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.0

Then install:

flutter pub get

Platform 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()).

OptionTypeDefaultDescription
publicKeyString-Your Credo public key (required)
environmentCredoEnvironment.live.demo for sandbox, .live for production
debugboolfalseLog HTTP requests, WebView events, and errors

Payment options

Parameters accepted by PaymentInitConfig:

ParameterTypeRequiredDescription
amountintYesAmount in lowest currency unit (kobo/cents)
emailStringYesCustomer email address
currencyCurrencyYes.ngn or .usd
bearerFeeBearerYes.customer or .merchant
referenceString?NoYour unique transaction reference
channelsList<PaymentChannel>?NoRestrict available payment methods
callbackUrlString?NoRedirect URL after payment
customerFirstNameString?NoCustomer first name
customerLastNameString?NoCustomer last name
customerPhoneNumberString?NoCustomer phone number
metadataTransactionMetadata?NoCustom key-value data attached to the transaction
narrationString?NoTransaction 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

PropertyTypeDescription
statusCheckoutStatussuccess, failed, cancelled, or unknown
transactionTransactionDetails?Full transaction details from the gateway
fieldErrorsMap<String, String>?Validation errors keyed by field name
referenceString?Transaction reference (convenience getter)
isSuccessbooltrue if payment succeeded
isCancelledbooltrue if user cancelled
hasFieldErrorsbooltrue if validation errors exist

TransactionDetails

PropertyTypeDescription
referenceString?Your transaction reference
transRefString?Credo's internal transaction reference
amountdouble?Transaction amount
currencyString?Currency code
processorFeedouble?Fee charged
messageString?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

On this page