DEV Community

Cover image for Supporting Offline Payments in Low-Connectivity Areas
uma victor for Flutterwave Engineering

Posted on

Supporting Offline Payments in Low-Connectivity Areas

You have built a payment integration that works for customers in Lagos, but what happens when your customer tries to pay from a remote fishing community like Brass in Bayelsa State, where network towers are sparse and 3G signals drop every few minutes? Only 38% of people across Africa had internet access in 2024. Compare that to the global average of 68%, and you'll see how big the challenge is.

In regions like these, connectivity is unpredictable, dropping out during important moments and returning when you least expect it. Despite these challenges, millions of people across Africa complete transactions, accept payments from customers, and keep their businesses running every single day. As fintech developers, our job is to make sure our payment systems work for everyone, not just those with perfect internet connections.

By the end of this guide, you’ll know exactly how offline payments work, how to set them up using Flutterwave’s existing tools, and how to keep every transaction secure, even when there’s no connection.

How Offline Payments Work Without Internet Connectivity

If you regularly make transactions online, there has surely been a moment when you had no internet connectivity and your transaction failed.

Online payment flow

On a high level, offline payments work by allowing you to get value for products and services even when you don’t have an internet connection (Wi-Fi, cellular). You get a perceived experience of a successful transaction, while in the background, the transaction hasn’t been approved; instead, it’s encrypted and cached on the device, waiting for an internet connection to really process the payment.

Think of offline payments like sending a letter through the postal system instead of an instant message. When internet connectivity drops, your payment system needs to become a reliable post office, securely storing transaction details until they can be delivered and processed.

The Tech Behind Accepting Offline Payments: Store and Forward (SAF)
The most common and reliable way to handle offline payments is a method called "Store and Forward" (SAF). It’s a hybrid approach that perfectly balances security with real-world business needs.

store and forward

Here’s how it works:

When a point-of-sale (POS) terminal or mobile application loses its internet connection, it transitions into an offline mode. In this state, instead of declining card payments, it captures the transaction details, encrypts them immediately, and stores them securely on the device's local memory. From the customer's perspective, the transaction appears to be completed; the customer gets a receipt and their goods right away. However, no real-time communication with the bank or payment processor occurs at this stage.

To fully grasp the differences, consider the following comparison between a standard online transaction and an offline SAF transaction.

Feature Online Transaction Offline (Store & Forward) Transaction
Authorization Time Real-time (seconds) Delayed (minutes, hours, or days after the transaction)
Risk of Decline Low risk; instant authorization result (success/decline) High risk; no real-time check, approval/decline unknown until sync
Funds Settlement Initiated immediately upon authorization Initiated only after successful delayed authorization
Customer Experience Instant confirmation of payment success or failure Instant "apparent" success; payment could fail later
Merchant Liability Low; protected by real-time authorization High; assumes 100% of the risk for declined transactions

Key Components Of A Great Offline System
These are the key components to have in mind when building a great offline system:

  • Data Encryption: All payment data must be encrypted at rest using AES-256 or equivalent standards. This protects sensitive information even if devices are compromised while offline.
  • Fail-Safe Mechanisms: Your system needs fallback options when primary payment methods fail. This might include alternative payment channels like USSD or SMS-based confirmations that work over basic cellular networks.
  • Conflict Resolution Logic: What happens if the same transaction tries to sync twice? Your system needs to be intelligent enough to identify duplicates, handle errors, and resolve conflicts without interruption.
  • Local Data Management: You can't keep transaction data on a device forever. A great offline system automatically and securely deletes old or processed transaction details to keep data safe.

How To Support Offline Payments

Now let's get into the actual implementation. Since this is a custom setup, you’ll be handling the implementation and compliance, but we’ll walk you through everything you need to know. First, let’s cover what we’re building and the basic requirements.

What You're Building
Let's say you're building a mobile app for small merchants selling goods at local markets. These merchants often work in areas where network coverage is spotty, meaning they sometimes have good connectivity and sometimes they don't. Your app needs to accept card payments regardless of network conditions.

The system operates in two distinct phases: an initial online phase to securely capture and tokenize a customer's payment card and subsequent offline phases that use this token for transactions.

offline system architecture

Technical Requirements

Client-Side Architecture
Your mobile application will be the core of the offline system and must be engineered to handle several key responsibilities:

  • Network State Detection: The app must reliably detect when the device is offline and online to switch between modes automatically.
  • Secure Local Database: An encrypted database is required to store a queue of pending transactions. Technologies like SQLite with an encryption layer (e.g., SQLCipher) are suitable for this purpose.
  • Transaction Queue Management: The app needs to manage a queue of transaction objects, each with a status (e.g., pending_sync, sync_in_progress, sync_successful, sync_failed), a unique transaction reference, timestamp, and all necessary payment details.

Risk Management Configuration
Given that the merchant assumes all financial risk, building configurable limits directly into the application is non-negotiable. These serve as critical financial fail-safes.

  • Per-Transaction Limit: A maximum monetary value that can be processed in a single offline transaction. Any amount exceeding this limit should be rejected, forcing an online attempt.
  • Cumulative Offline Limit: A ceiling for the total value of all transactions waiting in the offline queue. Once this limit is reached, the app must prevent further offline payments until the existing queue is successfully synced.
  • Time Limit: A mandatory synchronization window, typically between 24 and 72 hours. Any transaction not uploaded within this period will expire and cannot be processed. Your application logic must enforce this deadline rigorously.

Flutterwave Setup
Before you start building, make sure you have:

step by step implementation

Step 1: Acquiring the Payment Token
The golden rule here is that the very first payment from a customer must be online. This first transaction is key because it allows Flutterwave to create a secure token for the customer’s card. You’ll use this token for all their future offline payments. This token, a non-sensitive placeholder, is what will be used for all subsequent offline payments. This design means the solution is tailored for recurring payments from returning customers, not for first-time interactions with new customers who are already offline.

Note: Before you continue, make sure you thoroughly understand card tokenization.

  1. Initiate a Standard Online Charge: Using the Flutterwave mobile SDK, perform a standard card payment. This process involves collecting the customer's card details within the secure UI provided by the SDK, which handles the encryption and communication with Flutterwave's servers.
  2. Verify the Transaction: After the charge, verify the payment status. The token will be available in the verification response.
  3. Extract and Store the Token: In the verification response, you'll find the token in the data.card.token field.

Example successful response structure:

    {
      "status": "success",
      "message": "Charge successful",
      "data": {
        "id": 277036749,
        "tx_ref": "new-live-test",
        "flw_ref": "FLW253481676",
        "amount": 300,
        "status": "successful",
        "payment_type": "card",
        "customer": {
          "id": 210745229,
          "name": "Yemi Desola",
          "email": "user@gmail.com"
        },
        "card": {
          "first_6digits": "123456",
          "last_4digits": "7890",
          "issuer": "MASTERCARD...",
          "country": "NG",
          "type": "MASTERCARD",
          "expiry": "08/22",
          "token": "flw-t1nf-f9b3bf384cd30d6fca42b6df9d27bd2f-m03k"
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode

4. Secure the Token: Store this token (flw-t1nf-...) along with the customer's email address in your local database. Both are required for future tokenized charges.

Step 2: Secure Storage and Transaction Capture
With a token securely stored, your application is now equipped to handle payments without an internet connection.

  1. Secure Token Storage: The card token must be stored using the most secure, hardware-backed mechanisms available on the mobile platform.
    • On Android: Use the Android Keystore system to generate and store encryption keys. These keys can then be used to encrypt the Flutterwave token and the transaction queue data, which can be stored in DataStore with Tink encryption for simple data or a database encrypted with SQLCipher for more complex queue management.
    • On iOS: Use the native Keychain Services to store the token and other sensitive transaction data. The Keychain is a secure, encrypted container managed by the operating system, designed specifically for credentials and small pieces of sensitive data.
  2. Implement the Offline Checkout Flow:
    • When the app detects it is offline, the payment UI should switch to an "Offline Mode."
    • The user enters the transaction amount.
    • Your app validates the amount against the pre-configured per-transaction and cumulative limits.
    • Upon confirmation, the app creates a new record in its local, encrypted transaction queue. This record should include:
      • A unique transaction reference (tx_ref) generated on the client
      • The transaction amount and currency
      • The customer's identifier and the associated card token
      • A timestamp
      • An initial status of pending_sync
    • Provide the customer with a receipt indicating the payment was accepted offline and will be processed once connectivity is restored.

Step 3: Reconnecting (Online) Synchronization and Processing
This final stage is where the stored transactions are forwarded to Flutterwave for actual processing.

  1. Detect Network Reconnection: Implement a background service or a listener that triggers when the device's internet connection is restored.
  2. Process the Transaction Queue:
    • Once online, the service should read the pending transactions from the local database.
    • For each transaction in the queue, construct and send a POST request to Flutterwave's [v3/tokenized-charges](https://developer.flutterwave.com/v3.0.0/reference/charge-with-token-1) endpoint.
    • The body of the request will be populated with the data you stored locally, including the card token.

Example cURL request for a tokenized charge:

    curl --request POST \
         --url https://api.flutterwave.com/v3/tokenized-charges \
         --header 'Authorization: Bearer YOUR_SECRET_KEY' \
         --header 'Content-Type: application/json' \
         --data '{
            "token": "flw-t1nf-f9b3bf384cd30d6fca42b6df9d27bd2f-m03k",
            "email": "user@example.com",
            "currency": "NGN",
            "country": "NG",
            "amount": 2000,
            "tx_ref": "your-unique-offline-tx-ref-123",
            "first_name": "Yemi",
            "last_name": "Desola",
            "narration": "Offline purchase of Item X"
         }'

Enter fullscreen mode Exit fullscreen mode

3. Handle Responses and Update the Queue:

  • On Success (HTTP 200): If the API call is successful and the transaction is approved by the bank, update the transaction's status in your local queue to sync_successful.
  • On Failure (HTTP 4xx/5xx): Implement proper error handling.
    • If the transaction is definitively declined by the issuer (e.g., "Insufficient Funds," "Invalid Card"), update the status to sync_failed. This transaction must be flagged for manual follow-up with the customer.
    • If the failure is due to a temporary network issue or a server error on the gateway's end, leave the status as pending_sync and implement a retry mechanism with exponential backoff.
  • Once a transaction is successfully processed, the corresponding record should be securely cleared from the device to minimize data retention.

Safety Practices for Offline Payments

Building a custom offline payment solution introduces unique security challenges. Here are the practices to keep transactions safe:

  1. Always Encrypt Everything: Data must be protected everywhere. Use AES-256 to encrypt data stored on the device, and make sure you’re using TLS 1.2+ when the app communicates with Flutterwave’s servers.
  2. Use Hardware-Level Security: Store your encryption keys in the most secure place possible: the device’s hardware. Use Android’s Keystore and iOS’s Keychain services to protect your keys from attackers.
  3. Set Smart Transaction Limits: Since you can't check for fraud in real-time, your app needs to be the first line of defense. Build in hard limits for how much can be spent in a single offline transaction and a total limit for all pending payments. Think of these as safety switches that force the app to go online and sync before taking on more risk.
  4. Keep a Clear Audit Trail: Things can go wrong, and you’ll need a record of every transaction. Log every offline attempt, every sync (successful or not), and the final payment status. This trail is essential for finding errors, handling disputes, and spotting fraud.
  5. Protect Against a Lost or Stolen Device: What happens if the merchant's device falls into the wrong hands? Your app needs to be locked down. Require a PIN, password, or biometric scan (like a fingerprint) to open the app or make a payment. This simple step protects the stored data even if the phone itself is compromised.
  6. Develop a Clear Data Deletion Strategy: Don't keep sensitive data on a device longer than necessary. Once a transaction is successfully synced and its status is confirmed, your app should automatically and securely wipe it from local storage. The less data on the device, the lower the risk.

Wrapping Up

When your payment tool works everywhere, you open up your business to everyone. You’re able to serve merchants in remote markets, vendors during network outages, and millions of customers who can’t rely on a stable connection. It’s a huge step toward building a truly inclusive economy in Africa.

The method we've covered gives you the foundation for a powerful offline payment feature. Yes, it requires careful security, smart limits, and solid sync logic. But the payoff is huge: payments that just work, no matter what.

Ready to build experiences that work for everyone, everywhere? Get started with Flutterwave.

Top comments (0)