# SiPago Docs > Documentación pública de las integraciones de SiPago: API Checkout, Plugin WooCommerce, API QR, API Conciliación, API Suscripciones, API Caja POS. ## API Checkout Cobrá con links de pago desde tu sistema. ### API Cobros - [Introducción](https://docs.sipago.app/docs/api-checkout/introduccion): Requisitos para integrar la API Cobros de SiPago y cómo obtener el token. - [Integración Checkout](https://docs.sipago.app/docs/api-checkout/integracion-checkout): Cómo crear una orden de pago y mostrar el link al comprador. - [Integración avanzada](https://docs.sipago.app/docs/api-checkout/integracion-avanzada): Envío, URL de retorno, webhook y tiempo límite para pagar. - [Estado de la orden](https://docs.sipago.app/docs/api-checkout/estado-orden): Cómo consultar si una orden de pago ya se pagó y qué significa cada estado. - [Ambientes](https://docs.sipago.app/docs/api-checkout/ambientes): URLs de desarrollo y producción para la API Cobros. - [Credenciales de desarrollo](https://docs.sipago.app/docs/api-checkout/credenciales-desarrollo): Credenciales públicas para probar la API en el ambiente de desarrollo. - [Preguntas frecuentes](https://docs.sipago.app/docs/api-checkout/preguntas-frecuentes): Respuestas a las dudas más comunes sobre la API Cobros. ## Plugin WooCommerce Cobrá con tarjetas en tu tienda WooCommerce con el plugin de SiPago. ### Plugin WooCommerce - [Descargá el plugin](https://docs.sipago.app/docs/plugin-woocommerce/descargar-plugin): Descargá el plugin de SiPago para cobrar en tu tienda WooCommerce. - [Configurá WooCommerce](https://docs.sipago.app/docs/plugin-woocommerce/configurar-woocommerce): Generá tus credenciales y configurá el plugin de SiPago en WooCommerce. ## API QR Generá QRs de cobro dinámicos y estáticos, confirmá pagos en tiempo real y conciliá operaciones. ### Primeros pasos - [Introducción](https://docs.sipago.app/docs/api-qr/introduccion): Flujo de cobro con QR de SiPago (API v4) y sus cuatro etapas. - [Requisitos](https://docs.sipago.app/docs/api-qr/requisitos): Credenciales, TENANT_IDENTIFIER y datos del comercio para integrar la API QR. - [Ambientes](https://docs.sipago.app/docs/api-qr/ambientes): Hosts de la API QR y referencia rápida de endpoints. - [Autenticación](https://docs.sipago.app/docs/api-qr/autenticacion): Cómo obtener el access_token OAuth 2.0 para la API QR. ### Integración - [Points of Sale](https://docs.sipago.app/docs/api-qr/points-of-sale): Crear, actualizar, eliminar y listar puntos de venta. - [Generar QR](https://docs.sipago.app/docs/api-qr/generar-qr): Crear QRs dinámicos con monto o estáticos sin monto. - [Confirmación de pago](https://docs.sipago.app/docs/api-qr/confirmacion-pago): Confirmá el pago de un QR por SSE en tiempo real o por consulta de estado. - [Conciliación](https://docs.sipago.app/docs/api-qr/conciliacion): Detalle de venta por número de referencia y lista de movimientos. ### Referencia - [Recomendaciones](https://docs.sipago.app/docs/api-qr/recomendaciones): Flujo completo y buenas prácticas para integrar la API QR. ## API Conciliación Consultá movimientos y depósitos, descargá reportes y generá links de pago y códigos QR desde tu sistema. ### Primeros pasos - [Introducción](https://docs.sipago.app/docs/api-conciliacion/introduccion): Alcance de la API Conciliación y referencia de sus endpoints. - [Guía de inicio rápido](https://docs.sipago.app/docs/api-conciliacion/inicio-rapido): Obtené tu clave de API y realizá tu primera consulta de movimientos. - [Autenticación y encabezados](https://docs.sipago.app/docs/api-conciliacion/autenticacion): Clave de API, versión y trazabilidad de las solicitudes. - [Fechas, paginación y errores](https://docs.sipago.app/docs/api-conciliacion/convenciones): Formato de fechas, páginas desde cero y respuestas de error. ### Movimientos - [Consultar movimientos](https://docs.sipago.app/docs/api-conciliacion/consultar-movimientos): Listá movimientos por rango de fechas con paginación. - [Movimiento por referencia](https://docs.sipago.app/docs/api-conciliacion/movimiento-referencia): Buscá una venta por su número de referencia. - [Descargar movimientos](https://docs.sipago.app/docs/api-conciliacion/descargar-movimientos): Descargá un reporte Excel con filtros por fecha, sucursal, estado, operación y usuario. - [Consultar orden](https://docs.sipago.app/docs/api-conciliacion/consultar-orden): Consultá una orden de pago, su estado y los pagos asociados. - [Objeto Movimiento](https://docs.sipago.app/docs/api-conciliacion/objeto-movimiento): Referencia completa de los campos de un movimiento. ### Depósitos - [Consultar depósitos](https://docs.sipago.app/docs/api-conciliacion/depositos): Consultá depósitos recibidos o pendientes del comercio. ## API Suscripciones Gestioná planes, suscripciones, cobros recurrentes, clientes y métodos de pago desde tu sistema. ### Primeros pasos - [Introducción](https://docs.sipago.app/docs/api-suscripciones/introduccion): Recursos y URL base de la API de Pagos Recurrentes. - [Autenticación](https://docs.sipago.app/docs/api-suscripciones/autenticacion): Credenciales OAuth 2.0 y obtención del access token. - [Convenciones generales](https://docs.sipago.app/docs/api-suscripciones/convenciones): Paginación y nombres de parámetros y campos. ### Planes - [Conceptos de planes](https://docs.sipago.app/docs/api-suscripciones/conceptos-planes): Datos del comercio, precios, frecuencia, prorrateo y duración. - [Listar planes](https://docs.sipago.app/docs/api-suscripciones/listar-planes): Filtros y respuesta paginada de planes. - [Crear plan](https://docs.sipago.app/docs/api-suscripciones/crear-plan): Campos y ejemplo para crear un plan de suscripción. - [Actualizar plan](https://docs.sipago.app/docs/api-suscripciones/actualizar-plan): Campos opcionales para modificar un plan. - [Desactivar plan](https://docs.sipago.app/docs/api-suscripciones/desactivar-plan): Desactivá un plan sin cancelar sus suscripciones vigentes. - [Eliminar plan](https://docs.sipago.app/docs/api-suscripciones/eliminar-plan): Eliminá definitivamente un plan inactivo y sin suscripciones vigentes. - [Obtener plan](https://docs.sipago.app/docs/api-suscripciones/obtener-plan): Detalle de un plan y su enlace de suscripción. ### Suscripciones - [Listar suscripciones](https://docs.sipago.app/docs/api-suscripciones/listar-suscripciones): Filtros y campos de las suscripciones. - [Obtener suscripción](https://docs.sipago.app/docs/api-suscripciones/obtener-suscripcion): Consultá una suscripción por su identificador. - [Actualizar suscripción](https://docs.sipago.app/docs/api-suscripciones/actualizar-suscripcion): Estado, método de pago, próximo monto y pausa de una suscripción. - [Cancelar suscripción](https://docs.sipago.app/docs/api-suscripciones/cancelar-suscripcion): Cancelación y campos del cuerpo de la solicitud. ### Cobros - [Listar cobros](https://docs.sipago.app/docs/api-suscripciones/listar-cobros): Filtros, estados y campos de los cobros recurrentes. - [Obtener cobro](https://docs.sipago.app/docs/api-suscripciones/obtener-cobro): Detalle de un cobro e historial de intentos. ### Clientes - [Listar clientes](https://docs.sipago.app/docs/api-suscripciones/listar-clientes): Listado paginado y filtros de clientes. - [Obtener cliente](https://docs.sipago.app/docs/api-suscripciones/obtener-cliente): Detalle de un cliente por identificador. - [Métodos de pago del cliente](https://docs.sipago.app/docs/api-suscripciones/metodos-cliente): Tarjetas y métodos de pago asociados a un cliente. - [Suscripciones del cliente](https://docs.sipago.app/docs/api-suscripciones/suscripciones-cliente): Listado de suscripciones de un cliente. ### Referencia - [Obtener método de pago](https://docs.sipago.app/docs/api-suscripciones/obtener-metodo-pago): Detalle de un método de pago individual. - [Manejo de errores](https://docs.sipago.app/docs/api-suscripciones/errores): Diagnóstico de errores HTTP y reintentos de lectura. - [Resumen de endpoints](https://docs.sipago.app/docs/api-suscripciones/endpoints): Todos los métodos y rutas de la API Suscripciones. ## API Caja POS Conectá tu ERP con dispositivos SmartPOS para cobrar sin tipeo manual de montos. ### Primeros pasos - [Introducción](https://docs.sipago.app/docs/api-caja-pos/introduccion): Qué es CajaPOS y sus beneficios clave. - [Alcance y funcionalidades](https://docs.sipago.app/docs/api-caja-pos/alcance): Funcionalidades y flujo de cobro entre el ERP y el SmartPOS. ### Implementación - [Paso a paso](https://docs.sipago.app/docs/api-caja-pos/implementacion): Los seis pasos para implementar CajaPOS. --- # Contenido completo # Requisitos para integrar Con la **API Cobros** de SiPago generás links de pago (Checkout) desde tu sistema. Tu cliente paga en el formulario de SiPago y vos te enterás del resultado. ## Cómo funciona [Imagen: Flujo de integración: tu backend obtiene el token y crea la orden en SiPago, el comprador paga en el Checkout, SiPago avisa a tu webhookUrl y redirige al comprador, y tu backend confirma el estado final] | Paso | Qué hacés | Dónde está explicado | |------|-----------|----------------------| | 1 | Obtener el token de acceso | En esta página, más abajo | | 2 | Crear la orden de pago | [Integración Checkout](https://docs.sipago.app/docs/api-checkout/integracion-checkout) | | 3 | Mostrar el link al comprador | [Integración Checkout](https://docs.sipago.app/docs/api-checkout/integracion-checkout#mostrar-link) | | 4 y 5 | Recibir el resultado (webhook y redirección) | [Integración avanzada](https://docs.sipago.app/docs/api-checkout/integracion-avanzada) | | 6 | Consultar el estado de la orden | [Estado de la orden](https://docs.sipago.app/docs/api-checkout/estado-orden) | > **Consejo: Probá primero en desarrollo** > > Las [credenciales de desarrollo](https://docs.sipago.app/docs/api-checkout/credenciales-desarrollo) son públicas y funcionan en el [ambiente de desarrollo](https://docs.sipago.app/docs/api-checkout/ambientes). Podés seguir toda esta guía sin dar de alta nada. ## Credenciales Necesitás dos datos, que te entrega SiPago: | Dato | Para qué sirve | |------|----------------| | `client_id` | Identifica tu aplicación. | | `client_secret` | Clave privada con la que obtenés el token. | > **Peligro: Importante** > > El `client_secret` se usa **solo en tu servidor**. No lo pongas en el frontend, en una app móvil ni en un repositorio público. Cada ambiente (desarrollo y producción) tiene sus propias URLs y credenciales. Las encontrás en [Ambientes](https://docs.sipago.app/docs/api-checkout/ambientes). ## Obtener el token (JWT) Con el `client_id` y el `client_secret` le pedís al servidor de autenticación un token. Ese token va en todas las llamadas a la API de Checkout. **`POST {auth_url}/oauth/token`**, donde `{auth_url}` es la URL del *Auth Server* del ambiente que uses ([Ambientes](https://docs.sipago.app/docs/api-checkout/ambientes#auth-server)). | Campo | Valor | |-------|-------| | `grant_type` | `"client_credentials"` (siempre este valor) | | `client_id` | Tu `client_id` | | `client_secret` | Tu `client_secret` | | `scope` | `"*"` (siempre este valor) | **cURL** ```bash curl --request POST 'https://auth.stg.geopagos.io/oauth/token' \ --header 'Content-Type: application/json' \ --data '{ "grant_type": "client_credentials", "client_id": "TU_CLIENT_ID", "client_secret": "TU_CLIENT_SECRET", "scope": "*" }' ``` **Node.js** ```js // Node.js 18 o superior (fetch incluido) const res = await fetch('https://auth.stg.geopagos.io/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ grant_type: 'client_credentials', client_id: process.env.SIPAGO_CLIENT_ID, client_secret: process.env.SIPAGO_CLIENT_SECRET, scope: '*', }), }); const { access_token } = await res.json(); ``` **PHP** ```php true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'grant_type' => 'client_credentials', 'client_id' => getenv('SIPAGO_CLIENT_ID'), 'client_secret' => getenv('SIPAGO_CLIENT_SECRET'), 'scope' => '*', ]), ]); $response = json_decode(curl_exec($curl), true); curl_close($curl); $accessToken = $response['access_token']; ``` **Respuesta:** ```json { "token_type": "Bearer", "expires_in": 3600, "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` Usá el `access_token` en el header `Authorization` de las próximas llamadas: ```text Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... ``` > **Consejo: Reutilizá el token** > > No pidas un token nuevo para cada orden. Guardalo y reutilizalo hasta poco antes de que venza. El vencimiento exacto está en el campo `exp` del JWT (fecha Unix, en segundos). Es más confiable que `expires_in`, cuyo formato puede variar según el ambiente. **Siguiente paso:** [crear una orden de pago](https://docs.sipago.app/docs/api-checkout/integracion-checkout). --- # Integración Checkout Con **SiPago Checkout** cobrás a través de nuestro formulario web, desde cualquier dispositivo. Desde tu sistema creás una **orden de pago** y le mostrás al comprador el link para pagarla. **Antes de empezar** necesitás un `access_token`. Si todavía no lo tenés, mirá [Requisitos para integrar](https://docs.sipago.app/docs/api-checkout/introduccion#obtener-el-token-jwt). ## 1. Creá la orden de pago > **Atención: Desde tu backend** > > Este paso se hace **en tu servidor**: necesita el token y es donde se definen los precios. Si el precio viniera del navegador, el comprador podría modificarlo. **`POST {base_url}/api/v2/orders`**, donde `{base_url}` es la URL de *Checkout* del ambiente que uses ([Ambientes](https://docs.sipago.app/docs/api-checkout/ambientes#checkout)). **Headers:** ```text Authorization: Bearer {access_token} Content-Type: application/vnd.api+json ``` ### Cuerpo del pedido Todos los datos van dentro de `data.attributes`: ```json { "data": { "attributes": { "currency": "032", "items": [ { "id": 31, "name": "Silla Eames Base Madera", "unitPrice": { "currency": "032", "amount": 100000 }, "quantity": 1 } ], "redirect_urls": { "success": "https://mitienda.com/pago-ok", "failed": "https://mitienda.com/pago-fallido" }, "webhookUrl": "https://mitienda.com/webhooks/sipago" } } } ``` | Campo | Tipo | Obligatorio | Descripción | |-------|------|:-----------:|-------------| | `currency` | string | Recomendado | Moneda de la orden. `"032"` = pesos argentinos (código ISO 4217). | | `items` | array | Sí | Productos que se cobran. Al menos uno. | | `items[].id` | integer | No | El identificador del producto en tu sistema. | | `items[].name` | string | Sí | Nombre que ve el comprador en el Checkout. | | `items[].unitPrice.currency` | string | Sí | `"032"`. | | `items[].unitPrice.amount` | integer | Sí | Precio unitario **en centavos** (ver abajo). | | `items[].quantity` | integer | Sí | Cantidad. | | `redirect_urls` | object | No | Adónde vuelve el comprador después de pagar. [Ver más](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#url-de-retorno). | | `webhookUrl` | string | No, pero recomendado | URL a la que te avisamos el resultado del pago. [Ver más](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#webhook-url). | | `shipping` | object | No | Costo de envío. [Ver más](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#envio). | | `expireLimitMinutes` | integer | No | Minutos hasta que vence el link. [Ver más](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#expirelimitminutes). | > **Info: Los montos van en centavos** > > Todos los `amount` son números enteros en los que los dos últimos dígitos son los centavos: > > | Querés cobrar | `amount` | > |---------------|----------| > | $1.000,00 | `100000` | > | $200,69 | `20069` | > | $6,01 | `601` | ### Ejemplo **cURL** ```bash curl --request POST 'https://{base_url}/api/v2/orders' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/vnd.api+json' \ --data '{ "data": { "attributes": { "currency": "032", "items": [{ "id": 31, "name": "Silla Eames Base Madera", "unitPrice": { "currency": "032", "amount": 100000 }, "quantity": 1 }], "redirect_urls": { "success": "https://mitienda.com/pago-ok", "failed": "https://mitienda.com/pago-fallido" }, "webhookUrl": "https://mitienda.com/webhooks/sipago" } } }' ``` **Node.js** ```js // Node.js 18 o superior (fetch incluido) const res = await fetch(`${BASE_URL}/api/v2/orders`, { method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/vnd.api+json', }, body: JSON.stringify({ data: { attributes: { currency: '032', items: [ { id: 31, name: 'Silla Eames Base Madera', unitPrice: { currency: '032', amount: 100000 }, quantity: 1, }, ], redirect_urls: { success: 'https://mitienda.com/pago-ok', failed: 'https://mitienda.com/pago-fallido', }, webhookUrl: 'https://mitienda.com/webhooks/sipago', }, }, }), }); const order = (await res.json()).data.attributes; // Guardá order.uuid junto a tu pedido y mostrale al comprador order.links.checkout console.log(order.uuid, order.links.checkout); ``` **PHP** ```php [ 'attributes' => [ 'currency' => '032', 'items' => [[ 'id' => 31, 'name' => 'Silla Eames Base Madera', 'unitPrice' => ['currency' => '032', 'amount' => 100000], 'quantity' => 1, ]], 'redirect_urls' => [ 'success' => 'https://mitienda.com/pago-ok', 'failed' => 'https://mitienda.com/pago-fallido', ], 'webhookUrl' => 'https://mitienda.com/webhooks/sipago', ], ], ]; $curl = curl_init($baseUrl . '/api/v2/orders'); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $accessToken, 'Content-Type: application/vnd.api+json', ], CURLOPT_POSTFIELDS => json_encode($body, JSON_UNESCAPED_SLASHES), ]); $response = json_decode(curl_exec($curl), true); curl_close($curl); $order = $response['data']['attributes']; // Guardá $order['uuid'] junto a tu pedido y mostrale al comprador $order['links']['checkout'] $checkoutUrl = $order['links']['checkout']; ``` ### Respuesta Si la orden se creó bien, la respuesta es **`201 Created`** con la orden en estado `PENDING`. Abreviada: ```json { "data": { "id": "/api/v2/orders/7c7cb233-16fe-4be4-b7f6-4f8ef6b61904", "type": "Order", "attributes": { "uuid": "7c7cb233-16fe-4be4-b7f6-4f8ef6b61904", "orderNumber": "0000169-0000386917", "status": "PENDING", "price": { "currency": "032", "amount": 100000 }, "links": { "checkout": "https://checkout-mpos-cabal.stg.geopagos.io/orders/7c7cb233-16fe-4be4-b7f6-4f8ef6b61904", "redirect_url": { "success": "https://mitienda.com/pago-ok", "failed": "https://mitienda.com/pago-fallido" } }, "payment": null, "webhookUrl": "https://mitienda.com/webhooks/sipago" } } } ``` Te sirven dos campos: | Campo | Para qué | |-------|----------| | `data.attributes.uuid` | Identifica la orden. **Guardalo** asociado a tu pedido: lo vas a necesitar para [consultar el estado](https://docs.sipago.app/docs/api-checkout/estado-orden) y para reconocer el webhook. | | `data.attributes.links.checkout` | El link de pago que le mostrás al comprador. | > **Atención: Ojo con la ruta del link** > > El link está en `data.attributes.links.checkout`. La respuesta también trae un `data.links`, pero es un **array** y no conviene usarlo. ### Errores Si la orden no se puede crear, la API responde con un código `4xx`. El campo `detail` explica el motivo, y `violations` indica qué campo falló: ```json { "title": "An error occurred", "detail": "items: Se debe incluir al menos un item en la orden de compra", "violations": [ { "propertyPath": "items", "message": "Se debe incluir al menos un item en la orden de compra" } ] } ``` Si el token falta, es inválido o venció, pedí uno nuevo ([Obtener el token](https://docs.sipago.app/docs/api-checkout/introduccion#obtener-el-token-jwt)) y reintentá. ## 2. Mostrá el link de pago en tu sitio En tu frontend, agregá el link de Checkout donde quieras: como botón o con una redirección automática. ```html Pagar con SiPago ``` El link no vence, salvo que le pongas un tiempo límite con [`expireLimitMinutes`](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#expirelimitminutes). ## 3. El comprador paga El comprador completa el pago en el Checkout de SiPago. Cuando termina: 1. Si configuraste `webhookUrl`, **te avisamos el resultado** a esa URL ([Webhook](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#webhook-url)). 2. Si configuraste `redirect_urls`, **lo redirigimos** a tu sitio ([URL de retorno](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#url-de-retorno)). 3. En cualquier momento podés **consultar el estado** de la orden ([Estado de la orden](https://docs.sipago.app/docs/api-checkout/estado-orden)). > **Consejo: Checklist antes de pasar a producción** > > - [ ] El token se pide desde el servidor y se reutiliza hasta que vence. > - [ ] Los precios se calculan en el servidor, no vienen del navegador. > - [ ] Guardás el `uuid` de cada orden junto a tu pedido. > - [ ] Tu webhook responde rápido con `200` y es idempotente. > - [ ] Antes de entregar el producto, confirmás el estado con [`GET /api/v2/orders/{uuid}`](https://docs.sipago.app/docs/api-checkout/estado-orden). > - [ ] Cambiaste las URLs y credenciales a las de [producción](https://docs.sipago.app/docs/api-checkout/ambientes). --- # Integración avanzada Estos campos son **opcionales** y se agregan a la orden que creás con [`POST {base_url}/api/v2/orders`](https://docs.sipago.app/docs/api-checkout/integracion-checkout). Todos van dentro de `data.attributes`, junto a `currency` e `items`. | Campo | Para qué sirve | |-------|----------------| | [`shipping`](#envio) | Sumar el costo de envío a la orden. | | [`redirect_urls`](#url-de-retorno) | Llevar al comprador de vuelta a tu sitio después de pagar. | | [`webhookUrl`](#webhook-url) | Recibir en tu servidor el resultado del pago. **Muy recomendado.** | | [`expireLimitMinutes`](#expirelimitminutes) | Poner un tiempo límite para pagar. | Al final de la página hay un [ejemplo completo](#ejemplo-completo) con todos los campos juntos. ## Monto del envío Suma el costo de envío al total de la orden. El comprador lo ve como un ítem más en el detalle. ```json "shipping": { "name": "Envío por Correo Argentino", "price": { "currency": "032", "amount": 60100 } } ``` | Campo | Tipo | Obligatorio | Descripción | |-------|------|:-----------:|-------------| | `name` | string | No | Nombre del método de envío que ve el comprador. | | `price.currency` | string | Sí | `"032"`. | | `price.amount` | integer | Sí | Costo **en centavos**: `60100` = $601,00. | Por ejemplo: un ítem de `150000` ($1.500) más un envío de `60100` ($601) da una orden con `price.amount` = `210100` ($2.101). Si no hay envío, no mandes el campo `shipping`. ## URL de retorno Cuando termina el pago, redirigimos al comprador a tu sitio: a una URL si el pago se aprobó y a otra si falló. ```json "redirect_urls": { "success": "https://mitienda.com/pago-ok?pedido=1234", "failed": "https://mitienda.com/pago-fallido?pedido=1234" } ``` | Campo | Tipo | Obligatorio | Descripción | |-------|------|:-----------:|-------------| | `success` | string | No | URL a la que vuelve el comprador si el pago fue aprobado. | | `failed` | string | No | URL a la que vuelve el comprador si el pago falló. | - Solo se admiten URLs **HTTPS**. - Podés agregar parámetros propios (como `?pedido=1234`) para saber de qué pedido se trata cuando el comprador vuelve. > **Atención: La redirección no confirma el pago** > > Que el comprador llegue a tu URL de `success` no prueba que haya pagado: cualquiera puede abrir esa URL a mano. Antes de mostrar "pago aprobado" o entregar el producto, confirmá el estado con [`GET /api/v2/orders/{uuid}`](https://docs.sipago.app/docs/api-checkout/estado-orden) o con el [webhook](#webhook-url). ## Webhook URL Cuando un pago termina, le enviamos un `POST` a esta URL con el resultado. Es la forma más confiable de enterarte de un pago, porque llega aunque el comprador cierre el navegador antes de volver a tu sitio. ```json "webhookUrl": "https://mitienda.com/webhooks/sipago?token=UN_SECRETO_TUYO" ``` | Campo | Tipo | Obligatorio | Longitud máxima | |-------|------|:-----------:|-----------------| | `webhookUrl` | string | No | 255 caracteres | La URL tiene que ser pública: SiPago tiene que poder llegar a ella desde internet. Para probar en tu computadora, podés usar un túnel como [ngrok](https://ngrok.com/). ### Qué te enviamos Un `POST` con un JSON como este: **Pago aprobado** ```json { "data": { "type": "Payment", "order": { "uuid": "ea138a99-c9df-44a5-b2bf-09e5db6f8d0c", "status": "SUCCESS", "source": "api_checkout" }, "payment": { "id": 1823, "authorizationCode": "901159", "refNumber": "62b4a8ff60fee", "status": "APPROVED" } } } ``` **Pago rechazado** ```json { "data": { "type": "Payment", "order": { "uuid": "bd499ea5-79f8-4f18-b18d-dfbef7987f52", "status": "PENDING", "source": "api_checkout" }, "payment": { "id": 7364888, "refNumber": "65df288a484b5", "status": "DENIED" } } } ``` Cuando el pago se rechaza, la orden sigue en `PENDING`. | Campo | Descripción | |-------|-------------| | `data.order.uuid` | El `uuid` de la orden que creaste. Usalo para identificar tu pedido. | | `data.order.status` | Estado de la orden. Ver [estados](https://docs.sipago.app/docs/api-checkout/estado-orden#estados). | | `data.payment.status` | Resultado del intento de pago, por ejemplo `APPROVED` o `DENIED`. | | `data.payment.id` | Identificador del pago en SiPago. | | `data.payment.refNumber` | Número de referencia del pago. | | `data.payment.authorizationCode` | Código de autorización. **No siempre viene**: si lo necesitás, consultalo en el [estado de la orden](https://docs.sipago.app/docs/api-checkout/estado-orden). | ### Cómo responder - Respondé **`200`** lo antes posible. Si tenés que hacer algo lento (mandar un mail, facturar…), hacelo después de responder. - Si tu endpoint responde con un error o no responde, **reintentamos el envío**: hasta 4 llamados, con unos 2 minutos entre cada uno. - Por los reintentos, podés recibir el mismo aviso **más de una vez**. Tu código tiene que ser idempotente: procesar dos veces el mismo `uuid` no puede, por ejemplo, despachar dos veces el pedido. ### Seguridad El webhook **no viene firmado**, así que cualquiera que conozca tu URL podría enviarte un aviso falso. Para protegerte: 1. **Confirmá siempre el estado** con [`GET /api/v2/orders/{uuid}`](https://docs.sipago.app/docs/api-checkout/estado-orden) antes de dar el pedido por pagado. No confíes solo en el contenido del webhook. 2. **Agregá un secreto a la URL**, por ejemplo `?token=UN_SECRETO_TUYO`. Rechazá con `401` los avisos que no lo traigan. ```js // Ejemplo con Express app.post('/webhooks/sipago', express.json(), async (req, res) => { if (req.query.token !== process.env.WEBHOOK_SECRET) return res.sendStatus(401); res.sendStatus(200); // respondé primero const uuid = req.body?.data?.order?.uuid; const order = await consultarOrden(uuid); // GET /api/v2/orders/{uuid} if (order.status === 'SUCCESS') { await marcarPedidoComoPagado(uuid); // idempotente } }); ``` ## Tiempo límite para pagar (`expireLimitMinutes`) Pone un tiempo límite, en minutos, para pagar la orden. Una vez vencido, el link ya no sirve. ```json "expireLimitMinutes": 30 ``` | Campo | Tipo | Obligatorio | Descripción | |-------|------|:-----------:|-------------| | `expireLimitMinutes` | integer | No | Minutos desde la creación de la orden. Por ejemplo, `14400` = 10 días. | > **Info: Sin vencimiento** > > Si enviás `0` o no enviás el campo, la orden **no vence**: el link se puede pagar sin límite de tiempo. Cuando vence el tiempo: | Situación | Qué ve el comprador | |-----------|---------------------| | Abre el link después del vencimiento | **"Tu link de pago expiró"** | | Abrió el link a tiempo, pero paga después del vencimiento | **"Tuvimos un inconveniente en nuestro sistema"** | > **Atención: No hay webhook al vencer** > > En ninguno de los dos casos te enviamos un webhook. Si necesitás saber que una orden venció, consultá su estado con [`GET /api/v2/orders/{uuid}`](https://docs.sipago.app/docs/api-checkout/estado-orden): va a estar en `EXPIRED`. ## Ejemplo completo Una orden con todos los campos opcionales: **cURL** ```bash curl --request POST 'https://{base_url}/api/v2/orders' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/vnd.api+json' \ --data '{ "data": { "attributes": { "currency": "032", "items": [{ "id": 31, "name": "Silla Eames Base Madera", "unitPrice": { "currency": "032", "amount": 100000 }, "quantity": 1 }], "shipping": { "name": "Envío por Correo Argentino", "price": { "currency": "032", "amount": 60100 } }, "redirect_urls": { "success": "https://mitienda.com/pago-ok?pedido=1234", "failed": "https://mitienda.com/pago-fallido?pedido=1234" }, "webhookUrl": "https://mitienda.com/webhooks/sipago?token=UN_SECRETO_TUYO", "expireLimitMinutes": 30 } } }' ``` **Node.js** ```js // Node.js 18 o superior (fetch incluido) const res = await fetch(`${BASE_URL}/api/v2/orders`, { method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/vnd.api+json', }, body: JSON.stringify({ data: { attributes: { currency: '032', items: [ { id: 31, name: 'Silla Eames Base Madera', unitPrice: { currency: '032', amount: 100000 }, quantity: 1, }, ], shipping: { name: 'Envío por Correo Argentino', price: { currency: '032', amount: 60100 }, }, redirect_urls: { success: 'https://mitienda.com/pago-ok?pedido=1234', failed: 'https://mitienda.com/pago-fallido?pedido=1234', }, webhookUrl: 'https://mitienda.com/webhooks/sipago?token=UN_SECRETO_TUYO', expireLimitMinutes: 30, }, }, }), }); const order = (await res.json()).data.attributes; ``` **PHP** ```php [ 'attributes' => [ 'currency' => '032', 'items' => [[ 'id' => 31, 'name' => 'Silla Eames Base Madera', 'unitPrice' => ['currency' => '032', 'amount' => 100000], 'quantity' => 1, ]], 'shipping' => [ 'name' => 'Envío por Correo Argentino', 'price' => ['currency' => '032', 'amount' => 60100], ], 'redirect_urls' => [ 'success' => 'https://mitienda.com/pago-ok?pedido=1234', 'failed' => 'https://mitienda.com/pago-fallido?pedido=1234', ], 'webhookUrl' => 'https://mitienda.com/webhooks/sipago?token=UN_SECRETO_TUYO', 'expireLimitMinutes' => 30, ], ], ]; $curl = curl_init($baseUrl . '/api/v2/orders'); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $accessToken, 'Content-Type: application/vnd.api+json', ], CURLOPT_POSTFIELDS => json_encode($body, JSON_UNESCAPED_SLASHES), ]); $order = json_decode(curl_exec($curl), true)['data']['attributes']; curl_close($curl); ``` --- # Estado de la orden Con el `uuid` que recibiste al [crear la orden](https://docs.sipago.app/docs/api-checkout/integracion-checkout), podés consultar en cualquier momento si ya se pagó. Como en el resto de los endpoints, la consulta se autentica con tu `access_token`. Si todavía no lo tenés, mirá [Obtener el token](https://docs.sipago.app/docs/api-checkout/introduccion#obtener-el-token-jwt). **Cuándo consultarlo:** - Cuando recibís un [webhook](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#webhook-url), para confirmar que el aviso es real. - Cuando el comprador vuelve a tu sitio por la [URL de retorno](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#url-de-retorno). - Si no te llegó el webhook, por ejemplo para revisar periódicamente las órdenes pendientes. ## Consultar una orden **`GET {base_url}/api/v2/orders/{uuid}`**, donde `{base_url}` es la URL de *Checkout* del ambiente que uses ([Ambientes](https://docs.sipago.app/docs/api-checkout/ambientes#checkout)). **Headers:** ```text Authorization: Bearer {access_token} Content-Type: application/vnd.api+json ``` **cURL** ```bash curl --request GET 'https://{base_url}/api/v2/orders/{uuid}' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/vnd.api+json' ``` **Node.js** ```js // Node.js 18 o superior (fetch incluido) const res = await fetch(`${BASE_URL}/api/v2/orders/${uuid}`, { headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/vnd.api+json', }, }); const order = (await res.json()).data.attributes; if (order.status === 'SUCCESS') { // pago aprobado } ``` **PHP** ```php true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $accessToken, 'Content-Type: application/vnd.api+json', ], ]); $order = json_decode(curl_exec($curl), true)['data']['attributes']; curl_close($curl); if ($order['status'] === 'SUCCESS') { // pago aprobado } ``` Si el `uuid` no existe, la respuesta es `404`. Si el token falta, es inválido o venció, pedí uno nuevo ([Obtener el token](https://docs.sipago.app/docs/api-checkout/introduccion#obtener-el-token-jwt)) y reintentá. ## Respuesta ```json { "data": { "id": "/api/v2/orders/0b7f233a-c5ca-4968-9b72-40a239f80355", "type": "Order", "attributes": { "uuid": "0b7f233a-c5ca-4968-9b72-40a239f80355", "orderNumber": "00000001-0000000012", "status": "SUCCESS", "price": { "currency": "032", "amount": 100000 }, "shipping": null, "items": [ { "name": "Silla Eames Base Madera", "quantity": 1, "unitPrice": { "currency": "032", "amount": 100000 }, "itemId": null } ], "links": { "checkout": "https://{checkout_url}/orders/0b7f233a-c5ca-4968-9b72-40a239f80355", "redirect_url": { "success": null, "failed": null } }, "hasPendingPayment": false, "payment": { "id": 123, "authorization_code": "012345", "reference_number": "62d6c4784212b", "status": "APPROVED" }, "payments": [ { "id": 123, "authorization_code": "012345", "reference_number": "62d6c4784212b", "status": "APPROVED" } ] } } } ``` | Campo (dentro de `data.attributes`) | Descripción | |-------------------------------------|-------------| | `status` | Estado de la orden. Es el campo principal: ver [estados](#estados). | | `price.amount` | Total de la orden en centavos, con envío incluido. | | `payment` | El último intento de pago, o `null` si todavía no hubo ninguno. | | `payment.status` | Resultado de ese intento, por ejemplo `APPROVED`. | | `payment.authorization_code` | Código de autorización del pago. | | `payment.reference_number` | Número de referencia del pago. | | `payments` | Todos los intentos de pago de la orden. | | `hasPendingPayment` | `true` si hay un pago en proceso. | > **Nota: Nombres de campos distintos al webhook** > > Acá los campos del pago se llaman `authorization_code` y `reference_number`. En el webhook, los mismos datos se llaman `authorizationCode` y `refNumber`. ## Estados Son los mismos valores que trae el campo `order.status` del webhook. | `status` | Qué significa | Qué hacer | |----------|---------------|-----------| | `PENDING` | La orden está creada y todavía no tiene un pago aprobado. También queda así si el comprador intentó pagar y se rechazó: puede volver a intentarlo. | Esperar. | | `SUCCESS` | El pago se aprobó. | Dar el pedido por pagado. | | `EXPIRED` | Venció el tiempo para pagar, o la intención de pago falló. | Cancelar el pedido o crear una orden nueva. | | `FAILED_CHECKOUT` | Hubo un error durante el checkout, por ejemplo con los datos de pago. | Crear una orden nueva si el comprador quiere reintentar. | | `FAILED` | No se pudo crear la intención de pago, antes de que el comprador intentara pagar. | Revisar los datos de la orden y crear una nueva. | --- # Ambientes Hay dos ambientes, cada uno con sus propias URLs y credenciales: - **Desarrollo:** para probar la integración. Los pagos no son reales. - **Producción:** pagos reales. Usá siempre las URLs y credenciales del **mismo** ambiente: un token de desarrollo no sirve en producción, ni al revés. ## Resumen | | Desarrollo | Producción | |---|---|---| | **Auth Server** (para obtener el token) | `https://auth.stg.geopagos.io/oauth/token` | `https://auth.prd.geopagos.io/oauth/token` | | **Checkout** (`{base_url}` de la API) | `https://api-cabal.preprod.geopagos.com` | `https://api.sipago.coop` | | **Credenciales** | [Credenciales de desarrollo](https://docs.sipago.app/docs/api-checkout/credenciales-desarrollo) (públicas) | Las que te entrega SiPago | ## Checkout Es el `{base_url}` que aparece en las páginas de la API. Por ejemplo, en desarrollo, crear una orden es `POST https://api-cabal.preprod.geopagos.com/api/v2/orders`. | Ambiente | Base URL | |----------|----------| | Desarrollo | `https://api-cabal.preprod.geopagos.com` | | Producción | `https://api.sipago.coop` | ## Auth Server Es donde pedís el token ([Obtener el token](https://docs.sipago.app/docs/api-checkout/introduccion#obtener-el-token-jwt)). | Ambiente | Base URL | URL completa | |----------|----------|--------------| | Desarrollo | `https://auth.stg.geopagos.io` | `https://auth.stg.geopagos.io/oauth/token` | | Producción | `https://auth.prd.geopagos.io` | `https://auth.prd.geopagos.io/oauth/token` | --- # Credenciales de desarrollo Para empezar a probar la API en el ambiente de **desarrollo**, podés usar estas credenciales: | Dato | Valor | |------|-------| | `client_id` | `3c21db0f-6913-43db-88d6-2ced87b99a91` | | `client_secret` | `ft6z30q2ftsmu90au0mp` | Funcionan solo con las URLs de desarrollo (ver [Ambientes](https://docs.sipago.app/docs/api-checkout/ambientes)). Por ejemplo, para obtener un token: ```bash curl --request POST 'https://auth.stg.geopagos.io/oauth/token' \ --header 'Content-Type: application/json' \ --data '{ "grant_type": "client_credentials", "client_id": "3c21db0f-6913-43db-88d6-2ced87b99a91", "client_secret": "ft6z30q2ftsmu90au0mp", "scope": "*" }' ``` > **Atención** > > Estas credenciales son públicas y compartidas: no las uses en producción. Para producción, SiPago te entrega tus propias credenciales. ## Soporte Si tenés dudas o necesitás tarjetas de prueba, escribinos a [consultas@sipago.coop](mailto:consultas@sipago.coop). --- # Preguntas frecuentes ## Ambiente de pruebas ### ¿Qué tarjetas de prueba puedo usar para simular un pago aprobado y uno rechazado? Para simular un pago aprobado, pedile al equipo SiPago una tarjeta habilitada para el ambiente de pruebas. Para simular un pago rechazado, podés usar cualquier tarjeta Visa o Mastercard que tengas. El ambiente de pruebas va a rechazar el pago. ### Las credenciales de desarrollo son compartidas. ¿Los demás integradores pueden ver mis órdenes? ¿Puedo tener credenciales de prueba propias? Las [credenciales de desarrollo](https://docs.sipago.app/docs/api-checkout/credenciales-desarrollo) son compartidas. Sin embargo, los demás integradores no pueden ver las órdenes que creás, porque necesitarían conocer el UUID de la orden para acceder a ella. No es posible generar credenciales individuales para cada comercio en el ambiente de pruebas, por eso las credenciales son compartidas. ## Paso a producción ### ¿Hace falta alguna homologación o aprobación antes de operar en producción, o alcanza con generar las credenciales en el portal? Para operar en producción, tenés que generar las credenciales en el portal (**Tiendas → Credenciales Checkout**). Para eso, solicitá autorización a SiPago a través de la filial correspondiente. Una vez que Sistemas apruebe la solicitud, te habilitamos el acceso al módulo de credenciales. ### ¿Cuál es el dominio del checkout en producción? El dominio es `https://checkout.sipago.coop`. Los links de pago tienen este formato: ``` https://checkout.sipago.coop/orders/{uuid} ``` ## Webhook y estados ### ¿El webhook viene firmado o se envía desde IPs fijas que pueda validar? El webhook no viene firmado: no incluye ningún encabezado de firma, HMAC ni mecanismo de autenticación. No tenemos confirmación de nuestro proveedor, pero, según nuestras pruebas, todos los pagos en producción generaron webhooks desde la IP `54.173.130.251`, que pertenece a AWS. > **Atención: No uses la IP como única validación** > > Esa IP no está garantizada y puede cambiar sin aviso. Si filtrás los webhooks solo por IP y cambia, vas a dejar de recibir los avisos sin enterarte. Para validar un webhook: > > 1. Agregá un token secreto en tu [`webhookUrl`](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#webhook-url) (por ejemplo, `?token=UN_SECRETO_TUYO`) y rechazá los avisos que no lo traigan. > 2. Confirmá siempre el estado con [`GET /api/v2/orders/{uuid}`](https://docs.sipago.app/docs/api-checkout/estado-orden) antes de dar el pedido por pagado. ### ¿Se envía un webhook cuando un pago es rechazado y el comprador reintenta dentro de la misma orden? ¿La orden pasa por otros estados? Sí. Cada intento de pago dentro de la misma orden genera su propio webhook. Lo verificamos en producción. El intento rechazado llegó con la orden en estado `PENDING` y el pago en estado `DENIED`. En cambio, el reintento aprobado llegó con la orden en estado `SUCCESS` y el pago en estado `APPROVED`. La orden se queda en estado `PENDING` mientras el comprador hace nuevos intentos de pago. Los estados posibles de la orden son `PENDING`, `SUCCESS`, `EXPIRED`, `FAILED_CHECKOUT` y `FAILED`. No observamos otros estados. **Dos aclaraciones importantes:** - **Consulta de la orden por API:** si hubo intentos rechazados, el campo `payment` puede venir en `null`, incluso cuando la orden está en estado `SUCCESS`. Todos los intentos están en el listado `payments[]`, donde figura el intento rechazado con su código de error (por ejemplo, `05 – Denegada`). Para confirmar el pago, buscá en `payments[]` el registro cuyo `status` sea `APPROVED`. - **Vencimiento:** si usás [`expireLimitMinutes`](https://docs.sipago.app/docs/api-checkout/integracion-avanzada#expirelimitminutes) y el link vence, no se envía ningún webhook. En esos casos, consultá el estado de la orden por API. ### ¿Cuáles son los valores mínimo y máximo de `expireLimitMinutes`? - **Mínimo:** `0`, que significa que la orden no vence. Lo mismo pasa si no enviás el campo. El vencimiento efectivo más corto es de 1 minuto, con unos segundos de margen. - **Máximo:** no tiene un límite de negocio establecido. - **Formato:** tiene que ser un número entero en el JSON. ## Checkout ### ¿Puedo personalizar el checkout con mi logo? ¿Cómo figura el comercio en el resumen de la tarjeta del comprador? No es posible personalizar el checkout, porque depende de nuestro proveedor, que ofrece un formato fijo. ### ¿Puedo enviar el email o el documento del comprador para precargarlos? No es posible precargar los datos del comprador en el checkout, por el mismo motivo: el formato depende de nuestro proveedor. ### ¿SiPago le envía un comprobante al comprador? Sí. Si el comprador ingresa su email, recibe una notificación cuando el pago se aprueba. ### ¿Hay restricciones de formato en los ítems? Por ejemplo, que el campo `id` sea numérico o que `name` tenga una longitud máxima: - **`id`:** no tiene restricciones de formato. Acepta números, texto, valores negativos y `0`. También lo podés omitir. Sin embargo, el identificador no se guarda y la consulta de la orden devuelve `itemId: null`. No lo uses para conciliar operaciones: para eso, guardá el UUID de la orden. - **`name`:** no se valida la longitud. Se aceptan hasta 100.000 caracteres sin error, pero el valor se guarda truncado a 100 caracteres. Enviá como máximo 100 caracteres para que el texto no se corte en el checkout. - **`quantity`:** tiene que ser un número entero. Si enviás un decimal, recibís un error HTTP 400. - **`unitPrice.amount`:** tiene que ser un número entero que represente el importe en centavos. Por ejemplo, para $200,69 enviá `20069`. Si enviás un decimal, recibís un error HTTP 422. --- Descargá el plugin ========= **¿Qué es WooCommerce?** WooCommerce permite a los comercios crear una tienda online en un sitio de WordPress e impulsar las ventas por internet. Sipago tiene un plugin para que puedas cobrar con tarjetas de débito y crédito en tu tienda online hecha con WooCommerce. Empezá a usar el plugin de Sipago en tu tienda WooCommerce con unos pocos pasos. No necesitas desarrolladores. ¡Instalá el plugin gratis! [Imagen: woocommerce] Creá una cuenta en Sipago ========= Ingresá en [www.sipago.coop](https://www.sipago.coop) para darte de alta como comercio. Si ya tenés una cuenta en Sipago omití este paso. Descargá el plugin Sipago para tiendas de WooCommerce ========= Descargá la versión más reciente del plugin: PHP 8 o superior: [sipago-woocommerce-plugin2.0.2.zip](https://web-sipago-online.oss-us-west-1.aliyuncs.com/plugin/sipago-woocommerce-2.0.2.zip) PHP menor a 8: [sipago-woocommerce-plugin1.9.22.zip](https://web-sipago-online.oss-us-west-1.aliyuncs.com/plugin/sipago-woocommerce-plugin1.9.22.0.zip) Hacé clic en Descargar y guardá el archivo .zip en tu computadora. Una vez descargado, podrás instalarlo directamente desde el panel de administración de WooCommerce, sin necesidad de descomprimir el archivo. --- Configurá WooCommerce ========= ## Generación de credenciales Para generar credenciales, primero deberás comunicarte con nuestro equipo para solicitar autorización. Una vez autorizado, ingresá a portal.sipago.coop. En el panel principal, dirigite a la sección **Tiendas**, donde podrás generar tus credenciales. Asegurate de guardar el **Client ID** y el **Client Secret** en un lugar seguro, ya que solo se mostrarán una única vez. [Imagen: credentials] ## Configuración del plugin 1. Descargá el plugin como se indica en la sección anterior. 2. Instalalo en WooCommerce y activalo. 3. Dirigite a **WooCommerce → Ajustes → Pagos → SiPago → Administrar (Manage)**. 4. Allí deberás ingresar el **Client ID** y el **Client Secret** que generaste anteriormente. Además, verificá que el ambiente esté configurado como **Producción**. [Imagen: configCredentials] Desde esta pantalla también podrás configurar: - El título y la descripción que verá el cliente durante el checkout. - El estado de la orden una vez aprobado el pago (Completado o Procesando, si necesitás gestionar envíos). - Otras opciones de configuración del plugin. [Imagen: plugin_example] --- # Flujo de cobro con QR (API v4) Con la **API QR** de SiPago generás códigos QR de cobro (dinámicos y estáticos), escuchás la confirmación del pago en tiempo real y conciliás las operaciones. ## Cómo funciona El flujo tiene cuatro etapas: [Imagen: Flujo de cobro con QR: 1. Autenticación con access_token OAuth 2.0, 2. Crear un Point of Sale por vendedor, 3. Generar un QR dinámico o estático y obtener qr_id, 4. Confirmar el pago mediante SSE o polling.] | Etapa | Qué hacés | Dónde está explicado | |-------|-----------|----------------------| | 1 | **Autenticación:** obtener un `access_token` OAuth 2.0 con las credenciales que te entrega SiPago. | [Autenticación](https://docs.sipago.app/docs/api-qr/autenticacion) | | 2 | **Points of Sale:** crear los puntos de venta necesarios (por ejemplo, uno por vendedor). | [Points of Sale](https://docs.sipago.app/docs/api-qr/points-of-sale) | | 3 | **Generación de QR:** crear QRs dinámicos (con monto) o estáticos (sin monto) asociados a un point of sale. Se obtiene un `qr_id`. | [Generar QR](https://docs.sipago.app/docs/api-qr/generar-qr) | | 4 | **Conciliación:** con el `qr_id` consultás el estado del pago, por suscripción SSE (tiempo real) o por consulta directa (polling), y obtenés el detalle de la autorización. | [Confirmación de pago](https://docs.sipago.app/docs/api-qr/confirmacion-pago) y [Conciliación](https://docs.sipago.app/docs/api-qr/conciliacion) | > **Consejo: Antes de empezar** > > Revisá los [requisitos](https://docs.sipago.app/docs/api-qr/requisitos): necesitás credenciales y los datos de tu comercio. **Siguiente paso:** [requisitos](https://docs.sipago.app/docs/api-qr/requisitos). --- # Requisitos ## Credenciales Antes de comenzar, pedile credenciales al equipo de SiPago: | Credencial | Descripción | |------------|-------------| | `client_id` | Identificador del cliente OAuth. Es **particular de cada cliente/integrador** y debe solicitarse al equipo de SiPago. | | `client_secret` | Secreto del cliente OAuth. Es **particular de cada cliente/integrador** y debe solicitarse al equipo de SiPago. | | `TENANT_IDENTIFIER` | Identificador del tenant (UUID del realm), necesario para la autenticación y la suscripción SSE. **Valor fijo:** `5662e892-59f2-4a54-aea8-cfbf77942843` | > **Info: TENANT_IDENTIFIER es el mismo para todos** > > El valor `5662e892-59f2-4a54-aea8-cfbf77942843` es un identificador **único y compartido**: lo usan **todos los clientes/integradores** por igual, tanto en el path del endpoint de [autenticación](https://docs.sipago.app/docs/api-qr/autenticacion) como en el tópico de la [suscripción SSE](https://docs.sipago.app/docs/api-qr/confirmacion-pago#sse). No hace falta pedirlo ni varía entre integraciones, a diferencia de `client_id` y `client_secret`, que sí son de cada cliente. > **Peligro: Las credenciales son sensibles** > > Guardalas de forma segura (vault / secret manager). Nunca las subas a un repositorio ni las expongas en el front-end. ## Información del comercio Vas a necesitar estos datos de tu comercio: - `SUBSIDIARY_ID` - `MERCHANT_ID` Los obtenés ingresando en [https://portal.sipago.coop/](https://portal.sipago.coop/). En tu perfil, en **Información del Comercio**, aparecen como **ID Sucursal** (`SUBSIDIARY_ID`) e **ID Merchant** (`MERCHANT_ID`). [Imagen: Perfil del portal SiPago: sección Información del Comercio con Comercio, ID Sucursal e ID Merchant] **Siguiente paso:** [ambientes](https://docs.sipago.app/docs/api-qr/ambientes). --- # Ambientes y hosts | Servicio | Host | |----------|------| | Identity Provider (Keycloak) | `https://idp.prd.geopagos.io` | | API principal | `https://api.prd.geopagos.io` | | API mPOS (generación de QR) | `https://api-mpos-cabal.prd.geopagos.io` | > **Nota: Hosts de Points of Sale** > > En la documentación original, crear, actualizar y eliminar points of sale usan `https://api.geopagos.io`, mientras que listar usa `https://api.prd.geopagos.io`. Se respetan tal cual en [Points of Sale](https://docs.sipago.app/docs/api-qr/points-of-sale). ## Referencia rápida de endpoints | # | Operación | Método | Endpoint | |---|-----------|--------|----------| | 1 | Obtener token | POST | `idp.prd.geopagos.io/auth/realms/{tenant}/protocol/openid-connect/token` | | 2 | Crear point of sale | POST | `api.geopagos.io/api/v4/points-of-sale` | | 3 | Actualizar point of sale | PUT | `api.geopagos.io/api/v4/points-of-sale/{uuid}` | | 4 | Eliminar point of sale | DELETE | `api.geopagos.io/api/v4/points-of-sale/{uuid}` | | 5 | Listar points of sale | GET | `api.prd.geopagos.io/api/v4/points-of-sale/` | | 6 | Crear QR (dinámico/estático) | POST | `api-mpos-cabal.prd.geopagos.io/api/v4/payments/qr/{pos_uuid}` | | 7 | Suscripción SSE | GET | `api.prd.geopagos.io/sse/notifications?topic=urn:{tenant}:intent_{qr_id}` | | 8 | Estado de QR (dinámico) | GET | `api.prd.geopagos.io/api/v4/payments/intents/{qr_id}/sse` | | 9 | Detalle de intent (dinámico) | GET | `api.prd.geopagos.io/api/v4/payments/intents/{qr_id}` | | 10 | Detalle de venta | GET | `api.prd.geopagos.io/api/v4/payments/authorizations/{reference_number}` | | 11 | Lista de movimientos | GET | `api.prd.geopagos.io/api/v4/payments/authorizations[?point_of_sale={uuid}]` | --- # Autenticación Se usa el flujo OAuth 2.0 `client_credentials` contra el Identity Provider. ## Request **`POST https://idp.prd.geopagos.io/auth/realms/{TENANT_IDENTIFIER}/protocol/openid-connect/token`** `Content-Type: application/x-www-form-urlencoded` ```bash curl --location 'https://idp.prd.geopagos.io/auth/realms/{TENANT_IDENTIFIER}/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id={CLIENT_ID}' \ --data-urlencode 'client_secret={CLIENT_SECRET}' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode 'scope=openid' ``` ## Response — 200 OK ```json { "access_token": "eyJhbGciOiJSUzI1NiIs...", "expires_in": 900, "refresh_expires_in": 0, "token_type": "Bearer", "id_token": "eyJhbGciOiJSUzI1NiIs...", "not-before-policy": 0, "scope": "openid platform merchant", "tid": "{TENANT_IDENTIFIER}" } ``` ## Consideraciones - El token expira a los 900 segundos (15 minutos). No hay refresh token (`refresh_expires_in: 0`): al expirar, pedí un token nuevo repitiendo este request. - El token se envía en las siguientes llamadas con el header `Authorization: Bearer {access_token}`. > **Consejo: Renová el token antes de que venza** > > Cacheá el token y renovalo antes de su expiración (por ejemplo, a los ~13 minutos) para evitar errores 401 en llamadas concurrentes. **Siguiente paso:** [crear un point of sale](https://docs.sipago.app/docs/api-qr/points-of-sale). --- # Points of Sale Un point of sale (PoS) representa un punto de cobro. La recomendación es crear uno por vendedor (o por caja/dispositivo, según el modelo de negocio). Los QRs siempre se generan asociados a un point of sale. Para crear un PoS necesitás el `merchant_id` y el `subsidiary_id` (ver [Información del comercio](https://docs.sipago.app/docs/api-qr/requisitos#comercio)). ## Crear point of sale **`POST https://api.geopagos.io/api/v4/points-of-sale`** El `SUBSIDIARY_ID` y el `MERCHANT_ID` del body se obtienen desde el portal de SiPago. ```bash curl --location 'https://api.geopagos.io/api/v4/points-of-sale' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer {access_token}' \ --data '{ "name": "point 3", "description": "punto de venta point 3", "reference": "subsidiary.{SUBSIDIARY_ID}", "merchant_id": "{MERCHANT_ID}" }' ``` **Response — 200 OK** ```json { "uuid": "1ddddecf-3bfa-4dab-96db-607afe061a5c", "name": "point 3", "description": "punto de venta point 3", "reference": "subsidiary.1739" } ``` > **Info: Guardá el uuid** > > El campo `uuid` es el identificador del point of sale (`point_of_sale_id`). Guardalo: se usa para generar QRs y filtrar movimientos. ## Actualizar point of sale **`PUT https://api.geopagos.io/api/v4/points-of-sale/{point_of_sale_uuid}`** ```bash curl --location --request PUT 'https://api.geopagos.io/api/v4/points-of-sale/{point_of_sale_uuid}' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer {access_token}' \ --data '{ "name": "point 3 updated" }' ``` **Response — 200 OK** ```json { "uuid": "1ddddecf-3bfa-4dab-96db-607afe061a5c", "name": "point 3 updated", "description": "", "reference": "" } ``` > **Atención: El PUT reemplaza todo** > > Los campos no enviados vuelven vacíos (ver `description` y `reference` en el ejemplo). Enviá siempre el objeto completo si querés conservar los valores existentes. ## Eliminar point of sale **`DELETE https://api.geopagos.io/api/v4/points-of-sale/{point_of_sale_uuid}`** **Response — 204 No Content** ## Listar points of sale **`GET https://api.prd.geopagos.io/api/v4/points-of-sale/`** ```bash curl --location 'https://api.prd.geopagos.io/api/v4/points-of-sale/' \ --header 'Authorization: Bearer {access_token}' \ --header 'Accept: application/json' ``` **Response — 200 OK** ```json [ { "uuid": "6fda9b64-d317-4723-9480-cda88579c987", "name": "Federico kucher", "description": "Point of sale for user kucherfederico@gmail.com", "reference": "subsidiary.1739" } ] ``` **Siguiente paso:** [generar un QR](https://docs.sipago.app/docs/api-qr/generar-qr). --- # Generar QR Los QRs se generan por point of sale, indicando el `uuid` del PoS en el path. Hay dos tipos: | Tipo | Uso | Monto | Vigencia | |------|-----|-------|----------| | Dinámico | Una venta puntual (se incluyen ítems y monto) | Fijo, definido al crearlo | Expira (~1 hora) | | Estático | QR impreso/permanente del punto de venta | Lo ingresa el pagador | No expira | ## Crear QR dinámico **`POST https://api-mpos-cabal.prd.geopagos.io/api/v4/payments/qr/{point_of_sale_uuid}`** ```bash curl --location 'https://api-mpos-cabal.prd.geopagos.io/api/v4/payments/qr/{point_of_sale_uuid}' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/json' \ --data '{ "source": "mpos_app", "type": "GENERIC", "items": [ { "name": "Venta", "amount": 111.0, "quantity": 1 } ], "currency": "032" }' ``` | Campo | Descripción | |-------|-------------| | `source` | Origen de la operación (ej.: `mpos_app`) | | `type` | Tipo de QR (`GENERIC`) | | `items[]` | Ítems de la venta: `name`, `amount`, `quantity` | | `currency` | Código ISO 4217 numérico (`032` = Peso argentino) | **Response — 200 OK** ```json { "id": "8cf1ce0f-8969-4029-b8ba-87406971d54d", "hash": "8cf1ce0f-8969-4029-b8ba-87406971d54d", "qr": "00020101021230150011coop.sipago...", "expires_at": "2026-06-16T15:41:19.1549225Z", "expires_in_seconds": 3599 } ``` | Campo | Descripción | |-------|-------------| | `id` / `hash` | `qr_id`: identificador del QR / intent de pago. Se usa para consultar el estado y conciliar. Guardalo asociado a la venta. | | `qr` | Payload EMVCo del QR. Es el contenido a mostrar como imagen QR. | | `expires_at` / `expires_in_seconds` | Vencimiento del QR dinámico (~3600 segundos) | > **Consejo: Cómo mostrar el QR** > > El string `qr` se convierte en imagen con cualquier librería estándar de generación de códigos QR (formato EMVCo / QR interoperable). ## Obtener QR estático Mismo endpoint, sin `items` (no lleva monto): ```bash curl --location 'https://api-mpos-cabal.prd.geopagos.io/api/v4/payments/qr/{point_of_sale_uuid}' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/json' \ --data '{ "source": "mpos_app", "type": "GENERIC", "currency": "032" }' ``` **Response — 200 OK** ```json { "id": "7c25437f-5543-4dde-beff-f606ead88cb0", "hash": "7c25437f-5543-4dde-beff-f606ead88cb0", "qr": "00020101021130150011coop.sipago..." } ``` > **Atención: Conciliación del QR estático** > > La [consulta de estado](https://docs.sipago.app/docs/api-qr/confirmacion-pago#estado) y el [detalle del intent](https://docs.sipago.app/docs/api-qr/confirmacion-pago#intent) aplican solo a QR dinámico. Para QR estático, conciliá con la [lista de movimientos](https://docs.sipago.app/docs/api-qr/conciliacion#movimientos) filtrada por point of sale o con las [notificaciones SSE](https://docs.sipago.app/docs/api-qr/confirmacion-pago#sse). **Siguiente paso:** [confirmar el pago](https://docs.sipago.app/docs/api-qr/confirmacion-pago). --- # Confirmación de pago Una vez generado el QR y mostrado al pagador, hay dos formas de conocer el resultado: - **Tiempo real (recomendada):** suscripción SSE al tópico del QR. - **Polling:** consulta periódica del estado del intent. ## Suscripción SSE (tiempo real) **`GET https://api.prd.geopagos.io/sse/notifications?topic=urn%3A{TENANT_IDENTIFIER}%3Aintent_{QrHash}`** `Accept: text/event-stream` ```bash curl --location 'https://api.prd.geopagos.io/sse/notifications?topic=urn%3A{TENANT_IDENTIFIER}%3Aintent_{QrHash}' \ --header 'Authorization: Bearer {access_token}' \ --header 'Accept: text/event-stream' ``` | Parámetro | Descripción | |-----------|-------------| | `TENANT_IDENTIFIER` | UUID del tenant, valor compartido por todos los clientes: `5662e892-59f2-4a54-aea8-cfbf77942843` (ver [Credenciales](https://docs.sipago.app/docs/api-qr/requisitos#credenciales)) | | `QrHash` | `id` / `hash` del QR generado | El tópico tiene la forma `urn:{TENANT_IDENTIFIER}:intent_{QrHash}` (URL-encoded: `urn%3A...%3Aintent_...`). Cuando el QR se paga, la notificación SSE incluye el `reference_number` de la autorización, que permite obtener el [detalle completo de la venta](https://docs.sipago.app/docs/api-qr/conciliacion#detalle-venta). > **Nota: Consideraciones de implementación** > > Mantené la conexión abierta mientras el QR esté vigente, implementá reconexión ante cortes y tené en cuenta la expiración del token (15 min) para streams de larga duración. ## Consultar estado de un QR (solo QR dinámico) **`GET https://api.prd.geopagos.io/api/v4/payments/intents/{qr_id}/sse`** ```bash curl --location 'https://api.prd.geopagos.io/api/v4/payments/intents/{qr_id}/sse' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer {access_token}' ``` | Código | Significado | |--------|-------------| | `204 No Content` | El QR no fue pagado aún | | `200 OK` | El QR fue pagado (se devuelve la autorización) | **Response — 200 OK (pagado)** ```json { "id": "3f59b249-59f3-42f7-aa12-42a99dadc92b", "type": "SALE", "status": "APPROVED", "refNumber": "09841023-b6da-4384-a4d4-a852dc4e923e", "entryMode": "QR", "totalAmount": 85.00 } ``` El campo `refNumber` es el `reference_number` de la autorización, necesario para obtener el [detalle completo](https://docs.sipago.app/docs/api-qr/conciliacion#detalle-venta). ## Obtener detalle del intent (solo QR dinámico) Devuelve el estado y la composición del intent de pago (venta), aunque todavía no se haya pagado. **`GET https://api.prd.geopagos.io/api/v4/payments/intents/{qr_id}`** **Response — 200 OK** ```json { "uuid": "8cf1ce0f-8969-4029-b8ba-87406971d54d", "state": "PENDING", "amount": 111.0, "tip_amount": 0.0, "currency": "032", "origin": "mpos_app", "items": [ { "amount": 111.0, "quantity": 1, "name": "Venta" } ], "taxes": [], "authorizations": [], "metadata": { "can_refund": false, "refund_without_card": false }, "point_of_sale_id": "6fda9b64-d317-4723-9480-cda88579c987" } ``` Estados observados del intent: `PENDING` (a la espera de pago) → `SALE_CONFIRMED` (venta confirmada). Cuando el intent se paga, el array `authorizations[]` contiene la autorización con su `reference_number`. **Siguiente paso:** [conciliación](https://docs.sipago.app/docs/api-qr/conciliacion). --- # Conciliación ## Detalle de venta por número de referencia El `reference_number` se obtiene cuando un QR se paga, desde cualquiera de estas fuentes: - La [notificación SSE](https://docs.sipago.app/docs/api-qr/confirmacion-pago#sse) - La [consulta de estado del QR](https://docs.sipago.app/docs/api-qr/confirmacion-pago#estado) (campo `refNumber`) - La [lista de movimientos](#movimientos) (campo `tracking.reference_number`) **`GET https://api.prd.geopagos.io/api/v4/payments/authorizations/{reference_number}`** **Response — 200 OK (resumen de campos relevantes)** ```json { "reference_number": "2147792a-a875-4651-bd01-223f803243f8", "type": "SALE", "status": "APPROVED", "amount": 111.00, "date_time": "2026-06-16T15:08:09Z", "currency": "032", "entry_mode": { "source": "QR", "qr": { "content": "0002010102...", "id": "561f4def-3874-4e67-8ab3-ac2ea65996fc" } }, "payment_method": { "type": "TRANSFER" }, "merchant": { "identification": { "type": "CUIT", "number": "20338975938" }, "legal_name": "federico kucher", "trade_name": "federico kucher" }, "processor_result": { "date": "2026-06-16T15:08:09Z", "authorization_code": "86VRPQ2GMLY40QKG2GLY0M", "processor_reference_number": "2147792a-a875-4651-bd01-223f803243f8" }, "payment_plan": { "installment_quantity": 1 }, "payment_intent": { "uuid": "561f4def-3874-4e67-8ab3-ac2ea65996fc", "state": "SALE_CONFIRMED", "point_of_sale_id": "6fda9b64-d317-4723-9480-cda88579c987" } } ``` Campos clave para conciliar: | Campo | Descripción | |-------|-------------| | `reference_number` | Identificador único de la autorización | | `status` | Estado de la operación (`APPROVED`) | | `entry_mode.qr.id` / `payment_intent.uuid` | `qr_id` del QR que originó el pago (vínculo con la venta local) | | `processor_result.authorization_code` | Código de autorización del procesador | | `payment_method.type` | Medio de pago usado (ej.: `TRANSFER`) | | `payment_intent.point_of_sale_id` | Point of sale (vendedor) asociado | ## Lista de movimientos Permite la conciliación batch de todas las operaciones del comercio. La respuesta está paginada. **`GET https://api.prd.geopagos.io/api/v4/payments/authorizations`** Filtrado por point of sale (por ejemplo, para conciliar por vendedor): **`GET https://api.prd.geopagos.io/api/v4/payments/authorizations?point_of_sale={point_of_sale_uuid}`** **Response — 200 OK (resumen)** ```json { "data": [ { "date_time": "2026-06-16T15:08:09Z", "type": "SALE", "status": "APPROVED", "origin": "mpos_app", "amount": 111.0, "payment_method": { "type": "TRANSFER" }, "payment_plan": { "installment_quantity": 1, "financial_rate": 0.0 }, "tracking": { "authorization_code": "86VRPQ2GMLY40QKG2GLY0M", "reference_number": "2147792a-a875-4651-bd01-223f803243f8", "payment_intent_uuid": "561f4def-3874-4e67-8ab3-ac2ea65996fc" }, "entry_mode": { "source": "QR" }, "billing": { "currency": "032" }, "point_of_sale": "6fda9b64-d317-4723-9480-cda88579c987" } ], "pagination": { "items_per_page": 10, "current_page": 1, "pages": 2 } } ``` > **Consejo: Clave de conciliación** > > El campo `tracking.payment_intent_uuid` de cada movimiento es el `qr_id` del QR generado. Así podés vincular cada pago recibido con la venta registrada en tu sistema. --- # Recomendaciones de implementación ## Flujo completo 1. **Integrador → IdP:** `POST /token` (`client_credentials`). Devuelve `access_token` (15 min). 2. **Integrador → API:** `POST /points-of-sale` (una vez por vendedor). Devuelve `uuid` (`point_of_sale_id`). 3. **Integrador → API:** `POST /payments/qr/{pos_uuid}` (por cada venta). Devuelve `qr_id`, payload QR y expiración. 4. El integrador muestra el QR al pagador. 5. **Integrador → API:** `GET /sse/notifications?topic=urn:{tenant}:intent_{qr_id}`. Llega un evento SSE con el pago aprobado (`reference_number`). - Alternativa: `GET /payments/intents/{qr_id}/sse` → `204` = pendiente / `200` = pagado. 6. **Integrador → API:** `GET /payments/authorizations/{reference_number}`. Devuelve el detalle completo de la venta. ## Recomendaciones 1. **Gestión del token:** implementá un cache con renovación anticipada (el token dura 15 minutos y no hay refresh token). Ante un 401, reintentá con un token nuevo. 2. **Persistencia de identificadores:** guardá `point_of_sale_id` (por vendedor) y `qr_id` (por venta). El `qr_id` es la clave que vincula la venta local con el pago recibido (`tracking.payment_intent_uuid`). 3. **Estrategia de confirmación:** priorizá SSE para una experiencia en tiempo real; usá el endpoint de estado (`/intents/{qr_id}/sse`) como respaldo ante desconexiones, y la lista de movimientos como conciliación batch (por ejemplo, un cierre diario por point of sale). 4. **Expiración de QR dinámicos:** controlá `expires_at`; si el QR expira sin pagarse, generá uno nuevo para reintentar el cobro. 5. **Idempotencia y reintegros:** verificá `metadata.can_refund` en el intent antes de habilitar devoluciones desde tu aplicación. > **Peligro: Seguridad** > > Los tokens y credenciales nunca deben llegar al front-end del comercio; todas las llamadas a GeoPagos deben hacerse desde tu backend. --- # Introducción La API Sipago te permite integrar tu sistema con Sipago: consultar las ventas y depósitos de tu comercio y descargar reportes sin entrar al panel. Para usarla necesitás una **clave de API** (`apikey`) única para tu comercio. Podés solicitarla [contactando al equipo de Sipago](https://sipago.coop/contact). Todas las solicitudes se hacen en nombre del comercio dueño de esa clave: solo vas a ver y operar sobre tus propios datos. ## Resumen de endpoints | Sección | Método | Ruta | Para qué sirve | | --- | --- | --- | --- | | Movimientos | POST | /transactions/page/:page | Listar movimientos de un rango de fechas | | Movimientos | GET | /transactions/referenceNumber/:referenceNumber | Buscar un movimiento por número de referencia | | Movimientos | GET | /operations/downloadOperations | Descargar los movimientos en un archivo Excel NUEVO | | Movimientos | GET | /checkout/getLink/:uuid | Consultar una orden de pago y su estado | | Depósitos | POST | /deposits/paid/page/:page | Depósitos recibidos | | Depósitos | POST | /deposits/pending/page/:page | Depósitos pendientes | --- # Guía de inicio rápido En tres pasos haces tu primera solicitud. ## Paso 1 · Conseguí tu clave de API Pedila al [equipo de Sipago](https://sipago.coop/contact). Sin una clave válida, toda solicitud devuelve 401 Unauthorized. ## Paso 2 · Usá la URL base https://api-gwt.sipago.online/services/ Todas las rutas de este documento se agregan a esta URL. Por ejemplo, /transactions/page/0 se llama en https://api-gwt.sipago.online/services/transactions/page/0. ## Paso 3 · Consultá tus movimientos Esta solicitud devuelve los movimientos del 24/05/2024: ```bash curl --location 'https://api-gwt.sipago.online/services/transactions/page/0' \ --header 'apikey: TU_API_KEY' \ --header 'X-TRACE-ID: 3cc1aabd-eb40-48bc-bb84-d1c34f1e2934' \ --header 'X-VERSION: 1' \ --header 'Content-Type: application/json' \ --data '{"startDate": "2024-05-24", "endDate": "2024-05-24"}' ``` Si la respuesta es 200 OK con un objeto listTrx, ya estás integrado. El detalle de la respuesta está en [Consultar movimientos](https://docs.sipago.app/docs/api-conciliacion/consultar-movimientos). > **Peligro: Protegé tu clave de API** > > Nunca expongas tu clave de API en un repositorio público ni en código que corra en el navegador o en una app móvil. Llamá a la API siempre desde tu servidor. --- # Autenticación y encabezados Cada solicitud se autentica con tu clave de API, que se envía en el encabezado apikey. Estos son los encabezados que usa la API; aplican a todos los endpoints de este documento: | Encabezado | Valor | Obligatorio | Descripción | | --- | --- | --- | --- | | apikey | Tu clave de API | Sí | Identifica y autentica a tu comercio. | | X-VERSION | 1 | Sí | Versión de la API. | | Content-Type | application/json | En solicitudes con cuerpo | Necesario en los POST que envían JSON. | | X-TRACE-ID | Un UUID v4 nuevo por solicitud | Recomendado | Identifica la solicitud. Se devuelve en el encabezado de la respuesta; si no lo envías, la API genera uno. Guardalo: nos sirve para rastrear una solicitud si necesitas soporte. | Usá siempre **HTTPS**. La clave de API viaja en un encabezado, y por HTTP podría ser leída por terceros. ## Errores de autenticación | Caso | Respuesta 401 Unauthorized | | --- | --- | | No se envió apikey | {"message": "Missing API key found in request"} | | La apikey no es válida | {"message": "Invalid API key in request"} | --- # Fechas, paginación y errores ## Fechas Las fechas se envían en formato AAAA-MM-DD (por ejemplo, 2024-05-24). Los rangos incluyen el día inicial y el final. ## Paginación Los listados de movimientos y depósitos son paginados. El número de página va en la ruta ( :page ) y **empieza en 0**. Cada respuesta indica si hay más resultados: | Campo | Tipo | Descripción | | --- | --- | --- | | page | number | Página devuelta. | | existMore | boolean | true si hay otra página: pedí page + 1. false si es la última. | | listTrx | array | Resultados de la página. | ## Errores Además de los [errores de autenticación](https://docs.sipago.app/docs/api-conciliacion/autenticacion#errores-de-autenticacion), la API puede responder: | Código | Cuándo | | --- | --- | | 400 Bad Request | Falta un campo obligatorio o tiene el tipo incorrecto. La respuesta detalla qué campo falló. | | 401 Unauthorized | Falta la clave de API o no es válida. | | 500 Internal Server Error | Parámetros incorrectos (por ejemplo, una fecha mal formada) o un error interno. | ### Ejemplo 400 · validación ```json { "statusText": "Bad Request", "jsonSchemaValidation": true, "validations": { "body": [ { "keyword": "required", "dataPath": "", "schemaPath": "#/required", "params": { "missingProperty": "amount" }, "message": "should have required property 'amount'" } ] } } ``` ### Ejemplo 500 ```json { "status": 500, "message": "status 500", "timestamp": "2024-05-28T12:30:04Z" } ``` --- # Consultar movimientos **POST** /transactions/page/:page Devuelve tus movimientos de un rango de fechas, de forma paginada. ### PARÁMETROS DE RUTA | Nombre | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | page | number | Sí | Número de página, desde 0. | ### CUERPO (JSON) | Nombre | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | startDate | string ( AAAA-MM-DD ) | Sí | Fecha desde. | | endDate | string ( AAAA-MM-DD ) | Sí | Fecha hasta. | ### RESPUESTA 200 OK ```json { "page": 0, "existMore": false, "listTrx": [ /* Objeto Movimiento (ver Objeto Movimiento) */ ] } ``` ### EJEMPLOS **cURL** ```bash curl --location 'https://api-gwt.sipago.online/services/transactions/page/0' \ --header 'apikey: TU_API_KEY' \ --header 'X-TRACE-ID: 3cc1aabd-eb40-48bc-bb84-d1c34f1e2934' \ --header 'X-VERSION: 1' \ --header 'Content-Type: application/json' \ --data '{"startDate": "2024-05-24", "endDate": "2024-05-24"}' ``` **Node.js** ```javascript const axios = require('axios'); const { randomUUID } = require('crypto'); axios.post( 'https://api-gwt.sipago.online/services/transactions/page/0', { startDate: '2024-05-24', endDate: '2024-05-24' }, { headers: { 'apikey': process.env.SIPAGO_API_KEY, 'X-TRACE-ID': randomUUID(), 'X-VERSION': '1', 'Content-Type': 'application/json' } } ) .then((response) => console.log(response.data)) .catch((error) => console.error(error.response?.data ?? error.message)); ``` **Python** ```python import os, uuid, requests response = requests.post( "https://api-gwt.sipago.online/services/transactions/page/0", json={"startDate": "2024-05-24", "endDate": "2024-05-24"}, headers={ "apikey": os.environ["SIPAGO_API_KEY"], "X-TRACE-ID": str(uuid.uuid4()), "X-VERSION": "1", }, ) print(response.json()) ``` --- # Movimiento por referencia **GET** `/transactions/referenceNumber/:referenceNumber` Busca un movimiento por su número de referencia. ### PARÁMETROS DE RUTA | Nombre | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | referenceNumber | string | Sí | Número de referencia del movimiento. | ### RESPUESTA 200 OK Un array con los movimientos que coinciden (ver [Objeto Movimiento](https://docs.sipago.app/docs/api-conciliacion/objeto-movimiento)). ```json [ /* Objeto Movimiento */ ] ``` ### EJEMPLOS **cURL** ```bash curl --location 'https://api-gwt.sipago.online/services/transactions/referenceNumber/55268b71-e22f-4617-b5c0-8c34440df9d7' \ --header 'apikey: TU_API_KEY' \ --header 'X-TRACE-ID: 3cc1aabd-eb40-48bc-bb84-d1c34f1e2934' \ --header 'X-VERSION: 1' ``` **Node.js** ```javascript const axios = require('axios'); const { randomUUID } = require('crypto'); axios.get( 'https://api-gwt.sipago.online/services/transactions/referenceNumber/55268b71-e22f-4617-b5c0-8c34440df9d7', { headers: { 'apikey': process.env.SIPAGO_API_KEY, 'X-TRACE-ID': randomUUID(), 'X-VERSION': '1' } } ) .then((response) => console.log(response.data)) .catch((error) => console.error(error.response?.data ?? error.message)); ``` **Python** ```python import os, uuid, requests response = requests.get( "https://api-gwt.sipago.online/services/transactions/referenceNumber/55268b71-e22f-4617-b5c0-8c34440df9d7", headers={ "apikey": os.environ["SIPAGO_API_KEY"], "X-TRACE-ID": str(uuid.uuid4()), "X-VERSION": "1", }, ) response.raise_for_status() print(response.json()) ``` --- # Descargar movimientos **GET** /operations/downloadOperations Descarga tus movimientos de un rango de fechas en un archivo Excel, el mismo reporte de ventas que podés bajar desde el panel de Sipago. A diferencia de [Consultar movimientos](https://docs.sipago.app/docs/api-conciliacion/consultar-movimientos), no es paginado: el archivo trae todos los movimientos del período. ### PARÁMETROS DE CONSULTA (QUERY STRING) | Nombre | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | downloadType | string | Sí | Formato del archivo. Valor: xls . | | start_date | string (AAAA-MM-DD) | Sí | Fecha desde. | | end_date | string (AAAA-MM-DD) | Sí | Fecha hasta. | | subsidiary | string | No | ID de la sucursal, para descargar solo sus movimientos. Vacío: todas. | | status | string | No | Filtro por estado del movimiento. Vacío: todos. | | transactionTypes | string | No | Filtro por tipo de operación. Vacío: todos. | | users | string | No | Filtro por usuario que hizo la venta. Vacío: todos. | > **Consejo: Parámetros de fecha** > > Este endpoint usa `start_date` y `end_date` (con guion bajo), a diferencia de `startDate`/`endDate` de [Consultar movimientos](https://docs.sipago.app/docs/api-conciliacion/consultar-movimientos). Los parámetros van en la URL; no lleva cuerpo. ### RESPUESTA 200 OK El contenido del archivo, no JSON. Guardá el cuerpo de la respuesta tal como llega. | Encabezado | Valor | | --- | --- | | Content-Type | application/vnd.ms-excel | | Content-Disposition | `attachment; filename="sales_.xls"` | Si algo falla, la respuesta es JSON con el [formato de error](https://docs.sipago.app/docs/api-conciliacion/convenciones#errores) (`status`, `message`, `timestamp`), así que revisá el código HTTP antes de guardar el archivo. ### EJEMPLOS **cURL** ```bash curl --location --get 'https://api-gwt.sipago.online/services/operations/downloadOperations' \ --header 'apikey: TU_API_KEY' \ --header 'X-TRACE-ID: 3cc1aabd-eb40-48bc-bb84-d1c34f1e2934' \ --header 'X-VERSION: 1' \ --data-urlencode 'downloadType=xls' \ --data-urlencode 'start_date=2024-05-01' \ --data-urlencode 'end_date=2024-05-31' \ --fail --output movimientos-mayo.xls ``` **Node.js** ```javascript const axios = require('axios'); const fs = require('fs'); const { randomUUID } = require('crypto'); async function descargarMovimientos() { const response = await axios.get( 'https://api-gwt.sipago.online/services/operations/downloadOperations', { params: { downloadType: 'xls', start_date: '2024-05-01', end_date: '2024-05-31' }, headers: { 'apikey': process.env.SIPAGO_API_KEY, 'X-TRACE-ID': randomUUID(), 'X-VERSION': '1' }, responseType: 'arraybuffer' } ); fs.writeFileSync('movimientos-mayo.xls', response.data); } descargarMovimientos().catch((error) => console.error(error.response?.status, error.response?.data?.toString() ?? error.message) ); ``` **Python** ```python import os, uuid, requests response = requests.get( "https://api-gwt.sipago.online/services/operations/downloadOperations", params={"downloadType": "xls", "start_date": "2024-05-01", "end_date": "2024-05-31"}, headers={ "apikey": os.environ["SIPAGO_API_KEY"], "X-TRACE-ID": str(uuid.uuid4()), "X-VERSION": "1", }, ) response.raise_for_status() with open("movimientos-mayo.xls", "wb") as f: f.write(response.content) ``` ### Campos del archivo descargado El archivo Excel contiene las siguientes columnas, en el orden del archivo de ejemplo `ejemplo_descarga_movimientos.xls`. Cada fila corresponde a un movimiento; algunos campos pueden estar vacíos según la operación. | Campo | Descripción | | --- | --- | | Origen | Canal de origen de la operación, por ejemplo, `ECOMMERCE`, `Pagos recurrentes` o `QR`. | | Fecha | Fecha y hora del movimiento, con formato `AAAA-MM-DD HH:mm:ss` en el ejemplo. | | Nombre de comercio | Nombre del comercio al que pertenece el movimiento. | | Nombre de sucursal | Nombre de la sucursal asociada. | | Usuario | Nombre del usuario asociado a la operación. | | Estado | Estado del movimiento, por ejemplo, `Aprobada`. | | Nombre del cliente | Nombre del cliente; puede aparecer `UNKNOWN` cuando no está disponible. | | Número de documento | Número de documento del cliente, cuando está disponible. | | E-mail del cliente | Correo electrónico del cliente, cuando está disponible. | | Marca tarjeta | Marca o identificación del medio utilizado, por ejemplo, `Visa Débito`, `MasterCard Prepaga` o `PCT`. | | Número tarjeta | Número de tarjeta enmascarado; puede estar vacío en operaciones sin tarjeta. | | Método de pago | Método utilizado, por ejemplo, `Débito`, `Crédito` o `PREPAID`. | | Moneda | Código de moneda; en el ejemplo figura `032`. | | Importe bruto | Importe bruto del movimiento. | | Importe neto | Importe neto informado para el movimiento. | | Propina | Importe de la propina. | | Impuestos | Importe de impuestos informado para el movimiento. | | Arancel | Importe del arancel aplicado. | | IVA Arancel | IVA correspondiente al arancel. | | Costo Financiero | Importe del costo financiero. | | IVA Costo Financiero | IVA correspondiente al costo financiero. | | II.BB. | Importe informado bajo el concepto de Ingresos Brutos. | | Ret. IVA | Importe de la retención de IVA. | | Ret. Ganancias | Importe de la retención de Ganancias. | | Ret. IIBB SIRTAC | Importe de la retención de Ingresos Brutos SIRTAC. | | Ret. IIBB no inscripto | Importe de la retención de Ingresos Brutos por no inscripto. | | Ret. IIBB por falta de alta | Importe de la retención de Ingresos Brutos por falta de alta. | | Fecha de disponibilidad | Fecha de acreditación de fondos, con formato `AAAA-MM-DD`. | | Nº Referencia | Número de referencia del movimiento. | | Nº Autorización | Código de autorización de la operación. | | ID Terminal | Identificador de la terminal; puede ser numérico o texto, como `COELSA`. | | Tipo | Tipo de operación, por ejemplo, `Venta`. | | Lector | Información del lector, cuando está disponible; está vacío en el archivo de ejemplo. | | Cuentas de email a las que se envió boucher | Lista de correos a los que se envió el comprobante. El nombre de la columna conserva la escritura del archivo original. | | Geolocalización | Información de geolocalización, cuando está disponible; está vacía en el archivo de ejemplo. | #### Resumen de totales Al final del archivo, después del detalle de movimientos, aparece un resumen por moneda con estos campos: | Campo | Descripción | | --- | --- | | Moneda | Código de moneda del resumen. | | Total bruto | Total bruto informado para esa moneda. | | Total anuladas | Total de operaciones anuladas informado para esa moneda; está vacío en el ejemplo. | | Total impuestos | Total de impuestos informado en el resumen. | | Total neto | Total neto informado para esa moneda. | --- # Consultar orden **GET** /checkout/getLink/:uuid Devuelve una orden de pago (por ejemplo, la de un link de pago) con su estado y los pagos asociados. ### PARÁMETROS DE RUTA | Nombre | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | uuid | string | Sí | Identificador (UUID) de la orden. | ### RESPUESTA 200 OK ```json { "data": { "id": "string", "type": "string", "attributes": { "uuid": "string", "accountId": "number", "subsidiaryId": "number", "source": "string", "appId": "string", "paymentLimits": "number", "orderNumber": "string", "price": { "currency": "string", "amount": "number" }, "shipping": null, "tip": null, "items": [ { "name": "string", "quantity": "number", "unitPrice": { "currency": "string", "amount": "number" }, "itemId": null } ] }, "status": "string", "taxes": [], "externalData": { "user_uuid": "string", "checkout_url": "string", "referer": "string", "errorMessage": null }, "links": { "checkout": "string", "redirect_url": { "success": null, "failed": null } }, "hasPendingPayment": "boolean", "payment": { "id": "string", "authorization_code": "string", "reference_number": "string", "status": "string", "isRefunded": "boolean" }, "payments": [ /* misma forma que "payment" */ ], "store": null, "billing": null }, "links": [ { "checkout": "string", "redirect_url": { "success": null, "failed": null } } ] } ``` ## EJEMPLOS **cURL** ```bash curl --location 'https://api-gwt.sipago.online/services/checkout/getLink/99868576-6b2d-4e1b-8aae-784317301e46' \ --header 'apikey: TU_API_KEY' \ --header 'X-TRACE-ID: 3cc1aabd-eb40-48bc-bb84-d1c34f1e2934' \ --header 'X-VERSION: 1' ``` **Node.js** ```javascript const axios = require('axios'); const { randomUUID } = require('crypto'); axios.get( 'https://api-gwt.sipago.online/services/checkout/getLink/99868576-6b2d-4e1b-8aae-784317301e46', { headers: { 'apikey': process.env.SIPAGO_API_KEY, 'X-TRACE-ID': randomUUID(), 'X-VERSION': '1' } } ) .then((response) => console.log(response.data)) .catch((error) => console.error(error.response?.data ?? error.message)); ``` **Python** ```python import os, uuid, requests response = requests.get( "https://api-gwt.sipago.online/services/checkout/getLink/99868576-6b2d-4e1b-8aae-784317301e46", headers={"apikey": os.environ["SIPAGO_API_KEY"], "X-TRACE-ID": str(uuid.uuid4()), "X-VERSION": "1"}, ) print(response.json()) ``` --- # Objeto Movimiento Cada movimiento que devuelven [Consultar movimientos](https://docs.sipago.app/docs/api-conciliacion/consultar-movimientos) (dentro de `listTrx`) y [Movimiento por referencia](https://docs.sipago.app/docs/api-conciliacion/movimiento-referencia) tiene esta forma: ```json { "_id": "number", "amount": "number", "dateTime": "string", "firstName": "string", "lastName": "string", "userId": "number", "taxes": [], "commission": { "rate": "number", "amount": "number" }, "payment": { "paymentId": "number", "hash": "string", "method": "string", "totalAmount": "number", "hasRefund": "boolean", "canceled": "boolean", "cardNumber": "string", "cardHolderName": "string", "first5Numbers": "string", "last4Numbers": "string", "issuerName": "string", "issuerKey": "string", "paymentMethodId": "number", "name": "string", "es": "string", "en": "string", "paymentMethodType": "string", "adjustTipAvailable": "boolean", "issuerImageUrl": "string" }, "authorizationCode": "string", "authenticationType": "string", "operationCode": "string", "operation": "string", "operationId": "number", "saleId": "number", "cardName": "string", "abr": "string", "device": "string", "buyer": { "id": "number", "name": "string", "buyerId": "number", "firstName": "string", "lastName": "string", "email": "string", "address": "string", "addressLine2": "string", "province": "string", "zipCode": "string", "birthdate": "string", "phone": "string", "city": "string", "document": "string", "documentType": "string", "countryId": "string", "occupation": "string", "country": "string" }, "items": [ { "productId": "number", "isCustomAmount": "boolean", "quantity": "number", "productPrice": "number", "totalAmount": "number", "productName": "string", "productBackgroundColor": "string", "productImageUrl": "string" } ], "canAnnulate": "boolean", "canAnnulateWithoutCard": "boolean", "canRefund": "boolean", "canRefundWithoutCard": "boolean", "canceled": "boolean" } ``` --- # Depósitos Consulta los depósitos que recibió tu comercio o los que todavía están pendientes. ## Consultar depósitos **POST** `/deposits/paid/page/:page` · recibidos **POST** `/deposits/pending/page/:page` · pendientes ### PARÁMETROS DE RUTA | Nombre | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | page | number | Sí | Número de página, desde 0. | ### CUERPO (JSON) No lleva parámetros: envía un objeto vacío `{}`. ### RESPUESTA 200 OK ```json { "page": "number", "existMore": "boolean", "listTrx": [ { "_id": "string", "amount": "number", "dateTime": "string", "currency": "string", "commission": { "name": "string", "value": "number" }, "commissionAmount": "number", "commissionAndFinancialCostAmount": { "name": "string", "value": "number" }, "commissionAndFinancialCostTaxAmount": { "name": "string", "value": "number" }, "commissionTaxAmount": "number", "costPerInstallment": "number", "financialCostTaxAmount": "number", "withholdingTax": "number", "withholdingTaxAmount": "number", "total": "number", "state": "string", "toDeposit": "string" } ] } ``` ### EJEMPLOS **cURL** ```bash curl --location 'https://api-gwt.sipago.online/services/deposits/paid/page/0' \ --header 'apikey: TU_API_KEY' \ --header 'X-TRACE-ID: 3cc1aabd-eb40-48bc-bb84-d1c34f1e2934' \ --header 'X-VERSION: 1' \ --header 'Content-Type: application/json' \ --data '{}' ``` **Node.js** ```javascript const axios = require('axios'); const { randomUUID } = require('crypto'); axios.post( 'https://api-gwt.sipago.online/services/deposits/paid/page/0', { }, { headers: { 'apikey': process.env.SIPAGO_API_KEY, 'X-TRACE-ID': randomUUID(), 'X-VERSION': '1' } } ) .then((response) => console.log(response.data)) .catch((error) => console.error(error.response?.data ?? error.message)); ``` **Python** ```python import os, uuid, requests response = requests.post( "https://api-gwt.sipago.online/services/deposits/paid/page/0", json={}, headers={"apikey": os.environ["SIPAGO_API_KEY"], "X-TRACE-ID": str(uuid.uuid4()), "X-VERSION": "1"}, ) print(response.json()) ``` --- # Introducción La API de Pagos Recurrentes permite gestionar planes de suscripción, suscripciones, cobros, clientes y métodos de pago. La API expone cinco recursos principales: * **Plans**: planes de suscripción (precio, frecuencia de facturación, ciclos). * **Subscriptions**: suscripciones de un cliente a un plan. * **Charges**: cobros generados por una suscripción en cada ciclo de facturación. * **Customers**: clientes y sus métodos de pago asociados. * **Payment Methods**: métodos de pago individuales. La URL base de todos los endpoints es: ```text https://api.geopagos.io ``` | Recurso | Ruta base | | --- | --- | | Plans | `/api/v1.0/plans` | | Subscriptions | `/api/v1.0/subscriptions` | | Charges | `/api/v1.0/charges` | | Customers | `/api/v1.0/customers` | | Payment Methods | `/api/v1.0/payment-methods` | Consultá [Autenticación](https://docs.sipago.app/docs/api-suscripciones/autenticacion) para obtener el token y el [Resumen de endpoints](https://docs.sipago.app/docs/api-suscripciones/endpoints) para ver todas las operaciones. --- # Autenticación La API utiliza el flujo OAuth 2.0 client_credentials contra el Identity Provider (Keycloak) de GeoPagos. ## Credenciales necesarias | Credencial | Descripción | | --- | --- | | client_id | Identificador OAuth del cliente/integrador. Se solicita al equipo de Sipago. | | client_secret | Secreto OAuth del cliente/integrador. Se solicita al equipo de Sipago. | | TENANT_IDENTIFIER | UUID del realm, necesario para el path del endpoint de token. Valor fijo y compartido por todos los integradores: 5662e892-59f2-4a54-aea8-cfbf77942843. | > **Peligro: Protegé tus credenciales** > > Las credenciales son sensibles. Deben almacenarse en un vault/secret manager y nunca versionarse en repositorios ni exponerse en clientes front-end. ## Obtener el access token ```text POST https://idp.prd.geopagos.io/auth/realms/{TENANT_IDENTIFIER}/protocol/openid-connect/token Content-Type: application/x-www-form-urlencoded ``` ```bash curl --location 'https://idp.prd.geopagos.io/auth/realms/{TENANT_IDENTIFIER}/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id={CLIENT_ID}' \ --data-urlencode 'client_secret={CLIENT_SECRET}' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode 'scope=openid' ``` **Response 200 OK** ```json { "access_token": "eyJhbGci0iJSUzI1NiIs...", "expires_in": 900, "refresh_expires_in": 0, "token_type": "Bearer", "id_token": "eyJhbGci0iJSUzI1NiIs...", "not-before-policy": 0, "scope": "openid platform merchant", "tid": "{TENANT_IDENTIFIER}" } ``` ## Uso del token El access_token obtenido se envía en cada request contra https://api.geopagos.io mediante el header: ```text Authorization: Bearer ``` En las peticiones que envían body (POST, PUT, DELETE con body) se debe agregar además: ```text Content-Type: application/json ``` Si el header Authorization no está presente o el token es inválido, la API responde con un error de autenticación (ver [Manejo de errores](https://docs.sipago.app/docs/api-suscripciones/errores)). --- # Convenciones generales ## Paginación Los endpoints de listado (GET /plans, GET /subscriptions, GET /charges, GET /customers) aceptan limit y offset como parámetros de query y devuelven la información de paginación junto con los datos. **El valor máximo de limit es 100.** Valores mayores deben rechazarse o serán truncados por la API. Según el recurso, la respuesta puede venir como: ```json { "data": [ ... ], "total": 0, "limit": 20, "offset": 0 } ``` o ```json { "data": [ ... ], "pagination": { "total": 0, "limit": 20, "offset": 0 } } ``` ## Casing de parámetros y campos > **Atención: Nombres de campos** > > La casing de query params y campos de body no es uniforme entre recursos. Verificar la tabla de cada endpoint antes de integrar: | Recurso | Query params de filtro | Campos de body | | --- | --- | --- | | Plans | camelCase (pricingType, billingFrequency, externalReference) | snake_case (pricing_type, billing_frequency, merchant_id) | | Subscriptions | camelCase (planId, customerId, createdAfter, createdBefore) | snake_case (pause_until, cancel_at_period_end, payment_method_id) | | Charges | snake_case (subscription_id, created_after, created_before) | — | | Customers | camelCase (createdAfter) | — | ## Manejo de errores Ver [Manejo de errores](https://docs.sipago.app/docs/api-suscripciones/errores). --- # Conceptos de planes Antes de crear o actualizar planes, tené en cuenta el siguiente comportamiento del recurso. ### Información del comercio Para crear planes es necesario contar con merchant_name y merchant_id. Estos valores se obtienen ingresando a https://portal.sipago.coop/ con las credenciales del comercio y consultando la sección **Perfil**, donde se muestra la información del comercio: [Imagen: Información del Comercio en el perfil de Sipago: ID Merchant y Nombre del Merchant] merchant_name corresponde al campo **Nombre del Merchant** del portal; merchant_id corresponde al campo **ID Merchant**. Los demás campos (**Comercio, ID Sucursal**) no se usan en el body de creación de planes. ## Tipos de precio (pricing_type) | Tipo | Comportamiento | amount | | --- | --- | --- | | fixed | Todos los suscriptores pagan la misma cuota. | Obligatorio al crear el plan. Se puede modificar después (ver [Actualizar plan](https://docs.sipago.app/docs/api-suscripciones/actualizar-plan)). | | dynamic | No hay un monto inicial: cada suscripción se cobra por un monto que se define individualmente. | No aplica al crear el plan; se define por suscripción enviando next_amount en [Actualizar suscripción](https://docs.sipago.app/docs/api-suscripciones/actualizar-suscripcion). | ## Día de cobro (billing_day) El comportamiento depende de billing_frequency: * **monthly** — billing_day es el día del mes (1-31). Si se omite, el día de cobro queda fijado al día en que cada usuario se suscribe (ej.: alguien que se suscribe el 23 paga el 23 de cada mes en adelante). * **weekly** — billing_day representa un día de la semana. Si se omite, el día de cobro queda fijado al día de la semana en que cada usuario se suscribe. | billing_day | Día | | --- | --- | | 1 | Lunes | | 2 | Martes | | 3 | Miércoles | | 4 | Jueves | | 5 | Viernes | | 6 | Sábado | | 7 | Domingo | * **bimonthly, quarterly, yearly** — billing_day y billing_month son obligatorios. ## Prorrateo (prorate) Solo aplica a planes con billing_day definido. * prorate: true — la primera cuota se cobra proporcional al momento de la suscripción; desde el siguiente ciclo se cobra la cuota completa. Ej.: un plan que cobra el día 1 de cada mes, si alguien se suscribe el 15, paga la mitad de la cuota ese día y la cuota completa el 1 del mes siguiente. * prorate: false — se cobra la cuota completa sin importar el día de suscripción. ## Duración (billing_cycles) * Con billing_cycles — el plan tiene una cantidad fija de cobros. * Sin billing_cycles — el plan es indefinido, hasta que la suscripción se cancele. --- # Listar planes **`GET https://api.geopagos.io/api/v1.0/plans`** ### Query params | Parámetro | Tipo | Requerido | Descripción | | --- | --- | --- | --- | | limit | number | Sí | Cantidad de resultados por página | | offset | number | Sí | Desplazamiento para paginación | | status | string | No | Filtra por estado del plan | | pricingType | string | No | fixed o dynamic | | billingFrequency | string | No | Frecuencia de facturación (ver [Crear plan](https://docs.sipago.app/docs/api-suscripciones/crear-plan)) | | currency | string | No | Código de moneda | | search | string | No | Búsqueda por texto | | amount | number | No | Filtra por monto | | externalReference | string | No | Referencia externa del plan | ### Ejemplo de request ```bash curl -X GET "https://api.geopagos.io/api/v1.0/plans?limit=20&offset=0&status=active" \ -H "Authorization: Bearer " ``` ### Response 200 ```json { "data": [ { "plan_id": "plan_123", "subscription_url": "https://...", "name": "Plan Mensual", "amount": 1000, "currency": "ARS", "billing_frequency": "monthly", "status": "active", "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z" } ], "total": 1, "limit": 20, "offset": 0 } ``` --- # Crear plan **`POST https://api.geopagos.io/api/v1.0/plans`** ### Body — application/json | Campo | Tipo | Requerido | Notas | | --- | --- | --- | --- | | name | string | Sí | — | | description | string | No | — | | pricing_type | string | Sí | Valores: fixed, dynamic | | amount | number | Condicional | Requerido si pricing_type es fixed | | currency | string | Sí | — | | billing_frequency | string | Sí | Valores: weekly, monthly, bimonthly, quarterly, yearly | | billing_cycles | number | No | Cantidad total de ciclos de facturación | | billing_day | number | No | Día del mes/semana en que se factura | | prorate | boolean | No | — | | billing_month | number | No | — | | trial_period_days | number | No | — | | status | string | No | Valores: active, inactive | | redirect_url | string | No | — | | external_reference | string | No | — | | merchant_name | string | Sí | — | | merchant_fiscal_id | string | No | — | | merchant_id | number | Sí | — | | image_content | string | No | Imagen en base64 | | image_file_name | string | No | — | | background_color | string | No | — | ### Ejemplo de request ```bash curl -X POST "https://api.geopagos.io/api/v1.0/plans" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "Plan Mensual", "pricing_type": "fixed", "amount": 1000, "currency": "ARS", "billing_frequency": "monthly", "merchant_name": "Mi Comercio", "merchant_id": 456 }' ``` **Response** 2xx — mismo esquema que un ítem de GET /plans. --- # Actualizar plan **`PUT https://api.geopagos.io/api/v1.0/plans/{planId}`** ### Body — todos los campos opcionales name, description, amount, currency, billing_frequency, billing_cycles, trial_period_days, status ("active"|"inactive"), redirect_url, external_reference, merchant_name, merchant_fiscal_id. **Response 200** — mismo esquema que GET /plans/{planId}. --- # Desactivar plan Para desactivar un plan se usa el mismo endpoint de actualización enviando el estado inactive: **`PUT https://api.geopagos.io/api/v1.0/plans/{planId}`** ```json { "status": "inactive" } ``` **Response 200** — mismo esquema que GET /plans/{planId}. > **Atención: Suscripciones vigentes** > > Cuando un plan es cancelado (desactivado), todas las suscripciones vigentes de ese plan **siguen activas**. Desactivar un plan no cancela las suscripciones existentes; si se quieren cancelar, debe hacerse individualmente según [Cancelar suscripción](https://docs.sipago.app/docs/api-suscripciones/cancelar-suscripcion). ---- --- # Eliminar plan **`DELETE https://api.geopagos.io/api/v1.0/plans/{planId}`** Elimina el plan de forma definitiva. Requisitos previos: - El plan debe estar inactivo (ver [Desactivar plan](https://docs.sipago.app/docs/api-suscripciones/desactivar-plan)). - El plan no debe tener suscripciones vigentes. Si las tiene, se deben cancelar antes según [Cancelar suscripción](https://docs.sipago.app/docs/api-suscripciones/cancelar-suscripcion). ### Ejemplo de request ```bash curl -X DELETE "https://api.geopagos.io/api/v1.0/plans/plan_123" \ -H "Authorization: Bearer " ``` **Response 200 / 204** — plan eliminado, sin body. | Código | Causa | | --- | --- | | 404 | El plan no existe. | | 409 | El plan sigue activo o tiene suscripciones vigentes. | > **Atención: Eliminación irreversible** > > La eliminación es irreversible. Para dejar de ofrecer un plan conservando su historial, usá [Desactivar plan](https://docs.sipago.app/docs/api-suscripciones/desactivar-plan). --- # Obtener plan **`GET https://api.geopagos.io/api/v1.0/plans/{planId}`** ### Response 200 ```json { "plan_id": "plan_123", "subscription_url": "https://...", "name": "Plan Mensual", "amount": 1000, "currency": "ARS", "billing_frequency": "monthly", "status": "active", "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z" } ``` --- # Listar suscripciones **`GET https://api.geopagos.io/api/v1.0/subscriptions`** ### Query params | Parámetro | Requerido | Descripción | | --- | --- | --- | | limit | Sí | — | | offset | Sí | — | | planId | No | Filtra por plan | | customerId | No | Filtra por cliente | | status | No | — | | createdAt | No | Fecha ISO 8601 | | createdBefore | No | Fecha ISO 8601 | | search | No | Búsqueda por nombre, email, documento o referencia externa del cliente | > **Nota: Filtro de fecha en el documento original** > > El resumen de convenciones menciona `createdAfter`, pero la tabla de este endpoint indica `createdAt`. Se conserva la tabla original; confirmá el nombre del filtro con el equipo de Sipago antes de utilizarlo. ### Response 200 ```json { "data": [ { "subscription_id": "sub_123", "customer_id": "cus_456", "plan_id": "plan_123", "payment_method_id": "pm_789", "status": "active", "start_date": "2026-01-01T00:00:00Z", "current_period_start": "2026-07-01T00:00:00Z", "current_period_end": "2026-08-01T00:00:00Z", "next_billing_date": "2026-08-01T00:00:00Z", "billing_cycles_completed": 6, "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-07-01T00:00:00Z" } ], "pagination": { "total": 1, "limit": 20, "offset": 0 } } ``` Campos completos de cada suscripción: subscription_id, customer_id, plan_id, payment_method_id?, status, start_date, trial_end_date?, current_period_start, current_period_end, next_billing_date?, end_date?, cancelled_at?, billing_cycles_completed, billing_cycles_remaining?, last_amount?, next_amount?, next_amount_description?, external_reference?, terms_acceptance?, metadata?, created_at, updated_at. --- # Obtener suscripción **`GET https://api.geopagos.io/api/v1.0/subscriptions/{subscriptionId}`** **Response 200** — mismo esquema que un ítem de GET /subscriptions. --- # Actualizar suscripción **`PUT https://api.geopagos.io/api/v1.0/subscriptions/{subscriptionId}`** **Body — todos los campos son opcionales** | Campo | Tipo | Notas | | --- | --- | --- | | status | string | Valores: active, paused, cancelled | | payment_method_id | string<uuid> | — | | next_amount | number | — | | next_amount_description | string | — | | pause_until | string<date-time> | Fecha hasta la cual queda pausada | | pause_reason | string | — | | cancel_at_period_end | boolean | — | | cancellation_reason | string | — | | external_reference | string | Máximo 128 caracteres | | metadata | object | — | **Response 200** — mismo esquema que GET /subscriptions/{subscriptionId}. --- # Cancelar suscripción **`DELETE https://api.geopagos.io/api/v1.0/subscriptions/{subscriptionId}`** | Campo | Tipo | | --- | --- | | cancel_at_period_end | boolean | | cancellation_reason | string | | metadata | object | ### Ejemplo de request ```bash curl -X DELETE "https://api.geopagos.io/api/v1.0/subscriptions/sub_123" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "cancel_at_period_end": true, "cancellation_reason": "Solicitado por el cliente" }' ``` **Response 200** — mismo esquema que GET /subscriptions/{subscriptionId}. --- # Listar cobros **`GET https://api.geopagos.io/api/v1.0/charges`** **Query params** (snake_case) | Parámetro | Requerido | Descripción | | --- | --- | --- | | limit | Sí | — | | offset | Sí | — | | subscription_id | No | — | | status | No | Valores: pending, processing, succeeded, failed, cancelled | | created_after | No | — | | created_before | No | — | | search | No | Búsqueda por nombre, email, documento o referencia externa del cliente | > **Nota: Estado en el ejemplo original** > > La tabla de filtros enumera `pending`, `processing`, `succeeded`, `failed` y `cancelled`, mientras que el ejemplo de respuesta usa `paid`. Ambos se conservan tal como aparecen en el PDF; confirmá esta diferencia con el equipo de Sipago al integrar. ## Response 200 ```json { "data": [ { "charge_id": "chg_123", "subscription_id": "sub_123", "amount": 1000, "currency": "ARS", "status": "paid", "billing_period_start": "2026-07-01T00:00:00Z", "billing_period_end": "2026-08-01T00:00:00Z", "attempt_count": 1, "max_attempts": 3, "created_at": "2026-07-01T00:00:00Z", "updated_at": "2026-07-01T00:00:00Z", "customer_name": "Juan Pérez", "customer_email": "juan@example.com", "plan_name": "Plan Mensual", "payment_method_last4": "4242", "payment_method_brand": "visa" } ], "pagination": { "total": 1, "limit": 20, "offset": 0 } } ``` Campos completos: charge_id, subscription_id, amount, currency, status, reference_number?, billing_period_start, billing_period_end, attempt_count, max_attempts, last_attempt_at?, next_attempt_at?, failure_reason?, failure_code?, created_at, updated_at, customer_name, customer_email, plan_name, payment_method_last4, payment_method_brand, external_reference?, attempts?. --- # Obtener cobro GET `https://api.geopagos.io/api/v1.0/charges/{chargeId}` **Response 200** — mismo esquema que un ítem de GET `/charges`, incluyendo el detalle completo de attempts[] (historial de intentos de cobro). --- # Listar clientes **`GET https://api.geopagos.io/api/v1.0/customers`** ### Query params | Parámetro | Requerido | | --- | --- | | limit | Sí | | offset | Sí | | email | No | | name | No | | createdAfter | No | ### Response 200 ```json { "data": [ { "customer_id": "cus_456", "email": "juan@example.com", "name": "Juan Pérez", "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z" } ], "pagination": { "total": 1, "limit": 20, "offset": 0 } } ``` Campos completos: customer_id, merchant_id?, tenant_identifier?, email, name, phone?, document?, metadata?, active_subscriptions_count?, created_at, updated_at. --- # Obtener cliente **`GET https://api.geopagos.io/api/v1.0/customers/{customerId}`** **Response 200** — mismo esquema que un ítem de GET /customers. --- # Métodos de pago de un cliente **`GET https://api.geopagos.io/api/v1.0/customers/{customerId}/payment-methods`** ### Query params (opcionales) | Parámetro | Valores válidos | | --- | --- | | type | credit, debit, prepaid | | status | active, inactive, expired | | brand | visa, mastercard, amex, diners, cabal | ## Response 200 ```json { "data": [ { "payment_method_id": "pm_789", "customerId": "cus_456", "type": "credit", "brand": "visa", "last4": "4242", "expMonth": 12, "expYear": 2028, "holderName": "Juan Perez", "holderDocument": { "type": "DNI", "number": "12345678" }, "status": "active", "metadata": {}, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z" } ], "count": 1 } ``` Campos de cada elemento de data[]: payment_method_id (, único campo en snake_case), customerId (), type, brand, last4, expMonth (integer), expYear (integer), holderName, holderDocument ({ type, number }), status, metadata (object), createdAt (), updatedAt (). **Nota de casing:** a diferencia del resto de la respuesta (camelCase), payment_method_id se mantiene en snake_case. --- # Suscripciones de un cliente **`GET https://api.geopagos.io/api/v1.0/customers/{customerId}/subscriptions`** **Query params:** limit, offset (siempre), status (opcional). **Response 200** — mismo esquema que GET /subscriptions. --- # Obtener método de pago **`GET https://api.geopagos.io/api/v1.0/payment-methods/{paymentMethodId}`** **Response 200** ```json { "payment_method_id": "pm_789", "customer_id": "cus_456", "type": "credit", "brand": "visa", "last4": "4242", "exp_month": 12, "exp_year": 2028, "holder_name": "Juan Perez", "status": "active", "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z" } ``` --- # Manejo de errores * Los errores HTTP se devuelven con el código de estado correspondiente (4xx/5xx). * Si el header Authorization no está presente o es inválido, la petición falla antes de llegar a procesarse. * Se recomienda a los integradores logear siempre status y el body de la respuesta para diagnóstico, dado que el detalle específico del error puede variar según el recurso. * Ante timeouts o errores de red, reintentar con backoff exponencial es la estrategia recomendada, especialmente para operaciones de lectura (GET). --- # Resumen de endpoints | Recurso | Método | Endpoint | | --- | --- | --- | | Plans | GET | /api/v1.0/plans | | Plans | POST | /api/v1.0/plans | | Plans | PUT | /api/v1.0/plans/{planId} | | Plans | GET | /api/v1.0/plans/{planId} | | Plans | DELETE | /api/v1.0/plans/{planId} | | Subscriptions | GET | /api/v1.0/subscriptions | | Subscriptions | GET | /api/v1.0/subscriptions/{subscriptionId} | | Subscriptions | PUT | /api/v1.0/subscriptions/{subscriptionId} | | Subscriptions | DELETE | /api/v1.0/subscriptions/{subscriptionId} | | Charges | GET | /api/v1.0/charges | | Charges | GET | /api/v1.0/charges/{chargeId} | | Customers | GET | /api/v1.0/customers | | Customers | GET | /api/v1.0/customers/{customerId} | | Customers | GET | /api/v1.0/customers/{customerId}/payment-methods | | Customers | GET | /api/v1.0/customers/{customerId}/subscriptions | | Payment Methods | GET | /api/v1.0/payment-methods/{paymentMethodId} | --- # Introducción CajaPOS es una solución que conecta el ERP del comercio con dispositivos SmartPOS para automatizar y asegurar el proceso de cobro. El sistema elimina el ingreso manual de montos, reduce errores humanos, previene fraudes y agiliza la operación tanto en comercios como en servicios gastronómicos. La experiencia de pago se vuelve más rápida, segura y transparente para clientes y operadores. Mediante la comunicación directa entre el ERP y el SmartPOS, CajaPOS permite automatizar la captura de pagos, gestionar propinas, emitir vouchers y ejecutar devoluciones totales de forma integrada. El comercio obtiene mayor control operativo, estandarización de procesos y una experiencia moderna que mejora la satisfacción del cliente. ## Beneficios clave - **Reducción de errores humanos** al evitar tipeos manuales. - **Prevención de fraudes:** el monto en caja siempre coincide con el monto cobrado. - **Mayor agilidad** en el proceso de cobro. - **Experiencia moderna y transparente** para clientes y comercios. **Siguiente paso:** [alcance y funcionalidades](https://docs.sipago.app/docs/api-caja-pos/alcance). --- # Alcance y funcionalidades CajaPOS integra el ERP del comercio con un dispositivo SmartPOS para automatizar completamente el proceso de cobro. La solución permite: - Vincular cada dispositivo mediante un **token único**. - Recibir de forma automática las órdenes de pago enviadas desde la caja. - Mostrar en el SmartPOS el monto final de la transacción. - Capturar pagos con tarjeta y gestionar propinas cuando corresponda. - Imprimir o mostrar comprobantes digitales. - Devolver el resultado del pago al ERP para cerrar la venta sin intervención adicional del operador. También contempla la ejecución de **devoluciones totales** procesadas automáticamente desde el ERP. ## Flujo de cobro [Imagen: Flujo CajaPOS: la caja (ERP) envía el monto al SmartPOS, el cliente paga con tarjeta y el resultado vuelve a la caja] 1. El operador registra la venta en el ERP, que envía inmediatamente la intención de pago al SmartPOS. 2. El dispositivo muestra el monto y, si aplica, permite seleccionar propina antes de avanzar al pago. 3. El cliente confirma apoyando o insertando la tarjeta y el SmartPOS procesa la transacción. 4. El SmartPOS emite o muestra el comprobante y notifica al ERP el estado final de la operación, cerrando la venta de manera automática. **Siguiente paso:** [paso a paso de implementación](https://docs.sipago.app/docs/api-caja-pos/implementacion). --- # Paso a paso de implementación ## 1. Integración del comercio (ERP) con APIs de pagos Este es el primer paso y es fundamental para que funcione la solución. El software de caja debe integrarse con las APIs de Geopagos: [POS Mode | Geopagos Developers](https://docs.geopagos.io/documents/in-person-payments/terminals/smartpos/pos-mode/). Los endpoints a utilizar son: - Point of sales - Create intent - Get intent info ## 2. Configuración del lector en BackOffice Registrar el SmartPOS en el BackOffice y asociarlo a una sucursal: - Registrar el número de serie en **ABM de lectores**. - Ingresar a **Comercios → Lectores → Vincular lector**. - Ingresar el token generado por el SmartPOS (previamente se debe pushear la app de cobro al dispositivo). - Confirmar en el dispositivo con **Ya vinculé dispositivo**. > **Info: Punto de venta automático** > > En este paso, Geopagos crea y asigna internamente un *punto de venta* al SmartPOS, que luego será necesario para asociar el software de caja con el dispositivo. ## 3. Asignación de punto de venta El software de caja debe consumir el servicio **Point of sales** para asociar el POS (asignar el punto de venta al software de caja): - Consultar el listado de puntos de venta vinculados. - Seleccionar el punto de venta correspondiente. - Asociarlo al software de caja. ## 4. Generar orden de cobro El software de caja crea la orden usando la API **Create intent**. Dentro de esta creación de pago, el implementador debe enviar el dato del punto de venta seleccionado para el software de caja. Esta información se envía al backend de Geopagos. ## 5. Flujo transaccional en la app CajaPOS El dispositivo queda a la espera de recibir una intención de pago. Al recibirla, muestra en pantalla el monto a cobrar, el usuario inserta o acerca la tarjeta y se procesa el pago. - Si el comercio tiene habilitada la **propina**, antes de capturar la tarjeta el usuario puede adicionarla al monto total recibido desde la caja. - Si aplican **cuotas**, antes de capturar la tarjeta el usuario puede seleccionar las cuotas disponibles. - Al finalizar la transacción, el cliente puede imprimir el ticket correspondiente. ## 6. Consulta de estado El software de caja consulta el estado final de la orden utilizando el endpoint **Get intent info**. ---