Pago con tarjeta tokenizada
Útil cuando tu cliente compra varias veces y no querés que retipee los datos de la tarjeta en cada compra. Después del catastro inicial, cada compra es un click: el cliente elige su tarjeta guardada y autoriza el cobro.
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": "ORDEN-12345" }'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.