Para el equipo. Si nunca tocaste el repo, leé esto primero. Va de lo simple a lo técnico. Demo en video: docs/demo/passpay-flows.webm.
El comercio cobra en pesos (como siempre), pero el valor le queda en dólares on-chain (USDC/XLM en Stellar), y puede pasarlo a pesos cuando quiera.
Mental model: Passpay es el puente entre el rail de pagos argentino (Transferencias 3.0) y la red Stellar.
PESOS (mundo bancario) DÓLAR (on-chain, Stellar)
┌─────────────────────┐ ┌──────────────────────────┐
│ Cliente paga por QR │ Passpay (puente) │ El comercio recibe USDC │
│ Transferencias 3.0 │ ──────────────────► │ liquidado en Stellar │
│ (CVU / billetera) │ │ │
└─────────────────────┘ └──────────┬───────────────┘
│ cuando quiere
▼
┌──────────────────────────┐
│ Off-ramp: USDC → pesos │
│ a un CBU (BlindPay) │
└──────────────────────────┘
Todo lo que hace Passpay gira alrededor de ese viaje: peso entra → se liquida en dólar on-chain → sale a peso cuando hace falta.
| # | Flujo | Pantalla | En una línea |
|---|---|---|---|
| 1 | Cobro en ARS | /cobrar-ars |
El comercio genera un QR de Transferencias 3.0, el cliente paga en pesos y Passpay liquida en Stellar. |
| 2 | Cobro compartido (POS) | /pos |
Dividir una cuenta entre varias personas; cada una paga su parte con su wallet. |
| 3 | Off-ramp USDC → ARS | /offramp |
Pasar dólares on-chain a pesos en una cuenta bancaria (CBU/CVU) vía BlindPay. |
| 4 | Rampa dólar (anchor) | /ramp |
On/off-ramp dólar ↔ peso usando un anchor de Stellar (estándares SEP). |
Y el panel (/dashboard): balance del comercio (USDC ↔ ARS) + lista de movimientos. No es un flujo, es el "home" del comercio.
Qué ve el comercio: ingresa un monto en pesos → "Generar QR" → aparece un QR → (en la demo) "Simular pago" → pantalla de éxito con el hash de Stellar.
Qué pasa por detrás:
- Front llama
POST /transferencias3/qrcon el monto. - Backend arma un QR EMVCo real (el mismo formato de Transferencias 3.0): TLV + checksum CRC16-CCITT, con el CVU recaudador de Passpay. →
api/src/services/transferencias3.service.ts - El cliente pagaría desde su banco/billetera. En la demo,
POST /transferencias3/simulate-paymentsimula la acreditación de Coelsa (la cámara compensadora). - Apenas se acredita el peso, Passpay manda una transacción real en Stellar liquidando en la moneda del comercio (XLM/USDC). →
sendSettlementPayment()enapi/src/services/stellar.service.ts - La pantalla muestra el Coelsa ID + el hash on-chain (link a Stellar Expert).
Lo real: el QR EMVCo (validado contra el vector estándar) y la liquidación on-chain. Lo simulado: la acreditación bancaria de Coelsa (necesita webhook real de Coelsa, que es una integración bancaria).
Qué ve el comercio: monto total → elige cantidad de personas (±) → "Generar QR". El QR lleva a /pay/{id} y cada persona paga su parte.
Qué pasa por detrás:
POST /splitscrea un split (mode: OPEN_POOL, asset de liquidación XLM). →api/src/services/splits.service.ts- El QR codifica el link
/pay/{id}?personas=N. - Cada persona entra, elige su moneda y paga: el backend arma una transacción Stellar sin firmar (
/splits/:id/tx), la persona la firma con su wallet (Freighter/xBull) y se transmite (/splits/:id/tx/submit). →api/src/controllers/stellar-tx.controller.ts - El front del POS hace polling del estado; cuando pagaron todos, el split queda
SETTLED.
Estado: el cálculo del split + el QR son frontend real. La creación del split persiste en Supabase (Postgres vía Prisma), que ahora mismo está caída/inalcanzable desde la red del equipo (ver §5), por eso en el video esa llamada va stubbeada.
Qué ve el comercio: elige cliente → elige/crea una cuenta ARS (CBU/CVU) → ingresa monto en USDC → "Cotizar" → ve cuánto recibe en pesos → "Firmar" con la wallet.
Qué pasa por detrás (la parte importante de entender):
- BlindPay es un proveedor que convierte stablecoin on-chain → fiat local por rails (en AR:
transfers_bitso→ ARS). - La moneda de destino la define la cuenta bancaria, no un campo aparte. Si la cuenta es
transfers_bitso(CBU/CVU argentino) → el payout sale en ARS. - Secuencia:
POST /blindpay/quote(monto en centavos) →POST /blindpay/authorize(devuelve un XDR sin firmar) → la wallet firma →POST /blindpay/payoutejecuta. →api/src/services/blindpay.service.ts - En la demo se llega hasta el quote real contra el sandbox de BlindPay (ej: 50 USDC → ~76.300 ARS). La firma con wallet es un popup externo que no se automatiza en el video.
Lo real: cotización en vivo contra BlindPay. La firma final necesita una wallet con USDC/USDB.
Qué ve el usuario: elige depositar (fondear) o retirar dólares y se abre el flujo del anchor.
Qué pasa por detrás (esto es 100% Stellar estándar):
- SEP-1: Passpay lee el
stellar.tomldel anchor y descubre todos sus endpoints. →api/src/services/anchor.service.ts - SEP-10: autenticación web (firma un challenge → JWT).
- SEP-24: abre el flujo interactivo de on/off-ramp del anchor (hosted).
- Es agnóstico al anchor: cambiás
ANCHOR_PROVIDERen el.envy funciona con Anclap (AR real) o el anchor de referencia de la SDF (testnet). Sin tocar código.
Diferencia con el Flujo 3: el anchor (SEP-24) es el estándar nativo de Stellar para ramp; BlindPay es un proveedor que además resuelve el rail local ARS (CBU/CVU). Tenemos los dos caminos.
[Cliente] --paga ARS--> Transferencias 3.0 (QR EMVCo / CVU)
│
▼ Coelsa acredita el peso
Passpay backend
│ manda settlement on-chain
▼
Stellar (USDC / XLM) ← acá vive el valor del comercio
│
┌───────────────┴───────────────┐
▼ ▼
Off-ramp BlindPay (CBU/CVU) Anchor SEP-24 (dólar ↔ peso)
USDC → ARS a una cuenta on/off-ramp estándar de Stellar
El dashboard muestra ese saldo (en USDC, con su equivalente en ARS) y la lista de movimientos: cobro ARS → liquidación on-chain → off-ramp.
| Pieza | Estado |
|---|---|
| QR EMVCo de Transferencias 3.0 | ✅ Real (CRC16 validado) |
| Liquidación on-chain en Stellar | ✅ Real (tx + hash en Stellar Expert) |
| Quote de off-ramp (BlindPay) | ✅ Real contra el sandbox |
| Anchor SEP-1/10/24 | ✅ Real (validado contra testanchor.stellar.org) |
| Acreditación de Coelsa | 🟡 Simulada (falta webhook bancario real) |
| Persistencia (splits, payouts) | 🔴 Supabase caída/inalcanzable desde la red actual (db.zoobnfqtpavazeukytcw.supabase.co:5432). Hay que despausar/reconectar la DB o cambiar DATABASE_URL. |
| Firma de wallet (off-ramp / pay) | 🟡 Necesita Freighter/xBull con fondos; no se automatiza en el video |
Ahora mismo la DB no responde:
Can't reach database server at `db.zoobnfqtpavazeukytcw.supabase.co:5432`
Qué significa: el backend usa Postgres (Supabase) vía Prisma para guardar los splits y los payouts. Esa instancia está caída o inalcanzable desde la red del equipo. Causa más común: los proyectos free de Supabase se pausan solos por inactividad (también puede ser que la red bloquee el puerto 5432).
Qué SÍ sigue andando con la DB caída: el QR de Transferencias 3.0, la liquidación on-chain, el quote de BlindPay y el anchor SEP-24 (ninguno depende de la DB).
Qué NO: crear un split en el POS y persistir payouts (todo lo que hace prisma.*.create). Por eso en el video la creación del split va stubbeada.
Cómo arreglarlo (cualquiera del equipo con acceso):
- Entrar a app.supabase.com → proyecto Passpay.
- Si dice "Paused", tocar Restore / Resume y esperar 1–2 min.
- Verificar la connection string (Project Settings → Database) y que coincida con
DATABASE_URLenapi/.env(y en las env de Vercel). - Probar local:
curl -X POST http://localhost:3001/splits -H "Content-Type: application/json" -d '{"totalAmount":1000,"mode":"OPEN_POOL","settlementAsset":{"network":"stellar","type":"native","code":"XLM"}}'→ debe devolver unid, no un error de conexión. - Si sigue sin reachear desde esta máquina (proxy/firewall bloqueando 5432), usar el connection pooler de Supabase (puerto 6543, modo
transaction) en elDATABASE_URL.
Una vez arriba, el POS y la persistencia de payouts graban solos — no hay que tocar código.
- Transferencias 3.0: esquema del BCRA de pagos interoperables por QR (cualquier banco/billetera lee el mismo QR). Usa formato EMVCo.
- CVU: como un CBU pero de billetera virtual; es la "cuenta recaudadora" donde caen los pesos.
- Coelsa: la cámara compensadora que acredita las transferencias entre bancos.
- Settlement / liquidación: mover el valor a la moneda final del comercio (acá, on-chain en Stellar).
- Anchor: puente regulado fiat ↔ Stellar. Se habla con él por estándares SEP.
- SEP-1/10/24: descubrimiento / login / ramp interactivo, respectivamente.
- BlindPay: proveedor de off-ramp (stablecoin → fiat local por rails como CBU/CVU/PIX).
- Off-ramp / on-ramp: salir de cripto a fiat / entrar de fiat a cripto.
- USDC / XLM: dólar tokenizado / token nativo de Stellar.
¿Dudas? El código de cada flujo está en api/src/services/ (lógica) y frontend/app/<flujo>/page.tsx (pantalla). Para correr local mirá docs/HANDOFF.md.