<mxfile host="app.diagrams.net" agent="ApiPay Hub docs-portal" type="device" version="24.7.17">
  <diagram id="apipay-flujo-webpay-redirect" name="Flujo Webpay con redireccion">
    <mxGraphModel dx="1800" dy="1600" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="826" math="0" shadow="0">
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />

        <mxCell id="ll-ec" value="Backend del comercio&lt;br&gt;sk_test_" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;collapsible=0;recursiveResize=0;outlineConnect=0;size=50;fillColor=#FFF8E1;strokeColor=#F9A825;fontColor=#1A1A1A;fontSize=11" vertex="1" parent="1">
          <mxGeometry x="40" y="40" width="170" height="1200" as="geometry" />
        </mxCell>
        <mxCell id="ll-br" value="Navegador&lt;br&gt;del comprador" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;collapsible=0;recursiveResize=0;outlineConnect=0;size=50;fillColor=#FFF8E1;strokeColor=#F9A825;fontColor=#1A1A1A;fontSize=11" vertex="1" parent="1">
          <mxGeometry x="240" y="40" width="170" height="1200" as="geometry" />
        </mxCell>
        <mxCell id="ll-wg" value="checkout-widget&lt;br&gt;(iframe)" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;collapsible=0;recursiveResize=0;outlineConnect=0;size=50;fillColor=#E0F7FA;strokeColor=#00838F;fontColor=#1A1A1A;fontSize=11" vertex="1" parent="1">
          <mxGeometry x="440" y="40" width="170" height="1200" as="geometry" />
        </mxCell>
        <mxCell id="ll-pa" value="payment-api" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;collapsible=0;recursiveResize=0;outlineConnect=0;size=50;fillColor=#E3F2FD;strokeColor=#1565C0;fontColor=#1A1A1A;fontSize=11" vertex="1" parent="1">
          <mxGeometry x="640" y="40" width="170" height="1200" as="geometry" />
        </mxCell>
        <mxCell id="ll-db" value="PostgreSQL 17&lt;br&gt;(RLS)" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;collapsible=0;recursiveResize=0;outlineConnect=0;size=50;fillColor=#E8F5E9;strokeColor=#2E7D32;fontColor=#1A1A1A;fontSize=11" vertex="1" parent="1">
          <mxGeometry x="840" y="40" width="170" height="1200" as="geometry" />
        </mxCell>
        <mxCell id="ll-wp" value="Webpay Plus&lt;br&gt;(Servipag)" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;collapsible=0;recursiveResize=0;outlineConnect=0;size=50;fillColor=#F3E5F5;strokeColor=#6A1B9A;fontColor=#1A1A1A;fontSize=11" vertex="1" parent="1">
          <mxGeometry x="1040" y="40" width="170" height="1200" as="geometry" />
        </mxCell>
        <mxCell id="ll-ob" value="Outbox, Kafka y&lt;br&gt;webhook-dispatcher" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=0;collapsible=0;recursiveResize=0;outlineConnect=0;size=50;fillColor=#E8F5E9;strokeColor=#2E7D32;fontColor=#1A1A1A;fontSize=11" vertex="1" parent="1">
          <mxGeometry x="1240" y="40" width="170" height="1200" as="geometry" />
        </mxCell>

        <mxCell id="m01" value="1. POST /v1/payment-intents - sk_test_, Idempotency-Key, return_url" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.067;exitDx=0;exitDy=0;entryX=0;entryY=0.067;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-ec" target="ll-pa">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m02" value="2. SET LOCAL app.tenant_id; INSERT intent CREATED + outbox_events en UNA transaccion" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.104;exitDx=0;exitDy=0;entryX=0;entryY=0.104;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-db">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m03" value="3. 201 con pi_... y client_secret" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;dashed=1;endArrow=open;endFill=0;endSize=10;exitX=0;exitY=0.142;exitDx=0;exitDy=0;entryX=1;entryY=0.142;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-ec">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m04" value="4. entrega el client_secret a su pagina de checkout" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;dashed=1;endArrow=open;endFill=0;endSize=10;exitX=1;exitY=0.179;exitDx=0;exitDy=0;entryX=0;entryY=0.179;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-ec" target="ll-br">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m05" value="5. monta el iframe de checkout.apipay.io (postMessage con origin estricto y nonce)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.217;exitDx=0;exitDy=0;entryX=0;entryY=0.217;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-br" target="ll-wg">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m06" value="6. GET /v1/payment-intents/pi_... con pk_test_ y header X-Client-Secret" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.254;exitDx=0;exitDy=0;entryX=0;entryY=0.254;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-wg" target="ll-pa">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m07" value="7. 200 intent CREATED con las pasarelas habilitadas del tenant" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;dashed=1;endArrow=open;endFill=0;endSize=10;exitX=0;exitY=0.292;exitDx=0;exitDy=0;entryX=1;entryY=0.292;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-wg">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m08" value="8. POST /v1/payment-intents/pi_.../confirm - PRIMER confirm, sin token" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#1565C0;fontColor=#0D47A1;fontSize=10;fontStyle=1;endArrow=block;endFill=1;exitX=1;exitY=0.329;exitDx=0;exitDy=0;entryX=0;entryY=0.329;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-wg" target="ll-pa">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m09" value="9. createPayment via gateway-webpay con la return_url persistida" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.367;exitDx=0;exitDy=0;entryX=0;entryY=0.367;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-wp">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m10" value="10. token_ws y URL del formulario de pago" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;dashed=1;endArrow=open;endFill=0;endSize=10;exitX=0;exitY=0.404;exitDx=0;exitDy=0;entryX=1;entryY=0.404;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-wp" target="ll-pa">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m11" value="11. CREATED -&amp;gt; REQUIRES_ACTION, publica apipay_redirect_url + outbox_events" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.442;exitDx=0;exitDy=0;entryX=0;entryY=0.442;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-db">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m12" value="12. 200 con status REQUIRES_ACTION y redirect_url" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;dashed=1;endArrow=open;endFill=0;endSize=10;exitX=0;exitY=0.479;exitDx=0;exitDy=0;entryX=1;entryY=0.479;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-wg">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m13" value="13. redireccion top-level al formulario de Webpay, fuera del iframe" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=0;exitY=0.517;exitDx=0;exitDy=0;entryX=1;entryY=0.517;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-wg" target="ll-br">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m14" value="14. el comprador paga con su tarjeta en el sitio de Webpay" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#C62828;fontColor=#B71C1C;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.554;exitDx=0;exitDy=0;entryX=0;entryY=0.554;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-br" target="ll-wp">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m15" value="15. 302 hacia la return_url del comercio con token_ws en la query" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;dashed=1;endArrow=open;endFill=0;endSize=10;exitX=0;exitY=0.592;exitDx=0;exitDy=0;entryX=1;entryY=0.592;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-wp" target="ll-br">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m16" value="16. vuelve a la pagina de checkout con el token_ws" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.629;exitDx=0;exitDy=0;entryX=0;entryY=0.629;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-br" target="ll-wg">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m17" value="17. POST /v1/payment-intents/pi_.../confirm con token_ws - SEGUNDO confirm" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#1565C0;fontColor=#0D47A1;fontSize=10;fontStyle=1;endArrow=block;endFill=1;exitX=1;exitY=0.667;exitDx=0;exitDy=0;entryX=0;entryY=0.667;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-wg" target="ll-pa">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m18" value="18. REQUIRES_ACTION -&amp;gt; PROCESSING + outbox_events" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.704;exitDx=0;exitDy=0;entryX=0;entryY=0.704;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-db">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m19" value="19. commit del token_ws via gateway-webpay" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.742;exitDx=0;exitDy=0;entryX=0;entryY=0.742;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-wp">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m20" value="20. veredicto de la autorizacion: aprobada o rechazada" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;dashed=1;endArrow=open;endFill=0;endSize=10;exitX=0;exitY=0.779;exitDx=0;exitDy=0;entryX=1;entryY=0.779;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-wp" target="ll-pa">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m21" value="21. PROCESSING -&amp;gt; SUCCEEDED (transaction APPROVED) o FAILED (REJECTED) + outbox_events" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.817;exitDx=0;exitDy=0;entryX=0;entryY=0.817;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-db">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m22" value="22. 200 con el estado final del intent" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#455A64;fontColor=#1A1A1A;fontSize=10;dashed=1;endArrow=open;endFill=0;endSize=10;exitX=0;exitY=0.854;exitDx=0;exitDy=0;entryX=1;entryY=0.854;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-wg">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m23" value="23. el relay drena outbox_events y publica payment_intent.succeeded o .failed" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#2E7D32;fontColor=#1B5E20;fontSize=10;endArrow=block;endFill=1;exitX=1;exitY=0.892;exitDx=0;exitDy=0;entryX=0;entryY=0.892;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-pa" target="ll-ob">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="m24" value="24. POST firmado con ApiPay-Signature al webhook_endpoint del comercio" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;verticalAlign=bottom;labelBackgroundColor=#FFFFFF;strokeColor=#2E7D32;fontColor=#1B5E20;fontSize=10;endArrow=block;endFill=1;exitX=0;exitY=0.929;exitDx=0;exitDy=0;entryX=1;entryY=0.929;entryDx=0;entryDy=0" edge="1" parent="1" source="ll-ob" target="ll-ec">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

        <mxCell id="nota-returnurl" value="Paso 1 y 9 - la return_url. Se persiste al crear el intent en la clave reservada apipay_return_url y el confirm la reutiliza si el cuerpo no trae otra. En el contrato OpenAPI la return_url es solo de entrada: no aparece en ningun esquema de respuesta, y el prefijo apipay_ garantiza que no se serialice en el objeto metadata que ve el integrador." style="shape=note;size=16;whiteSpace=wrap;html=1;fillColor=#FFFDE7;strokeColor=#F9A825;fontColor=#1A1A1A;fontSize=10;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4" vertex="1" parent="1">
          <mxGeometry x="40" y="1290" width="670" height="120" as="geometry" />
        </mxCell>
        <mxCell id="nota-segundoconfirm" value="Paso 17 - el segundo confirm es lo mas sutil del producto. El mismo endpoint sirve dos fases: sin token hace el init, con token_ws hace el commit. El token puede llegar en gateway_params del cuerpo o en la query string, y el controlador fusiona ambos dando prioridad a la query, que es como vuelve el navegador. El token_ws es de un solo uso: por eso ningun SDK reintenta este POST automaticamente." style="shape=note;size=16;whiteSpace=wrap;html=1;fillColor=#FFFDE7;strokeColor=#F9A825;fontColor=#1A1A1A;fontSize=10;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4" vertex="1" parent="1">
          <mxGeometry x="740" y="1290" width="670" height="120" as="geometry" />
        </mxCell>
        <mxCell id="nota-cierre" value="Tres cosas que este flujo fija. (1) REQUIRES_ACTION modela exactamente el parentesis de la redireccion: es el unico estado desde el que se puede volver a PROCESSING. (2) Webpay entrega el veredicto en el commit, de forma sincrona, no por webhook: el paso 21 resuelve el pago dentro de la misma peticion. (3) Si el comprador nunca vuelve, el guard de conciliacion consulta fetchStatus y lleva el intent a EXPIRED al vencer su TTL, para que ninguno quede huerfano. El PAN y el CVV solo existen en el paso 14, dentro del sitio de Webpay (PCI DSS SAQ A)." style="shape=note;size=16;whiteSpace=wrap;html=1;fillColor=#E8EAF6;strokeColor=#283593;fontColor=#1A1A1A;fontSize=11;align=left;verticalAlign=top;spacingLeft=8;spacingTop=4" vertex="1" parent="1">
          <mxGeometry x="40" y="1430" width="1370" height="110" as="geometry" />
        </mxCell>
      </root>
    </mxGraphModel>
  </diagram>
</mxfile>
