Pago con tarjeta tokenizada
Útil cuando querés cobrar al mismo cliente varias veces sin pedirle la tarjeta en cada compra. Ej. suscripciones, recompra rápida, una sola compra dividida en cuotas internas.
El flow tiene dos etapas: catastro (una vez por cliente, abre un iframe) y charge (cada cobro, server-to-server con el token guardado).
Diagrama del catastro
sequenceDiagram
autonumber
participant Cliente
participant TuApp as Tu app
participant Ingalca as INGALCA Pay
participant Dinelco
TuApp->>Ingalca: POST /v1/cards
{customer.customerId}
Ingalca->>Dinelco: Crear sesión registry
Dinelco-->>Ingalca: integrity_token + session_id
Ingalca-->>TuApp: card_xxx + integrity_token + validate_url
TuApp-->>Cliente: Embed iframe (validate_url)
Cliente->>Dinelco: Tipear tarjeta + 3DS OTP
Dinelco-->>Ingalca: Callback con paymentToken
Ingalca->>Ingalca: Marcar card registered
Ingalca->>TuApp: webhook card.registered (con token_id)
TuApp-->>Cliente: "Tarjeta guardada"
Paso 1 — iniciar catastro
curl -X POST https://api.pay.ingalca.com/v1/cards \ -H "Authorization: Bearer sk_test_..." \ -H "Content-Type: application/json" \ -d '{ "provider": "dinelco", "target_origin": [ "https://tu-checkout.com", "https://app.tu-checkout.com" ], "customer": { "customerId": "user_42", "name": "Juan", "lastname": "Pérez", "email": "juan@example.com", "phone": "+595981234567" } }'Respuesta:
{ "data": { "card_id": "card_xyz789", "status": "pending", "registration": { "integrity_token": "eyJhbGc...", "session_id": 123456789, "validate_url": "https://test-checkout.dinelco.com.py:50106/registry?token=eyJhbGc..." } }}Paso 2 — embeber el iframe
En tu frontend:
<iframe src="<validate_url>" width="100%" height="600" style="border: 0" allow="payment"></iframe>El cliente tipea la tarjeta dentro del iframe (que sirve directamente desde Dinelco — vos nunca ves los datos sensibles). Al pasar 3DS, Dinelco le pega un callback a INGALCA Pay con el paymentToken.
Paso 3 — recibir webhook de catastro
{ "event_type": "card.registered", "data": { "card_id": "card_xyz789", "customer_id": "user_42", "status": "registered", "provider": "dinelco", "brand": "Visa", "last_four": "1096", "expires": "12/30" }}Ya podés cobrar con esta card.
Diagrama del charge
sequenceDiagram
autonumber
participant TuApp as Tu app
participant Ingalca as INGALCA Pay
participant Dinelco
TuApp->>Ingalca: POST /v1/cards/{card_id}/charge
{amount, reference}
Ingalca->>Dinelco: POST api/v3/payment
(con tokenId)
Dinelco-->>Ingalca: status + responseCode
Ingalca-->>TuApp: payment confirmed o failed
Ingalca->>TuApp: webhook payment.confirmed o payment.failed
Paso 4 — cobrar
curl -X POST https://api.pay.ingalca.com/v1/cards/card_xyz789/charge \ -H "Authorization: Bearer sk_test_..." \ -H "Content-Type: application/json" \ -d '{ "amount": 50000, "reference": "SUSCRIPCION-MAY-2026" }'A diferencia del checkout, el cobro es sincrónico — la respuesta ya tiene el resultado:
{ "data": { "id": "pay_def456", "status": "confirmed", "amount": 50000, "provider_data": { "operation_number": "0000123456", "authorization_number": "ABC123" } }}Estados de la tarjeta
stateDiagram-v2
[*] --> pending: POST /v1/cards
pending --> registered: callback OK + 3DS pasado
pending --> invalid: callback rechazado
registered --> deleted: DELETE /v1/cards/{id}*
invalid --> [*]
deleted --> [*]
* Hoy DELETE devuelve 501 — Dinelco no expone endpoint de unregister. Si querés “borrar” una tarjeta, marcala como inactiva en tu app y dejá de usarla; INGALCA Pay no la va a cobrar si no se la pedís.
Notas
- El
paymentTokense guarda encriptado del lado nuestro y nunca se devuelve en respuestas. Vos solo manejás elcard_id(card_xxx). - 3DS es obligatorio en el catastro de Dinelco — el cliente siempre pasa por el OTP.
- El
customerIdque mandamos a Dinelco es nuestropublic_idinterno (cust_xxx), no elexternal_idque vos enviás. Esto evita colisiones cross-tenant en sandboxes compartidos.