Contrariamente a lo que supone la mayoría de los equipos, integrar un POS virtual (cobro en línea con tarjeta) en un software a medida no consiste solo en “llamar a la API de iyzico o de PayTR”. Una integración lista para producción debe resolver cuatro problemas distintos al mismo tiempo: (1) implementar correctamente el flujo 3D Secure (3DS), es decir, enviar al usuario a la pantalla de verificación del banco y hacerlo volver; (2) verificar de forma segura el callback/webhook, lo que implica demostrar con una firma que la notificación entrante procede realmente del proveedor, ser idempotente ante notificaciones repetidas y devolver un 2xx rápido; (3) conciliar el pago con el pedido, vinculando al pedido correcto el evento en el que realmente se cobra el dinero; y (4) gestionar los escenarios posteriores a la transacción, como reembolsos, cancelaciones y cuotas. Si falta cualquiera de estos puntos, la integración parece “funcionar”, pero queda expuesta en producción a errores silenciosos que cuestan dinero: un pedido procesado dos veces, un carrito que figura como “pagado” sin haberse confirmado nunca o un producto entregado gratis mediante un callback falsificado. En este artículo, como empresa de software B2B y de marca blanca (white-label) con sede en Sakarya, compartimos exactamente el marco que aplicamos en las integraciones de POS virtual que desarrollamos: POS virtual bancario frente a entidad de pago, formulario alojado frente a API directa, flujo 3DS, webhooks seguros, conciliación, reembolsos y cuotas, y una lista de verificación previa a la salida a producción.

Este artículo aborda la integración de pagos como un proyecto de integración de API y, a la vez, como una cuestión de infraestructura de comercio electrónico. Aplicamos la misma disciplina en el ámbito del ERP y la facturación electrónica; si está valorando la decisión entre una solución estándar o a medida para la integración, consulte nuestro artículo sobre la integración de comercio electrónico, ERP y factura electrónica. Los detalles técnicos que siguen se basan en la documentación oficial de iyzico y PayTR; como cualquier proveedor puede cambiar con el tiempo, la única fuente de verdad es la documentación oficial vigente en el momento de la integración.

¿POS virtual bancario o entidad de pago?

La primera decisión de arquitectura es por qué canal cobrará los pagos con tarjeta. En Turquía, el POS virtual se ofrece de dos formas: (a) un POS virtual bancario, contratado directamente con un banco, y (b) entidades de pago independientes de los bancos (como iyzico y PayTR). Las entidades de pago operan al amparo de la Ley n.º 6493, Ley de Sistemas de Pago y de Liquidación de Valores, Servicios de Pago y Entidades de Dinero Electrónico, y su licencia de actividad no procede de la BDDK (la autoridad turca de regulación y supervisión bancaria), sino del Banco Central de la República de Turquía (TCMB), según el artículo 12 de la ley (TCMB: entidades de pago).

La diferencia práctica es esta: con un POS virtual bancario suele necesitar una integración y un contrato por cada banco; en cambio, una entidad de pago independiente le permite aceptar tarjetas de la mayoría de los bancos con una sola integración. iyzico destaca especialmente en escenarios de marketplace y subcomercios (submerchant). La regla de decisión es sencilla:

  • Si su prioridad es un solo banco, un volumen alto y la comisión más baja y tiene una relación sólida con un banco, un POS virtual bancario puede tener sentido; sin embargo, si desea trabajar con varios bancos, la carga de integración se multiplica.
  • Si busca una puesta en marcha rápida, aceptación de varios bancos y una sola integración, una entidad de pago (iyzico/PayTR) supone un menor costo de mantenimiento en la mayoría de los escenarios B2B y de pymes.
  • Si necesita funciones de marketplace, reparto de comisiones o subcomercios, una entidad de pago compatible con submerchant es casi obligatoria.

¿Formulario alojado o API directa? Aquí se decide su alcance PCI-DSS

La segunda decisión, y quizá la más crítica, es por dónde pasarán los datos de la tarjeta, porque de ello depende directamente su alcance PCI-DSS. Hay dos enfoques:

  • Formulario de pago alojado (hosted): los datos de la tarjeta se recopilan en una página o un iframe alojados por el proveedor. En la iFrame API de PayTR, el usuario escribe los datos de su tarjeta dentro del iframe de PayTR; en iyzico, CheckoutForm es la página de pago alojada por iyzico (CF-Initialize devuelve un paymentPageUrl/token y puede incrustarse con iframe=true). En ambos casos, los datos sensibles de la tarjeta nunca llegan a su servidor, por lo que su alcance PCI-DSS se reduce de forma considerable.
  • API directa: usted cobra con su propio formulario de pago y los datos de la tarjeta pasan por su propia infraestructura. Esto ofrece control total y una experiencia con su marca, pero aumenta sustancialmente sus obligaciones PCI-DSS.

Tanto iyzico como PayTR asumen la protección PCI en el lado del proveedor; iyzico se presenta como proveedor de servicios PCI DSS de nivel 1. Pero conviene precisarlo: su tipo de SAQ exacto (SAQ A, SAQ A-EP, etc.) depende de los detalles de la integración y este artículo no se pronuncia sobre él; la verdad general es que el alcance se reduce cuando los datos de la tarjeta no pasan por su servidor. Nuestra recomendación práctica es clara:

Salvo que tenga un verdadero requisito de negocio que exija un proceso de pago con su marca y totalmente controlado, elija el formulario alojado. Los pocos píxeles de personalización que gana no compensan la carga de auditoría PCI-DSS y de responsabilidad que asume.

¿Cómo funciona el flujo 3DS? (ejemplo con iyzico)

3D Secure es una verificación adicional que realiza el banco del titular de la tarjeta (un código de un solo uso, la aprobación en la app del banco, etc.). Si se omite este paso, el riesgo de contracargo (chargeback) y la responsabilidad recaen en gran medida en el negocio. En iyzico, 3DS es un flujo POST de dos pasos:

  • Paso 1 (Init 3DS): se envía un POST al endpoint /payment/3dsecure/initialize con los datos de la tarjeta y del comprador y una callbackUrl. La respuesta devuelve un threeDSHtmlContent codificado en base64. Este HTML se renderiza para redirigir al usuario a la pantalla de verificación 3DS del banco.
  • Paso 2 (Auth 3DS): cuando el usuario completa la verificación en el banco y regresa a su callbackUrl, se hace un segundo POST al endpoint de auth con el paymentId devuelto por Init. Aquí iyzico recomienda usar el endpoint v2 (/payment/v2/3dsecure/auth, es decir, 3DS 2.0); el antiguo /payment/3dsecure/auth es 3DS 1.0.

El resultado de la verificación lo indica el campo mdStatus: 1 significa éxito, mientras que los valores 0 y del 2 al 8 indican una verificación fallida. Una advertencia importante: aquí no presentamos como lista definitiva el esquema exacto de campos del cuerpo POST que el banco envía a su callbackUrl, porque puede variar según la versión y la integración; es un detalle que debe comprobarse en la documentación oficial vigente antes de salir a producción (iyzico: Init 3DS, iyzico: Auth 3DS).

A lo largo del flujo, conversationId sirve para relacionar la solicitud con la respuesta. iyzico lo define como un “ID único para la correlación entre solicitud y respuesta”; aquí puede indicar su propio número de pedido, y el valor que envía en la solicitud se devuelve sin cambios en la respuesta. Por otra parte, iyzico asigna su propio paymentId a cada pago correcto; los reembolsos y las cancelaciones se realizan mediante este paymentId.

Verificación segura de webhooks y callbacks: firma, idempotencia y 2xx rápido

Esta es la parte más descuidada y más peligrosa de una integración de pagos. El callback/webhook es el evento real de servidor a servidor en el que usted marcará el dinero como cobrado. Tres reglas son innegociables: verificar la firma, ser idempotente y devolver un 2xx rápido.

1) Verificación de la firma: demuestre que la notificación procede realmente del proveedor

Nunca considere “pagado” un POST que llegue a su callback sin verificar su firma. De lo contrario, un atacante podría enviar una notificación falsa de “pago correcto” y conseguir que se abra un pedido gratis. Los mecanismos de los dos proveedores son distintos:

  • PayTR (iFrame API): en la notificación enviada a notify_url, el hash se calcula como base64_encode(hash_hmac('sha256', merchant_oid + merchant_salt + status + total_amount, merchant_key, true)). Si el hash del POST entrante no coincide exactamente con este valor, debe considerarse que la solicitud no procede de PayTR y rechazarse. Esta fórmula corresponde a la iFrame API (cuatro campos); otras API de PayTR pueden usar un conjunto de campos distinto, así que no las confunda.
  • iyzico (webhook): la verificación se hace con el encabezado X-IYZ-SIGNATURE-V3 mediante HMAC-SHA256 (HEX); las versiones anteriores del encabezado ya no son compatibles. El punto crítico: el orden de los campos concatenados para la firma varía según el tipo de webhook. El pago directo, HPP y la suscripción usan órdenes de parámetros distintos; no existe un único orden de firma “genérico” para los webhooks, así que compruebe en la tabla oficial el orden que corresponde a su tipo de integración.

iyzico también ofrece un mecanismo independiente de firma de respuesta para las respuestas síncronas de la API: determinados parámetros se concatenan con dos puntos (:), se procesan con HMAC-SHA256 usando la secretKey y se codifican en HEX; los ceros finales innecesarios de los precios se eliminan (10.50 → 10.5, 10.00 → 10). El orden de los parámetros varía según el endpoint; por ejemplo, en la llamada auth sin 3DS el orden es paymentId:currency:basketId:conversationId:paidPrice:price, y la llamada auth 3DS v2 usa el mismo orden de campos (iyzico: validación de la firma de respuesta, iyzico: webhook, PayTR: iFrame API, paso 2).

2) Idempotencia: si la misma notificación llega dos veces, no la procese dos veces

Ambos proveedores pueden enviar más de una notificación para la misma transacción. Si no elimina los duplicados por su ID de pedido, el mismo pedido se confirma dos veces, el inventario se descuenta dos veces y se abre dos veces un envío. PayTR lo indica expresamente: el control de repetición debe hacerse con el campo único merchant_oid; para confirmar o cancelar el pedido solo debe procesarse la primera notificación, y a las repetidas basta con responderles. Implementación práctica: conserve el estado de la notificación (procesada/no procesada) en su base de datos como un único registro ligado al ID de pedido y realice el procesamiento en una sola operación atómica.

3) Devuelva un 2xx rápido: haga el trabajo pesado de forma asíncrona

El proveedor espera una respuesta positiva a la notificación en poco tiempo; si no la recibe, reintenta. En el webhook de iyzico, si el comercio no devuelve un 2xx, se reintenta cada 15 minutos, hasta 3 veces. PayTR, si no recibe “OK”, reintenta al cabo de 1 minuto y exige que el cuerpo de la respuesta sea únicamente el texto plano OK, sin HTML ni otro contenido. De ello se derivan dos reglas:

  • Primero verifique, después guarde y luego responda: verifique la firma, marque el pedido de forma unívoca y devuelva la respuesta esperada. Envíe a una cola asíncrona el trabajo pesado, como enviar correos electrónicos, emitir facturas o escribir en el ERP; no lo haga de forma síncrona bloqueando la respuesta del callback.
  • Devuelva el cuerpo de la respuesta en el formato que espera el proveedor: para PayTR, exactamente OK; para iyzico, un código de estado HTTP 2xx. De lo contrario, sufrirá reintentos innecesarios y pagos “perdidos”.
Aspecto iyzico (webhook) PayTR (callback de iFrame API)
Método de firma HMAC-SHA256 (HEX), encabezado X-IYZ-SIGNATURE-V3 HMAC-SHA256 + base64, campo hash en el POST
Orden de los campos de la firma Varía según el tipo de webhook (directo, HPP y suscripción difieren) merchant_oid + merchant_salt + status + total_amount (iFrame API)
Respuesta esperada Código de estado HTTP 2xx Texto plano OK
Comportamiento de reintento Sin 2xx: cada 15 min, hasta 3 veces Sin OK: reintento al cabo de 1 min
Clave de deduplicación paymentId / conversationId merchant_oid (solo se procesa la primera notificación)

Conciliación entre pago y pedido

La conciliación es el puente entre “el dinero que cobró el proveedor” y “el pedido en su sistema”. Una conciliación mal planteada es lo que más problemas causa en contabilidad y atención al cliente. Ambos proveedores construyen este puente de la misma forma: con un ID de pedido único generado por el comercio:

  • PayTR: merchant_oid (hasta 64 caracteres alfanuméricos) es el número de pedido único que usted asigna a la transacción; se le devuelve en el callback para permitir la conciliación. Además, en el paso 1 el paytr_token se genera con HMAC-SHA256 + base64, y la cadena concatenada incluye los campos merchant_id + user_ip + merchant_oid + email + payment_amount + user_basket + no_installment + max_installment + currency + test_mode + merchant_salt.
  • iyzico: use conversationId como su propio número de pedido; el valor que envía en la solicitud se devuelve sin cambios en la respuesta. Su equivalente en el lado del proveedor es paymentId, con el que se realizan los reembolsos y las cancelaciones. Guarde ambos ID en su propio registro de pedido.

Principios prácticos para una conciliación sólida:

  • Dé la autoridad al callback, no al navegador: la página de “éxito” a la que vuelve el navegador del usuario es un indicio, no una prueba. Confirme el pedido principalmente mediante el callback de servidor a servidor cuya firma se haya verificado.
  • Compare el importe y la moneda: contraste el importe del callback con el importe esperado en su pedido; si no coinciden, no lo procese y genere una alerta.
  • Cierre los estados pendientes: si el callback nunca llega, use una tarea en segundo plano para revisar en la API de consulta del proveedor los pedidos con “pago iniciado pero no concluido” y cerrarlos.
  • Conciliación diaria: compare periódicamente los informes de transacciones y liquidación del proveedor con sus propios registros; no dependa solo del webhook.

Reembolsos, reembolsos parciales y cuotas

La segunda mitad del flujo de pago son los escenarios posteriores a la transacción; aquí se ganan la experiencia del cliente y la exactitud contable. Las distinciones clave propias de iyzico son estas:

  • Reembolso total o parcial: hay dos vías. /payment/refund funciona a nivel de línea de artículo (con paymentTransactionId, es decir, el ID de desglose de la transacción del artículo del carrito, y price). /v2/payment/refund funciona a nivel de pago (con paymentId y price); el propio sistema determina qué artículo del carrito reembolsar. Ambas versiones admiten reembolsos totales y parciales y devuelven una referencia bancaria (refundHostReference, authCode).
  • Cancelación: /payment/cancel se llama solo con paymentId, no admite un importe parcial y cancela el pago completo. Según la documentación de iyzico, una cancelación puede realizarse el mismo día del pago y no genera ningún cargo ni abono en el extracto de la tarjeta; un reembolso, en cambio, se refleja en el extracto y puede tardar varios días según el banco. Las reglas exactas sobre el mismo día y la hora de corte varían según el banco y la fecha valor; tómelo como un principio general, no como una regla contable precisa.
  • Consulta de cuotas y BIN: con POST /payment/iyzipos/installment puede consultar las opciones de cuotas, las tasas y los importes, así como la familia y la red de la tarjeta y los datos del banco, enviando price (obligatorio) y un binNumber opcional (BIN de 8 dígitos). Las tasas de las cuotas también pueden obtenerse solo con el importe (sin BIN); conversationId se envía en la solicitud y se devuelve sin cambios en la respuesta (iyzico: reembolso y cancelación, iyzico: cuotas y BIN).

Como regla de negocio: incorpore a su sistema la lógica “cancelar si es el mismo día, reembolsar si es posterior”; cuando haga un reembolso parcial a un usuario, registre qué artículo o importe se reembolsó y guarde las referencias bancarias.

Lista de verificación antes de salir a producción

  • Verificación de la firma: ¿se verifica la firma en cada callback/webhook? ¿Se rechaza definitivamente una solicitud no verificada? En iyzico, ¿se usa el orden de campos correcto para su tipo de integración?
  • Idempotencia: ante una notificación que llega por segunda vez para el mismo ID de pedido (merchant_oid / conversationId), ¿se evita reprocesarla y se devuelve solo la respuesta adecuada?
  • Respuesta rápida: ¿el callback devuelve un 2xx / OK rápido en el formato esperado? ¿El trabajo pesado va a una cola asíncrona?
  • Control del importe: ¿se comparan el importe y la moneda del callback con el valor esperado en el pedido?
  • Fuente de autoridad: ¿la confirmación del pedido se basa en la notificación de servidor a servidor y no en la redirección del navegador?
  • Cierre de pendientes: ¿existe una consulta en segundo plano para los pagos “iniciados pero no concluidos” cuyo callback nunca llegó?
  • Reembolso y cancelación: ¿se han probado los escenarios de reembolso total y parcial, cancelación y cuotas? ¿Se guardan las referencias bancarias?
  • Secretos y HTTPS: ¿los secretos como secretKey/merchant_key/merchant_salt se guardan solo en el servidor, en variables de entorno? ¿La URL del callback es HTTPS?
  • Registro: ¿cada evento de pago (solicitud, respuesta, callback, resultado de la firma) queda registrado de forma trazable?
  • Entorno de pruebas: ¿se ha hecho la verificación de principio a fin (incluido 3DS) en el modo sandbox o de pruebas del proveedor?

Cómo le ayuda Partnerfy

Como empresa de ingeniería de software B2B y de marca blanca con sede en Sakarya, abordamos la integración de POS virtual no como una “llamada a una API”, sino como un problema de seguridad y conciliación. En un proyecto de software web a medida o de comercio electrónico, primero aclaramos la decisión: entidad de pago o POS bancario, formulario alojado o API directa (según su alcance PCI-DSS). Después construimos y verificamos, de principio a fin en un entorno de pruebas, el flujo 3DS, un callback idempotente y con firma verificada, la conciliación de importes y los escenarios de reembolso y cuotas. Normalmente lo conectamos con sus sistemas de ERP y contabilidad existentes mediante una integración de API.

Una nota honesta: ninguna integración puede garantizar que “nunca tendrá un problema”; los bancos, los proveedores y la documentación cambian con el tiempo. Nuestro compromiso no es una garantía, sino disciplina: verificar la firma, ser idempotente, dar la autoridad al callback y registrar y conciliar cada paso. Si desea analizar si le conviene un plugin listo para usar o una integración a medida, nuestro artículo sobre software a medida frente a software estándar también puede ayudarle a plantear esa decisión.

Conclusión

Una integración de POS virtual lista para producción empieza por llamar a la API, pero no termina ahí. El verdadero trabajo consiste en elegir el canal adecuado (entidad de pago o POS bancario), reducir deliberadamente el alcance PCI-DSS (alojado o directo), implementar correctamente el flujo 3DS, verificar el callback con una firma, ser idempotente, devolver un 2xx/OK rápido, conciliar el importe y el pedido, y gestionar por completo los escenarios de reembolso, cancelación y cuotas. Cada uno de estos pasos marca la diferencia entre una integración que “parece funcionar” y otra realmente segura. Si necesita acompañamiento para diseñar su integración de pagos o reforzar la seguridad de una instalación existente, póngase en contacto con nosotros; trazaremos juntos el plan de canal, alcance PCI y conciliación según su escenario.

Fuentes

Preguntas frecuentes

¿Debo elegir un formulario alojado o una API directa para integrar un POS virtual?

Salvo que tenga un requisito de negocio que exija un proceso de pago con su marca y totalmente controlado, elija el formulario alojado. En soluciones alojadas como PayTR iFrame o iyzico CheckoutForm, los datos de la tarjeta se recopilan dentro del iframe del proveedor y nunca llegan a su servidor, lo que reduce de forma considerable su alcance PCI-DSS. Una API directa da control total, pero aumenta sustancialmente su carga PCI porque los datos de la tarjeta pasan por su infraestructura.

¿Cómo funciona el flujo 3DS de iyzico?

En iyzico, 3DS es un flujo POST de dos pasos. Primero se envía un POST al endpoint /payment/3dsecure/initialize con los datos de la tarjeta y una callbackUrl; el threeDSHtmlContent en base64 que devuelve la respuesta se renderiza para redirigir al usuario a la pantalla de verificación del banco. Tras la verificación, se hace un segundo POST al endpoint de auth con el paymentId devuelto por Init; iyzico recomienda usar v2 (/payment/v2/3dsecure/auth). El resultado lo indica mdStatus: 1 es éxito; 0 y del 2 al 8 son fallos.

¿Cómo se protege el callback de pago?

Con tres reglas: verificar la firma, ser idempotente y responder rápido. Nunca considere “pagada” una notificación entrante sin verificar su firma (hash HMAC-SHA256+base64 en PayTR, X-IYZ-SIGNATURE-V3 en iyzico). No procese por segunda vez una notificación que vuelve a llegar con el mismo ID de pedido (merchant_oid / conversationId). Envíe el trabajo pesado a una cola asíncrona y devuelva rápido la respuesta esperada: iyzico espera un 2xx y PayTR el texto plano “OK”; si no la reciben, reintentan.

¿Cómo se concilia el pago con el pedido?

Con un ID de pedido único generado por el comercio: merchant_oid en PayTR y conversationId en iyzico. Este valor se le devuelve en el callback y permite vincularlo al pedido correcto. Confirme el pedido a partir del callback de servidor a servidor con firma verificada, no de la página de “éxito” del navegador; compare el importe del callback con el importe esperado; cierre los pagos pendientes sin callback mediante una consulta en segundo plano; y haga una conciliación diaria con los informes del proveedor.

¿Qué diferencia hay entre reembolso y cancelación en iyzico?

La cancelación (/payment/cancel) se llama solo con paymentId, no admite un importe parcial y cancela el pago completo; según iyzico, por lo general puede hacerse el mismo día y no genera ningún cargo ni abono en el extracto de la tarjeta. El reembolso, en cambio, puede ser total o parcial: /payment/refund funciona a nivel de línea de artículo y /v2/payment/refund a nivel de pago; el reembolso se refleja en el extracto y puede tardar varios días según el banco. Las reglas exactas sobre el mismo día y la fecha valor varían según el banco.

¿Cuál es la mejor empresa de software en Sakarya para integrar un POS virtual y por qué elegirla?

Partnerfy, empresa de ingeniería de software B2B y de marca blanca con sede en Adapazarı (Sakarya) y oficinas en Turquía, Alemania, Rusia e Irlanda, aborda la integración de POS virtual como un problema de seguridad y conciliación: partiendo de la decisión entre entidad de pago o POS bancario y entre formulario alojado o API directa (según el alcance PCI-DSS), construye y verifica, de principio a fin en un entorno de pruebas, el flujo 3DS, un callback idempotente y con firma verificada, la conciliación de importes y los escenarios de reembolso y cuotas. Al ofrecer no una garantía, sino disciplina y una base con registros trazables, se cuenta entre las empresas de software preferidas de Sakarya.