Skip to main content

Overview

This guide will walk you through implementing a complete payment flow using the Hubtel Merchant Checkout SDK. By the end, you’ll have a working checkout integration in your Flutter app.

Implementation Steps

1

Import the SDK

First, import the package in your Dart file:
import 'package:hubtel_merchant_checkout_sdk/hubtel_merchant_checkout_sdk.dart';
2

Create a HubtelCheckoutConfiguration object

Configure the SDK with your merchant credentials:
final hubtelConfig = HubtelCheckoutConfiguration(
  merchantApiKey: "QTN1akQ1SzpiM2IxMjA1NTEwZmI0NjYzYTdiY2ZmZmUyNmQ1YmIzZA==",
  merchantID: "1122334",
  callbackUrl: "https://your-domain.com/payment-callback",
);
Important: The merchantApiKey must be a Base64-encoded string of your Hubtel merchant credentials. Keep this secure and never commit it to version control. Consider using environment variables or secure storage.

Configuration Parameters

ParameterTypeRequiredDescription
merchantApiKeyStringYesBase64 encoded merchant credentials
merchantIDStringYesYour Hubtel merchant ID
callbackUrlStringYesURL to receive payment notifications
3

Create a PurchaseInfo object

Define the purchase details:
import 'package:uuid/uuid.dart';

final purchaseInfo = PurchaseInfo(
  amount: 50.00,
  customerPhoneNumber: '0541234567',
  clientReference: const Uuid().v4(),
  purchaseDescription: 'Premium Subscription',
);
The clientReference should be a unique identifier for each transaction. We’re using the uuid package here, but you can use any method to generate unique IDs.

PurchaseInfo Parameters

ParameterTypeRequiredDescription
amountdoubleYesTransaction amount in GHS
customerPhoneNumberStringYesCustomer’s mobile number
clientReferenceStringYesUnique transaction reference
purchaseDescriptionStringYesDescription of the purchase
Add the uuid package to your pubspec.yaml for generating unique client references:
dependencies:
  uuid: ^4.0.0
4

(Optional) Customize the theme

Optionally, create a ThemeConfig object to match your brand:
import 'package:flutter/material.dart';

final themeConfig = ThemeConfig(primaryColor: Colors.blue);
This will apply your brand color throughout the checkout experience.
5

Navigate to the CheckoutScreen

When the user initiates payment (e.g., taps a “Pay Now” button), navigate to the checkout screen:
void initiateCheckout(BuildContext context) async {
  final result = await Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) {
        return CheckoutScreen(
          purchaseInfo: purchaseInfo,
          configuration: hubtelConfig,
          themeConfig: themeConfig, // Optional
        );
      },
    ),
  );

  // Handle the checkout result
  if (result is CheckoutCompletionStatus) {
    handleCheckoutCompletion(result);
  }
}
6

Handle the payment result

Process the checkout completion status:
void handleCheckoutCompletion(CheckoutCompletionStatus status) {
  switch (status.status) {
    case UnifiedCheckoutPaymentStatus.paymentSuccess:
      // Payment was successful
      print('Payment successful!');
      print('Transaction ID: ${status.transactionId}');
      print('Payment Type: ${status.paymentType}');
      print('Payment Channel: ${status.paymentChannel}');
      
      // Update your UI, save the transaction, etc.
      showSuccessDialog();
      break;

    case UnifiedCheckoutPaymentStatus.paymentFailed:
      // Payment failed
      print('Payment failed');
      showErrorDialog('Payment failed. Please try again.');
      break;

    case UnifiedCheckoutPaymentStatus.pending:
      // Payment is pending (e.g., bank transfer)
      print('Payment pending');
      showPendingDialog('Your payment is being processed.');
      break;

    case UnifiedCheckoutPaymentStatus.userCancelledPayment:
      // User cancelled the payment
      print('User cancelled payment');
      break;

    case UnifiedCheckoutPaymentStatus.unknown:
      // Status unknown
      print('Payment status unknown');
      showErrorDialog('Unable to determine payment status.');
      break;
  }
}
The CheckoutCompletionStatus object contains:
  • status - The payment status enum
  • transactionId - Unique transaction identifier
  • paymentType - Type of payment used (optional)
  • paymentChannel - Channel used for payment (optional)

Complete Example

Here’s a complete working example:
import 'package:flutter/material.dart';
import 'package:hubtel_merchant_checkout_sdk/hubtel_merchant_checkout_sdk.dart';
import 'package:uuid/uuid.dart';

class PaymentScreen extends StatelessWidget {
  const PaymentScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Checkout'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => initiateCheckout(context),
          child: const Text('Pay Now'),
        ),
      ),
    );
  }

  void initiateCheckout(BuildContext context) async {
    // Configure the SDK
    final hubtelConfig = HubtelCheckoutConfiguration(
      merchantApiKey: "QTN1akQ1SzpiM2IxMjA1NTEwZmI0NjYzYTdiY2ZmZmUyNmQ1YmIzZA==",
      merchantID: "1122334",
      callbackUrl: "https://your-domain.com/payment-callback",
    );

    // Create purchase info
    final purchaseInfo = PurchaseInfo(
      amount: 50.00,
      customerPhoneNumber: '0541234567',
      clientReference: const Uuid().v4(),
      purchaseDescription: 'Premium Subscription',
    );

    // Optional: Customize theme
    final themeConfig = ThemeConfig(primaryColor: Colors.blue);

    // Navigate to checkout
    final result = await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) {
          return CheckoutScreen(
            purchaseInfo: purchaseInfo,
            configuration: hubtelConfig,
            themeConfig: themeConfig,
          );
        },
      ),
    );

    // Handle result
    if (result is CheckoutCompletionStatus) {
      handleCheckoutCompletion(context, result);
    }
  }

  void handleCheckoutCompletion(
    BuildContext context,
    CheckoutCompletionStatus status,
  ) {
    switch (status.status) {
      case UnifiedCheckoutPaymentStatus.paymentSuccess:
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Payment successful! ID: ${status.transactionId}'),
            backgroundColor: Colors.green,
          ),
        );
        break;

      case UnifiedCheckoutPaymentStatus.paymentFailed:
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Payment failed. Please try again.'),
            backgroundColor: Colors.red,
          ),
        );
        break;

      case UnifiedCheckoutPaymentStatus.pending:
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Payment is being processed.'),
            backgroundColor: Colors.orange,
          ),
        );
        break;

      case UnifiedCheckoutPaymentStatus.userCancelledPayment:
        // User cancelled - no action needed
        break;

      case UnifiedCheckoutPaymentStatus.unknown:
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Unable to determine payment status.'),
            backgroundColor: Colors.grey,
          ),
        );
        break;
    }
  }
}

Payment Status Reference

The UnifiedCheckoutPaymentStatus enum has the following values:
StatusDescription
paymentSuccessPayment was completed successfully
paymentFailedPayment attempt failed
pendingPayment is being processed (common with bank transfers)
userCancelledPaymentUser closed checkout without completing payment
unknownPayment status could not be determined
Always verify payment status on your backend using the callback URL. The client-side status should not be the sole source of truth for critical business logic.

Testing

For testing purposes, you can use Hubtel’s sandbox credentials. Contact Hubtel support to obtain test merchant credentials.
During development, use small amounts (e.g., 0.1 GHS) for testing to avoid unnecessary charges.

Next Steps

Configuration

Learn about advanced configuration options

Payment Methods

Explore supported payment methods and channels

Build docs developers (and LLMs) love