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

# Accepting payments

> Collect payments from your customers with card, OPay and USSD.

This guide covers the three ways to **collect a payment from a customer** with
the Hyparrow API: charging a **card**, redirecting to **OPay**, and generating a
**USSD** dial code. All of them settle into your Hyparrow account; pick whichever
suits your checkout.

All endpoints below live under the base path `/api/v1` and are authenticated with
your API key credentials:

```
X-API-Key: <your-api-key>
X-API-Secret: <your-api-secret>
```

<Note>
  Base URL is `https://api.hyparrow.cloud/api/v1` in production. To build and
  test without moving real money, point at the
  [Sandbox](/sandbox) — `https://sandbox.hyparrow.cloud/api/v1` — using a
  `pk_test_` key. See [Card test values](#testing-in-the-sandbox) below.
</Note>

## Card payments

Charging a card is a **stateful flow**. You start a purchase; depending on the
card, it may complete immediately or require the cardholder to confirm a
one-time password (OTP). You then submit the OTP to finish the charge.

```
purchase  →  (OTP required?)  →  authenticate-otp  →  success
                   │
                   └── no OTP ──────────────────────→ success
```

The flow is driven by the **`responseCode`** on each response:

| `responseCode` | Meaning                            | What to do next                               |
| -------------- | ---------------------------------- | --------------------------------------------- |
| `00`           | Approved                           | Payment complete — fulfil the order           |
| `T0`           | OTP required                       | Collect the OTP, then call `authenticate-otp` |
| `S0`           | Additional authentication required | Collect the OTP, then call `authenticate-otp` |
| anything else  | Declined / error                   | Show the cardholder the failure and stop      |

<Warning>
  **Amounts for card payments are sent in naira** as a string (for example
  `"1000"` for ₦1,000). Hyparrow converts to the minor unit (kobo) on your
  behalf before reaching the card network. Card details are encrypted server-side
  before they ever leave Hyparrow — you send the raw PAN/PIN/expiry/CVV over TLS
  and never store them yourself.
</Warning>

### Charge a card end-to-end

<Steps>
  <Step title="Initiate the purchase">
    Call `POST /card-payments/purchase` with the customer, amount and card
    details. Generate a unique `transactionRef` per attempt.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.hyparrow.cloud/api/v1/card-payments/purchase \
        -H "X-API-Key: <your-api-key>" \
        -H "X-API-Secret: <your-api-secret>" \
        -H "Content-Type: application/json" \
        -d '{
          "customerId": "9d8e7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
          "amount": "5000",
          "currency": "NGN",
          "transactionRef": "ord_5f3a1c9b2e",
          "pan": "5060990580000217499",
          "pin": "1234",
          "expiryDate": "2504",
          "cvv2": "123"
        }'
      ```

      ```json Body theme={null}
      {
        "customerId": "9d8e7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f",
        "amount": "5000",
        "currency": "NGN",
        "transactionRef": "ord_5f3a1c9b2e",
        "pan": "5060990580000217499",
        "pin": "1234",
        "expiryDate": "2504",
        "cvv2": "123"
      }
      ```
    </CodeGroup>

    `expiryDate` is `YYMM` (so `2504` is April 2025).

    A card that needs an OTP returns `responseCode: "T0"` and the identifiers you
    need for the next step (`paymentId`, `transactionId`):

    ```json Response — OTP required theme={null}
    {
      "success": true,
      "message": "Card purchase initiated",
      "data": {
        "responseCode": "T0",
        "message": "Kindly enter the OTP sent to your phone",
        "paymentId": "204512889031",
        "transactionId": "ord_5f3a1c9b2e"
      }
    }
    ```

    If the card clears without an OTP, you instead get `responseCode: "00"` and
    the payment is already complete — skip straight to checking status.
  </Step>

  <Step title="Authenticate the OTP">
    Collect the OTP from the cardholder and submit it with
    `POST /card-payments/authenticate-otp`, echoing the `paymentId` and
    `transactionId` from the previous response.

    ```bash cURL theme={null}
    curl https://api.hyparrow.cloud/api/v1/card-payments/authenticate-otp \
      -H "X-API-Key: <your-api-key>" \
      -H "X-API-Secret: <your-api-secret>" \
      -H "Content-Type: application/json" \
      -d '{
        "paymentId": "204512889031",
        "otp": "123456",
        "transactionId": "ord_5f3a1c9b2e"
      }'
    ```

    A `responseCode` of `00` means the charge succeeded:

    ```json Response — success theme={null}
    {
      "success": true,
      "message": "OTP authenticated successfully",
      "data": {
        "responseCode": "00",
        "message": "Approved by Financial Institution",
        "paymentId": "204512889031"
      }
    }
    ```

    If the cardholder didn't receive the OTP, request a new one with
    `POST /card-payments/resend-otp`:

    ```bash Resend OTP theme={null}
    curl https://api.hyparrow.cloud/api/v1/card-payments/resend-otp \
      -H "X-API-Key: <your-api-key>" \
      -H "X-API-Secret: <your-api-secret>" \
      -H "Content-Type: application/json" \
      -d '{
        "paymentId": "204512889031",
        "transactionId": "ord_5f3a1c9b2e"
      }'
    ```
  </Step>

  <Step title="Confirm the status">
    Verify the outcome any time with `GET /card-payments/status`, passing the
    `transactionRef` and `amount` (in naira) as query parameters.

    ```bash cURL theme={null}
    curl "https://api.hyparrow.cloud/api/v1/card-payments/status?transactionRef=ord_5f3a1c9b2e&amount=5000" \
      -H "X-API-Key: <your-api-key>" \
      -H "X-API-Secret: <your-api-secret>"
    ```

    ```json Response theme={null}
    {
      "success": true,
      "message": "Transaction status retrieved",
      "data": {
        "ResponseCode": "00",
        "ResponseDescription": "Approved by Financial Institution",
        "Amount": "500000"
      }
    }
    ```

    To list all card transactions for the authenticated client, use
    `GET /card-payments/transactions`.
  </Step>
</Steps>

### Card edge cases

<AccordionGroup>
  <Accordion title="OTP required">
    The first `purchase` response carries `responseCode: "T0"` (or `S0`). The
    charge is **not yet complete** — you must collect the OTP and call
    `authenticate-otp`. Until then the transaction is `processing`.
  </Accordion>

  <Accordion title="Declined card">
    Any `responseCode` other than `00`, `T0` or `S0` is a decline. The response
    surfaces the network's `message`, and an `errors` array yields
    `422 Unprocessable Entity`. Show the cardholder the reason and ask them to
    try another card.
  </Accordion>

  <Accordion title="Pending / unknown">
    If you lose the response or aren't sure of the outcome, poll
    `GET /card-payments/status`. It returns the authoritative state from the card
    network and updates the stored transaction. Treat anything that isn't `00` as
    not-yet-paid.
  </Accordion>
</AccordionGroup>

## OPay

OPay is a **redirect** flow: you initialize the payment, send the customer to the
returned URL to pay, then confirm the result.

<Steps>
  <Step title="Initialize the payment">
    `POST /payments/opay/initialize` with the `amount` (in kobo, as an integer).
    A `transactionReference` is generated for you if you omit it.

    ```bash cURL theme={null}
    curl https://api.hyparrow.cloud/api/v1/payments/opay/initialize \
      -H "X-API-Key: <your-api-key>" \
      -H "X-API-Secret: <your-api-secret>" \
      -H "Content-Type: application/json" \
      -d '{
        "amount": 500000,
        "currency": "NGN",
        "transactionReference": "opay_ord_5f3a1c9b2e"
      }'
    ```

    ```json Response theme={null}
    {
      "success": true,
      "message": "OPay payment initialized. Redirect the customer to the provided URL.",
      "redirectUrl": "https://checkout.example/opay/204512889031",
      "transactionReference": "opay_ord_5f3a1c9b2e",
      "responseCode": "09"
    }
    ```

    `responseCode` `09` is the normal "redirect required" result. Send the
    customer to `redirectUrl` to complete payment.
  </Step>

  <Step title="Confirm the result">
    After the customer returns, verify the payment with
    `POST /payments/opay/status`, passing the `reference`.

    ```bash cURL theme={null}
    curl https://api.hyparrow.cloud/api/v1/payments/opay/status \
      -H "X-API-Key: <your-api-key>" \
      -H "X-API-Secret: <your-api-secret>" \
      -H "Content-Type: application/json" \
      -d '{ "reference": "opay_ord_5f3a1c9b2e" }'
    ```

    ```json Response theme={null}
    {
      "success": true,
      "transactionReference": "opay_ord_5f3a1c9b2e",
      "responseCode": "00",
      "paid": true
    }
    ```

    `paid: true` (`responseCode: "00"`) means the payment settled. Until then,
    keep the order pending.
  </Step>
</Steps>

## USSD

USSD lets a customer pay by dialling a short code on their phone. You fetch the
list of supported banks, then generate a dial code for the chosen bank.

<Steps>
  <Step title="List supported banks">
    `GET /payments/ussd/issuers` returns the banks that support USSD payments,
    each with the `bankCode` you'll pass when generating the code.

    ```bash cURL theme={null}
    curl https://api.hyparrow.cloud/api/v1/payments/ussd/issuers \
      -H "X-API-Key: <your-api-key>" \
      -H "X-API-Secret: <your-api-secret>"
    ```

    ```json Response theme={null}
    {
      "success": true,
      "data": [
        { "bankName": "Example Bank", "bankCode": "058" },
        { "bankName": "Sample Bank", "bankCode": "011" }
      ]
    }
    ```
  </Step>

  <Step title="Generate the dial code">
    `POST /payments/ussd/generate` with the `amount` (in naira, as a string) and
    the chosen `bankCode`. A `merchantTransactionReference` is generated if you
    omit it.

    ```bash cURL theme={null}
    curl https://api.hyparrow.cloud/api/v1/payments/ussd/generate \
      -H "X-API-Key: <your-api-key>" \
      -H "X-API-Secret: <your-api-secret>" \
      -H "Content-Type: application/json" \
      -d '{
        "amount": "5000",
        "bankCode": "058"
      }'
    ```

    ```json Response theme={null}
    {
      "success": true,
      "message": "USSD code generated. Instruct the customer to dial the provided code.",
      "merchantTransactionReference": "ussd_a1b2c3d4e5f6",
      "data": {
        "transactionReference": "ussd_a1b2c3d4e5f6",
        "ussdCode": "*966*000*5000#"
      }
    }
    ```

    Show the customer the returned `ussdCode` and ask them to dial it to
    complete the payment.
  </Step>
</Steps>

## Testing in the sandbox

In the [Sandbox](/sandbox) every provider is simulated, so you can run all three
flows end-to-end without real money. Use a `pk_test_` key against
`https://sandbox.hyparrow.cloud/api/v1`.

<Note>
  **Card test values**

  * **Success card:** `5060990580000217499` (always approved)
  * **Decline card:** `0000000000000000000` (always declined)
  * **OTP:** `123456` succeeds; any other OTP is rejected
  * **Magic amounts** (any other card): an amount ending in `.01` forces
    `failed`, `.99` forces `pending`, anything else settles as success
</Note>
