Portal de Integración
PaymentForm
"El foco son los comercios, la prioridad su rentabilidad, el objetivo la innovación y el pragmatismo."
PaymentForm es el formulario de pago hospedado de PROCASH. Tu sitio nunca ve datos de tarjeta: el formulario es servido desde un dominio PCI-compliant. Solo ejecutás dos llamadas server-to-server desde tu backend: OrderInitial para crear la orden y OrderFinal para confirmar el resultado.
Solo 2 llamadas S2S
OrderInitial para crear la orden, OrderFinal para confirmar el resultado. Todo lo demás es automático.
PCI SAQ-A incluido
Tu servidor nunca ve ni procesa datos de tarjeta. El alcance PCI es mínimo por diseño.
2 modos de integración
Redirect completo (más sencillo) o iframe embebido con postMessage para una experiencia sin salir de tu sitio.
Cuotas y QR
Soporte nativo de cuotas (1 a N) con planes financieros configurables por adquirente. Pagos por QR dinámico disponibles según configuración de la plataforma.
Contexto argentino
Moneda ARS (032) · Montos en centavos · CUIT/CUIL como identificador tributario · Zona horaria UTC-3 (Buenos Aires).
Cómo funciona
Diagrama de secuencia completo: actores, llamadas y flujo de datos.
Modos de integración
PaymentForm soporta tres modos según el nivel de control de UX e integración que necesitás. Los tres mantienen PCI SAQ-A — el comercio nunca captura datos de tarjeta.
Modo A — Redirect
El browser del comprador sale de tu sitio y va al formulario hospedado. El modo más sencillo y rápido de integrar.
✓ PCI SAQ-A Más fácilModo B — Embebido (iframe completo)
El formulario completo se muestra en un iframe cross-origin dentro de tu página. Comunicación mediante postMessage.
✓ PCI SAQ-A Mejor UXModo C — Hosted Fields
Solo los campos de tarjeta (PAN + CVV) van en un iframe de PROCASH. El comercio arma el resto del formulario — máximo control de diseño, sin tocar CHD.
✓ PCI SAQ-A Máximo control Diseñado · implementandoRedirigís al comprador usando CustomerRedirectAddress que devuelve OrderInitial. El formulario procesa el pago y devuelve al comprador a tu MerchantRedirectURL.
// 1. Llamar OrderInitial desde tu servidor
const response = await fetch('https://api.procash.dev/OrderInitial', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
OrderInitial: {
MerchantSystemID: 'SHOP-01', // requerido
MerchantCompanyID: '0', // requerido
TransactionAmount: 1000000, // $10.000,00 ARS (en centavos)
CurrencyCode: '032', // deseable — si se omite asume ARS
ReferenceNumber: 'ORD-2026-0001',
MerchantRedirectURL: 'https://www.mi-tienda.com.ar/pago/retorno', // opcional
MerchantNotifyURL: 'https://www.mi-tienda.com.ar/webhooks/pago', // recomendado
FacilityNumber: 3, // opcional — cuotas (1 = contado)
}
})
});
const { OrderInitialResponse: r } = await response.json();
// 2. Verificar que la orden se creó correctamente
// ResponseActions es la fuente de verdad — debe contener "OK"
// ResponseCode "-1" es el indicador de éxito en este contrato
if (!r.ResponseActions?.includes('OK') || r.ResponseCode !== '-1') {
// Orden rechazada por la plataforma — no redirigir
throw new Error(`OrderInitial fallido: [${r.ResponseActions}] ${r.ResponseMessage}`);
}
// 3. Validar presencia de los campos requeridos en la respuesta
if (!r.InitialIdentification || !r.CustomerRedirectAddress) {
throw new Error('Respuesta incompleta: faltan InitialIdentification o CustomerRedirectAddress');
}
// 4. ⚠️ Guardá InitialIdentification en tu DB ANTES de redirigir
// Es PRIVADO — nunca viaja al browser
await db.orders.update({ referenceNumber: 'ORD-2026-0001' }, {
initialIdentification: r.InitialIdentification
});
// 5. Redirigir al comprador al formulario — solo el InitialToken (PÚBLICO) viaja en la URL
res.redirect(302, r.CustomerRedirectAddress);
Embebé el formulario en tu página con un iframe usando la misma URL. Escuchá eventos postMessage — nunca viajan datos de tarjeta fuera del iframe.
paymentform:ready · paymentform:resize · paymentform:submitted · paymentform:error
<!-- iframe cross-origin — PCI SAQ-A mantenido -->
<iframe
id="payment-frame"
src="https://payment.procash.dev/?token=INITIAL_TOKEN_AQUI"
style="width:100%;border:none;min-height:420px;"
allow="payment"
></iframe>
<script>
window.addEventListener('message', (e) => {
// Siempre verificar el origen
if (e.origin !== 'https://payment.procash.dev') return;
const { type, result } = e.data;
if (type === 'paymentform:submitted') {
if (result === 'approved') {
// Llamar a tu backend para ejecutar OrderFinal
confirmOrder(result);
} else if (result === 'refused') {
showRefusedMessage();
}
}
if (type === 'paymentform:resize') {
document.getElementById('payment-frame').style.height = e.data.height + 'px';
}
});
</script>
embed-fields.ts) está implementada. La infraestructura de subdominio cross-origin y el cifrado WASM→JWE están pendientes. Consultá al equipo de PROCASH para disponibilidad en sandbox.¿Qué es Hosted Fields?
En lugar de embeber el formulario completo, solo los campos sensibles (PAN + CVV) están en un iframe cross-origin servido por PROCASH. Tu página arma el resto del checkout — monto, cuotas, datos del comprador, diseño — mientras los datos de tarjeta nunca tocan tu DOM.
Tiers de hosting disponibles
| Tier | Dominio del iframe | Para quién |
|---|---|---|
| T1 — Compartido | {tenant}.payment.procash.dev | Default — rápido de onboardear |
| T2 — White-label | pay.tudominio.com (CNAME → PROCASH) | Comercios con branding propio |
| T3 — Enterprise | Dominio completamente custom | Bancos / grandes retailers (hosting regional/on-prem) |
Contrato postMessage (eventos del iframe)
| Evento | Dirección | Payload |
|---|---|---|
paymentform:ready | iframe → parent | { mode: 'fields' } — iframe listo para recibir interacción |
paymentform:fieldValidity | iframe → parent | { field: 'pan'|'cvv', valid: bool } — para habilitar/deshabilitar el botón |
paymentform:resize | iframe → parent | { height: px } — ajustar altura del iframe |
paymentform:submit | parent → iframe | { token } — trigger de envío desde tu botón |
paymentform:submitted | iframe → parent | { result: 'approved'|'refused'|'challenge' } |
paymentform:error | iframe → parent | { code, message } |
<!-- Solo el iframe de campos sensibles (cross-origin) -->
<div id="checkout">
<!-- Tu UI: monto, cuotas, nombre del titular -->
<p>Total: <strong>$10.000,00 ARS</strong> en 3 cuotas</p>
<!-- iframe de PROCASH: solo PAN + CVV -->
<iframe
id="fields-frame"
src="https://{tenant}.payment.procash.dev/fields?token=INITIAL_TOKEN"
style="width:100%;border:none;height:120px;"
allow="payment"
sandbox="allow-scripts allow-same-origin"
></iframe>
<!-- Tu propio botón de pago -->
<input type="text" placeholder="Nombre en la tarjeta" id="cardName">
<button id="pay-btn" disabled>Pagar</button>
</div>
<script>
const frame = document.getElementById('fields-frame');
const payBtn = document.getElementById('pay-btn');
let panOk = false, cvvOk = false;
// Escuchar eventos del iframe
window.addEventListener('message', (e) => {
if (e.origin !== 'https://{tenant}.payment.procash.dev') return;
const { type, field, valid, height, result } = e.data;
if (type === 'paymentform:resize')
frame.style.height = height + 'px';
if (type === 'paymentform:fieldValidity') {
if (field === 'pan') panOk = valid;
if (field === 'cvv') cvvOk = valid;
payBtn.disabled = !(panOk && cvvOk); // habilitar solo si ambos OK
}
if (type === 'paymentform:submitted' && result === 'approved')
confirmOrder(); // llamar tu backend → OrderFinal
});
// Tu botón dispara el submit en el iframe
payBtn.addEventListener('click', () => {
frame.contentWindow.postMessage(
{ type: 'paymentform:submit' },
'https://{tenant}.payment.procash.dev'
);
});
</script>
sandbox del iframe y el allow-same-origin están configurados por la plataforma para que la comunicación postMessage funcione sin exponer el DOM sensible.Paso a paso
Seguí estos pasos para integrar PaymentForm en tu backend.
OrderInitial (S2S)
Llamada desde tu servidor para crear la orden de pago. Nunca desde el browser.
Auth: Authorization: Bearer {API_KEY}
{
"OrderInitial": {
"MerchantSystemID": "SHOP-01", // requerido
"MerchantCompanyID": "0", // requerido
"TransactionAmount": 1000000, // $10.000,00 ARS (en centavos)
"CurrencyCode": "032", // deseable — si se omite asume ARS
"ReferenceNumber": "ORD-2026-0001", // tu ID interno de orden
"MerchantRedirectURL": "https://www.mi-tienda.com.ar/pago/retorno", // opcional
"MerchantNotifyURL": "https://www.mi-tienda.com.ar/webhooks/pago", // recomendado
"FacilityNumber": 3, // opcional — cuotas (1 = contado)
"Products": [
{ "Code": "PLAN-PRO", "Name": "Plan Pro Mensual", "Quantity": 1, "UnitAmount": 1000000 }
]
}
}
{
"OrderInitialResponse": {
"ResponseActions": ["OK"], // ← fuente de verdad
"ResponseCode": "-1", // ← informativo (-1 = OK en este contrato)
"ResponseMessage": "Orden creada", // ← informativo
"InitialToken": "11111111-1111-5111-8111-111111111111",
"InitialIdentification": "99999999-9999-5999-8999-999999999999",
"CustomerRedirectAddress": "https://payment.procash.dev/?token=11111111-1111-5111-8111-111111111111",
"MerchantRedirectURL": "https://www.mi-tienda.com.ar/pago/retorno",
"TransactionValidThru": "2026-06-04T23:59:59-03:00", // UTC-3 · Buenos Aires
"Sequence": ""
}
}
InitialIdentification en tu base de datos asociado a la orden. Es PRIVADO — nunca lo envíes al browser ni lo expongas en URLs.
Redirigir al comprador
Usá CustomerRedirectAddress (ya tiene el token embebido). Equivale a https://payment.procash.dev/?token={InitialToken}
const { OrderInitialResponse: r } = await orderInitial(); // ver Paso 1
// ✅ Solo redirigir si ResponseActions contiene "OK" y ResponseCode es "-1"
if (r.ResponseActions?.includes('OK') && r.ResponseCode === '-1'
&& r.InitialIdentification && r.CustomerRedirectAddress) {
res.redirect(302, r.CustomerRedirectAddress);
} else {
// Manejar error — la orden no se creó
res.status(400).json({ error: r.ResponseMessage });
}
El formulario procesa el pago (interno)
El formulario gestiona todo el procesamiento del pago de forma interna — obtiene la configuración de la orden, captura los datos de tarjeta del comprador y procesa la transacción contra la plataforma. No requiere código de tu parte en este paso.
Retorno a tu sitio
El browser vuelve a tu MerchantRedirectURL. El redirect no incluye el resultado del pago — el status siempre se obtiene con OrderFinal (o OrderStatus) desde tu servidor.
https://www.mi-tienda.com.ar/pago/retorno
?token=11111111-1111-5111-8111-111111111111
// ← sin status, sin resultado — solo el token público
// El resultado real se obtiene llamando OrderFinal desde tu servidor
MerchantNotifyURL) también lo confirma de forma asíncrona.
MerchantNotifyURL) y/o OrderStatus para conocer el resultado en tu backend.OrderFinal (S2S)
Confirmá el resultado desde tu servidor usando el InitialIdentification que guardaste en el Paso 1.
{
"OrderFinal": {
"InitialIdentification": "99999999-9999-5999-8999-999999999999"
}
}
{
"OrderFinalResponse": {
"ResponseActions": ["OK", "Approve", "Tickets", "Completed"], // ← fuente de verdad
// OK → operación exitosa
// Approve → pago aprobado
// Tickets → hay comprobantes para renderizar
// Completed → circuito completo: OrderInitial→form→OrderFinal ejecutado
"ResponseCode": "-1", // ← informativo (-1 = aprobado)
"ResponseMessage": "Aprobado", // ← informativo
"AuthCode": "AUTH123",
"TransactionAmount": 1000000,
"CurrencyCode": "032",
"Tickets": [
{ "Type": "Customer", "Content": "..." },
{ "Type": "Merchant", "Content": "..." }
]
}
}
{
"OrderFinalResponse": {
"ResponseActions": ["OK", "Refuse", "Completed"], // ← fuente de verdad — pago NO acreditado
// OK → operación procesada
// Refuse → rechazado por el emisor
// Completed → circuito completo ejecutado (igual que en Approve)
"ResponseCode": "05", // ← informativo, solo un ejemplo — puede ser cualquier código
"ResponseMessage": "Tarjeta rechazada por el emisor", // ← informativo, solo un ejemplo
"TransactionAmount": 1000000,
"CurrencyCode": "032"
}
}
OK → siempre presente junto con Approve o Refuse.
Approve → pago aprobado · confirmar y entregar.
Refuse → rechazado · no cobrado · notificar al comprador.
Completed → circuito completo: el comercio ejecutó OrderFinal y cerró el ciclo de integración.
Tickets → hay comprobantes en Tickets[] para renderizar al comprador.
No uses ResponseCode ni ResponseMessage como criterio de decisión. Lógica correcta:
includes('Approve') para aprobado · includes('Refuse') para rechazado · includes('Completed') para circuito cerrado.
📡 Webhook — MerchantNotifyURL
El Core llama tu MerchantNotifyURL de forma asíncrona con el resultado del pago. Garantiza que recibís el resultado aunque el browser no vuelva a tu sitio.
Look & Feel del formulario
PaymentForm se adapta completamente al branding del comercio. Los estilos, el modelo de interfaz, los productos y el logo se configuran por orden, directamente en el OrderInitial vía AdditionalInformation — sin tocar código del formulario.
AdditionalInformation[].Name = "PaymentForm.Styles" con el JSON de estilos en Base64. El branding del comercio (nombre + logo) va en Name = "PaymentForm.Company". Ambos se envían en OrderInitial y el formulario los aplica con prioridad sobre la configuración base.Modelos de interfaz
Se selecciona con FormMode en la configuración. Podés combinar cualquier modelo con cualquier tema de color.
Capturas — la misma orden en las cuatro combinaciones
interactive-card · ShowProducts:true
interactive-card · ShowProducts:false
classic · ShowProducts:true
classic · ShowProducts:falseCómo configurar los estilos desde OrderInitial
Los estilos se envían en AdditionalInformation como JSON en Base64. El formulario los aplica con prioridad sobre cualquier configuración por defecto.
Background, Text y Form. Cualquier propiedad de personalización de inputs o tarjetas debe asignarse dentro de estos bloques (por ejemplo, usar Form.input_color en lugar de un objeto Input independiente). Los bloques raíz obsoletos como Card o Input no son reconocidos y serán ignorados.// Objeto de estilos (JSON plano antes de encodear)
const stylesArray = [
{ "Background": { "color": "#fcf9fb" } },
{ "Text": { "color": "#130a25" } },
{
"Form": {
"background_color": "#fcf9fb",
"button_color": "#a47fab",
"price_color": "#68ac5c",
"error_color": "#dc2626",
"border_radius": "10px",
"max_width": "520px",
"input_color": "#ffffff",
"input_innertext_color": "#130a25",
"input_border_color": "#f3f3f4",
"form_label": "orden", // "pedido" | "orden" | "operacion" | "transaction"
"show_cft": true,
"show_tea": false,
"show_interest_rate": true,
"show_installment_calculation": false,
"installment_calculation_mode": "simple"
}
}
];
const stylesB64 = btoa(JSON.stringify(stylesArray)); // encodear a Base64
// Branding del comercio
const companyB64 = btoa(JSON.stringify({
"Name": "Tu Comercio",
"LogoUrl": "https://www.mi-tienda.com.ar/logo.png" // URL pública — requiere acceso desde el browser del comprador
// — o —
// "LogoUrl": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0i..." // Data URI — cero requests externos (recomendado para PCI)
}));
// Ejemplo real de Data URI con un ícono SVG (64×64, fondo #004785)
// Podés generar el tuyo con: btoa(svgString) en el browser, o base64.b64encode(svg.encode()).decode() en Python
const iconoBase64 = "data:image/svg+xml;base64," +
// PNG: ícono + blanco sobre fondo #004785 (32×32 px)
"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAc0lEQVR42u3WWw4AERQDULuzMuvmW+J1S+sRN36nJ0Zm1LnjxgdCYnsRo3HGFG1m4PQhYzK9Y9SfiaWxG1yguWszUDC4QO/cECAzxEBE51lg7I0jQM34Z7D/OyAA9//snrhwFHeyolUoepGi2Ym66Yp2nQBekRDCxivTugAAAABJRU5ErkJggg==";
{
"OrderInitial": {
"MerchantSystemID": "SHOP-01",
"MerchantCompanyID": "0",
"TransactionAmount": 1000000,
"CurrencyCode": "032",
"ReferenceNumber": "ORD-2026-0001",
"Products": [
{
"Code": "PLAN-PRO",
"Name": "Plan Pro Mensual",
"Quantity": 1,
"UnitAmount": 1000000,
"ImageUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAc0lEQVR42u3WWw4AERQDULuzMuvmW+J1S+sRN36nJ0Zm1LnjxgdCYnsRo3HGFG1m4PQhYzK9Y9SfiaWxG1yguWszUDC4QO/cECAzxEBE51lg7I0jQM34Z7D/OyAA9//snrhwFHeyolUoepGi2Ym66Yp2nQBekRDCxivTugAAAABJRU5ErkJggg=="
// ↑ PNG real 32×32 px — ícono + blanco sobre fondo #004785 — 172 bytes / 232 chars base64
}
],
"AdditionalInformation": [
{
"Name": "PaymentForm.Styles",
"Value": "<stylesB64>" // resultado de btoa(JSON.stringify(stylesArray))
},
{
"Name": "PaymentForm.Company",
"Value": "<companyB64>" // resultado de btoa(JSON.stringify(companyObj))
}
]
}
}
Tokens de estilo disponibles
| Token | Objeto | Descripción | Ejemplo |
|---|---|---|---|
color | Background | Color de fondo de la página | #fcf9fb · #0f172a |
color | Text | Color de los textos principales de la tarjeta | #130a25 · #ffffff |
background_color | Form | Color de fondo de la tarjeta del formulario | #fcf9fb · #ffffff |
button_color | Form | Color del botón "Pagar" | #a47fab · #004785 |
price_color | Form | Color del monto total | #68ac5c · #22c55e |
error_color | Form | Color de mensajes de error de validación | #dc2626 · #fb7185 |
input_color | Form | Color de fondo de los campos de texto | #ffffff · #f8fafc |
input_innertext_color | Form | Color de los textos ingresados en los campos | #130a25 · #0f172a |
input_border_color | Form | Color del borde de los campos de texto | #f3f3f4 · #e2e8f0 |
border_radius | Form | Redondez de la tarjeta del formulario | 10px · 0 |
max_width | Form | Ancho máximo del formulario | 520px |
form_label | Form | Título del formulario | orden · pedido · operacion · transaction |
show_cft | Form | Habilita/Deshabilita mostrar el Costo Financiero Total (CFT) en el selector de cuotas | true · false |
show_tea | Form | Habilita/Deshabilita mostrar la Tasa Efectiva Anual (TEA) en el selector de cuotas | true · false |
show_interest_rate | Form | Habilita/Deshabilita mostrar la Tasa Nominal Anual (TNA) en el selector de cuotas | true · false |
show_installment_calculation | Form | Si es true, calcula el valor de cada cuota aplicando el interés del plan en lugar de dividir el total simple | true · false |
installment_calculation_mode | Form | Fórmula para calcular la cuota con interés: simple (directo), french_tna (Francés TNA), french_tea (Francés TEA), cft (Francés CFT) | simple · french_tna · french_tea · cft |
header_align | Form | Alineación horizontal del encabezado de la orden | left · center · right |
header_font | Form | Familia tipográfica del encabezado de la orden | Outfit, sans-serif |
header_size | Form | Tamaño de letra del encabezado de la orden | 14px · 0.875rem |
header_color | Form | Color del texto del encabezado de la orden | #475569 |
button_align | Form | Alineación y estiramiento del botón de pago | flex-start · center · flex-end · stretch |
button_width | Form | Ancho explícito del botón de pago | 200px · 100% |
button_padding | Form | Espaciado interno (padding) del botón de pago | 12px 24px |
button_font_size | Form | Tamaño de letra del botón de pago | 16px |
button_icon_url | Form | URL de icono personalizado para reemplazar el candado del botón | https://example.com/shield.png |
amount_font | Form | Familia tipográfica de la cifra del importe | Inter, sans-serif |
amount_font_size | Form | Tamaño de letra del importe | 24px · 1.5rem |
amount_color | Form | Color del texto de la cifra del importe | #16a34a |
PaymentForm.Styles) dentro del bloque "Form" (ej: Styles[].Form.form_mode). Si no se envían, la plataforma aplicará los valores preconfigurados para tu cuenta o los valores por defecto del formulario.Configuración del formulario
| Campo | Tipo | Default | Descripción |
|---|---|---|---|
FormMode | string | classic | classic — formulario tradicional · interactive-card — tarjeta 3D interactiva (premium) |
ShowProducts | boolean | true | Muestra u oculta el detalle de productos. Si false, el formulario es más compacto. |
AllowInstallments | boolean | true | Habilita el selector de cuotas. Los planes disponibles los determina el BIN de la tarjeta. |
AllowAmountEdit | boolean | false | Permite que el comprador modifique el monto antes de pagar. |
ProductLayout | string | list | list — vertical · grid — grilla 2 col · compact — sin imágenes, ideal para carritos grandes |
Locale | string | es-AR | Idioma y formato. es-AR · en-US · pt-BR. Cambia labels, formatos de fecha y moneda. |
ImageUrl en Products[] acepta URL pública o Data URI base64. Cuando se usa base64, la imagen no genera peticiones de red externas — cumplimiento PCI estricto. Las imágenes también pueden venir en el Products[] que devuelve el Core.ImageUrl:data:<mime>;base64,<datos> — RFC 2397. El header iVBORw0KGgo identifica siempre un PNG (bytes mágicos \x89PNG). Soporta image/png, image/jpeg, image/webp, image/svg+xml.reader.onload = e =>
reader.readAsDataURL(imageFile);
with
b64 = base64.b64encode(f.read()).decode()
uri = f"data:image/png;base64,{b64}"
const b64 = fs.readFileSync('logo.png')
.toString('base64');
const uri = `data:image/png;base64,${b64}`;
Datos de prueba
Usá estas tarjetas y escenarios en el ambiente sandbox de PROCASH.
Tarjetas de prueba
| Número de tarjeta | Marca | CVV | Vencimiento | Resultado |
|---|---|---|---|---|
4111 1111 1111 1111 |
Visa | 123 |
12/28 |
✅ Aprobada |
4000 0000 0000 0002 |
Visa | 123 |
12/28 |
❌ Rechazada |
5555 5555 5555 4444 |
Mastercard | 123 |
12/28 |
✅ Aprobada |
5105 1051 0510 5100 |
Mastercard | 123 |
12/28 |
❌ Rechazada |
ResponseActions — valores posibles
ResponseActions es un array — siempre puede contener múltiples valores a la vez. OK y Completed acompañan siempre a Approve o Refuse cuando el circuito está completo.
| ResponseActions (array) | ResponseCode | Significado |
|---|---|---|
["OK", "Approve", "Completed"] |
-1 |
✅ Pago aprobado · circuito cerrado |
["OK", "Approve", "Tickets", "Completed"] |
-1 |
✅ Aprobado + comprobantes disponibles |
["OK", "Refuse", "Completed"] |
05 (ej.) |
❌ Rechazado — el código es informativo y puede variar |
⚠️ Los valores de ResponseCode son solo ejemplos. Existe una gran variedad de códigos posibles según el emisor y la plataforma. La fuente de verdad es siempre el action Refuse o Error en ResponseActions — ese es el indicador real de que el pago no fue aprobado, independientemente del código.
|
||
actions.includes('Approve') → aprobado, acreditar.actions.includes('Refuse') → rechazado, no cobrado.actions.includes('Completed') → el comercio ejecutó OrderFinal y cerró el ciclo.actions.includes('Tickets') → renderizar Tickets[] al comprador.Pago NO aprobado:
includes('Refuse') o includes('Error') son los únicos indicadores reales de rechazo. ResponseCode y ResponseMessage son informativos y pueden variar — nunca los uses como criterio de decisión.
Escenarios de prueba
Escenarios configurables en el sandbox — consultá al equipo de PROCASH para activarlos.
Métodos de pago en sandbox
| Método | Disponible en sandbox | Notas |
|---|---|---|
| 💳 Tarjeta de crédito (cuotas) | ✅ Sí | Visa, Mastercard, Amex · hasta 12 cuotas según plan del BIN |
| 💳 Tarjeta de débito | ✅ Sí | Solo 1 cuota · PIN no requerido en canal CNP |
| 📱 QR dinámico | ⚡ Según config | Requiere habilitación en la plataforma · consultá al equipo de PROCASH |
| 🏦 Transferencia / CVU | ⚡ Según config | Disponible si el plan lo incluye · el form muestra el CVU destino |
Endpoints Sandbox
Referencia rápida
Campos clave de la request OrderInitial.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
MerchantSystemID |
string | ✅ Sí | Identificador del sistema merchant |
MerchantCompanyID |
string | ✅ Sí | ID de empresa |
MerchantBranch |
string | No aplica | No se usa en integración web CNP. La plataforma lo resuelve por configuración del comercio. |
MerchantPOSID |
string | No aplica | No se usa en integración web CNP. La plataforma lo resuelve por configuración del comercio. |
TransactionAmount |
integer | ✅ Sí | Monto en centavos (ej: 1000000 = $10.000,00 ARS) |
CurrencyCode |
string | ⚡ Deseable | ISO 4217 — 032 = ARS (Pesos argentinos). Si se omite, la plataforma asume la moneda del país del comercio. Se recomienda enviarlo siempre. |
ReferenceNumber |
string | ✅ Sí | Tu ID de orden interno (para reconciliación) |
MerchantRedirectURL |
string | Opcional | URL a la que vuelve el comprador tras el pago. Si se omite, no hay redirect: el formulario muestra directamente el resultado final (aprobado / rechazado) sin salir de la página del form. |
MerchantNotifyURL |
string | ⚡ Recom. | Webhook — el Core notifica el resultado de forma asíncrona |
Products |
array | Opcional | Detalle de productos para mostrar en el formulario |
FacilityNumber |
integer | Opcional | Cantidad de cuotas (1 = contado, 3/6/12… = cuotas). Requiere plan financiero habilitado para el BIN. |
TaxIdentification |
string | Opcional | Identificador tributario del comprador — CUIT/CUIL para Argentina (ej: 20-12345678-9) |
Montos en ARS — formato
El campo TransactionAmount va en centavos (sin coma decimal). Usá el separador de miles con punto y el decimal con coma al mostrarlo al usuario.
| Monto para el usuario | TransactionAmount | Nota |
|---|---|---|
| $1.000,00 ARS | 100000 | Mil pesos |
| $5.000,00 ARS | 500000 | Cinco mil pesos |
| $10.000,00 ARS | 1000000 | Diez mil pesos |
| $50.000,00 ARS | 5000000 | Cincuenta mil pesos |
| $100.000,00 ARS | 10000000 | Cien mil pesos |
💡 Para convertir: TransactionAmount = Math.round(montoPesos * 100)
Flujo resumido
Obtené token + ID
Comprador ingresa tarjeta
+ webhook asíncrono
Confirmá el resultado real
Seller · Payer · Customer · Shipping
OrderInitial acepta cuatro objetos opcionales que enriquecen la transacción con datos de las partes involucradas. Todos son opcionales — incluirlos permite mayor trazabilidad, análisis antifraude y cumplimiento regulatorio.
Payer no está presente, la plataforma toma los datos de Customer como pagador. En la mayoría de los casos de e-commerce son la misma persona — basta con enviar Customer.
Datos del comprador/cliente. Es el objeto más común en e-commerce. Si Payer no se envía, estos datos se usan también como pagador.
| Campo | Tipo | Descripción |
|---|---|---|
FirstName | string | Primer nombre |
LastName | string | Apellido |
MiddleName | string | Nombre/s del medio |
Email | string | Email del cliente |
Phone | string | Número de teléfono |
DocumentType | enum | CI · PAS · ACCOUNT_NUMBER · CONTRACT · OTHER |
DocumentNumber | string | Número de documento |
TaxIdentificationType | string | Tipo de identificador tributario — en Argentina: CUIT o CUIL |
TaxIdentification | string | Número de CUIT/CUIL u otro identificador tributario |
AddressStreet | string | Calle |
AddressNumber | string | Número exterior |
AddressInternal | string | Piso, depto, unidad |
AddressSuburb | string | Barrio / colonia |
City | string | Ciudad |
State | string | Provincia / estado |
Country | string | País |
ZipCode | string | Código postal |
NotifyURL | string | URL de notificación específica para el cliente |
{
"OrderInitial": {
"MerchantSystemID": "SHOP-01",
"MerchantCompanyID": "0",
"TransactionAmount": 1000000,
"CurrencyCode": "032",
"ReferenceNumber": "ORD-2026-0001",
"Customer": {
"FirstName": "María",
"LastName": "González",
"Email": "maria.gonzalez@email.com",
"Phone": "1123456789",
"DocumentType": "CI",
"DocumentNumber": "12345678",
"TaxIdentificationType": "CUIL",
"TaxIdentification": "27-12345678-3",
"AddressStreet": "Av. Corrientes",
"AddressNumber": "1234",
"AddressInternal": "Piso 3 Dpto B",
"City": "Buenos Aires",
"State": "CABA",
"Country": "AR",
"ZipCode": "C1043"
}
}
}
Datos del pagador — la persona que efectúa el pago con su tarjeta. Mismo schema que Customer. Solo necesario cuando el pagador es distinto del comprador (ej: alguien paga en nombre de otro).
| Campo | Tipo | Descripción |
|---|---|---|
FirstName | string | Primer nombre del pagador |
LastName | string | Apellido del pagador |
Email | string | Email del pagador |
Phone | string | Teléfono del pagador |
DocumentType | enum | CI · PAS · ACCOUNT_NUMBER · CONTRACT · OTHER |
DocumentNumber | string | Número de documento del pagador |
TaxIdentificationType | string | Tipo de identificador tributario — en Argentina: CUIT o CUIL |
TaxIdentification | string | Número de CUIT/CUIL del pagador |
AddressStreet · AddressNumber · City · State · Country · ZipCode | string | Dirección completa del pagador |
NotifyURL | string | URL de notificación específica para el pagador |
Customer y omití Payer. La plataforma usará Customer como pagador automáticamente.Datos del vendedor. Útil en modelos marketplace o cuando el comercio necesita identificar al vendedor específico dentro de la plataforma.
| Campo | Tipo | Descripción |
|---|---|---|
FirstName | string | Nombre del vendedor |
LastName | string | Apellido del vendedor |
Email | string | Email del vendedor |
DocumentType | enum | CI · PAS · ACCOUNT_NUMBER · CONTRACT · OTHER |
DocumentNumber | string | Número de documento del vendedor |
TaxIdentificationType | string | Tipo de identificador tributario — en Argentina: CUIT |
TaxIdentification | string | CUIT del vendedor |
Identification | string | Identificador interno del vendedor en la plataforma |
IdentificationType | enum | Tipo de identificador: CI · PAS · ACCOUNT_NUMBER · CONTRACT · OTHER |
AddressStreet · AddressNumber · City · State · Country | string | Dirección del vendedor |
{
"OrderInitial": {
// ... campos base ...
"Seller": {
"Identification": "SELLER-0042",
"IdentificationType": "CONTRACT",
"FirstName": "Distribuidora",
"LastName": "Sur S.A.",
"TaxIdentificationType": "CUIT",
"TaxIdentification": "30-71234567-8",
"Email": "ventas@distribuidorasur.com.ar"
}
}
}
Dirección de entrega del pedido. Independiente de la dirección de facturación del comprador. Necesario cuando el envío físico es parte del flujo.
| Campo | Tipo | Max | Descripción |
|---|---|---|---|
FirstName | string | 100 | Nombre del destinatario |
LastName | string | 100 | Apellido del destinatario |
Address1 | string | 255 | Dirección principal (calle y número) |
Address2 | string | 255 | Complemento (piso, dpto, unidad) |
City | string | 100 | Ciudad de entrega |
StateProvince | string | 100 | Provincia |
Country | string | 50 | País (ej: AR) |
ZipCode | string | 20 | Código postal |
PhoneNumber | string | 50 | Teléfono de contacto para la entrega |
Email | string | 255 | Email de contacto para notificaciones de envío |
{
"OrderInitial": {
// ... campos base + Customer ...
"Shipping": {
"FirstName": "María",
"LastName": "González",
"Address1": "Av. Corrientes 1234",
"Address2": "Piso 3 Dpto B",
"City": "Buenos Aires",
"StateProvince": "CABA",
"Country": "AR",
"ZipCode": "C1043",
"PhoneNumber": "1123456789",
"Email": "maria.gonzalez@email.com"
}
}
}
Ejemplo completo — todos los objetos
{
"OrderInitial": {
"MerchantSystemID": "SHOP-01",
"MerchantCompanyID": "0",
"TransactionAmount": 1000000, // $10.000,00 ARS
"CurrencyCode": "032",
"ReferenceNumber": "ORD-2026-0001",
"FacilityNumber": 3, // 3 cuotas
"MerchantRedirectURL": "https://www.mi-tienda.com.ar/pago/retorno",
"MerchantNotifyURL": "https://www.mi-tienda.com.ar/webhooks/pago",
"Products": [
{ "Code": "PROD-001", "Name": "Zapatillas Running", "Quantity": 1, "UnitAmount": 1000000 }
],
"Customer": { // comprador = pagador (caso más común)
"FirstName": "María",
"LastName": "González",
"Email": "maria.gonzalez@email.com",
"Phone": "1123456789",
"DocumentType": "CI",
"DocumentNumber": "12345678",
"TaxIdentificationType": "CUIL",
"TaxIdentification": "27-12345678-3"
},
"Shipping": { // dirección de entrega (puede diferir de la de Customer)
"FirstName": "María",
"LastName": "González",
"Address1": "Av. Corrientes 1234",
"Address2": "Piso 3 Dpto B",
"City": "Buenos Aires",
"StateProvince": "CABA",
"Country": "AR",
"ZipCode": "C1043",
"PhoneNumber": "1123456789",
"Email": "maria.gonzalez@email.com"
}
// Seller solo si es marketplace — Payer solo si difiere del Customer
}
}