---
title: "API para Punto de Venta (POS): Sincronización en Tiempo Real de Inventario y Facturación Electrónica"
description: "Diseño de APIs REST y WebSockets para sistemas POS y retail. Sincronización offline-first, conciliación de stock omnicanal y facturación electrónica DIAN / SAT."
date: 2026-08-20
category: "E-Commerce"
imageUrl: "/assets/images/blog/api-para-punto-de-venta-pos-inventario-facturacion.webp"
imageAlt: "Terminal de punto de venta retail conectada mediante API cloud a servidores de inventario en tiempo real y emisión de facturación electrónica con código QR."
lang: "es"
translationSlug: "pos-api-real-time-inventory-electronic-invoicing"
---

El comercio omnicanal ha transformado radicalmente las operaciones del sector retail en América Latina. Hoy en día, un negocio con tiendas físicas que también comercializa a través de canales digitales (e-commerce en Shopify o WooCommerce, marketplaces como Mercado Libre y ventas por WhatsApp) no puede permitirse gestionar inventarios de manera aislada. 

El escenario donde un cliente compra el último par de zapatos en la tienda física de un centro comercial mientras otro usuario adquiere exactamente la misma unidad en la tienda online con un segundo de diferencia representa una pesadilla operativa: sobreventa (*overselling*), cancelaciones forzosas, clientes frustrados y penalizaciones en reputación digital. La columna vertebral que previene este caos es una **API para Punto de Venta (POS) orientada a eventos con sincronización en tiempo real y soporte offline-first**.

> 💡 **Resumen Ejecutivo:** Una API moderna para Punto de Venta (POS) sincroniza inventarios y transacciones de forma bidireccional entre terminales físicas y la nube mediante WebSockets o webhooks ligeros. Incorpora arquitectura offline-first con colas locales para continuar vendiendo sin conexión a internet, resolución determinista de conflictos de stock y emisión asíncrona de facturación electrónica validada ante entidades tributarias (DIAN en Colombia, SAT en México).

---

## 1. El Dilema del Retail: Sincronización por Lotes vs. Tiempo Real

Tradicionalmente, las cadenas de tiendas utilizaban programas POS cerrados que exportaban archivos CSV o ejecutaban scripts de sincronización por lotes (*batch*) cada noche. En el comercio acelerado de hoy, este esquema es obsoleto y peligroso:

| Dimensión Operativa | POS Tradicional por Lotes (Batch Nocturno) | API POS Cloud en Tiempo Real (DoneAPI) |
| :--- | :--- | :--- |
| **Latencia de Actualización de Stock** | 4 a 24 horas de desfase | Menor a 250 milisegundos a nivel omnicanal |
| **Riesgo de Sobreventa en Campañas** | Extremadamente alto durante Cyberlunes o Black Friday | Cero: bloqueo atómico de inventario en la base de datos |
| **Disponibilidad ante Cortes de Internet** | El cajero opera pero los datos quedan desactualizados | **Offline-First:** almacén local sincronizado al restablecer red |
| **Emisión de Facturación Electrónica** | Lenta, manual o dependiente de un software satélite | Integración API directa con generación instantánea de QR y CUFE/UUID |
| **Escalabilidad en Nuevas Sucursales** | Requiere servidores locales caros en cada tienda | 100% Cloud: conectar terminal web o tablet a la API |

---

## 2. Arquitectura Offline-First y Resolución de Conflictos

En muchas ciudades de América Latina, la estabilidad de las conexiones de internet en centros comerciales o tiendas de calle suele presentar microcortes. Un sistema POS **jamás debe detener la fila de cobro porque el enlace de internet cayó**.

La arquitectura obligatoria es el patrón **Offline-First con Sincronización Diferida**:

```
[Terminal POS en Tienda (Hardware / Web)]
      |
      +---> ¿Hay conexión a Internet?
               |
               +--- SÍ ---> Envía venta a API REST Cloud (Deducción inmediata)
               |
               +--- NO ---> Guarda venta en base de datos local (IndexedDB / SQLite)
                            Encola payload firmado con timestamp monótono
                            Imprime ticket fiscal provisional
                                   |
                                   v  (Al volver la conectividad)
                            Despacha lote en cola hacia endpoint /v1/pos/sync
                            Aplica regla 'Last-Write-Wins' o balance de stock
```

### Reglas para Prevenir Desajustes de Inventario:
1. **Transacciones Atómicas con `SELECT ... FOR UPDATE`:** En la base de datos en la nube, el descuento de stock debe realizarse dentro de una transacción aislada para garantizar que dos cajas no descuenten la misma unidad en paralelo.
2. **Identificadores UUID Generados en la Terminal:** La caja genera el ID de la venta localmente; si la petición HTTP se reintenta por inestabilidad de red, el backend descarta duplicados gracias a la idempotencia.

---

## 3. Implementación Técnica: API REST de Registro de Venta con Fastify y TypeScript

El siguiente módulo ilustra cómo procesar una venta POS con descuento atómico de existencias y preparación para facturación electrónica:

```typescript
import { z } from 'zod';
import { FastifyRequest, FastifyReply } from 'fastify';

// 1. Esquema de validación estricto de la venta POS
export const PosSaleItemSchema = z.object({
  sku: z.string().min(3),
  quantity: z.number().int().positive(),
  unitPrice: z.number().positive(),
  taxRate: z.number().min(0).max(1), // ej. 0.19 para 19% de IVA
});

export const PosSaleRequestSchema = z.object({
  terminalId: z.string(),
  cashierId: z.string(),
  transactionUuid: z.string().uuid(),
  paymentMethod: z.enum(['CASH', 'CREDIT_CARD', 'DEBIT_CARD', 'DIGITAL_WALLET']),
  items: z.array(PosSaleItemSchema).nonempty(),
  customerTaxId: z.string().optional(), // Cédula o NIT para factura electrónica
});

export type PosSaleRequest = z.infer<typeof PosSaleRequestSchema>;

// 2. Controlador de la API de Punto de Venta
export class PosSaleController {
  constructor(
    private db: {
      transaction: <T>(callback: (tx: any) => Promise<T>) => Promise<T>;
    },
    private invoiceQueue: { enqueueInvoice: (saleId: string) => Promise<void> }
  ) {}

  public async handleSale(request: FastifyRequest, reply: FastifyReply) {
    const parseResult = PosSaleRequestSchema.safeParse(request.body);
    if (!parseResult.success) {
      return reply.status(400).send({
        error: 'BAD_REQUEST',
        details: parseResult.error.issues,
      });
    }

    const sale = parseResult.data;

    try {
      // Ejecución de transacción atómica en la base de datos
      const result = await this.db.transaction(async (tx) => {
        // 1. Verificar idempotencia: ¿ya existe este transactionUuid?
        const existing = await tx.query(
          'SELECT id, invoice_number FROM sales WHERE transaction_uuid = $1',
          [sale.transactionUuid]
        );
        if (existing.rows.length > 0) {
          return { saleId: existing.rows[0].id, duplicated: true };
        }

        // 2. Descontar stock con bloqueo para evitar sobreventa
        for (const item of sale.items) {
          const stockResult = await tx.query(
            'UPDATE inventory SET stock = stock - $1 WHERE sku = $2 AND stock >= $1 RETURNING stock',
            [item.quantity, item.sku]
          );

          if (stockResult.rows.length === 0) {
            throw new Error(`STOCK_INSUFFICIENTE: El SKU ${item.sku} no dispone de inventario suficiente.`);
          }
        }

        // 3. Registrar la venta en la base de datos central
        const insertSale = await tx.query(
          'INSERT INTO sales (terminal_id, transaction_uuid, payment_method, total, created_at) VALUES ($1, $2, $3, $4, NOW()) RETURNING id',
          [sale.terminalId, sale.transactionUuid, sale.paymentMethod, this.calculateTotal(sale.items)]
        );

        return { saleId: insertSale.rows[0].id, duplicated: false };
      });

      // 4. Si la venta requirió factura electrónica, encolar el procesamiento asíncrono
      if (sale.customerTaxId && !result.duplicated) {
        await this.invoiceQueue.enqueueInvoice(result.saleId);
      }

      return reply.status(result.duplicated ? 200 : 201).send({
        success: true,
        saleId: result.saleId,
        message: result.duplicated ? 'Transacción ya procesada previamente' : 'Venta confirmada exitosamente',
      });
    } catch (error: any) {
      return reply.status(409).send({
        error: 'SALE_FAILED',
        message: error.message,
      });
    }
  }

  private calculateTotal(items: Array<{ quantity: number; unitPrice: number }>): number {
    return items.reduce((acc, item) => acc + item.quantity * item.unitPrice, 0);
  }
}
```

---

## 4. Facturación Electrónica en el POS: DIAN (Colombia) y SAT (México)

En América Latina, la emisión del comprobante en la caja registradora ya no es un simple recibo térmico: por ley debe generarse como documento electrónico validado ante el ente tributario correspondiente (**DIAN en Colombia con el documento equivalente POS electrónico**, o **SAT en México mediante CFDI 4.0**).

### Buenas Prácticas de Integración Tributaria:
1. **Emisión Asíncrona (Background Workers):** Nunca bloquees la terminal del cajero esperando la respuesta sincrónica de los servidores gubernamentales (que pueden tardar de 3 a 10 segundos o experimentar caídas temporales).
2. **Generación Local del Código QR:** La API debe calcular el hash de la transacción (CUFE en Colombia) de acuerdo con los algoritmos normativos de inmediato para permitir la impresión del ticket físico con su código QR reglamentario sin demoras.
3. **Resiliencia ante Caídas del Ente Fiscal:** Las ventas se almacenan bajo estado "Pendiente de Transmisión" y un worker las despacha en lotes cuando los servicios tributarios restablecen su operación.

---

## Preguntas Frecuentes (FAQ)

### ¿Qué hardware se necesita para conectar un POS a una API en la nube?
Cualquier dispositivo con capacidad de ejecutar un navegador web moderno o una app ligera (tablets Android/iOS, computadoras con Windows/Linux o terminales inteligentes Sunmi/Pax) con acceso a red local y conectividad a impresoras térmicas ESC/POS vía USB, Bluetooth o red local.

### ¿Cómo sincronizar cambios masivos de precios desde la nube hacia las tiendas?
Se utiliza una arquitectura de eventos mediante WebSockets o Server-Sent Events (SSE). Cuando el administrador actualiza los precios en el ERP central, el servidor emite un evento `catalog.updated` que las terminales conectadas consumen en memoria para refrescar su caché local instantáneamente.

### ¿Qué protocolo de comunicación es mejor para POS: REST o gRPC?
Para la comunicación entre la terminal de caja y el API Gateway, **REST sobre HTTPS/JSON** o WebSockets es el estándar ideal por su facilidad de depuración e interoperabilidad con navegadores. Para la comunicación interna de microservicios en el backend (entre el inventario y la facturación), **gRPC** ofrece mayor rendimiento y menor consumo de ancho de banda.

### ¿Se pueden integrar balanzas digitales y lectores de códigos de barra?
Sí. Los lectores de código de barra operan habitualmente emulando teclados HID. Para balanzas de peso, la interfaz de punto de venta interactúa mediante Web Serial API o pequeños daemons locales en Node.js que leen los bytes del puerto COM y los envían a la API del POS.

---

## Conclusión y Asesoría Especializada

Una API para Punto de Venta robusta es el puente que transforma un negocio minorista tradicional en una empresa omnicanal ágil, eficiente y libre de discrepancias de inventario. Al diseñar bajo principios offline-first, transacciones atómicas y facturación electrónica desacoplada, garantizas que tus cajas registradoras nunca dejen de facturar.

> 💬 **¿Necesitas Desarrollar o Integrar una API para Punto de Venta (POS)?** En **DoneAPI** diseñamos arquitecturas de inventario en tiempo real, sincronización offline-first e integración con facturación electrónica:
> 
> 👉 [**Solicitar Asesoría para POS por WhatsApp (+57 320 817 3939)**](https://wa.me/573208173939?text=Hola,%20me%20interesa%20asesoria%20en%20desarrollo%20de%20APIs%20para%20Puntos%20de%20Venta%20POS%20e%20inventarios)
