Card Payments
Charge customers' credit and debit cards.
Card payments allow you to accept payments from customers using their credit and debit cards linked to their bank accounts. Customers complete the payment online by entering their card information during checkout.
Supported Card Networks
- Mastercard
- Visa
- American Express
- Verve
- Afrigo
Requirements
- Ensure that your Flutterwave account is live.
- Retrieve your API credentials (client ID and client Secret) from your dashboard to authorize your API requests.
- Configure your webhooks to manage payment status updates.
How Card Payments Work
When a customer pays online with their card, these steps happen behind the scenes:
-
Encrypt and send payment details: The customer enters their card details. The merchant encrypts this information along with the transaction data (like amount and currency), then sends it to Flutterwave.
-
Forward the request to the acquiring bank: Flutterwave securely forwards the encrypted payment request to our acquiring bank, including any extra metadata needed to process the transaction.
-
Route through the card scheme: The acquiring bank sends the request to the relevant card scheme (e.g., Visa, Mastercard). The scheme then passes it to the customer’s issuing bank.
-
Perform checks on the customer's account: The issuing bank runs key validations, such as:
- Confirming the customer has sufficient funds
- Checking for restrictions like blacklisted accounts, liens, or transaction limits
-
Initiate authentication and approve the transaction: If the checks pass, the issuing bank initiates payment authentication using the appropriate model. Once the customer approves, the bank sends the authorization back through the scheme and acquiring bank to Flutterwave.
-
Notify your system of the payment: Flutterwave confirms the completed payment and notifies you via webhook.
-
Settle the transaction: We settle the payment based on your settlement schedule.
Payment Flow
To successfully collect card payments from customers, follow these steps:
- Collect the customer's information to initiate the transaction. You need to specify their
email
,name
andmobile
at this stage. - Create a payment method object using the customer's card information and encryption
nonce
:encrypted_card_number
,encrypted_expiry_month
,encrypted_expiry_year
andencrypted_cvv
. Sensitive information must be encrypted before passing them into your request. - Initiate the charge with the
customer_id
,payment_id
and transaction details like amount, currency, and transactionreference
. - Prompt the customer to authenticate the transaction using the auth model returned from the charge response.
- Verify the payment
status
,amount
,customer_id
andtransaction_id
before providing the value to the customer.
Steps to Collecting Card Payments with Flutterwave
Integration Method
This guide follows the general flow for demonstration. Please refer to the orchestrator flow for the alternative integration method.
Step 1: Create a Customer
To create a new customer, send a request to the create customer endpoint including the name
, email
, phone
, address
, and other details. Only the email
is required, but we recommend collecting as much information as possible for future use.
To fetch an existing customer's details, use the retrieve customer endpoint with the customer's id
. This is useful when initiating charges for returning users.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/customers' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"address":{
"city":"Gotham",
"country":"US",
"line1":"221B Baker Street",
"line2":"",
"postal_code":"94105",
"state":"Colorado"
},
"name":{
"first":"King",
"middle":"Leo",
"last":"James"
},
"phone":{
"country_code":"1",
"number":"6313958745"
},
"email":"[email protected]"
}'
You'll get a response similar to this:
{
"status":"success",
"message":"Customer created",
"data":{
"id":"cus_X0yJv3ZMpL",
"address":{
"city":"Gotham",
"country":"US",
"line1":"221B Baker Street",
"line2":"",
"postal_code":"94105",
"state":"Colorado"
},
"email":"[email protected]",
"name":{
"first":"King",
"middle":"Leo",
"last":"James"
},
"phone":{
"country_code":"1",
"number":"6313958745"
},
"meta":{
},
"created_datetime":"2025-01-29T12:44:53.049Z"
}
}
Step 2: Create the Card Payment Method
Next, create a payment method for the customer. Send the customer's card details in a payment method request to generate a card object. Be sure to encrypt all sensitive information before sending the request.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/payment-methods' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"type": "card",
"card": {
"encrypted_card_number": "{{$encrypted_card_number}}",
"encrypted_expiry_month": "{{$encrypted_expiry_month}}",
"encrypted_expiry_year": "{{$encrypted_expiry_year}}",
"encrypted_cvv": "{{$encrypted_cvv}}",
"nonce": "{{$randomly_generated_nonce}}"
}
}'
You'll get a response similar to this:
{
"status": "success",
"message": "Payment method created",
"data": {
"type": "card",
"card": {
"expiry_month": 8,
"expiry_year": 32,
"first6": "123412",
"last4": "2222",
"network": "mastercard"
},
"id": "pmd_wlVhaYmkl2",
"meta": {},
"created_datetime": "2024-12-03T14:29:26.650973145Z"
}
}
Step 3: Create the Charge
To initiate a card charge, send a request to the create charge endpoint with the following:
customer_id
: Returned after creating the customer.payment_method_id
: Returned after creating the payment method.- Transaction details: Include the
amount
,currency
, and a uniquereference
.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/charges' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"reference": "cedfa85a-a803-4a06-a586-0f81fb9b9f22",
"currency": "USD",
"customer_id": "cus_IpH7CKCUtD",
"payment_method_id": "pmd_wlVhaYmkl2",
"redirect_url":"https://google.com",
"amount": 250,
"meta":{
"person_name": "King James",
"role": "Developer"
}
}
'
Step 4: Authorising the Payment
After you initiate a charge, the customer must authenticate the transaction. Authentication requirements vary by card. Flutterwave returns an authmodel
to indicate the type of authentication needed.
You can check out the Authorising Payments section to learn more about the steps required for handling different authentication models.
Once the payment is completed and funds are received, Flutterwave sends you a webhook with the final transaction status.
Authorising Payments (Auth Models)
When you make a card charge request, Flutterwave evaluates the card to determine what information is required to complete the charge on the card.
Required Authorization
For transactions that do not require authorization, you can skip to verifying transactions section .
For cards that require authorization, we return the parameters needed to authorize each payment.
3DS / VBVSECURECODE
3DS
also referred to as VBVSECURECODE
involves redirecting the customer to their Bank to authorize the transaction. When charging a card using 3DS, we'd return a redirect URL in the response for you to redirect your customer.
{
"status": "success",
"message": "Charge created",
"data": {
"id": "chg_VoUhmFMhmF",
...
"next_action": {
"type": "redirect_url",
"redirect_url": {
"url": "https://developer-sandbox-ui-sit.flutterwave.cloud/redirects?card&token=eyJhbGciOiJIUzI1NiJ9.eyJjbGllbnRJZCI6ImFjODMxOTNlLWFhMmItNDc2ZS1hZTNjLTMxYmVmMTc4NDUxZCIsImNoYXJnZUlkIjoiY2hnX1JDRHNDQkR5YVYiLCJzdWIiOiJhYzgzMTkzZS1hYTJiLTQ3NmUtYWUzYy0zMWJlZjE3ODQ1MWQiLCJpYXQiOjE3MzA5NjQ1MTIsImV4cCI6MTczMDk2NDgxMn0.tMv1VZ4WevvZ_dg37AzYnhj0Z1YtXK_tQIOJcErNscI"
}
},
"payment_method_details": {},
"status": "pending",
...
}
}
External 3DS
External 3DS allows you to control how your customers' identity is verified. With this model, you can authorize the payment using your preferred provider.
Unlike the regular 3DS, where Flutterwave handles the identity verification process, External 3DS allows you to manage this verification process according to your specific needs.
"next_action": {
"type": "redirect_url",
"redirect_url": {
"url": "https://developer-sandbox-ui-sit.flutterwave.cloud/redirects?card&token=eyJhbGciOiJIUzI1NiJ9.eyJjbGllbnRJZCI6ImFjODMxOTNlLWFhMmItNDc2ZS1hZTNjLTMxYmVmMTc4NDUxZCIsImNoYXJnZUlkIjoiY2hnX2tPMWVSblF6N1AiLCJzdWIiOiJhYzgzMTkzZS1hYTJiLTQ3NmUtYWUzYy0zMWJlZjE3ODQ1MWQiLCJpYXQiOjE3MzgyNDU3MjAsImV4cCI6MTczODI0NjAyMH0.Le9JEoZx87G1cE6OtMaRHSbKMqlYB8eMq2-9PdNkdco"
}
},
AVS
Address Verification (AVS
) requires the customer to provide their address information to authorize the payment. This model is popular with cards issued in the US and Canada.
If the charge response contains data.next_action.requires_additional_fields
, it means that you are charging an AVS card.
{
"status":"success",
"message":"Charge created",
"data":{
"id":"chg_VoUhmFMhmF",
...
"next_action":{
"type":"requires_additional_fields",
"requires_additional_fields":{
"fields":[
"authorization.avs.address.postal_code",
"authorization.avs.address.line1",
"authorization.avs.address.state",
"authorization.avs.address.country",
"authorization.avs.address.city"
]
}
},
"payment_method_details":{},
"status":"pending",
...
}
}
To complete a card charge using theAVS
auth model, update the charge with the address information.
curl --request PUT \
--url 'https://api.flutterwave.cloud/developersandbox/charges/id' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"authorization":{
"type":"avs",
"avs":{
"address":{
"city":"Gotham",
"country":"US",
"line1":"221B Baker Street",
"line2":"Coker Estate",
"postal_code":"94105",
"state":"Colorado"
}
}
}
}'
PIN
PIN card transactions are the most widely used authorization method. In this process, customers enter their card PIN
with a one-time password (OTP) to securely complete the transaction. Flutterwave informs you about a PIN card charge by returning thenext_action
object with the value of requires_pin
in the charge response.
{
"status":"success",
"message":"Charge created",
"data":{
"id":"chg_VoUhmFMhmF",
...
"next_action":{
"type":"requires_pin",
"requires_pin":{}
},
"payment_method_details":{},
"status":"pending",
...
}
}
{
"status":"success",
"message":"Charge created",
"data":{
"id":"chg_VoUhmFMhmF",
...
"next_action":{
"type":"requires_otp",
"requires_otp":{}
},
"payment_method_details":{},
"status":"pending",
...
}
}
To complete a charge using the PIN
auth model, You’ll first need to update the charge with the encrypted card PIN.
curl --request PUT \
--url 'https://api.flutterwave.cloud/developersandbox/charges/id' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"authorization": {
"type": "pin",
"pin": {
"nonce": "{{$randomly_generated_nonce}}",
"encrypted_pin": "{{$encrypted_pin}}"
}
}
}'
Followed by updating the charge request with the OTP.
curl --request PUT \
--url 'https://api-sit.flutterwave.cloud/developersandbox/charges/id' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"authorization": {
"type": "otp",
"otp": {
"code": "123456"
}
}
}'
Making Recurring Payments
Recurring payments allow businesses to automatically charge customers at regular intervals, such as weekly, monthly, or annually. This is ideal for subscription-based services, memberships, and installment plans.
There are two ways you can collect recurring payments from your customers on Flutterwave:
- Tokenization
- Credential-on-File Transaction.
Tokenization
Authorization Requirement
Charging a customer using the recurring flow doesn't require authorization.
Tokenization is a secure method for handling recurring payments by replacing sensitive card details with a unique token or ID. Instead of storing card information, you store IDs that can be used for future transactions.
How does it Work?
- The merchant collects the customer's card information and creates a card payment method object.
- The merchant stores the payment method ID linked to the card.
- For subsequent payments, the merchant provides the
payment_method_id
and specifies therecurring
flag in their charge request.
To charge a customer using the recurring payment flow: provide the payment_method_id
, customer_id
and set the recurring
flag to true
in your charge request.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/charges' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"reference": "cedfa85a-a803-4a06-a586-0f81fb9b9f22",
"currency": "USD",
"customer_id": "cus_IpH7CKCUtD",
"payment_method_id": "pmd_TusjN23anB",
"recurring":true,
"redirect_url":"https://google.com",
"amount": 250
}
'
Credential-on-File (COF) transactions
Approval Required
This feature is available only by request. To enable it on your account, contact Flutterwave support.
The Credential on File (COF) feature enables merchants to securely save a reference to their customers' card details for future payments. This allows merchants to charge customers later without asking for their card information again.
Unlike tokenization, COF references can be used across different platforms, even if the initial transaction wasn't processed through Flutterwave
To initiate a COF card transaction, include the COF
object in your request. This object would contain the agreement_id
that acts as a reference to track the transaction.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/payment-methods' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"type": "card",
"card": {
"encrypted_card_number": "{{$encrypted_card_number}}",
"encrypted_expiry_month": "{{$encrypted_expiry_month}}",
"encrypted_expiry_year": "{{$encrypted_expiry_year}}",
"encrypted_cvv": "{{$encrypted_cvv}}",
"nonce": "{{$randomly_generated_nonce}}",
"cof": {
"enabled": true,
"agreement_id": "Agreement00w02W1"
}
}
}'
curl --location 'https://api.flutterwave.cloud/developersandbox/orchestration/direct-charges' \
--header 'X-Trace-Id: {{UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{UNIQUE_IDEMPOTENCY_KEY}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{API_ACCESS_TOKEN}}' \
--data-raw '{
"amount":"1234.56",
"currency":"NGN",
"reference":"{{UNIQUE_TRANSACTION_REFERENCE}}",
"customer":{
"address":{
"city":"Gotham",
"country":"US",
"line1":"221B Baker Street",
"line2":"",
"postal_code":"94105",
"state":"Colorado"
},
"email":"[email protected]",
"meta":{},
"name":{
"first":"King",
"middle":"Leo",
"last":"James"
},
"phone":{
"country_code":"234",
"number":"9010001111"
}
},
"payment_method":{
"type":"card",
"card":{
"encrypted_card_number":"{{$encrypted_card_number}}",
"encrypted_expiry_month":"{{$encrypted_expiry_month}}",
"encrypted_expiry_year":"{{$encrypted_expiry_year}}",
"encrypted_cvv":"{{$encrypted_cvv}}",
"nonce":"{{$randomly_generated_nonce}}",
"cof":{
"enabled":true,
"agreement_id": "Agreement00w02W1"
}
}
}
}'
Verifying Transactions
Before you provide value to the customer, confirm the transaction's final status and amount. You can verify the transaction information either using webhooks or by retrieving the charge details:
- Webhooks: It is important to have webhooks enabled on your Flutterwave dashboard. If you have webhooks enabled, we'll call your webhook URL with the payment details when the transaction is completed or fails. Below is a sample webhook payload:
{
"webhook_id":"wbk_yXvsB4LzWSwhUCpAPCBR",
"timestamp":1739456704200,
"type":"charge.completed",
"data":{
"id":"chg_zam88NgLb7",
"amount":2000,
"currency":"NGN",
"customer":{
"id":"cus_dc0FUyBpd0",
"address":{
"city":"Gotham",
"country":"US",
"line1":"221B Baker Street",
"line2":"",
"postal_code":"94105",
"state":"Colorado"
},
"email":"[email protected]",
"name":{
"first":"King",
"middle":"Leo",
"last":"James"
},
"phone":{
"country_code":"1",
"number":"6313958745"
},
"meta":{},
"created_datetime":"2024-12-25T20:16:38.246Z"
},
"description":null,
"meta":{},
"payment_method":{
"type":"card",
"card":{
"expiry_month":9,
"expiry_year":32,
"first6":"553188",
"last4":"2950",
"network":"MASTERCARD",
"billing_address":null,
"cof":null,
"card_holder_name":null
},
"id":"pmd_ujxuBcf4cJ",
"customer_id":null,
"meta":{},
"device_fingerprint":null,
"client_ip":null,
"created_datetime":"2025-02-05T14:06:10.344Z"
},
"redirect_url":null,
"reference":"ex61m23ja3feykheoidho8ilrri",
"status":"succeeded",
"processor_response":{
"type":"approved",
"code":"00"
},
"created_datetime":"2025-02-13T14:24:43.133Z"
}
}
- Retrieve the Charge: You can manually check the status of a charge by calling the retrieve a charge endpoint using the charge ID, which is returned in the
data.id
field of the response after successfully initiating a charge.
curl --request GET \
--url 'https://api.flutterwave.cloud/developersandbox/charges/id' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
You'll get a response similar to this:
{
"status": "success",
"message": "Charge updated",
"data": {
"id": "chg_VoUhmFMhmF",
"amount": 1500.25,
"currency": "NGN",
"customer": "cus_EFE4TQhBSf",
"meta": {},
"next_action": {
"type": "requires_pin"
},
"payment_method_details": {
"type": "card",
"card": {
"expiry_month": 8,
"expiry_year": 2024,
"first6": "123412",
"last4": "4444",
"network": "mastercard"
},
"id": "pmd_NkWibrRJIy",
"customer": "cus_EFE4TQhBSf",
"meta": {}
},
"redirect_url": "www.flutterwave.com",
"reference": "XYZ123",
"status": "succeeded",
"issuer_response": {
"type": "approved",
"code": "00"
}
}
}
Testing your Integration
Flutterwave provides scenario keys to simulate various outcomes during testing, such as successful and failed payments. The scenario key follows this form: scenario:<value>&issuer:<value>
. The scenario tells us which auth model you're testing, while the issuer indicates the processor message you want to mock.
Refer to the testing helpers for a complete list of scenario keys available for card payments.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/charges' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Scenario-Key: scenario:auth_3ds&issuer:approved' \
--data '{
"reference": "cedfa85a-a803-4a06-a586-0f81fb9b9f22",
"currency": "USD",
"customer_id": "cus_IpH7CKCUtD",
"payment_method_id": "pmd_wlVhaYmkl2",
"redirect_url":"https://google.com",
"amount": 250,
"meta":{
"person_name": "King James",
"role": "Developer"
}
}
'
Next steps
Congrats! You have successfully integrated the card payments, but it doesn't end there; there is more:
- Learn about settlements of successful payments into your Flutterwave balance.
- For cases where refunds are necessary, see the refunds guide for more information on how to process transaction refunds.
Updated about 6 hours ago