> ## Documentation Index
> Fetch the complete documentation index at: https://flutterwaveinc.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Card Tokenization

> Learn how to use your customer's card to process recurring payments.

Tokenization allows you to implement recurring payments by saving a token representing the customer's card and using that on subsequent charges.

<Warning>
  You should **never** save a customer's card details; the details are
  represented by a token, which is what you save.
</Warning>

Unlike [payment plans](/other-features/recurring-payments/payment-plans), you are responsible for managing the customer's subscription yourself—cancellation, reactivation, and charging them are all your responsibility.

To implement a recurring charge, first, you'll need to charge the customer normally, for instance, via [direct charge](/payment-api/card) or [Flutterwave Inline](/payments-embed/inline). Make sure to allow payments only via card, as only cards can be tokenized.

<img src="https://mintcdn.com/flutterwaveinc/8LJ4mpYLmd_SNb9Z/images/tokenization.png?fit=max&auto=format&n=8LJ4mpYLmd_SNb9Z&q=85&s=eed29490d36b4dbb6b51a49529d0b455" alt="tokenization" width="5760" height="14022" data-path="images/tokenization.png" />

When the payment is made, you can [verify the payment](/transaction-verification) as usual. In the verification response, you'll find a `token` field in the `data.card` object. This is the token representing the card.

<Warning>
  **Token Expiry**

  Customer card tokens are valid for one year only. To avoid
  transaction errors caused by expired tokens, it is important to re-tokenize
  the card before the existing token expires.
</Warning>

```json theme={null}
{
  "status": "success",
  "message": "Transaction fetched successfully",
  "data": {
    "status": "successful",
    "payment_type": "card",
    //...
    "card": {
      "first_6digits": "455605",
      "last_4digits": "2643",
      "issuer": "MASTERCARD GUARANTY TRUST BANK Mastercard Naira Debit Card",
      "country": "NG",
      "type": "MASTERCARD",
+     "token": "flw-t1nf-93da56b24f8ee332304cd2eea40a1fc4-m03k",
      "expiry": "01/23"
    },
    "customer": {
      "email": "user@gmail.com",
      // ...
    }
  }
}
```

You'll need to store this token (along with the customer's email you provided). To charge the card again in the future, pass the token and the email to charge as a payload to the [token endpoint](/api-reference/tokenized-charges/create-a-tokenized-charge).

<Warning>
  **Tokens are Tied to Email Addresses**

  The token is tied to the email address you specified on the first charge. This means that if a customer
  changes their email address on your app, you'll need to update the [token
  details](/api-reference/tokenized-charges/update-a-card-token) for future
  charges to work.
</Warning>

<CodeGroup>
  ```javascript Node.js theme={null}
  // 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 details = {
  	token: user.card_token,
  	currency: 'NGN',
  	country: 'NG',
  	amount: 24000,
  	email: user.email,
  	tx_ref: this.generateTransactionReference(),
  	narration: 'Payment for monthly magazine subscription',
  };
  await flw.Tokenized.charge(details);
  ```

  ```php PHP theme={null}
  // Install with: composer require flutterwavedev/flutterwave-v3

  $flw = new \Flutterwave\Rave(getenv('FLW_SECRET_KEY')); // Set `PUBLIC_KEY` as an environment variable
  $tokenizedChargeService = new \Flutterwave\TokenizedCharge();
  $details = [
      "token" => $user->card_token,
      "currency" => "NGN",
      "country" => "NG",
      "amount" => 24000,
      "email" => $user->email,
      "tx_ref" => $this->generateTransactionReference(),
      "narration" => "Payment for monthly magazine subscription",
  ];
  $response = $tokenizedChargeService->tokenCharge($details);
  ```

  ```ruby Ruby theme={null}
  # Install with: gem install flutterwave_sdk

  require 'flutterwave_sdk'

  flw = Flutterwave.new(ENV["FLW_PUBLIC_KEY"], ENV["FLW_SECRET_KEY"], ENV["FLW_ENCRYPTION_KEY"])
  charge = TokenizedCharge.new(flw)
  details = {
      token: user.card_token,
      currency: "NGN",
      country: "NG",
      amount: 24000,
      email: user.email,
      tx_ref: generate_transaction_reference,
      narration: "Payment for monthly magazine subscription",
  }
  response = charge.tokenized_charge details
  print response
  ```

  ```json cURL theme={null}
  curl --request POST 'https://api.flutterwave.com/v3/tokenized-charges' \
    --header 'Authorization: Bearer YOUR_SECRET_KEY' \
    --header 'Content-Type: application/json' \
    --data-raw '{
       "token": "flw-t1nf-93da56b24f8ee332304cd2eea40a1fc4-m03k",
       "currency": "NGN",
       "country": "NG",
       "amount": 24000,
       "email": "user@gmail.com",
       "tx_ref": "8676oi9764823kefth",
       "narration": "Payment for monthly magazine subscription",
  }'
  ```
</CodeGroup>

Check out our [endpoint](/api-reference/tokenized-charges/create-a-tokenized-charge) documentation for details and more options.

You can store the information in the `data.card` object and use it to provide a better experience for your customers. For example, you could send a reminder when their card is about to expire, or display `"MasterCard ending in 2643"` in their billing settings, so they can confirm which card will be charged.
You can also charge multiple tokens in one go. See the [reference docs](/api-reference/tokenized-charges/create-a-tokenized-charge) for more details.

## Tokenizing 3DS Cards

When a merchant tries to charge a customer with a card token, the charge is processed as `NOAUTH` without any need for authentication.

If you choose to enforce 3DS i.e. allow your users to authenticate with a challenge, you should add `do_3ds` and `redirect_url` to the card token payload. By setting `do_3ds` to `true`, Flutterwave will charge the tokenized charge via `VBVSECURECODE (3DS auth)`.

We will also redirect the user to the `data.meta.authorization.redirect` to authorize the token charge.

<CodeGroup>
  ```json Request Payload theme={null}
  {
      "token": "flw-t1nf-1dd5c21361bb85c64deb7ff57ec891b2-m03k",
      "currency": "NGN",
      "country": "NG",
      "amount": 12000,
      "email": "developers@flutterwavego.com",
      "full_name": "Flutterwave Developers",
      "ip": "pstmn",
      "narration": "test_token_charge",
      "tx_ref": "testToken111",
  +    "do_3ds": true,
  +    "redirect_url": "https://flutterwave.com/ng",
      "meta": {
          "sub_meta_1": "meta_child_1",
          "sub_meta_2": "meta_child_2"
      }
  }
  ```
</CodeGroup>

You'll get a response like this:

<CodeGroup>
  ```json 200 OK theme={null}
  {
  	"status": "success",
  	"message": "Charge initiated",
  	"data": {
  		"id": 2534420,
  		"tx_ref": "testToken111",
  		"flw_ref": "FLW-MOCK-0a6b80654370ff4b0987851ce0e611c0",
  		"redirect_url": "https://flutterwave.com/ng",
  		"device_fingerprint": "N/A",
  		"amount": 12000,
  		"charged_amount": 12000,
  		"app_fee": 168,
  		"merchant_fee": 0,
  		"processor_response": "Approved",
  		"auth_model": "VBVSECURECODE",
  		"currency": "NGN",
  		"ip": "pstmn",
  		"narration": "test_token_charge",
  		"status": "pending",
  		"payment_type": "card",
  		"created_at": "2021-10-07T17:43:46.000Z",
  		"account_id": 20937,
  		"customer": {
  			"id": 1409822,
  			"phone_number": null,
  			"name": "Flutterwave Developers",
  			"email": "developers@flutterwavego.com",
  			"created_at": "2021-10-07T17:40:57.000Z"
  		},
  		"card": {
  			"first_6digits": "553188",
  			"last_4digits": "2950",
  			"issuer": "MASTERCARD  CREDIT",
  			"country": "NG",
  			"type": "MASTERCARD",
  			"expiry": "09/32"
  		},
  		"meta": {
  			"authorization": {
  				"mode": "redirect",
  				"redirect": "https://ravesandboxapi.flutterwave.com/mockvbvpage?ref=FLW-MOCK-0a6b80654370ff4b0987851ce0e611c0&code=00&message=Approved. Successful&receiptno=RN1633628627056"
  			}
  		}
  	}
  }
  ```

  ```json 400 Bad Request theme={null}
  {
  	"status": "error",
  	"message": "Wrong token or email passed",
  	"data": null
  }
  ```
</CodeGroup>

## Bulk Tokenization

Flutterwave supports tokenizing cards in bulk, using this method, you can add multiple card tokens and save your customers details so they can complete recurring payments with ease.

To add multiple card tokens, you need to send the following information to the [bulk tokenize charge endpoint](/api-reference/tokenized-charges/get-bulk-tokenized-transactions):

1. `Bulk_data`: This array contains the card token and customer details to be tokenized.
2. `Retry_strategy`: This object informs us on how to treat failed tokenization attempts. You can specify the time interval between each attempt and the number of attempts to make.

<CodeGroup>
  ```json Request Payload theme={null}
  {
  	"title": "Sample bulk tokenization payment",
  	"retry_strategy": {
  		"retry_interval": 120,
  		"retry_amount_variable": 60,
  		"retry_attempt_variable": 2
  	},
  	"bulk_data": [
  		{
  			"currency": "NGN",
  			"token": "flw-t1nf-f9b3bf384cd30d6fca42b6df9d27bd2f-m03k",
  			"country": "NG",
  			"amount": 3500,
  			"email": "user@example.com",
  			"first_name": "Example",
  			"last_name": "User",
  			"ip": "123.876.0997.9",
  			"tx_ref": "akhlm-pstmn-blkchrg-xx6"
  		},
  		{
  			"currency": "NGN",
  			"token": "flw-t1nf-f9b3bf384cd30d6fca42b6df9d27bd2f-m03k",
  			"country": "NG",
  			"amount": 3000,
  			"email": "test@example.com",
  			"first_name": "Test",
  			"last_name": "User",
  			"ip": "123.876.0997.9",
  			"tx_ref": "akhlm-pstmn-blkchrge-xx7"
  		}
  	]
  }
  ```
</CodeGroup>

You'll get a response like this:

<CodeGroup>
  ```json 200 OK theme={null}
  {
  	"status": "success",
  	"message": "Bulk charge successful",
  	"data": {
  		"id": 130,
  		"created_at": "2020-01-19T21:43:39.000Z",
  		"approver": "N/A"
  	}
  }
  ```
</CodeGroup>

In our commitment to uphold the service uptime, we enforce specific limitations on your retry attempts. These restrictions come into effect based on the error message received during the card charge. The ensuing rules are then applied accordingly:

| S/N | Error message      | Restriction                                                                                                                                        |
| --- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1.  | Insufficient funds | 3 sequential attempts and the subsequent tokenization attempts on the card would be declined for a day. The cycle resets at the end of each day.   |
| 2.  | Do not Honour      | 2 sequential attempts and the subsequent tokenization attempts on the card would be declined for a week. The cycle resets at the end of each week. |

After initiating the bulk tokenization, you must [confirm the status](/api-reference/tokenized-charges/get-status-of-bulk-tokenized-charge) of the bulk payment before giving value.
