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

# Testing Guide

> Test your integration thoroughly in our sandbox environment

## Sandbox Environment

Test your integration thoroughly in our sandbox environment before going live. No real money, no risk.

Our sandbox works exactly like production - same API, same behavior:

```
https://api.sandbox.cheqpay.mx/pos
```

### Sandbox Features

<CardGroup cols={2}>
  <Card title="No Real Money" icon="shield-check">
    Process test payments without financial risk
  </Card>

  <Card title="Test Cards" icon="credit-card">
    Use pre-defined test card numbers for success, decline, and 3DS scenarios
  </Card>

  <Card title="Full Feature Parity" icon="equals">
    All production features available in sandbox
  </Card>

  <Card title="Faster Processing" icon="bolt">
    Instant processing for rapid testing
  </Card>
</CardGroup>

Offline payment methods (SPEI, PayCash, and CIE Cash Net) use separate sandbox flows — they do not use test card numbers. See the guides below:

| Payment method | Sandbox testing                                                                                              | Feature guide                                            |
| -------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- |
| SPEI           | Mock transfers can auto-complete; use [webhook simulators](/testing/webhook-simulators) for STP/OPM          | [SPEI Transfers](/features/spei-transfers)               |
| PayCash        | Does not auto-complete; use the [PayCash simulator](/testing/webhook-simulators#paycash-simulator)           | [PayCash Payments](/features/paycash-payments)           |
| CIE Cash Net   | Does not auto-complete; use the [CIE Cash Net simulator](/testing/webhook-simulators#cie-cash-net-simulator) | [CIE Cash Net Payments](/features/cie-cash-net-payments) |

## Test Cards

Use these cards to test different scenarios:

### Successful Payments

| Card Number        | Brand      | Behavior           |
| ------------------ | ---------- | ------------------ |
| `4000000000002503` | Visa       | Successful payment |
| `5200000000002151` | Mastercard | Successful payment |

<Note>
  For 3D Secure (3DS) testing, see the [complete 3DS testing guide](/features/3d-secure#3d-secure-testing-data) which includes specific test cards and amount thresholds for different 3DS scenarios.
</Note>

### Failed Payments

In sandbox, **declines are triggered by the transaction amount**, not the card number. You can use any of the test card numbers listed under "Successful Payments" (or a generic test card) — what determines the response is the amount you submit.

#### How it works

The amount must end in two specific digits that map to a known decline reason. Use a base amount of **9000.xx** where `xx` is the response code you want to trigger. The same trigger amounts apply to all supported currencies (MXN, USD) in sandbox.

* Amounts in the API are submitted as integers in the smallest currency unit (cents).
* Example (MXN): to trigger "Expired card" (code `33`), submit `amount: 900033` with `currency: "MXN"` (= 9000.33 MXN).
* Example (USD): to trigger "No sufficient funds" (code `51`), submit `amount: 900051` with `currency: "USD"` (= 9000.51 USD).

<Note>
  This amount-based trigger mechanism is **only available in sandbox**. In production, the same amounts are processed normally and will not generate synthetic declines.
</Note>

<Note>
  Any other amount that does not match an entry below will be **approved**. To test a successful payment, use any amount that does not end in one of the listed two-digit codes.
</Note>

#### Decline trigger amounts

| Amount (MXN) | API value (cents) | Decline reason                              |
| ------------ | ----------------- | ------------------------------------------- |
| 9000.01      | `900001`          | Refer to card issuer                        |
| 9000.02      | `900002`          | Refer to special conditions for card issuer |
| 9000.03      | `900003`          | Invalid merchant                            |
| 9000.04      | `900004`          | Pick-up card                                |
| 9000.05      | `900005`          | Do not honor                                |
| 9000.06      | `900006`          | Error                                       |
| 9000.07      | `900007`          | Pick-up card, special condition             |
| 9000.09      | `900009`          | Request in progress (duplicate)             |
| 9000.12      | `900012`          | Invalid transaction                         |
| 9000.13      | `900013`          | Invalid amount                              |
| 9000.14      | `900014`          | Invalid card number (no such number)        |
| 9000.15      | `900015`          | No such issuer                              |
| 9000.17      | `900017`          | Customer cancellation                       |
| 9000.22      | `900022`          | Suspected malfunction                       |
| 9000.30      | `900030`          | Format error                                |
| 9000.31      | `900031`          | Bank not supported by switch                |
| 9000.33      | `900033`          | Expired card                                |
| 9000.34      | `900034`          | Suspected fraud                             |
| 9000.35      | `900035`          | Card acceptor contact acquirer              |
| 9000.36      | `900036`          | Restricted card                             |
| 9000.37      | `900037`          | Card acceptor call acquirer security        |
| 9000.38      | `900038`          | Allowable PIN tries exceeded                |
| 9000.39      | `900039`          | No credit account                           |
| 9000.40      | `900040`          | Command rejected                            |
| 9000.41      | `900041`          | Lost card                                   |
| 9000.43      | `900043`          | Stolen card, pick-up                        |
| 9000.51      | `900051`          | No sufficient funds                         |
| 9000.54      | `900054`          | Expired card                                |
| 9000.55      | `900055`          | Incorrect personal identification number    |
| 9000.56      | `900056`          | No card record                              |
| 9000.57      | `900057`          | Transaction not permitted to cardholder     |
| 9000.58      | `900058`          | Transaction not permitted to terminal       |
| 9000.61      | `900061`          | Exceeds withdrawal amount limit             |
| 9000.62      | `900062`          | Restricted card                             |
| 9000.65      | `900065`          | Exceeds withdrawal frequency limit          |
| 9000.68      | `900068`          | Response received too late                  |

### Test Card Details

For all test cards:

* **Expiry Date:** Any future date (e.g., 12/2025)
* **CVC:** Any 3 digits (e.g., 123)
* **Billing Address:** Any valid address

<Tip>
  Test cards work only in sandbox. They will be rejected in production.
</Tip>

<Warning>
  **Consistency between runs:** because the response is determined by the amount, you must keep the amount fixed across executions of the same scenario. Changing the amount will change the response — even if the card stays the same.
</Warning>

## Test 3D Secure

Test the complete 3DS authentication flow using specific test cards and amount thresholds.

### Trigger 3DS Challenge

Use amounts >= 100,000 cents (1,000 MXN) with the 3DS test card:

```bash theme={null}
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "test-3ds-001",
    "amount": 100000,
    "currency": "MXN",
    "paymentMethod": {
      "type": "card",
      "cardDetails": {
        "number": "4000000000002503",
        "expiryMonth": "12",
        "expiryYear": "25",
        "cvc": "123"
      }
    }
  }'
```

<Note>
  **3DS Test Cards:**

  * `4000000000002503` with amount >= 100,000 cents triggers full 3DS challenge
  * `4000000000002701` with amount >= 100,000 cents triggers frictionless 3DS
  * `4000000000002701` with amount \< 100,000 cents skips 3DS entirely

  See the [complete 3DS testing guide](/features/3d-secure#3d-secure-testing-data) for all scenarios.
</Note>

### Complete Authentication

When prompted for a verification code in sandbox, use:

```
123456
```

This simulates successful customer authentication.

<Card title="Complete 3DS Guide" icon="shield" href="/features/3d-secure">
  Learn how to implement 3D Secure authentication
</Card>

## Test Refunds

Refunds process instantly in sandbox (instead of 5-10 days in production):

```bash theme={null}
# Create a successful payment
PAYMENT_ID=$(curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -d '{"externalId": "test-001", "amount": 10000, ...}' \
  | jq -r '.paymentOrder.id')

# Issue a refund
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders/$PAYMENT_ID/refund \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -d '{"reason": "Test refund"}'
```

## Common Test Scenarios

### Scenario 1: Successful Card Payment

```bash theme={null}
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "test-001",
    "customer": {
      "firstName": "Test",
      "lastName": "User",
      "email": "test@example.com"
    },
    "amount": 10000,
    "currency": "MXN",
    "paymentMethod": {
      "type": "card",
      "cardDetails": {
        "number": "4000000000002503",
        "expiryMonth": "12",
        "expiryYear": "25",
        "cvc": "123"
      }
    }
  }'
```

Expected: Status `COMPLETED`, payment successful.

### Scenario 2: Declined Payment

Trigger a decline by submitting one of the amounts listed in the [Failed Payments](#failed-payments) table. Any test card works — the amount is what determines the response.

The example below triggers `Expired card` (code `33`) by submitting `amount: 900033` (= 9000.33 MXN):

```bash theme={null}
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "test-002",
    "amount": 900033,
    "currency": "MXN",
    "paymentMethod": {
      "type": "card",
      "cardDetails": {
        "number": "4000000000002503",
        "expiryMonth": "12",
        "expiryYear": "25",
        "cvc": "123"
      }
    }
  }'
```

Expected: Status `FAILED` with `Expired card` reason.

To test a different decline reason, change the last two digits of the `amount` field to match the desired code from the table.

#### Same scenario in USD

The same trigger amounts work for any supported currency. The example below triggers `No sufficient funds` (code `51`) in USD by submitting `amount: 900051` with `currency: "USD"`:

```bash theme={null}
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "test-002-usd",
    "amount": 900051,
    "currency": "USD",
    "paymentMethod": {
      "type": "card",
      "cardDetails": {
        "number": "4000000000002503",
        "expiryMonth": "12",
        "expiryYear": "25",
        "cvc": "123"
      }
    }
  }'
```

Expected: Status `FAILED` with `No sufficient funds` reason.

### Scenario 3: 3DS Authentication Flow

1. Create payment with 3DS card
2. Receive `PAYER_AUTHENTICATION_CHALLENGE_REQUIRED` status
3. Display authentication challenge
4. Customer enters code `123456`
5. Validate authentication
6. Payment completes

### Scenario 4: Save & Reuse Card

```bash theme={null}
# 1. Save card during payment
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -d '{
    "externalId": "test-003",
    "amount": 10000,
    "currency": "MXN",
    "paymentMethod": {
      "type": "card",
      "cardDetails": {
        "number": "4000000000002503",
        "expiryMonth": "12",
        "expiryYear": "25",
        "cvc": "123"
      },
      "persist": true
    }
  }'

# 2. Get paymentMethodId from response
# 3. Use saved card for next payment
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -d '{
    "externalId": "test-004",
    "amount": 5000,
    "currency": "MXN",
    "paymentMethod": {
      "type": "payment_method_id",
      "paymentMethodId": "pm_xxx",
      "cvc": "123"
    }
  }'
```

### Scenario 5: Idempotency Test

Send the same request twice with same `externalId`:

```bash theme={null}
# First request
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -d '{"externalId": "test-005", "amount": 10000, ...}'

# Second request (duplicate)
curl -X POST https://api.sandbox.cheqpay.mx/pos/v2/payment-orders \
  -H "x-api-key: YOUR_SANDBOX_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID" \
  -d '{"externalId": "test-005", "amount": 10000, ...}'
```

Expected: Both requests return the same payment. No duplicate charge.

## Integration Testing Checklist

Test these scenarios before going live:

<AccordionGroup>
  <Accordion title="Card Payment Processing" icon="credit-card">
    * ✅ Successful payment with Visa
    * ✅ Successful payment with Mastercard
    * ✅ Successful payment with Amex
    * ✅ Declined payment handling
    * ✅ Expired card error
    * ✅ Invalid CVC error
  </Accordion>

  <Accordion title="3D Secure Authentication" icon="shield">
    * ✅ Trigger 3DS challenge
    * ✅ Display authentication iframe
    * ✅ Handle successful authentication
    * ✅ Handle failed authentication
    * ✅ Handle authentication timeout
  </Accordion>

  <Accordion title="Saved Payment Methods" icon="floppy-disk">
    * ✅ Save card during payment
    * ✅ Charge saved card
    * ✅ Handle expired saved card
    * ✅ Update saved card
  </Accordion>

  <Accordion title="Refunds" icon="rotate-left">
    * ✅ Full refund
    * ✅ Partial refund
    * ✅ Multiple partial refunds
    * ✅ Handle refund errors
  </Accordion>

  <Accordion title="Error Handling" icon="triangle-exclamation">
    * ✅ Network errors
    * ✅ Validation errors
    * ✅ Declined payments
    * ✅ Rate limiting
    * ✅ Retry logic
  </Accordion>
</AccordionGroup>

## Performance Testing

Test your integration under load:

```javascript theme={null}
// Example: Load test with 100 concurrent payments
const promises = [];
for (let i = 0; i < 100; i++) {
  promises.push(
    createPayment({
      externalId: `load-test-${i}`,
      amount: 10000,
      currency: 'MXN',
      // ...
    })
  );
}

const results = await Promise.allSettled(promises);
const successful = results.filter(r => r.status === 'fulfilled').length;
const failed = results.filter(r => r.status === 'rejected').length;

console.log(`Successful: ${successful}, Failed: ${failed}`);
```

## Go Live Checklist

Ready to switch to production?

<Steps>
  <Step title="Get Production Credentials">
    Contact [support@cheqpay.mx](mailto:support@cheqpay.mx) for live API keys.
  </Step>

  <Step title="Update Base URL">
    Change from sandbox.cheqpay.com to api.cheqpay.com.
  </Step>

  <Step title="Register Production Webhooks">
    Provide your production webhook URL to support.
  </Step>

  <Step title="Secure Your Keys">
    Use environment variables, never hardcode keys.
  </Step>

  <Step title="Set Up Monitoring">
    Track payments, errors, and webhook delivery.
  </Step>

  <Step title="Test in Production">
    Process a small test transaction to verify everything works.
  </Step>
</Steps>

<Card title="Going Live Guide" icon="rocket" href="/guides/going-live">
  Complete production readiness checklist
</Card>

## Need Help?

<CardGroup cols={2}>
  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    View complete API documentation
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/guides/error-handling">
    Learn how to handle errors
  </Card>

  <Card title="Best Practices" icon="lightbulb" href="/guides/best-practices">
    Follow recommended patterns
  </Card>

  <Card title="Contact Support" icon="headset" href="mailto:support@cheqpay.mx">
    Get help from our team
  </Card>
</CardGroup>
