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

# Card charge

> Collect card payments and handle 3DS, OTP, PIN, and address verification steps.

Card charges let you collect card details server-side and process the payment through the API.
Because card authentication varies by issuer and market, a single charge can require one or more
follow-up steps before it settles.

<Warning>
  Handling raw card numbers puts your systems in PCI DSS scope. Confirm your compliance obligations
  before you collect card details server-side, and never log or persist a full PAN or CVV.
</Warning>

## Happy path

<Steps>
  <Step title="Initiate the charge">
    Send card details, amount, currency, and customer information.
  </Step>

  <Step title="Handle any required step">
    The transaction may ask for a PIN, OTP, address, or a 3DS redirect.
  </Step>

  <Step title="Wait for the final webhook">
    A `successful` or `failed` `transaction_updated` event is the real outcome.
  </Step>

  <Step title="Fulfil">
    Only once the status is `successful`.
  </Step>
</Steps>

## 1. Initiate the charge

Send a request to [Card Charge](/api-reference/endpoint/card-charge).

| Field                  | Type   | Required | Description                                        |
| :--------------------- | :----- | :------: | :------------------------------------------------- |
| `amount`               | Number |    Yes   | Amount to charge, e.g. `1500`                      |
| `currency`             | String |    Yes   | 3-letter currency code, e.g. `KES`, `NGN`, `USD`   |
| `email`                | String |    Yes   | Customer email                                     |
| `firstName`            | String |    Yes   | Card holder first name                             |
| `lastName`             | String |    Yes   | Card holder last name                              |
| `externalReference`    | String |    Yes   | Your own unique ID for tracking                    |
| `cardDetails.number`   | String |    Yes   | Card PAN                                           |
| `cardDetails.cvv`      | String |    Yes   | Card CVV                                           |
| `cardDetails.expMonth` | String |    Yes   | Expiry month, `MM`                                 |
| `cardDetails.expYear`  | String |    Yes   | Expiry year, `YY`                                  |
| `address`              | Object |     —    | Billing address. Required for address verification |
| `device3DSInfo`        | Object |     —    | Device data used for 3DS risk assessment           |
| `ipAddress`            | String |     —    | Customer IP address                                |
| `redirectUrl`          | String |     —    | Return URL after a redirect, provider dependent    |

<CodeGroup>
  ```json Required fields theme={null}
  {
    "amount": 100,
    "ipAddress": "102.218.103.10",
    "currency": "NGN",
    "email": "w.west@starlabs.com",
    "firstName": "Wally",
    "lastName": "West",
    "externalReference": "525252525",
    "cardDetails": {
      "cvv": "052",
      "number": "4242424242424242",
      "expMonth": "09",
      "expYear": "28"
    },
    "address": {
      "zip": "64101",
      "country": "US",
      "street1": "52 Flash Boulevard",
      "city": "Central City",
      "state": "MO"
    }
  }
  ```

  ```json All fields theme={null}
  {
    "amount": 10,
    "ipAddress": "159.26.101.81",
    "currency": "USD",
    "email": "b.wayne@waynetech.com",
    "firstName": "Bruce",
    "lastName": "Wayne",
    "externalReference": "123456789",
    "cardDetails": {
      "cvv": "000",
      "number": "4242424242424242",
      "expMonth": "09",
      "expYear": "28"
    },
    "address": {
      "zip": "10025",
      "country": "US",
      "street1": "1007 Mountain Drive",
      "city": "Gotham City",
      "state": "NJ"
    },
    "device3DSInfo": {
      "colorDepth": 30,
      "screenHeight": 1050,
      "screenWidth": 1680,
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
      "timeZone": 180,
      "deviceId": "29b435613857666dhgfdhg3b0a778471fbe9dc72db044886c7cc1175e4af632d4eb"
    }
  }
  ```
</CodeGroup>

```json Response theme={null}
{
  "success": true,
  "status": "PENDING",
  "message": "Request queued"
}
```

<Note>
  Card charges are processed asynchronously. Track the outcome with the `transactionId` via
  [Get Transaction](/api-reference/endpoint/get-transaction), or by listening for webhooks.
</Note>

<Tip>
  Supplying `device3DSInfo` and `ipAddress` gives the issuer more signal for risk assessment, which
  can reduce how often a customer is challenged with 3DS.
</Tip>

## 2. Identify the required step

Both the transaction status response and the webhook carry a `stepRequired` field when the charge
is waiting on the customer.

| `stepRequired`         | What it means                                         | What you do                                                                                 |
| :--------------------- | :---------------------------------------------------- | :------------------------------------------------------------------------------------------ |
| `pin`                  | Card PIN required                                     | Prompt for the PIN, submit via [Authorise Charge](/api-reference/endpoint/authorise-charge) |
| `otp`                  | Code sent to the customer's phone or email            | Prompt for the code, submit via Authorise Charge                                            |
| `address_verification` | Billing address must be verified against card records | Collect the address, submit via Authorise Charge                                            |
| `redirect`             | 3-D Secure authentication                             | Send the customer to `redirectUrl`                                                          |

<CodeGroup>
  ```json PIN required theme={null}
  {
    "success": true,
    "data": {
      "transactionId": "TMNT-JLA-CRISIS-2026",
      "amount": 100,
      "currency": "NGN",
      "createdAt": 1772739561749,
      "chargeStatus": "pending",
      "status": "PENDING",
      "type": "deposit",
      "method": "card",
      "fullTimestamp": "2026-03-05T19:39:21+00:00",
      "externalReference": "EARTH-PRIME-01",
      "stepRequired": "pin",
      "metadata": {
        "senderAccountName": "Diana Prince",
        "cardFirstSix": "194110",
        "cardLastFour": "1984"
      }
    }
  }
  ```

  ```json OTP required theme={null}
  {
    "success": true,
    "data": {
      "transactionId": "TMNT-JLA-CRISIS-2026",
      "amount": 100,
      "currency": "NGN",
      "chargeStatus": "pending",
      "status": "PENDING",
      "type": "deposit",
      "method": "card",
      "externalReference": "EARTH-PRIME-01",
      "stepRequired": "otp",
      "metadata": {
        "senderAccountName": "Diana Prince",
        "cardFirstSix": "194110",
        "cardLastFour": "1984"
      }
    }
  }
  ```

  ```json Address verification required theme={null}
  {
    "success": true,
    "data": {
      "transactionId": "TMNT-JLA-CRISIS-2026",
      "amount": 100,
      "currency": "NGN",
      "chargeStatus": "pending",
      "status": "PENDING",
      "type": "deposit",
      "method": "card",
      "externalReference": "EARTH-PRIME-01",
      "stepRequired": "address_verification",
      "metadata": {
        "senderAccountName": "Diana Prince",
        "cardFirstSix": "194110",
        "cardLastFour": "1984"
      }
    }
  }
  ```

  ```json Redirect (3DS) required theme={null}
  {
    "success": true,
    "data": {
      "transactionId": "ARKHAM-ASYLUM-777",
      "amount": 0.5,
      "currency": "USD",
      "chargeStatus": "pending",
      "status": "PENDING",
      "type": "deposit",
      "method": "card",
      "externalReference": "PUDDIN-01",
      "stepRequired": "redirect",
      "metadata": {
        "senderAccountName": "Harleen Quinzel",
        "cardFirstSix": "091192",
        "cardLastFour": "1992"
      },
      "redirectUrl": "https://redirect.com"
    }
  }
  ```
</CodeGroup>

## 3. Handle webhooks

The same `stepRequired` values arrive as `transaction_updated` events.

<CodeGroup>
  ```json PIN required theme={null}
  {
    "event": "transaction_updated",
    "data": {
      "transactionId": "luQybnMnvjgnWEF6ZFyJ",
      "status": "pending",
      "type": "deposit",
      "stepRequired": "pin",
      "externalReference": "1234567890"
    },
    "timestamp": "2026-03-05T19:39:30.624Z"
  }
  ```

  ```json OTP required theme={null}
  {
    "event": "transaction_updated",
    "data": {
      "transactionId": "luQybnMnvjgnWEF6ZFyJ",
      "status": "pending",
      "type": "deposit",
      "stepRequired": "otp",
      "externalReference": "1234567890"
    },
    "timestamp": "2026-03-05T19:39:30.624Z"
  }
  ```

  ```json Address verification required theme={null}
  {
    "event": "transaction_updated",
    "data": {
      "transactionId": "luQybnMnvjgnWEF6ZFyJ",
      "status": "pending",
      "type": "deposit",
      "stepRequired": "address_verification",
      "externalReference": "1234567890"
    },
    "timestamp": "2026-03-05T19:39:30.624Z"
  }
  ```

  ```json Redirect required theme={null}
  {
    "event": "transaction_updated",
    "data": {
      "transactionId": "RFJZcgeeTGilZgPp9zyn",
      "status": "pending",
      "type": "deposit",
      "stepRequired": "redirect",
      "externalReference": "12fd3456789x",
      "redirectUrl": "https://redirect.com"
    },
    "timestamp": "2026-03-05T19:42:56.538Z"
  }
  ```
</CodeGroup>

## 4. Authorise the charge

For `pin`, `otp`, and `address_verification`, collect the value from the customer and submit it to
[Authorise Charge](/api-reference/endpoint/authorise-charge).

| Field   | Type             | Required | Description                                                                |
| :------ | :--------------- | :------: | :------------------------------------------------------------------------- |
| `type`  | String           |    Yes   | `otp`, `pin`, or `address_verification`                                    |
| `value` | String or Object |    Yes   | A string for `otp` and `pin`; an address object for `address_verification` |

<CodeGroup>
  ```json PIN theme={null}
  {
    "type": "pin",
    "value": "1234"
  }
  ```

  ```json OTP theme={null}
  {
    "type": "otp",
    "value": "123456"
  }
  ```

  ```json Address verification theme={null}
  {
    "type": "address_verification",
    "value": {
      "street1": "123 Main St",
      "city": "Lagos",
      "state": "Lagos",
      "zip": "100001",
      "country": "NG"
    },
    "transactionsLocation": "transactions"
  }
  ```
</CodeGroup>

<CodeGroup>
  ```json Authorised theme={null}
  {
    "success": true,
    "message": "Charge Authorisation successful."
  }
  ```

  ```json Another step required theme={null}
  {
    "success": true,
    "message": "Verification successful. Next step: redirect",
    "stepData": {
      "stepRequired": "redirect",
      "redirectUrl": "https://3ds.bank.com/authorize?token=..."
    }
  }
  ```
</CodeGroup>

<Warning>
  Authorising one step can surface another. Always inspect `stepData` in the response — a
  successful PIN submission may still be followed by a 3DS redirect. Loop until no further step is
  returned, then wait for the final webhook.
</Warning>

## 5. Handle 3DS redirects

When `stepRequired` is `redirect`:

<Steps>
  <Step title="Extract the URL">
    Take `redirectUrl` from the webhook, the transaction status, or the `stepData` of an authorise
    response.
  </Step>

  <Step title="Send the customer">
    They land on their bank's secure authentication page.
  </Step>

  <Step title="Wait for the final webhook">
    After the customer completes or abandons authentication, the outcome arrives as
    `transaction_updated` with `successful` or `failed`.
  </Step>
</Steps>

<Note>
  Do not poll aggressively during a redirect flow. The final outcome is delivered by webhook once
  the bank responds.
</Note>
