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

# Testing

> Reproduce card approvals, declines, and provider errors on demand with deterministic sandbox test scenarios.

Rinne's sandbox lets you build and verify your integration end to end without moving real money. For card payments, the sandbox returns **deterministic acquirer outcomes** — you choose an approval, a decline with a specific reason code, or a provider error on every request — so you can reproduce each branch of your checkout reliably instead of waiting for the real acquirer to happen to return them.

## Sandbox environment

Point your requests at the sandbox host and authenticate with a sandbox API key:

<CodeGroup>
  ```bash Sandbox theme={null}
  https://api-sandbox.rinne.com.br/core
  ```

  ```bash Production theme={null}
  https://api.rinne.com.br/core
  ```
</CodeGroup>

```bash theme={null}
curl https://api-sandbox.rinne.com.br/core/v1/companies/me \
  -H "x-api-key: YOUR_API_KEY"
```

<Note>
  Card outcomes in the sandbox are **simulated deterministically** — the amount you send decides the result (see below). Production always routes to the real acquirer.
</Note>

## Card payment test scenarios

<Warning>
  **The amount decides the outcome — not the card number.** Rinne selects the acquirer result from the transaction **amount**; a card number only sets the **brand** and the **3DS behavior**. This keeps one consistent selector across raw cards and wallet tokens (Apple Pay / Google Pay), which have no integrator-chosen card number.
</Warning>

### Drive a scenario

<Steps>
  <Step title="Capture a test card with rinne-js">
    Enter a [test card](#test-cards) into the [Card Element](/rinne-js/card-element), which encrypts the number and CVC in the customer's browser. You never send a raw PAN — card data that reaches the API unencrypted is rejected with `400 VALIDATION_ERROR` before any processing.
  </Step>

  <Step title="Choose the amount that selects the outcome">
    Set the transaction `amount` (in cents) so its **last two digits** match the outcome you want from the [table below](#outcome-by-amount). For example, `1051` (R\$ 10.51) simulates an insufficient-funds decline.
  </Step>

  <Step title="Read the result">
    Card transactions resolve synchronously, so the create response already carries the final `status` and the `acquirer_response_code` you selected.
  </Step>
</Steps>

### Outcome by amount

The outcome is chosen by the **last two digits** of the amount (in cents). Detect a successful payment from `status`; for declines, `acquirer_response_code` carries the acquirer's reason code, matching what production would store.

| Amount ends in                         | Outcome        | Status     | Code  | Meaning                                                                                         |
| -------------------------------------- | -------------- | ---------- | ----- | ----------------------------------------------------------------------------------------------- |
| `00` *(and anything not listed below)* | Approved       | `APPROVED` | `—`   | Authorized                                                                                      |
| `51`                                   | Declined       | `REFUSED`  | `51`  | Insufficient funds                                                                              |
| `05`                                   | Declined       | `REFUSED`  | `05`  | Do not honor                                                                                    |
| `54`                                   | Declined       | `REFUSED`  | `54`  | Expired card                                                                                    |
| `14`                                   | Declined       | `REFUSED`  | `14`  | Invalid card                                                                                    |
| `96`                                   | Provider error | `FAILED`   | `500` | Provider error (HTTP status, not an issuer reason code) — `status_reason` is `ACQUIRER_TIMEOUT` |

<Warning>
  Amounts are always in **cents**, so the selector is the last two digits of the cents value: `114` is R\$ 1.**14**, which **declines** on `14` (invalid card). Prefer a `…00` amount for approvals so a small test value doesn't land on a decline suffix by accident.

  Worked examples: `1000` → `APPROVED`; `1051` → `REFUSED` `51`; `1014` → `REFUSED` `14`; `1096` → `FAILED`.
</Warning>

### Test cards

Any valid card number works. These are the recommended ones — the amount still decides approve vs. decline; the card only sets the brand and the 3DS behavior.

| Card number        | Brand      | 3DS behavior             |
| ------------------ | ---------- | ------------------------ |
| `5155901222280001` | Mastercard | No 3DS                   |
| `4012001037141112` | Visa       | No 3DS                   |
| `4242424242424242` | Visa       | 3DS — challenge required |
| `5555555555554444` | Mastercard | 3DS — challenge required |
| `4111110116638870` | Visa       | 3DS — frictionless pass  |
| `5555550130659057` | Mastercard | 3DS — frictionless pass  |
| `4111111738973695` | Visa       | 3DS — frictionless fail  |
| `5555550487847545` | Mastercard | 3DS — frictionless fail  |

<Note>
  The cards marked 3DS run the real 3D Secure flow (see [3D Secure authentication](/guides/three-d-secure-authentication)) — **challenge** cards present a verification step, **frictionless** cards authenticate silently — while the acquirer outcome still comes from the amount. For wallet payments (Apple Pay / Google Pay) there is no test card — pass the real wallet token and pick the outcome with the amount.
</Note>

<Note>
  An invalid or malformed card number is declined as `REFUSED` with `acquirer_response_code` `14` (invalid card), regardless of the amount. All the test cards above are valid.
</Note>

### Examples

The request is a standard card transaction — only the `amount` changes to select the outcome. This example uses the Mastercard test card `5155901222280001` (last 4 digits `0001`):

```bash cURL theme={null}
curl -X POST 'https://api-sandbox.rinne.com.br/core/v1/transactions' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "provider": "RINNE",
    "request_id": "test-card-approved-0001",
    "amount": 1000,
    "currency": "BRL",
    "capture_method": "ECOMMERCE",
    "payment_method": "CREDIT_CARD",
    "installments": 1,
    "card_data": {
      "number": "ev:encrypted:card_number",
      "cvv": "ev:encrypted:cvc",
      "expiry_month": "12",
      "expiry_year": "2028",
      "cardholder_name": "Maria Santos",
      "last_digits": "0001"
    }
  }'
```

Send the same request with a different `amount` to reach each branch:

<CodeGroup>
  ```json Approved (amount 1000) theme={null}
  {
    "id": "a974a0aa-0b9b-4408-8f52-b83b687943bd",
    "status": "APPROVED",
    "amount": 1000,
    "currency": "BRL",
    "nsu": "000123456789"
  }
  ```

  ```json Refused (amount 1051) theme={null}
  {
    "id": "b1966800-bb2a-40fa-b73f-57e8ce8f9d80",
    "status": "REFUSED",
    "acquirer_response_code": "51",
    "amount": 1051,
    "currency": "BRL"
  }
  ```

  ```json Failed (amount 1096) theme={null}
  {
    "id": "c91e591f-ea0b-4628-9901-0f3cfa5acce1",
    "status": "FAILED",
    "status_reason": "ACQUIRER_TIMEOUT",
    "acquirer_response_code": "500",
    "amount": 1096,
    "currency": "BRL"
  }
  ```
</CodeGroup>

<Note>
  A `REFUSED` result is a clean issuer decline — surface the reason and let the customer retry or use another card. A `FAILED` result means the payment couldn't be completed (a provider timeout or error) — treat it as retryable. See [Error handling](/guides/error-handling) for the full model.
</Note>

### Refunds and cancellations

Refunds and cancellations also resolve deterministically under simulation: a cancel succeeds and the refund reaches `COMPLETED`, moving the transaction to `REFUNDED` (full) or `PARTIALLY_REFUNDED` (partial). The acquirer refund rules still apply — one successful refund per card transaction, and no same-day partial refunds. See [Refunding card transactions](/guides/card-transactions#refunding-card-transactions).

## Testing 3D Secure

The [Test cards](#test-cards) table above includes a card for each 3DS path — **challenge** (a verification step the cardholder completes), **frictionless pass** (silent success), and **frictionless fail** (silent failure). Pick the one matching the path you want to exercise and follow the [3D Secure authentication](/guides/three-d-secure-authentication) guide. The card drives the authentication path; the **amount still selects the final acquirer outcome**. For example, `4242424242424242` at amount `1000` runs a challenge and then approves, while the same card at `1051` runs the challenge and then declines with `51`.

## Next steps

<CardGroup cols={2}>
  <Card title="Card transactions" icon="credit-card" href="/guides/card-transactions">
    Implement the full credit and debit card flow
  </Card>

  <Card title="3D Secure" icon="shield-check" href="/guides/three-d-secure-authentication">
    Add session-first or transaction-first 3DS
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Receive real-time transaction status updates
  </Card>

  <Card title="Error handling" icon="triangle-exclamation" href="/guides/error-handling">
    Handle declines, failures, and retries
  </Card>
</CardGroup>
