Saltar al contenido
ApiPay Hub · Docs

Changelog y deprecación#

Esta página cubre tres cosas distintas que conviene no mezclar: qué cambió, cómo se avisa de lo que va a desaparecer y cuánto tiempo se mantiene cada línea.

Contrato /v1#

La fuente de verdad es contracts/openapi/payment-api.v1.yaml. La referencia de la API se genera de ahí: corregir la referencia es corregir el YAML, nunca la página.

1.0.0#

Primera versión del contrato público. Superficie completa:

POST   /v1/payment-intents                 sk_        Idempotency-Key obligatoria
GET    /v1/payment-intents/{id}            sk_ | pk_ + X-Client-Secret
POST   /v1/payment-intents/{id}/confirm    sk_ | pk_ + X-Client-Secret
POST   /v1/payment-intents/{id}/cancel     sk_
POST   /v1/refunds                         sk_        Idempotency-Key obligatoria
GET    /v1/refunds/{id}                    sk_
GET    /v1/transactions                    sk_        cursor + limit
POST   /v1/gateways/{gatewayId}/webhooks   sin auth   firma por pasarela

Decisiones que quedaron fijadas con esta versión y que no pueden cambiar dentro de /v1:

  • Autenticación por header X-API-Key; el client_secret siempre en X-Client-Secret, nunca como query param.
  • Dinero: amount_minor entero en unidades menores + currency ISO 4217. CLP tiene exponente 0.
  • Identificadores públicos opacos con prefijo: pi_, re_, txn_, evt_.
  • Errores RFC 9457 application/problem+json con el miembro de extensión code estable.
  • Paginación sólo por cursor, nunca por offset.
  • Siete estados canónicos del intent y su matriz de transiciones.
  • Diecisiete códigos de error. Ver Errores.
  • Envelope de webhooks salientes { id, type, created, livemode, data: { object } } y firma ApiPay-Signature: t=…,v1=… sobre "{t}.{cuerpo_crudo}".
  • Siete tipos de evento: payment_intent.succeeded|failed|canceled|requires_action|expired, refund.succeeded|failed.

Pasarelas disponibles#

gateway_idEstadoNotas
sandboxDisponible, sólo livemode=falseResultados deterministas por los dos últimos dígitos del monto
webpayDisponibleRedirección top-level y segundo confirm con token_ws
mercadopagoParcialCreación y preferencia con back_urls cableadas; la confirmación con el token del Brick necesita que el confirm transporte importe y divisa

sandbox se niega a resolver con una clave live y responde 400 validation_error; el backoffice no lo ofrece en la configuración live de un tenant.

Este catálogo no incluye Stripe. Hubo un adaptador de Stripe, escrito como ejemplo del SPI PaymentGateway, y se retiró antes de publicar 1.0.0 porque no es un medio de pago que la plataforma quiera ofrecer. Se anota aquí porque el catálogo de pasarelas es parte de lo que ves, no por versionado: como 1.0.0 nunca llegó a publicarse, retirarlo no es un cambio incompatible ni arrastra un /v2: no existió integración que pudiera depender de gateway_id: "stripe".

Deudas conocidas del contrato#

Diferencias reales entre el YAML y la implementación, listadas aquí porque afectan a lo que ves:

PuntoSituación
payment_methodEl contrato lo declara opcional y un ejemplo del confirm lo muestra poblado con marca y last4. En la práctica es siempre null: ningún adaptador captura metadatos de tarjeta por alcance PCI DSS SAQ A. No construyas UI sobre ese campo
gateway_params en el confirmEl borde REST lo acepta (es el canal del token_ws de Webpay) y fusiona además los parámetros de query, pero el schema del contrato no lo declara. Los cuatro SDKs modelan sólo payment_method_token y return_url
title de los 422El contrato dice Unprocessable Entity; el servicio emite Unprocessable Content. Es inocuo —title es texto para humanos y sólo se usa como relleno— pero el desactualizado es el YAML
Idempotency-ReplayedEl contrato define el header, y ningún SDK lo expone. Hoy no puedes distinguir una creación nueva de la respuesta replicada tras reintentar con la misma clave
POST /v1/…/captureNo existe. SEPARATE_CAPTURE es una capacidad del adaptador de MercadoPago sin superficie pública: no uses capture_method: manual

SDKs#

SemVer independiente por SDK: un fix del cliente PHP no obliga a publicar nada en Python. La única regla estructural es que el major del contrato arrastra el major de los cuatro a la vez.

Versión del contrato@apipay/nodeapipay/apipay-phpapipaycom.apipay:apipay-java
/v11.x1.x1.x1.x
/v2 (futuro)2.x2.x2.x2.x

1.0.0 — los cuatro server-side#

Primera versión. Superficie idéntica en los cuatro, y la capa artesanal completa:

  • Transporte propio sin SDK de terceros: fetch nativo en Node 22, PSR-18 descubierto en PHP, httpx en Python (síncrono y asíncrono) y java.net.http en Java.
  • Asimetría de reintentos: GET sí, POST del que se recibió respuesta jamás. Backoff exponencial con jitter completo (base 500 ms, factor 2, tope 8 s, maxRetries 2), Retry-After honrado sólo en 429 y 503 y con tope de 60 s.
  • Idempotency-Key UUID v4 generada con el CSPRNG del lenguaje y expuesta al llamador.
  • Auto-paginación por cursor; cursor y next_cursor nunca visibles.
  • Los diecisiete códigos mapeados a excepciones tipadas, con code, status, problem y requestId.
  • Verificación de webhooks con HMAC en tiempo constante, tolerancia 300 s y varias firmas v1.
  • Una pk_ se rechaza localmente al construir el cliente, en los cuatro.

1.0.0@apipay/checkout-js#

  • Monta el checkout alojado en un iframe de checkout.apipay.io; cero dependencias de runtime y menos de 15 kB gzip, verificado en CI.
  • Cuatro validaciones en cascada sobre cada postMessage: origen exacto, event.source idéntico al iframe montado, forma tipada y nonce por sesión de 16 bytes.
  • client_secret y nonce viajan en el fragment del src, no en el querystring: no llegan a logs de servidor ni a la cabecera Referer.
  • Reenvía token_ws y TBK_TOKEN de la URL del comercio al iframe, que es lo que permite el segundo confirm de Webpay desde un iframe de otro origen.
  • Distribución dual: npm y script tag en rutas versionadas inmutables con SRI.

Estado de verificación#

SDKQué se ejecutóResultado
@apipay/nodetypecheck, lint, test, build92 tests en verde
apipay (Python)pytest, mypy --strict, ruff165 tests en verde
com.apipay:apipay-javabuild (incluye compilar los ejemplos)28 tests en verde
apipay/apipay-phpSin ejecutar: hace falta una máquina con PHP 8.3 y Composer

285 tests en verde en los tres lenguajes verificables. El de PHP se revisó por lectura exhaustiva y el veredicto fue que pasaría, pero eso no sustituye a ejecutarlo: es requisito antes de publicar apipay/apipay-php.

Divergencias conocidas entre los cuatro#

PuntoSituación
Verificación de webhooks en PHPEstática (ApiPay\Webhooks::constructEvent) en lugar de colgar del cliente. Deliberada: el handler no debe construir un cliente con la sk_ para comprobar un HMAC
Jerarquía de errores de PHPMás gruesa: 16 clases frente a 22 en Python y 23 en Java. card_declined y gateway_rejected comparten GatewayDeclinedException y toda la familia 422 cae en ValidationException. Un comercio en PHP no puede escribir catch (CardDeclinedException)
Reintento de errores de red pre-envíoPython y Java lo distinguen; TypeScript y PHP no pueden (fetch y NetworkExceptionInterface no separan pre de post envío) y por eso no reintentan. Es la mitad más conservadora, nunca la menos segura
Baseline de Java17, no el 25 del backend: el artefacto lo consumen terceros
Modelos "generados"Los cuatro los tienen escritos a mano contra el contrato. El generador existe y funciona, pero escribe en sdks/generator/out/ como material de comparación: no sobreescribe código artesanal. Un cambio del contrato hay que reflejarlo a mano en los cuatro SDKs, en el mismo PR

Política de deprecación#

Cambios aditivos: no rompen versión#

Estos cambios pueden llegar en cualquier momento dentro de /v1 y tu integración tiene que tolerarlos:

  • Campos opcionales nuevos en las respuestas.
  • Valores nuevos y documentados en un enum.
  • Tipos de evento nuevos en el catálogo de webhooks.
  • Miembros de extensión nuevos en un documento problem+json.

Cambios incompatibles: /v2#

Un cambio incompatible exige payment-api.v2.yaml con path /v2 y el major sincronizado de los cuatro SDKs, con un periodo de convivencia /v1/v2. Mientras exista sólo /v1 hay una única versión del portal; cuando nazca /v2, el contenido se publica en paralelo con selector de versión y cada versión referencia el YAML de su major.

Son cambios incompatibles, entre otros: eliminar o renombrar un campo, hacer obligatorio uno opcional, cambiar el tipo de un campo, retirar un valor de enum, cambiar el significado de un code y cambiar el formato de la firma de webhooks.

Aviso: Deprecation y Sunset#

Todo endpoint o versión en retirada anuncia su estado en las propias respuestas:

CabeceraRFCQué dice
DeprecationRFC 9745Que el recurso está deprecado, y desde cuándo
SunsetRFC 8594La fecha a partir de la cual dejará de responder

Entre el anuncio y el corte hay un mínimo de seis meses. Los avisos se publican además en el portal (banner y esta página) y por email a los tenants afectados, identificados por telemetría de uso real: el User-Agent de los SDKs y las métricas por endpoint de OpenTelemetry. No por listas manuales, que siempre están desactualizadas y siempre avisan a quien no toca.

Las cabeceras Deprecation y Sunset todavía no aparecen en el contrato: nada está deprecado en 1.0.0, así que no hay nada que anunciar. Se añadirán al YAML con la primera deprecación real.

Política de soporte de SDKs#

LíneaEstadoRecibeDuración
Major actual (N)ActivaFuncionalidad, fixes y seguridadMientras sea la vigente
Major anterior (N−1)MantenimientoSólo parches de seguridad12 meses desde la publicación de N
N−2 y anterioresFin de vidaNada; el portal y el README lo marcan EOL

Hoy sólo existe la línea 1.x, así que no hay nada en mantenimiento ni en EOL. Los runtimes mínimos son parte del contrato de cada major y sólo pueden subir en un major:

SDKRuntime mínimo de 1.x
@apipay/nodeNode.js 22 LTS
apipay/apipay-phpPHP 8.3
apipayPython 3.12
com.apipay:apipay-javaJava 17
@apipay/checkout-jsNavegadores evergreen y Safari 16.4+

Cómo se genera este changelog#

No se redacta: se deriva. Los conventional commits de las áreas sdks/ y contracts/ alimentan la decisión de bump SemVer y el texto de cada entrada. release-sdks.yml lo compila al publicar cada tag sdk-v<X.Y.Z>-<lenguaje> (o -all para los majors sincronizados), publica el GitHub Release y el portal se reconstruye en el siguiente push a main.

Suscribirse a los cambios#

Mientras no exista el feed automático, lo fiable es:

  1. Vigilar las cabeceras Deprecation y Sunset en tus respuestas, y loguearlas. Es el aviso que llega a tu código sin que nadie tenga que leer un correo.
  2. Ramificar siempre por code, no por detail, y tener el default: ignorar en su sitio.
  3. Fijar la versión mayor del SDK (^1.0.0, 1.x) y no la exacta: los patches de seguridad de la línea activa llegan solos.
  4. Mantener el User-Agent con telemetría activada para que el aviso por email te alcance.