la primer plataforma que le da autonomía real a los comercios

Portal de Integración
PaymentForm

PROCASH · API de Pagos v5.8.2 · Documentación para desarrolladores

"El foco son los comercios, la prioridad su rentabilidad, el objetivo la innovación y el pragmatismo."

🔒 PCI SAQ-A ↗ Redirect ⬜ Embebido (iframe) ⇄ S2S 💳 Cuotas 📱 QR 🇦🇷 ARS · Argentina

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).

Flujo de pago

Cómo funciona

Diagrama de secuencia completo: actores, llamadas y flujo de datos.

🖥️ Tu sitioEC / Backend
⚡ GatewayAPI Core
💳 PaymentFormFormulario hospedado
👤 CompradorBrowser
1
POST /OrderInitial
monto, moneda, MerchantRedirectURL, MerchantNotifyURL
InitialToken + InitialIdentification
+ CustomerRedirectAddress — guardá InitialIdentification en tu DB
3 · Redirect 302 → CustomerRedirectAddress?token=…
EC redirige al browser al formulario hospedado
4 · Abre PaymentForm
5 · Formulario obtiene configuración (interno)
6 · Ingresa datos de tarjeta
7 · Formulario procesa el pago (interno)
8 · Redirect → MerchantRedirectURL?token=…
9 · Browser vuelve a tu sitio
10 · POST /OrderFinal (InitialIdentification)
11 · AuthCode, Tickets, monto confirmado
ℹ️
Regla de oro: El resultado del redirect (paso 8–9) es solo una señal de navegación. El resultado real y definitivo siempre se obtiene con OrderFinal (paso 10–11) desde tu servidor.
Opciones de integración

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ácil
⬜

Modo 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 UX
EN CONSTRUCCIÓN
🧩

Modo 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 · implementando

Redirigís al comprador usando CustomerRedirectAddress que devuelve OrderInitial. El formulario procesa el pago y devuelve al comprador a tu MerchantRedirectURL.

Node.js / Express
// 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.

📡
Eventos postMessage disponibles:
paymentform:ready · paymentform:resize · paymentform:submitted · paymentform:error
HTML + JavaScript
<!-- 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>
🚧
Estado: diseñado y en construcción (Fase 16). El contrato de integración v1.0.0 está definido y la lógica client-side (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.

// Tu página (out of PCI scope)
TU FORMULARIO
[ Monto: $10.000,00 ARS ]
[ Cuotas: 3 sin interés ]
⬛ IFRAME DE PROCASH (cross-origin · SAQ-D scope de PROCASH)
[ PAN: ________________ ]
[ CVV: ___ ] [ Vto: __/__ ]
→ WASM cifra a JWE antes de salir
[ Nombre del titular: ________ ]
[ Pagar → ]
↕ postMessage: "ready" · "fieldValidity" · "submit" · "error" · "challenge"

Tiers de hosting disponibles

TierDominio del iframePara quién
T1 — Compartido{tenant}.payment.procash.devDefault — rápido de onboardear
T2 — White-labelpay.tudominio.com (CNAME → PROCASH)Comercios con branding propio
T3 — EnterpriseDominio completamente customBancos / grandes retailers (hosting regional/on-prem)

Contrato postMessage (eventos del iframe)

EventoDirecciónPayload
paymentform:readyiframe → parent{ mode: 'fields' } — iframe listo para recibir interacción
paymentform:fieldValidityiframe → parent{ field: 'pan'|'cvv', valid: bool } — para habilitar/deshabilitar el botón
paymentform:resizeiframe → parent{ height: px } — ajustar altura del iframe
paymentform:submitparent → iframe{ token } — trigger de envío desde tu botón
paymentform:submittediframe → parent{ result: 'approved'|'refused'|'challenge' }
paymentform:erroriframe → parent{ code, message }
HTML + JavaScript — Hosted Fields (Modelo C)
<!-- 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>
🔒
Seguridad: el iframe cifra PAN + CVV con WASM → JWE antes de que salgan del iframe. Tu página nunca ve los datos en claro, aunque son misma ventana de browser. El 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.
Guía de integración

Paso a paso

Seguí estos pasos para integrar PaymentForm en tu backend.

1

OrderInitial (S2S)

Llamada desde tu servidor para crear la orden de pago. Nunca desde el browser.

POST https://api.procash.dev/OrderInitial

Auth: Authorization: Bearer {API_KEY}

Request JSON
{
  "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 }
    ]
  }
}
Response JSON
{
  "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": ""
  }
}
ℹ️
ResponseActions es la fuente de verdad. ResponseCode y ResponseMessage son informativos — siempre están presentes pero no deben usarse como criterio de decisión.
⚠️
Crítico: Guardá InitialIdentification en tu base de datos asociado a la orden. Es PRIVADO — nunca lo envíes al browser ni lo expongas en URLs.
2

Redirigir al comprador

Usá CustomerRedirectAddress (ya tiene el token embebido). Equivale a https://payment.procash.dev/?token={InitialToken}

Express / Node.js
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 });
}
3

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.

🔒
Tu sitio nunca ve ni toca los datos de tarjeta. Todo ocurre en el entorno PCI de PROCASH.
4

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.

URL de retorno (lo que llega al browser)
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
⚠️
El redirect no trae el resultado. Nunca asumas aprobado/rechazado por el solo hecho de que el comprador volvió a tu sitio. Ejecutá siempre OrderFinal (S2S) para obtener el resultado real. El webhook (MerchantNotifyURL) también lo confirma de forma asíncrona.
💡
Sin MerchantRedirectURL: si omitís este campo en OrderInitial, no hay redirect. El formulario muestra el resultado directamente en pantalla. En ese caso, usá el webhook (MerchantNotifyURL) y/o OrderStatus para conocer el resultado en tu backend.
5

OrderFinal (S2S)

Confirmá el resultado desde tu servidor usando el InitialIdentification que guardaste en el Paso 1.

POST https://api.procash.dev/OrderFinal
Request JSON
{
  "OrderFinal": {
    "InitialIdentification": "99999999-9999-5999-8999-999999999999"
  }
}
Response JSON — Aprobado
{
  "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": "..." }
    ]
  }
}
Response JSON — Rechazado
{
  "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"
  }
}
ℹ️
Lógica de decisión — ResponseActions es siempre un array:

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.

🔁 Puede llegar más de una vez — hacelo idempotente ✓ Úsalo junto a OrderFinal 📬 Verificar firma si está disponible
Personalización visual

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.

ℹ️
Los estilos viajan en 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

Con tarjeta y con productos
Con tarjeta · con productos
interactive-card · ShowProducts:true
Con tarjeta sin productos
Con tarjeta · sin productos
interactive-card · ShowProducts:false
Sin tarjeta con productos
Sin tarjeta · con productos
classic · ShowProducts:true
Sin tarjeta sin productos
Sin tarjeta · sin productos
classic · ShowProducts:false

Có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.

⚠️
Importante (Estructura del JSON): El motor de renderizado del formulario solo admite tres objetos raíz dentro del array de estilos: 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.
Paso 1 — Armá el objeto de estilos
// 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==";
Paso 2 — Incluirlos en OrderInitial
{
  "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

TokenObjetoDescripciónEjemplo
colorBackgroundColor de fondo de la página#fcf9fb · #0f172a
colorTextColor de los textos principales de la tarjeta#130a25 · #ffffff
background_colorFormColor de fondo de la tarjeta del formulario#fcf9fb · #ffffff
button_colorFormColor del botón "Pagar"#a47fab · #004785
price_colorFormColor del monto total#68ac5c · #22c55e
error_colorFormColor de mensajes de error de validación#dc2626 · #fb7185
input_colorFormColor de fondo de los campos de texto#ffffff · #f8fafc
input_innertext_colorFormColor de los textos ingresados en los campos#130a25 · #0f172a
input_border_colorFormColor del borde de los campos de texto#f3f3f4 · #e2e8f0
border_radiusFormRedondez de la tarjeta del formulario10px · 0
max_widthFormAncho máximo del formulario520px
form_labelFormTítulo del formularioorden · pedido · operacion · transaction
show_cftFormHabilita/Deshabilita mostrar el Costo Financiero Total (CFT) en el selector de cuotastrue · false
show_teaFormHabilita/Deshabilita mostrar la Tasa Efectiva Anual (TEA) en el selector de cuotastrue · false
show_interest_rateFormHabilita/Deshabilita mostrar la Tasa Nominal Anual (TNA) en el selector de cuotastrue · false
show_installment_calculationFormSi es true, calcula el valor de cada cuota aplicando el interés del plan en lugar de dividir el total simpletrue · false
installment_calculation_modeFormFó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_alignFormAlineación horizontal del encabezado de la ordenleft · center · right
header_fontFormFamilia tipográfica del encabezado de la ordenOutfit, sans-serif
header_sizeFormTamaño de letra del encabezado de la orden14px · 0.875rem
header_colorFormColor del texto del encabezado de la orden#475569
button_alignFormAlineación y estiramiento del botón de pagoflex-start · center · flex-end · stretch
button_widthFormAncho explícito del botón de pago200px · 100%
button_paddingFormEspaciado interno (padding) del botón de pago12px 24px
button_font_sizeFormTamaño de letra del botón de pago16px
button_icon_urlFormURL de icono personalizado para reemplazar el candado del botónhttps://example.com/shield.png
amount_fontFormFamilia tipográfica de la cifra del importeInter, sans-serif
amount_font_sizeFormTamaño de letra del importe24px · 1.5rem
amount_colorFormColor del texto de la cifra del importe#16a34a
💡
Nota de Configuración: Las siguientes propiedades corresponden a la configuración por defecto del Tenant (comercio/plataforma). Sin embargo, puedes sobreescribirlas dinámicamente para cada transacción enviándolas en el JSON de estilos (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

CampoTipoDefaultDescripción
FormModestringclassicclassic — formulario tradicional · interactive-card — tarjeta 3D interactiva (premium)
ShowProductsbooleantrueMuestra u oculta el detalle de productos. Si false, el formulario es más compacto.
AllowInstallmentsbooleantrueHabilita el selector de cuotas. Los planes disponibles los determina el BIN de la tarjeta.
AllowAmountEditbooleanfalsePermite que el comprador modifique el monto antes de pagar.
ProductLayoutstringlistlist — vertical · grid — grilla 2 col · compact — sin imágenes, ideal para carritos grandes
Localestringes-ARIdioma y formato. es-AR · en-US · pt-BR. Cambia labels, formatos de fecha y moneda.
🖼️
Imágenes de productos: el campo 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.
Vista previa — Data URI renderizado en el browser
Ícono producto
32×32 · PNG · 172 bytes
Ícono
Plan Pro Mensual
SKU: PLAN-PRO · x1
$10.000
El string que viaja en ImageUrl:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAc0lEQVR42u3WWw4AERQDULuzMuvmW+J1S+sRN36nJ0Zm1LnjxgdCYnsRo... (232 chars · PNG 32×32 px)
Formato: 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.
¿Cómo generar el Data URI?
// Browser (JS)
const
reader =
new
FileReader();
reader.onload = e =>
console.log
(e.target.result);
reader.readAsDataURL(imageFile);
# Python
import
base64
with
open
("logo.png", "rb") as f:
  b64 = base64.b64encode(f.read()).decode()
uri = f"data:image/png;base64,{b64}"
# Node.js
const fs = require('fs');
const b64 = fs.readFileSync('logo.png')
  .toString('base64');
const uri = `data:image/png;base64,${b64}`;
Sandbox & Testing

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.
💡
Lógica de código correcta:
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.

baseline
Flujo estándar aprobado sin fricción
refused-response
Respuesta rechazada por el emisor
3ds-frictionless
3DS sin challenge — aprobación automática
3ds-snap-challenge
3DS con challenge interactivo
high-risk
Transacción de alto riesgo — revisión adicional

Métodos de pago en sandbox

MétodoDisponible en sandboxNotas
💳 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

S2S https://api.procash.dev · Gateway / API Core
WEB https://payment.procash.dev · PaymentForm (formulario hospedado)
API Reference

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 usuarioTransactionAmountNota
$1.000,00 ARS100000Mil pesos
$5.000,00 ARS500000Cinco mil pesos
$10.000,00 ARS1000000Diez mil pesos
$50.000,00 ARS5000000Cincuenta mil pesos
$100.000,00 ARS10000000Cien mil pesos

💡 Para convertir: TransactionAmount = Math.round(montoPesos * 100)

Flujo resumido

1️⃣
OrderInitial
Tu server → Gateway
Obtené token + ID
2️⃣
Redirect
Browser → PaymentForm
Comprador ingresa tarjeta
3️⃣
Retorno
Browser → tu MerchantRedirectURL
+ webhook asíncrono
4️⃣
OrderFinal
Tu server → Gateway
Confirmá el resultado real
✅
¿Dudas? Contactá al equipo de integración de PROCASH. Mencioná el número de contrato PROCASH Payments API v5.8.2 en tu consulta.
Elementos opcionales de OrderInitial

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 vs Customer: si 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.

CampoTipoDescripción
FirstNamestringPrimer nombre
LastNamestringApellido
MiddleNamestringNombre/s del medio
EmailstringEmail del cliente
PhonestringNúmero de teléfono
DocumentTypeenumCI · PAS · ACCOUNT_NUMBER · CONTRACT · OTHER
DocumentNumberstringNúmero de documento
TaxIdentificationTypestringTipo de identificador tributario — en Argentina: CUIT o CUIL
TaxIdentificationstringNúmero de CUIT/CUIL u otro identificador tributario
AddressStreetstringCalle
AddressNumberstringNúmero exterior
AddressInternalstringPiso, depto, unidad
AddressSuburbstringBarrio / colonia
CitystringCiudad
StatestringProvincia / estado
CountrystringPaís
ZipCodestringCódigo postal
NotifyURLstringURL de notificación específica para el cliente
Ejemplo — Customer en OrderInitial (Argentina)
{
  "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).

CampoTipoDescripción
FirstNamestringPrimer nombre del pagador
LastNamestringApellido del pagador
EmailstringEmail del pagador
PhonestringTeléfono del pagador
DocumentTypeenumCI · PAS · ACCOUNT_NUMBER · CONTRACT · OTHER
DocumentNumberstringNúmero de documento del pagador
TaxIdentificationTypestringTipo de identificador tributario — en Argentina: CUIT o CUIL
TaxIdentificationstringNúmero de CUIT/CUIL del pagador
AddressStreet · AddressNumber · City · State · Country · ZipCodestringDirección completa del pagador
NotifyURLstringURL de notificación específica para el pagador
💡
En la mayoría de los casos de e-commerce el comprador y el pagador son la misma persona — solo enviá 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.

CampoTipoDescripción
FirstNamestringNombre del vendedor
LastNamestringApellido del vendedor
EmailstringEmail del vendedor
DocumentTypeenumCI · PAS · ACCOUNT_NUMBER · CONTRACT · OTHER
DocumentNumberstringNúmero de documento del vendedor
TaxIdentificationTypestringTipo de identificador tributario — en Argentina: CUIT
TaxIdentificationstringCUIT del vendedor
IdentificationstringIdentificador interno del vendedor en la plataforma
IdentificationTypeenumTipo de identificador: CI · PAS · ACCOUNT_NUMBER · CONTRACT · OTHER
AddressStreet · AddressNumber · City · State · CountrystringDirección del vendedor
Ejemplo — Seller en OrderInitial (marketplace)
{
  "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.

CampoTipoMaxDescripción
FirstNamestring100Nombre del destinatario
LastNamestring100Apellido del destinatario
Address1string255Dirección principal (calle y número)
Address2string255Complemento (piso, dpto, unidad)
Citystring100Ciudad de entrega
StateProvincestring100Provincia
Countrystring50País (ej: AR)
ZipCodestring20Código postal
PhoneNumberstring50Teléfono de contacto para la entrega
Emailstring255Email de contacto para notificaciones de envío
Ejemplo — Shipping en OrderInitial
{
  "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 con Customer + Shipping (caso e-commerce típico Argentina)
{
  "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
  }
}