Bank account charge

Hey👋. We recommend checking out the overview to understand the basics of direct charge first. This guide assumes you've read that.

Charging bank accounts

Direct account charge allows you to collect payments directly from your customers' bank accounts.

There are three "flavours" of direct bank account charge, depending on the bank account's currency/country:

  1. Direct debit (for Nigerian accounts): The customer authorizes the payment with their bank, and the money is debited from their account.
  2. UK (GBP) accounts: The customer logs in to their bank's Internet/mobile banking and makes a transfer to a generated bank account.
  3. ACH payment (for USD and ZAR accounts)

This guide covers NG accounts (direct debit). See the guides for UK payments and ACH payments.

Process

Supported banks

Direct bank account debit is currently only supported for the following banks:

  • Access Bank (044)
  • Guaranty Trust Bank (058)
  • First Bank (011)
  • Sterling Bank (232)
  • UBA (033)
  • Zenith Bank (057)

Charging a Nigerian bank account involves three main steps:

  1. Initiate the payment: You send the payment details to the charge endpoint.
  2. Authorize the charge: The customer authorizes the payment with their bank. The means of authorization will depend on the bank.
  3. Verify the payment: As a failsafe, you'll call our API to verify that the payment was successful before giving value (the verify transaction endpoint).

Direct debit is different from bank transfers. In direct debit, the customer enters their account details and authorizing info on your page; in bank transfers, you give them account details, and they make a transfer to you.

Initiating the payment

First, you'll need the customer's bank account details:

  • their account_number, and
  • their account_bank, the code for their bank (get a list of bank codes with the get banks endpoint.

Combine those with the rest of the payment details to create the payload. You'll need to specify amount, currency, email, fullname, and a unique tx_ref. You can also specify more details, such as the customer's phone_number and custom meta information. See the [reference documentation] for details.

The currency you specify must match the account's currency (NGN).

Send these payment details to the charge NG account endpoint. Here are some examples with our SDKs:

// Install with: npm i flutterwave-node-v3

const Flutterwave = require('flutterwave-node-v3');
const flw = new Flutterwave(process.env.FLW_PUBLIC_KEY, process.env.FLW_SECRET_KEY);
const payload = {
    account_bank: "044",
    account_number: "0690000037",
    amount: 7500,
    currency: 'NGN',
    email: 'twista@rove.press',
    tx_ref: this.generateTransactionReference(),
}
await flw.Charge.ng(payload);
// Install with: composer require flutterwavedev/flutterwave-v3

$flw = new \Flutterwave\Rave(getenv('FLW_SECRET_KEY'));
// Set `PUBLIC_KEY` as an environment variable
$accountChargeService = new \Flutterwave\Account();
$payload = [
    "type" => "debit_ng_account",
    "account_bank" => "044",
    "account_number" => "0690000037",
    "amount" => 7500,
    "currency" => 'NGN',
    "email" => 'twista@rove.press',
    "tx_ref" => $this->generateTransactionReference(),
];
$response = $accountChargeService->accountCharge($payload);
# Install with: gem install flutterwave_sdk

require 'flutterwave_sdk'

flw = Flutterwave.new(ENV["FLW_PUBLIC_KEY"], ENV["FLW_SECRET_KEY"], ENV["FLW_ENCRYPTION_KEY"])
account_payment = AccountPayment.new(flw)
payload = {
    account_bank: "044",
    account_number: "0690000037",
    amount: 7500,
    currency: 'NGN',
    email: 'twista@rove.press',
    tx_ref: generate_transaction_reference,
}
# Automatically uses the right endpoint based on the currency
response = account_payment.initiate_charge payload
curl --request POST \
   --url https://api.flutterwave.com/v3/charges?type=debit_ng_account \
   --header 'Authorization: Bearer YOUR_SECRET_KEY' \
   --header 'content-type: application/json' \
   --data '{
     "account_bank": "044",
     "account_number": "0690000037",
     "amount": 7500,
     "currency": "NGN",
     "email": "twista@rove.press",
     "tx_ref": "BJUYU399fcd43"
}'

Handling the response

If all goes well, you'll get a success response that gives you the details of the created transaction, and information on how to authorize that. Here are some examples for various banks:

These examples are correct as at the time of writing, but a bank may choose to change its authorization scheme without prior notice. Your integration should not tie banks to schemes.

// Typically used by Access Bank
{
  "status": "success",
  "message": "Charge initiated",
  "data": {
    "id": 1190962,
    "tx_ref": "MC-1585230ew9v5050e8",
    "flw_ref": "URF_1585311768729_7845335",
    "device_fingerprint": "N/A",
    "amount": 7500,
    "charged_amount": 7500,
    "app_fee": 147.2,
    "merchant_fee": 0,
    "auth_model": "AUTH",
    "currency": "NGN",
    "ip": "N/A",
    "narration": "MerchantName",
+   "status": "pending",
    "auth_url": "NO-URL",
    "payment_type": "account",
    "fraud_status": "ok",
    "created_at": "2020-03-27T12:22:48.000Z",
    "account_id": 74843,
    "customer": {
      "id": 349094,
      "email": "twista@rove.press",
    },
    "account": {
      "account_number": "0690000040",
      "bank_code": "044",
      "account_name": "Yemi Desola"
    },
    "meta": {
      "authorization": {
+       "mode": "otp",
+       "validate_instructions": "Please validate with the OTP sent to your mobile or email"
      }
    }
  }
}
// Typically used by FirstBank and GTB
{
  "status": "success",
  "message": "Charge initiated",
  "data": {
    "id": 421777409,
    "tx_ref": "refs-fbn-011-1",
    "flw_ref": "OCJF8461608196461",
    "device_fingerprint": "N/A",
    "amount": 1,
    "charged_amount": 1,
    "app_fee": 0.02,
    "merchant_fee": 0,
    "processor_response": "Pending Validation",
    "auth_model": "AUTH",
    "currency": "NGN",
    "ip": "N/A",
    "narration": "Globus Technologies",
+   "status": "pending",
    "auth_url": "https://coreflw.com/flwcpay/flwngprocessor?fbid=FLW6dd5d663-e66c-4145-bdd8-3b70ea14901b",
    "payment_type": "account",
    "fraud_status": "ok",
    "created_at": "2021-05-04T21:52:39.000Z",
    "account_id": 82913,
    "customer": {
      "id": 219419048,
      "phone_number": "08138460518",
      "name": "Ayomide Desola",
      "email": "jimioniay@gmail.com",
      "created_at": "2020-08-10T22:55:51.000Z"
    },
    "account": {
      "account_number": "2033349714",
      "bank_code": "011",
      "account_name": "NO-NAME NO-LNAME"
    },
    "meta": {
      "authorization": {
+       "mode": "redirect",
+       "redirect": "https://coreflw.com/flwcpay/flwngprocessor?fbid=FLW6dd5d663-e66c-4145-bdd8-3b70ea14901b"
      }
    }
  }
}

Some key details in the response:

  • status is "successful", meaning that the charge was initiated successfully
  • data.status is "pending", meaning that the customer needs to authorize the transaction with their bank
  • meta.authorization contains the important details to authorize the payment:
    • When the mode is "otp", this means the customer has been sent an OTP by their bank, and you need to get this and send to the validate OTP endpoint. You'll have a validate_instructions field, which contains instructions you can display to your customer.
    • When the mode is "redirect", this means the customer should be redirected to their bank for authorization. You'll have a redirect field, which is where you should redirect your customer to.

Authorizing the transaction

As explained in handling the response, the customer needs to authorize the transaction to complete it.

OTP

The otp authorization mode means an OTP has been sent to your customer. You can display the validate_instructions to your customer.

When the customer enters the OTP, you'll need to validate the transaction by calling our validate charge endpoint with the customer's OTP and the flw_ref for the transaction (which was returned in the earlier response).

const response = await flw.Charge.validate({
    otp: req.body.otp,
    flw_ref: req.session.flw_ref
});
$response = $cardChargeService->validateTransaction([
    'otp' => $req->body->get('otp'),
    'flw_ref' => session('flw_ref'),
]);
response = account_payment.validate_charge(session[:flw_ref], params[:otp])
curl --request POST \
   --url https://api.flutterwave.com/v3/validate-charge \
   --header 'Authorization: Bearer YOUR_SECRET_KEY' \
   --header 'content-type: application/json' \
   --data '{
        "otp": "398761",
        "flw_ref": "URF_1585311768729_7845335"
    }'

You'll get a response like this:

{
  "status": "success",
  "message": "Charge validated",
  "data": {
    "id": 1190962,
    "tx_ref": "MC-1585230ew9v5050e8",
    "flw_ref": "URF_1585311768729_7845335",
    "device_fingerprint": "N/A",
    "amount": 7500,
    "charged_amount": 7500,
    "app_fee": 147.2,
    "merchant_fee": 0,
    "auth_model": "AUTH",
    "currency": "NGN",
    "ip": "N/A",
    "narration": "MerchantName",
    "status": "successful",
    "auth_url": "NO-URL",
    "payment_type": "account",
    "fraud_status": "ok",
    "created_at": "2020-03-27T12:22:48.000Z",
    "account_id": 74843,
    "customer": {
      "id": 349094,
      "email": "twista@rove.press",
    },
    "account": {
      "account_number": "0690000040",
      "bank_code": "044",
      "account_name": "Yemi Desola"
    }
  }
}

The data.status field indicates that the charge was successful. However, to be on the safe side, you should always verify the payment.

Redirect

For redirect mode, all you need to do is redirect the customer to the URL in the redirect field:

if (transaction.meta.authorization.mode == "redirect") {
    return res.redirect(transaction.meta.authorization.redirect);
}
if ($transaction["meta"]["authorization"]["mode"] == "redirect") {
  return redirect($transaction["meta"]["authorization"]["redirect"]);
}
if transaction["meta"]["authorization"]["mode"] == "redirect"
  return redirect to: transaction["meta"]["authorization"]["redirect"]
end

Completing the payment

When the payment is completed by the customer, we'll send you a webhook notification. Here's what the payload looks like:

{
  "event": "charge.completed",
  "data": {
    "id": 1190962,
    "tx_ref": "MC-1585230ew9v5050e8",
    "flw_ref": "URF_1585311768729_7845335",
    "device_fingerprint": "N/A",
    "amount": 7500,
    "currency": "NGN",
    "charged_amount": 7500,
    "app_fee": 147.2,
    "merchant_fee": 0,
    "processor_response": "Approved by Financial Institution",
    "auth_model": "AUTH",
    "ip": "N/A",
    "narration": "MerchantName",
    "status": "successful",
    "payment_type": "account",
    "created_at": "2020-03-27T12:22:48.000Z",
    "account_id": 74843,
    "customer": {
      "id": 349094,
      "email": "twista@rove.press",
    },
    "account": {
      "account_number": "0690000040",
      "bank_code": "044",
      "account_name": "Yemi Desola"
    }
  }
}

In your webhook handler, you can then verify the payment and credit your customer with whatever they paid for. See our guide to transaction verification for details.

Loading...