# Crear transacción

> Crea una única transacción de venta. Para crear varias transacciones a la vez, consulta la página Envío por lotes.

Página: https://doc.rapidchargeback.com/es/canais/transactions/criar-transacao/

Crea una única transacción de venta. Para crear varias transacciones a la vez, consulta la página [Envío por lotes](https://doc.rapidchargeback.com/es/canais/transactions/envio-em-lote/).

## Endpoint

```
POST https://api.rapidchargeback.com/api/v1/transactions
```

## Autenticación

```
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/json
```

---

## Campos obligatorios

| Campo | Tipo | Descripción |
|---|---|---|
| `merchant_id` **o** `merchant_ref` | string | Informa **exactamente uno**: `merchant_id` (UUID del merchant en Rapid, debe pertenecer a tu empresa) **o** `merchant_ref` (ID del vendedor en **tu** sistema, hasta 100 caracteres, para canales/plataformas con varios vendedores). Si `merchant_ref` es desconocido, el merchant se **crea** automáticamente con `merchant_name` (opcional, hasta 255 caracteres; se ignora cuando el merchant ya existe). Enviar los dos → `422 VALIDATION_ERROR`. Tus merchants y sus `merchant_id` están en [Listar merchants](https://doc.rapidchargeback.com/es/canais/merchants/listar-merchants/). |
| `external_id` | string | ID de la transacción en el sistema de origen |
| `amount` | number | Monto de la transacción (debe ser positivo) |
| `currency` | string | Código ISO 4217 con 3 letras mayúsculas (ej.: `USD`, `BRL`) |
| `transaction_date` | string | Fecha de la transacción en formato ISO 8601 (ej.: `2026-04-01T15:30:00Z`) |
| `card_last4` | string | Últimos 4 dígitos de la tarjeta (exactamente 4 dígitos numéricos) |
| `items` | array | Al menos 1 ítem (ver los campos del ítem abajo) |

## Campos del ítem

### Obligatorios

| Campo | Tipo | Descripción |
|---|---|---|
| `product_description` | string | Descripción del producto (hasta 1000 caracteres; por encima de eso la solicitud falla) |
| `unit_price` | number | Precio unitario |

### Opcionales

| Campo | Tipo | Descripción |
|---|---|---|
| `product_name` | string | Nombre del producto |
| `quantity` | number | Cantidad |
| `unit_of_measure` | string | Unidad de medida |
| `category` | string | Categoría del producto |

---

## Campos opcionales

| Campo | Tipo | Descripción |
|---|---|---|
| `external_source` | string | Sistema de origen (por defecto: `custom`) |
| `order_number` | string | Número de pedido |
| `transaction_id` | string | ID de la transacción en el gateway de pago. Ver la nota abajo |
| `arn` | string | Acquirer Reference Number |
| `banknet_ref` | string | Referencia Banknet (Mastercard) |
| `auth_code` | string | Código de autorización |
| `network` | string | Red de la tarjeta: `visa`, `mastercard`, `amex`, `discover`, `other` |
| `card_bin` | string | BIN de la tarjeta (6 a 8 dígitos numéricos) |
| `ip_address` | string | Dirección IP del comprador |
| `clearing_datetime` | string | Fecha de compensación (ISO 8601) |
| `mcc` | string | Merchant Category Code |
| `eci` | string | Electronic Commerce Indicator |
| `pos_entry_mode` | string | Modo de entrada POS |
| `descriptor` | string | Descriptor de cobro |
| `correlation_id` | string | ID de correlación |
| `device_id` | string | ID del dispositivo (mín. 15 caracteres) |
| `device_fingerprint` | string | Fingerprint del dispositivo (mín. 20 caracteres) |
| `customer` | object | Datos del comprador (ver abajo) |
| `addresses` | array | Direcciones de envío y/o de facturación (ver abajo) |
| `payments` | array | Datos de pago (ver abajo) |
| `refunds` | array | Datos de reembolso (ver abajo) |

### Límites de tamaño

Todo campo de texto tiene un tope. Por encima de él la respuesta es `422 VALIDATION_ERROR` señalando el campo, nunca un error de servidor.

| Límite | Campos |
|---|---|
| 1000 | `product_description` |
| 255 | `descriptor`, `merchant_name`, `product_name`, `device_fingerprint`, `street`, `reason` (refund) |
| 200 | `billing_name` |
| 100 | `external_id`, `order_number`, `transaction_id`, `arn`, `banknet_ref`, `correlation_id`, `merchant_ref`, `device_id`, `first_name`, `last_name`, `city`, `state`, `category` |
| 64 | `method_masked`, `reference_number` |
| 45 | `ip_address` (cabe IPv6) |
| 50 | `external_source`, `auth_code`, `account_id` |
| 32 | `unit_of_measure` |
| 30 | `phone`, `postal_code`, `wallet_indicator` |
| 20 | `number` (dirección) |
| 16 | `payment_type` |
| 10 | `mcc`, `pos_entry_mode` |
| 7 | `card_expiry` |
| 5 | `eci` |
| 2 | `cvv2_presence_indicator`, `avs_result` |

`email` sigue el límite de 255 y necesita un formato válido. `currency`, `card_bin`, `card_last4`, `country` y `network` tienen formato fijo, descrito en la tabla de campos.

### Nota sobre `transaction_id`

Es opcional, pero complétalo siempre que el cobro haya pasado por un gateway de pago: es por este campo que Rapid vincula un contracargo futuro a la transacción. Envía el identificador del cobro en el gateway, no tu número de pedido (ese es el `order_number`).

Si usas un gateway cuyas disputas llegan a Rapid y el campo viene vacío, el contracargo igual se recibe, pero sin transacción asociada: sin historial, sin evidencia adjunta y sin los datos de autenticación, que son justamente el material de la defensa. Alternativa equivalente: enviar `external_source` con el nombre del gateway y `external_id` con el id del cobro.

---

## Objeto `customer` (todos opcionales)

| Campo | Tipo | Descripción |
|---|---|---|
| `first_name` | string | Nombre |
| `last_name` | string | Apellido |
| `email` | string | Correo electrónico (formato válido) |
| `billing_name` | string | Nombre en la tarjeta |
| `account_id` | string | ID de la cuenta del comprador en tu sistema |
| `phone` | string | Teléfono |

## Objeto `address` (dentro del array `addresses`)

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `type` | string | Sí | `shipping` o `billing` |
| `street` | string | No | Calle |
| `number` | string | No | Número |
| `city` | string | No | Ciudad |
| `state` | string | No | Estado/Región |
| `postal_code` | string | No | Código postal |
| `country` | string | No | Código de país ISO 3166-1, 2 o 3 letras mayúsculas (`BR`/`BRA`, `US`/`USA`). Se recomienda alpha-3. |

## Campos de autenticación del pago (raíz, todos opcionales)

Insumos de la [protección contra fraude](https://doc.rapidchargeback.com/es/produtos/prevencao/integracao/#para-la-protección-contra-fraude) y de 3-DS. Cuanto más completes, más fuerte la deflexión y la defensa.

| Campo | Tipo | Descripción |
|---|---|---|
| `cvv2_presence_indicator` | string | Indicador de presencia del CVV2 en la autorización |
| `cvv2_result` | string | Resultado de la verificación del CVV2: `M`, `N`, `P`, `S`, `U` o `Y`, tal como vino en la respuesta de la autorización |
| `avs_result` | string | Resultado del AVS (verificación de dirección) |
| `cardholder_verification_approved` | boolean | 3-DS/verificación del titular aprobada |
| `authentication_response_type` | string | Tipo de respuesta de la autenticación 3-DS: `attempt` (intento) o `confirm` (autenticación confirmada) |
| `wallet_indicator` | string | Billetera digital usada (ej.: Apple Pay, Google Pay) |

### Cuenta del comprador (dentro de `customer`, todos opcionales)

Indican si la compra la hizo una cuenta conocida, y desde cuándo existe. Son el material más fuerte de la defensa en una disputa por fraude: un comprador con una cuenta antigua y con sesión iniciada es difícil de disputar como "no lo reconozco".

| Campo | Tipo | Descripción |
|---|---|---|
| `registered_at` | string (ISO 8601) | Cuándo se creó la cuenta del comprador en tu sistema |
| `registered_at_source` | string | De dónde viene `registered_at`: `merchant_api` (registro de la cuenta en tu sistema) o `client_declared` (declaración tuya, sin ese registro) |
| `account_authenticated` | boolean | El comprador tenía la sesión iniciada en la cuenta al comprar |
| `account_authenticated_source` | string | Cómo lo sabes: `per_account` (verificado en esta compra) o `onboarding_flag` (regla de tu tienda, por ejemplo, toda compra exige login) |
| `account_authenticated_at` | string (ISO 8601) | Cuándo fue el login |
| `auth_method` | string | Cómo entró el comprador: `password`, `otp`, `2fa`, `biometric`, `oauth` u `other` |
| `account_source_method` | string | De dónde vienen los datos de la cuenta: `merchant_api` o `client_declared` |
| `checkout_type` | string | Cómo se hizo la compra: `guest` (sin cuenta), `authenticated` (cuenta existente, con sesión iniciada) o `registered_at_checkout` (cuenta creada durante la compra) |

Un valor fuera de las listas responde `422 VALIDATION_ERROR`.

## Objeto `payment` (todos opcionales)

| Campo | Tipo | Descripción |
|---|---|---|
| `payment_type` | string | Tipo de pago (credit, debit, pix, etc.) |
| `method_masked` | string | Número enmascarado |
| `matched_payment` | boolean | Si este pago fue el utilizado (por defecto: true) |
| `card_bin` | string | BIN (6-8 dígitos) |
| `card_last4` | string | Últimos 4 dígitos (4 dígitos numéricos) |
| `installments` | integer | Número de cuotas |
| `card_expiry` | string | Vencimiento (MM/YYYY) |

## Objeto `refund`

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `amount` | number | Sí | Monto del reembolso (positivo) |
| `currency` | string | Sí | Moneda (3 letras mayúsculas, ISO 4217) |
| `reference_number` | string | No | Identificador del reembolso |
| `reason` | string | No | Motivo del reembolso |
| `refund_datetime` | string | No | Fecha del reembolso (ISO 8601) |

---

## Validaciones

- `transaction_date` debe estar en ISO 8601 con zona horaria
- `currency` debe ser un código ISO 4217 (3 letras mayúsculas)
- `amount` debe ser mayor que cero
- `card_last4` debe tener exactamente 4 dígitos numéricos
- `card_bin` debe tener de 6 a 8 dígitos numéricos
- `device_id` debe tener como mínimo 15 caracteres
- `device_fingerprint` debe tener como mínimo 20 caracteres
- La combinación `external_source + external_id` no puede duplicar una transacción existente de tu empresa

## Warnings

La API devuelve avisos (sin bloquear la creación) cuando faltan campos opcionales importantes:

| Cuándo | Aviso |
|---|---|
| Sin `card_bin` | `card_bin missing: reduces match accuracy` |
| Sin `order_number` | `order_number missing: recommended to identify the order in dispute responses` |
| Sin `ip_address`, `device_id` **y** `device_fingerprint` | `ip_address, device_id and device_fingerprint missing: recommended, fraud protection relies on them` |

Basta **uno** de los tres identificadores para que desaparezca el último aviso: la IP no es obligatoria. Y a propósito no hay aviso de dirección: quien vende bienes digitales no debe enviar dirección de envío (ver [protección contra fraude](https://doc.rapidchargeback.com/es/produtos/prevencao/integracao/#bienes-digitales-no-envíes-dirección-de-envío)).

La clave `warnings` **solo aparece** en la respuesta cuando hay al menos un aviso. No cuentes con `warnings: []`.

---

## Ejemplo de solicitud

Las credenciales vienen de variables de entorno (`RAPID_CLIENT_ID` y `RAPID_CLIENT_SECRET`), nunca escritas en el código.

**cURL**

```bash
curl -X POST https://api.rapidchargeback.com/api/v1/transactions \
  -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "00000000-0000-0000-0000-000000000001",
    "external_source": "shopify",
    "external_id": "TXN-2026-001",
    "transaction_date": "2026-04-01T15:30:00Z",
    "amount": 150.00,
    "currency": "USD",
    "card_last4": "4242",
    "card_bin": "424242",
    "order_number": "ORD-001",
    "auth_code": "AUTH999",
    "network": "visa",
    "descriptor": "LOJA EXEMPLO",
    "ip_address": "203.0.113.10",
    "items": [
      {
        "product_description": "Assinatura Premium - Mensal",
        "product_name": "Plano Premium",
        "quantity": 1,
        "unit_price": 150.00
      }
    ],
    "customer": {
      "first_name": "João",
      "last_name": "Silva",
      "email": "joao@exemplo.com"
    },
    "addresses": [
      {
        "type": "shipping",
        "street": "Rua das Flores",
        "number": "123",
        "city": "São Paulo",
        "state": "SP",
        "postal_code": "01001000",
        "country": "BRA"
      }
    ]
  }'
```

**Node.js**

```javascript
const credenciales = Buffer.from(`${process.env.RAPID_CLIENT_ID}:${process.env.RAPID_CLIENT_SECRET}`).toString('base64')

const respuesta = await fetch('https://api.rapidchargeback.com/api/v1/transactions', {
  method: 'POST',
  headers: { Authorization: `Basic ${credenciales}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    merchant_id: '00000000-0000-0000-0000-000000000001',
    external_source: 'shopify',
    external_id: 'TXN-2026-001',
    transaction_date: '2026-04-01T15:30:00Z',
    amount: 150.0,
    currency: 'USD',
    card_last4: '4242',
    card_bin: '424242',
    order_number: 'ORD-001',
    auth_code: 'AUTH999',
    network: 'visa',
    descriptor: 'LOJA EXEMPLO',
    ip_address: '203.0.113.10',
    items: [
      {
        product_description: 'Assinatura Premium - Mensal',
        product_name: 'Plano Premium',
        quantity: 1,
        unit_price: 150.0,
      },
    ],
    customer: {
      first_name: 'João',
      last_name: 'Silva',
      email: 'joao@exemplo.com',
    },
    addresses: [
      {
        type: 'shipping',
        street: 'Rua das Flores',
        number: '123',
        city: 'São Paulo',
        state: 'SP',
        postal_code: '01001000',
        country: 'BRA',
      },
    ],
  }),
})

const cuerpo = await respuesta.json()
if (!respuesta.ok) throw new Error(`${respuesta.status} ${cuerpo.error.code}: ${cuerpo.error.message}`)

console.log('Transacción creada:', cuerpo.data.id)
for (const aviso of cuerpo.warnings ?? []) console.warn('Aviso:', aviso)
```

**Python**

```python
import os

import requests

respuesta = requests.post(
    "https://api.rapidchargeback.com/api/v1/transactions",
    auth=(os.environ["RAPID_CLIENT_ID"], os.environ["RAPID_CLIENT_SECRET"]),
    json={
        "merchant_id": "00000000-0000-0000-0000-000000000001",
        "external_source": "shopify",
        "external_id": "TXN-2026-001",
        "transaction_date": "2026-04-01T15:30:00Z",
        "amount": 150.00,
        "currency": "USD",
        "card_last4": "4242",
        "card_bin": "424242",
        "order_number": "ORD-001",
        "auth_code": "AUTH999",
        "network": "visa",
        "descriptor": "LOJA EXEMPLO",
        "ip_address": "203.0.113.10",
        "items": [
            {
                "product_description": "Assinatura Premium - Mensal",
                "product_name": "Plano Premium",
                "quantity": 1,
                "unit_price": 150.00,
            }
        ],
        "customer": {
            "first_name": "João",
            "last_name": "Silva",
            "email": "joao@exemplo.com",
        },
        "addresses": [
            {
                "type": "shipping",
                "street": "Rua das Flores",
                "number": "123",
                "city": "São Paulo",
                "state": "SP",
                "postal_code": "01001000",
                "country": "BRA",
            }
        ],
    },
    timeout=30,
)

cuerpo = respuesta.json()
if not respuesta.ok:
    raise RuntimeError(f"{respuesta.status_code} {cuerpo['error']['code']}: {cuerpo['error']['message']}")

print("Transacción creada:", cuerpo["data"]["id"])
for aviso in cuerpo.get("warnings", []):
    print("Aviso:", aviso)
```

**PHP**

```php
<?php
$transaccion = [
    'merchant_id' => '00000000-0000-0000-0000-000000000001',
    'external_source' => 'shopify',
    'external_id' => 'TXN-2026-001',
    'transaction_date' => '2026-04-01T15:30:00Z',
    'amount' => 150.00,
    'currency' => 'USD',
    'card_last4' => '4242',
    'card_bin' => '424242',
    'order_number' => 'ORD-001',
    'auth_code' => 'AUTH999',
    'network' => 'visa',
    'descriptor' => 'LOJA EXEMPLO',
    'ip_address' => '203.0.113.10',
    'items' => [
        [
            'product_description' => 'Assinatura Premium - Mensal',
            'product_name' => 'Plano Premium',
            'quantity' => 1,
            'unit_price' => 150.00,
        ],
    ],
    'customer' => [
        'first_name' => 'João',
        'last_name' => 'Silva',
        'email' => 'joao@exemplo.com',
    ],
    'addresses' => [
        [
            'type' => 'shipping',
            'street' => 'Rua das Flores',
            'number' => '123',
            'city' => 'São Paulo',
            'state' => 'SP',
            'postal_code' => '01001000',
            'country' => 'BRA',
        ],
    ],
];

$ch = curl_init('https://api.rapidchargeback.com/api/v1/transactions');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_USERPWD => getenv('RAPID_CLIENT_ID') . ':' . getenv('RAPID_CLIENT_SECRET'),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode($transaccion),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
]);

$crudo = curl_exec($ch);
if ($crudo === false) {
    throw new RuntimeException('Error de red: ' . curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$cuerpo = json_decode($crudo, true);

if ($status >= 400) {
    throw new RuntimeException("$status {$cuerpo['error']['code']}: {$cuerpo['error']['message']}");
}

echo 'Transacción creada: ', $cuerpo['data']['id'], PHP_EOL;
foreach ($cuerpo['warnings'] ?? [] as $aviso) {
    echo 'Aviso: ', $aviso, PHP_EOL;
}
```

---

## Ejemplos de respuesta

### Éxito

```json
{
  "data": {
    "id": "00000000-0000-0000-0000-000000000042",
    "company_id": "...",
    "merchant_id": "00000000-0000-0000-0000-000000000001",
    "external_id": "TXN-2026-001",
    "amount": 150.00,
    "currency": "USD",
    "transaction_date": "2026-04-01T15:30:00.000Z",
    "card_last4": "4242",
    "created_at": "2026-04-01T15:31:00.000Z",
    "transaction_items": [...],
    "transaction_customers": [...],
    "transaction_addresses": [...]
  }
}
```

### Duplicado

```json
{
  "error": {
    "code": "TRANSACTION_DUPLICATE",
    "message": "Transaction already exists"
  }
}
```

### Error de validación

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "card_last4 must be exactly 4 digits"
  }
}
```
