Charging a card
Quickly charge a customer's card
We assume that you have completed onboarding. Retrieve your API credentials before proceeding with the guide.
Follow these steps to charge a customer's card on your account.
Generate Access Token
Send your Client-Id and Client-Secret to our authentication endpoint to generate your access token. Each token is valid for 10 minutes, after which you should generate a new token.
curl -X POST 'https://idp.flutterwave.com/realms/flutterwave/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id={{CLIENT_ID}}' \
--data-urlencode 'client_secret={{CLIENT_SECRET}}' \
--data-urlencode 'grant_type=client_credentials'
{
"access_token": "SAMPLE_TOKEN",
"expires_in": 600,
"refresh_expires_in": 0,
"token_type": "Bearer",
"not-before-policy": 0,
"scope": "profile email"
}
Create a Customer object
Create an entity to store the customer's details; this object links customer information to payment data.
curl --request POST \
--url 'https://developersandbox-api.flutterwave.com/customers' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--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]"
}'
{
"status": "success",
"message": "Customer created",
"data": {
"id": "cus_J0PvwvJB2n",
"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-03T13:54:21.546559974Z"
}
}
Collect and encrypt the card information
Encrypt the customer's card number, expiry date and CVV using AES-256. You'll need to provide your encryption nonce to us to validate your encryption.
export async function encryptAES(data: string, token: string, nonce: string): Promise<string> {
if (nonce.length !== 12) {
throw new Error("Nonce must be exactly 12 characters long");
}
const cryptoSubtle = globalThis.crypto?.subtle || require("crypto").webcrypto?.subtle;
if (!cryptoSubtle) {
throw new Error("Crypto API is not available in this environment.");
}
const decodedKeyBytes = Uint8Array.from(atob(token), c => c.charCodeAt(0));
const key = await cryptoSubtle.importKey(
"raw",
decodedKeyBytes,
{ name: "AES-GCM" },
false,
["encrypt"]
);
const iv = new TextEncoder().encode(nonce);
const encryptedData = await cryptoSubtle.encrypt(
{
name: "AES-GCM",
iv: iv,
},
key,
new TextEncoder().encode(data)
);
return btoa(String.fromCharCode(...new Uint8Array(encryptedData)));
}
Create a card object
Use the encrypted card information to create a card object using the payment method endpoint.
curl --request POST \
--url 'https://developersandbox-api.flutterwave.com/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}}"
}
}'
{
"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"
}
}
Store the id of the card object as it would be useful for recurrent charges.
Charge the card
Send the card ID, customer ID and transaction information like amount, currency and reference to the charge endpoint.
curl --request POST \
--url 'https://developersandbox-api.flutterwave.com/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' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"reference": "cedfa85a-a803-4a06-a586-0f81fb9b9f22",
"currency": "NGN",
"customer_id": "cus_IpH7CKCUtD",
"payment_method_id": "pmd_wlVhaYmkl2",
"redirect_url":"https://google.com",
"amount": 2500,
"meta":{
"person_name": "King James",
"role": "Developer"
}
}
'
{
"status": "pending",
"message": "Charge requires authorization",
"data": {
"id": "chg_VoUhmFMhmF",
"status": "pending",
"next_action": {
"type": "authorize",
"authorization": {
"type": "pin"
}
},
...
}
}
Update the charge request with the customer's pin. Use the mock value 12345 to complete your charge, Encrypt the pin before passing it to your request.
curl --location --request PUT 'https://developersandbox-api.flutterwave.com/charges/{{CHARGE_ID}}' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Scenario-Key: scenario:auth_3ds&issuer:approved' \
--header 'Content-type: application/json' \
--data '{
"authorization": {
"type": "pin",
"pin": {
"nonce": "{{YOUR_PIN_NONCE}}",
"encrypted_pin": "{{ENCRYPTED_PIN}}"
}
}
}'
{
"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",
...
}
}
Redirect the customer to their bank page(response.data.next_action.redirect_url.url) to authorize the payment.
Once the payment is completed, we send you a charge.completed event via webhooks.
{
"webhook_id":"wbk_yXvsB4LzWSwhUCpAPCBR",
"timestamp":1739456704200,
"type":"charge.completed",
"data":{
"id":"chg_VoUhmFMhmF",
"amount":2500,
"currency":"NGN",
"customer":{
"id":"cus_J0PvwvJB2n",
"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":8,
"expiry_year":32,
"first6":"123412",
"last4":"2222",
"network":"MASTERCARD",
"billing_address":null,
"cof":null,
"card_holder_name":null
},
"id":"pmd_wlVhaYmkl2",
"customer_id":null,
"meta":{},
"device_fingerprint":null,
"client_ip":null,
"created_datetime":"2025-02-05T14:06:10.344Z"
},
"redirect_url":null,
"reference":"cedfa85a-a803-4a06-a586-0f81fb9b9f22",
"status":"succeeded",
"processor_response":{
"type":"approved",
"code":"00"
},
"created_datetime":"2025-02-13T14:24:43.133Z"
}
}
Updated about 5 hours ago
