Saltar al contenido principal

Comunicación con punto de venta

El registro de un punto de venta y generacion de codigo de activacion otp se realiza mediante el endpoint POST /api/v1/merchants/{merchantId}/pos-terminals/{serialNumber}/pairing

El endpoint para activar el punto de venta y obtener la secret key es POST /api/v1/pos-terminals/{serialNumber}/pairing/activate

Seguridad SPIDI: Dos Modelos de Confianza

Para garantizar la integridad y el control de las operaciones, SPIDI utiliza dos modelos de seguridad distintos según el actor que realiza la petición. Esta separación evita que el robo de un dispositivo físico comprometa la cuenta del comercio, y viceversa.

ActorAlcanceSeguridadCredencial
Merchant / AdminBackend, ERP, CRM, Dashboards.JWT (Bearer Auth)Token de corta duración.
Terminal / POSDispositivo físico (Hardware).HMAC (POS Signature)Secret Key persistente en el dispositivo.

Fase 1: El Proceso de Pairing (Vinculación)

El pairing ocurre una sola vez en la vida del dispositivo. Es el acto de "presentar" el hardware al servidor para generar una identidad secreta.

Paso 1: Registro Administrativo

Antes de iniciar la vinculación, el administrador o sistema administrativo debe autenticarse (ver Guía de Registro y Login) para obtener un Bearer Token. Sin este token, no es posible registrar terminales.

Una vez autenticado, solicita vincular un sistema terminal con el Serial de Hardware y genera un código de activación (OTP).

  • Petición: POST /api/v1/merchants/{merchantId}/pos-terminals/{serialNumber}/pairing
  • Seguridad: Requiere Header Authorization: Bearer <token>.

Paso 2: El Intercambio de Secretos

El técnico ingresa el código en el POS. El dispositivo termina realizando una petición al servidor:

  • Petición: POST /api/v1/pos-terminals/{serialNumber}/pairing/activate
  • Cuerpo: { "code": "889904" }

Paso 3: Persistencia de Identidad

El servidor valida el código y genera una Secret Key (un hash aleatorio y largo).

  • El Servidor guarda la clave asociada a ese Serial.
  • El POS recibe la clave y la guarda en su memoria segura.

Importante: A partir de aquí, la Secret Key nunca vuelve a viajar por internet.


Fase 2: Autenticación por Firma HMAC (Operación)

Una vez vinculado, el POS no usa el código de activación ni contraseñas. Cada petición (lectura o escritura) se autentica mediante una firma HMAC-SHA256 construida a partir de 4 elementos:

#ElementoDescripción
1Request BodyEl JSON serializado del cuerpo. Para GET o peticiones sin cuerpo se usa una cadena vacía "".
2TimestampMarca temporal ISO 8601 enviada en la cabecera x-pos-timestamp.
3URLPath completo de la petición, incluyendo query string si existe.
4Secret KeyLa clave secreta persistida en el dispositivo durante el pairing.

Fórmula de firma:

message  = body + "\n" + timestamp + "\n" + url
signature = HMAC_SHA256(message, secret_key)

Cabeceras obligatorias en toda petición POS:

CabeceraValor
x-pos-signatureResultado hexadecimal de la firma HMAC-SHA256.
x-pos-timestampMarca temporal ISO 8601 usada en la firma (ej. 2024-04-10T15:05:00Z).

Ejemplo A: Firma para una petición POST (con cuerpo)

Operación: Confirmar un pago.

url       = /api/v1/pos-terminals/98202003219630/transactions/3ddc4cfb/commit
timestamp = 2024-04-10T15:05:00Z
body = {"authorizationCode":"171599","processCode":"002000","commitAt":"2024-04-10T15:05:00Z","amountReference":10.50,"terminalNumber":"T-123","trace":"000535","utcDate":"1007151715","cardTypeForRpt":"C","visOrMccCard":"MCC","batchNumber":"998877"}
message   = body + "\n" + timestamp + "\n" + url
signature = HMAC_SHA256(message, secret_key)

Ejemplo B: Firma para una petición GET (sin cuerpo)

Operación: Listar transacciones pendientes.

url       = /api/v1/pos-terminals/98202003219630/transactions?status=PENDING
timestamp = 2024-04-10T15:00:00Z
body = "" (cadena vacía)
message   = "" + "\n" + "2024-04-10T15:00:00Z" + "\n" + "/api/v1/pos-terminals/98202003219630/transactions?status=PENDING"
signature = HMAC_SHA256(message, secret_key)

Nota: Al no existir un cuerpo en las peticiones GET, el message comienza con el salto de línea directamente. La URL debe incluir los parámetros de consulta (query string) tal como se envían al servidor.


Operaciones del Terminal POS

Consultar Transacciones (Lectura)

Antes de procesar un cobro o una anulación, el dispositivo debe consultar el servidor para obtener la lista de órdenes pendientes.

  • Para listar pagos pendientes: GET /api/v1/pos-terminals/{serialNumber}/transactions?status=PENDING
  • Para listar anulaciones pendientes: GET /api/v1/pos-terminals/{serialNumber}/transactions?status=VOID_PENDING
  • Para consultar una orden específica: GET /api/v1/pos-terminals/{serialNumber}/transactions/{orderId}

Confirmar un Pago

Cuando el punto de venta procesa un cobro, notifica a la API sobre este cambio al estado PAID.

  • Petición: POST /api/v1/pos-terminals/{serialNumber}/transactions/{orderId}/commit
  • Cuerpo:
{
"authorizationCode": "171599",
"processCode": "002000",
"commitAt": "2024-04-10T15:05:00Z",
"amountReference": 10.50,
"terminalNumber": "T-123",
"trace": "000535",
"utcDate": "1007151715",
"cardTypeForRpt": "C",
"visOrMccCard": "MCC",
"batchNumber": "998877"
}

Confirmar Anulación del Pago

Si el dispositivo detecta que necesita confirmar una anulación generada externamente, no hay necesidad de enviar un cuerpo JSON extra.

  • Petición: POST /api/v1/pos-terminals/{serialNumber}/transactions/{orderId}/invalidation/commit
  • Cuerpo: (Sin cuerpo/Payload vacío — usar "" como body en la firma)

Crear un Cierre de Lote (Settlement)

Al final de la jornada, el POS envía el resumen para conciliar operaciones.

  • Petición: POST /api/v1/pos-terminals/{serialNumber}/settlements
  • Cuerpo:
{
"batchNumber": "998877",
"transactionCount": 12,
"closedAt": "2023-04-04T15:26:51.187Z",
"currencyReference": "USD",
"terminal": "98202003219630",
"debitBatch": "DB-9901"
}

Tabla Resumen de Endpoints POS

OperaciónMétodoEndpointBody
Listar transaccionesGET/.../transactions?status=...""
Consultar transacciónGET/.../transactions/{orderId}""
Confirmar pagoPOST/.../transactions/{orderId}/commitJSON
Confirmar anulaciónPOST/.../transactions/{orderId}/invalidation/commit""
Crear cierre de lotePOST/.../settlementsJSON

Todas las peticiones anteriores requieren las cabeceras x-pos-signature y x-pos-timestamp.


Resumen de Seguridad para el Comercio

  • No hay contraseñas: No hay nada que un empleado pueda anotar en un post-it.
  • Integridad: Si alguien intercepta la señal e intenta cambiar el monto del pago, la URL o la marca temporal, la firma fallará y el servidor rechazará la petición.
  • Protección contra Replay: El x-pos-timestamp permite al servidor rechazar peticiones antiguas o duplicadas.
  • Control Total: Si un POS se pierde o es robado, el administrador lo "desvincula" desde el panel y la Secret Key queda invalidada instantáneamente.