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

# Usar pagos en el SDK

> Instala paquetes de pago, regístralos en el pedido y cóbralo.

Cada método de pago tiene su propio paquete, `@fire-spark/payments-<provider>-<method>`. Los que necesitan código nativo terminan en `-rn` y solo corren en apps React Native o Expo. Instalas solo los métodos que ofrece tu canal.

## Instalar

```bash theme={null}
pnpm add @fire-spark/storefront @fire-spark/payments-cash
```

Todos los paquetes dependen de `@fire-spark/payments`, el contrato compartido. Solo lo importas para utilidades como `formatPaymentError`.

## Registrar los adaptadores

Registra cada paquete instalado en el pedido. Fire spark une cada pago del merchant con su adaptador por proveedor y método.

```typescript theme={null}
import { cashPayments } from "@fire-spark/payments-cash";

order.setPaymentAdapters([cashPayments()]);
order.setPaymentOptions({
  isProduction: merchant.env_is_prod,
  collectAt: "KIOSK", // "DEVICE" en web o app
  cashCollectAt: "COUNTER", // "DOOR" en delivery
  deviceSettings: kioskConfig.overrides.payment_settings, // solo kioscos, de getKioskConfig
  deviceId: kioskConfig.device.id // solo kioscos
});
```

## Mostrar los métodos

`getPaymentOptions` deja solo los pagos del merchant que tienen un adaptador instalado.

```typescript theme={null}
import { services } from "@fire-spark/storefront";

const payments = await services.payments.list({ order_id: order.id });
const options = order.getPaymentOptions(payments);
```

`services.payments.list` no incluye los pagos `REDEMPTION` (vales, recompensas o saldo): se aplican con las entradas `redeem` de la orden, no se eligen como método. Pasa `{ includeRedemptions: true }` como segundo argumento cuando un flujo de canje los necesite, o filtra una lista que ya tienes con `selectablePayments(payments)`.

En kioscos, `getMissingPaymentFields(payment)` lista los datos del dispositivo que siguen vacíos. Oculta o deshabilita el método hasta que estén completos.

## Cobrar el pedido

Guarda los tenders y luego cobra los del dispositivo. `payIntent` coloca el pedido la primera vez y corre cada tender real `DEVICE` en orden.

```typescript theme={null}
await order.setTenders([
  {
    method: payment.method.method,
    provider: payment.provider.provider_id,
    payment_id: payment.id,
    collect_at: "DEVICE",
    allocation: null
  }
]);

const result = await order.payIntent({
  onProgress: progress => render(progress) // QR, esperando al terminal, …
});

if (result.status === "FAILED") {
  showError(result.result.error);
}
```

`order.pay(payment)` sigue funcionando y llama `payIntent({ payment })`. Ese atajo coloca un tender real. Prefiere `setTenders` cuando el checkout tiene más de un método.

1. La primera llamada coloca el pedido y bloquea el intent. Las siguientes cobran los tenders que ya están en ese intent.
2. El paquete de cada método habla con el terminal, muestra el QR o le pide a Fire spark que cobre en el servidor.
3. Cada resultado se guarda en su tender. Los reintentos, los resultados desconocidos y el vencimiento de QR siguen las [reglas de errores](https://firespark.cloud/docs/es/guides/payments/errors.md).

`PAID` significa que todos los tenders vivos están liquidados. `PENDING` significa que todavía hay tenders en persona. El paso `PAYING` del fulfillment avanza cuando los tenders de su alcance están liquidados.

## Pagos mixtos

Pon cada método en el borrador antes de colocar. `allocation: null` en un solo tender real significa que cubre el resto. Con dos o más tenders reales, nombra las líneas (y `units` si aplica) que cubre cada uno. El servidor llena `amount` y `allocation.amounts`.

```typescript theme={null}
await order.setTenders([
  {
    method: "FINANCING",
    provider: "cashea",
    payment_id: financing.id,
    collect_at: "DEVICE",
    allocation: { lines: [{ line_uid: lineUid, units: 1 }], fees: [] }
  },
  {
    method: "CARD",
    provider: "kushki",
    payment_id: card.id,
    collect_at: "DEVICE",
    allocation: null
  }
]);
await order.payIntent();
```

`getRemainingAmount()` es el total del pedido menos los tenders liquidados (y los que esperan la inyección). Algunos métodos retienen el primer cobro hasta que el resto sale bien. Llama a `order.confirmPaymentsAfterInject()` cuando el pedido llegue a cocina para confirmarlos. Si el pedido no se puede completar, `order.reversePayments()` anula o reembolsa cada tender pagado. Si un proveedor no lo permite, el tender queda marcado para reverso manual y aparece en monitoreo.

Para volver atrás antes de que se capture algo, llama `order.reopen()`. Anula los tenders y devuelve un pedido `OPEN` nuevo. Pasa `genOrderId(storeId)` o cualquier `genId()` si el pedido nuevo debe usar ese id; omítelo para conservar el id generado. `genId().hash(length, format)` fija la longitud del hash (al menos 8, por defecto 12) y el formato (`alphanumeric`, `numeric` o `alphabetic`). `genOrderId` siempre conserva el prefijo `ord-<tienda>-`. `genId().literal("BB21921").hash(8, "numeric").build()` produce un id como `BB2192100000001`. Un hash de menos de 12 caracteres avisa, y un hash numérico de menos de 16 dígitos avisa; los dos igual se devuelven. Un hash más corto choca más seguido. El campo del cuerpo es `order_id`. Un formato inválido es `400` `ORDER_ID_INVALID`. Un id que ya existe es `409` `ORDER_ID_EXISTS`.

## Pagar con recompensas

Los vales, las recompensas o el saldo son un canje. `payment_id` (el pago `REDEMPTION` del merchant) y `amount` son obligatorios. Pasa `line_uid` para canjear una línea de producto, u omítelo para todo el pedido:

```typescript theme={null}
order.applyRedeem({
  reward_id: "rw_burger",
  quantity: 1,
  payment_id: rewardsPayment.id,
  amount: 10,
  line_uid: burger.uid
});
```

`amount` es el total en dinero de toda la entrada, no por unidad: dos combos de 3 cada uno son `amount: 6`. `quantity` es informativo. Si quitas unidades con `removeRedeem`, las que quedan conservan su parte de `amount`.

El canje se muestra como descuento de la línea (o del pedido). Los totales de la línea y del pedido conservan su valor completo. `before_taxes` (por defecto `true`) limita el canje al valor antes de impuestos: con una hamburguesa de 10 + 15% de impuesto, el canje cubre 10 y el cliente paga 1.50. Con `before_taxes: false` puede cubrir los 11.50. `redeemedTotal(line.redeem)` y `orderPayableTotal(order)` te dan lo que debes mostrar: el descuento y lo que le queda al cliente.

El SDK agrega un tender `REDEMPTION` por cada `payment_id` con el monto limitado, y `getTenders(method)` agrega el método del cliente por el resto. En el ejemplo eso es `REDEMPTION` 10 y `CASH` 1.50, o solo `REDEMPTION` 11.50. Los canjes que comparten `payment_id` van en el mismo tender, que lista cada recompensa en `details.rewards` y suma sus `amount` en `details.amount`. Los canjes con `payment_id` distintos van en tenders separados.

## Preguntas del terminal y almacenamiento

Los paquetes de tarjeta presente necesitan preguntarle cosas al cliente (cuotas, tipo de cuenta, confirmación) y recordar contadores del terminal entre sesiones. Dales tu UI y tu almacenamiento una vez:

```typescript theme={null}
order.setPaymentUI({
  message: ({ text, kind }) => showBanner(text, kind),
  select: ({ title, options }) => openPicker(title, options),
  input: ({ label, type }) => openInput(label, type),
  confirm: ({ text }) => openConfirm(text),
  action: action => showAction(action) // QR, deeplink o redirección
});

order.setPaymentStorage({
  get: key => AsyncStorage.getItem(key),
  set: (key, value) => AsyncStorage.setItem(key, value),
  remove: key => AsyncStorage.removeItem(key)
});
```

Los paquetes nunca dibujan UI. Tu canal decide cómo se ven las preguntas.

## Operaciones del terminal

Las pantallas de administración del kiosco pueden correr operaciones del terminal, como una prueba de conexión o el cierre de lote:

```typescript theme={null}
await order.runPaymentTerminal(payment, "test");
await order.runPaymentTerminal(payment, "closeBatch");
```

Solo corre una operación a la vez por terminal. Una operación de terminal fallida se reporta en [monitoreo](https://firespark.cloud/docs/es/guides/monitoring-alerts.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.