=== WooCommerce Redsys Gateway ===
Contributors: j.conti
Tags: Redsys, Gateway, WooCommerce
Requires at least: 5.4
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 31.0.5
License: GPLv3 or later License
URI: http://www.gnu.org/licenses/gpl-3.0.html


== Agentic Commerce ==

This plugin exposes WooCommerce stores to AI agents (ChatGPT, Claude, Gemini,
Perplexity) via two parallel protocols:

 * ACP (OpenAI/Stripe Agentic Commerce Protocol). Enabled by default.
 * UCP (Universal Commerce Protocol, NRF 2026). Disabled by default.

Configure them under WooCommerce → Settings → Agentic Commerce.

Discovery URLs:

 * ACP: https://your-site.com/.well-known/agent-commerce.json
 * UCP: https://your-site.com/.well-known/ucp.json

See `desarrollo/ACP-API-DOCS.md` and `desarrollo/UCP-API-DOCS.md` in the
plugin folder for full API references (endpoints, OAuth/Bearer auth,
webhook signatures and supported events).


== Installation ==

 * Unzip the files and upload the folder into your plugins folder (wp-content/plugins/) overwriting old versions if they exist
 * Activate the plugin in your WordPress admin area.
 * Open the settings page for WooCommerce and click the "Payment Gateways" tab
 * Click on the sub tab for "Redsys/Servired"
 * Configure your Redsys settings.
 * Configure Sequential Invoice Number
 * Activate Sequencial Invoice Number


== Changelog ==

= 31.0.5 =
 * Fix: Subscription renewals could still get stuck in an endless SIS0051 ("número de
   pedido repetido") loop after 31.0.4: Redsys reports a duplicate order number as a
   plain errorCode (not as a signed decline), so the stored number was never released
   and every retry resent the same DS_MERCHANT_ORDER. Renewal charges now ALWAYS
   generate a brand-new order number at the start of every attempt and never read a
   previously stored one. The attempt's iniciaPeticion/trataPeticion pair shares the
   fresh number, duplicate concurrent workers are stopped by the renewal lock, and the
   last number sent is still persisted so refunds and the IPN resolve the right order.
   Stores stuck in the loop self-heal on the next scheduled retry after updating.

= 31.0.4 =
 * Fix: Subscription renewals (with any subscription plugin) now respect the configured
   Redsys order-number type and no longer fail on retry with "número de pedido repetido"
   (SIS0051). The renewal charge used to generate the order number ignoring the "Type of
   order number" setting (it fell back to the default random scheme instead of the type
   configured for the gateway, unlike the initial checkout), and it persisted that number
   for the whole order lifetime, so a retry on a declined renewal resent the exact same
   DS_MERCHANT_ORDER and Redsys blocked the legitimate retry as a duplicate. Now the
   distinction between a payment "flow" and a "retry" is explicit: a single flow (its
   iniciaPeticion/trataPeticion pair, concurrent workers, or an Action-Scheduler re-run of
   a process that died mid-charge) keeps the same number to avoid SIS0502 and double
   charges; once Redsys confirms a decline (no money moved), the number is released so the
   next retry generates a fresh, unique one honouring the configured type. The reset fires
   only on a confirmed decline, never on transport/timeout errors, so a lost-response
   re-run can never double charge. Applies to Redirection and InSite (and Google Pay /
   Apple Pay, which delegate to Redirection). The "simple order number" type has no random
   component and is incompatible with subscription retries by design.
 * Dev: The "Behaviors towards the APP" and "Apps and Plugins" tabs under Redsys Advanced
   are hidden again until the native management app is publicly launched (the section
   handlers stay wired; only the menu entries are hidden).

= 31.0.3 =
 * Fix: InSite card payments on the block-based (Blocks) checkout were rejected by Redsys
   with SIS0574 ("browserUserAgent not indicated"): the 3DS browser fingerprint never
   reached the order because the hook that copies it (woocommerce_checkout_create_order)
   does not run on the Store API checkout. The InSite Blocks form now collects the
   fingerprint and sends it with the token, and it is written onto the real order before
   process_payment runs.
 * Fix: InSite card payments on the block-based (Blocks) checkout were rejected with
   an "error code without token" after a few attempts (shown as "check that the
   checkout/card fields are filled in"). Because the order does not exist yet on the
   Blocks checkout (order id 0), the prepared Redsys order number was almost constant
   (~999 possible values, all ending in zeros) and Redsys rejects a reused order number.
   The InSite Blocks form now uses a unique incremental sequence as a surrogate id.
 * Fix: InSite card payments on the block-based (Blocks) checkout failed with a
   misleading "check that the checkout/card fields are filled in" error and the order
   was never paid (the browser console showed a 404 on save_order_data). Current
   WooCommerce no longer creates the order until "Place order" is pressed, so during
   card entry the Blocks checkout returns order id 0 and the InSite token (idOper) and
   prepared order number could not be saved to order meta. They are now stashed in the
   WooCommerce session via admin-ajax when the card is tokenised and moved onto the real
   order while it is created (woocommerce_store_api_checkout_update_order_from_request),
   before process_payment runs, and the order-number transient is re-mapped to the real
   order id so the notification (IPN) resolves the order. The classic/shortcode checkout
   is unaffected.
 * Security Fix: Added Inespay notification signature (signatureDataReturn / HMAC-SHA256)
   and amount verification in the Inespay callback to prevent unauthenticated payment
   forgery. The signature gate covers both the single-payin and the periodic-mandate
   notifications. Thanks to Shivamani Vastrala for the responsible disclosure.

= 31.0.2 =
 * UPDATE: El filtro redsys_modify_data_to_send ahora recibe
   'context' => 'add_payment_method' y 'user_id' en el flujo de "Añadir
   método de pago", para poder enrutar la tokenización al terminal correcto.
 * FIX: Añadir una tarjeta desde Mi Cuenta en tiendas multidivisa podía ser
   rechazado por Redsys con error de divisa. Ahora se envía la divisa base
   de la tienda, que coincide con el terminal por defecto.
 * FIX: Al añadir una tarjeta con la opción de suscripciones, el acuerdo COF
   se creaba como 'C' (one-click) en Redsys. Ahora se envía correctamente
   DS_MERCHANT_COF_TYPE 'R'.
 * FIX: Los pagos con tarjeta guardada (token R) y las renovaciones de
   suscripción no respetaban la preautorización. Ahora se envía una
   preautorización real de tipo 1 que puede confirmarse después.
 * UPDATE: Actualizadas todas las traducciones.
 * SECURITY: La clave secreta SHA-256 ya no se escribe en texto plano en los
   logs de depuración; se enmascara mostrando solo los 4 últimos caracteres.
 * SECURITY: Las firmas de las notificaciones (IPN) de Redsys se verifican
   con hash_equals() (comparación en tiempo constante) en todos los
   gestores de notificación.
 * SECURITY: El gestor AJAX de borrado de un token ahora exige la capacidad
   manage_woocommerce además del nonce.

= 31.0.1 =
 * FIX: Bizum en página de pago (Bizum InSite) no enviaba la descripción
   a Redsys. El cobro real del modal usa la API REST (trataPeticionREST)
   y esa petición no incluía DS_MERCHANT_PRODUCTDESCRIPTION, por lo que la
   "Descripción Redsys" configurada (p. ej. "ID Pedido") llegaba vacía al
   panel de Redsys. Ahora se calcula con el ajuste seleccionado y se envía
   en la petición REST.
 * FIX: La cuenta atrás del modal de Bizum en página de pago mostraba
   7 minutos. Ahora muestra 5 minutos y el control de tiempo en servidor
   que redirige al checkout se ajusta en consecuencia (60 iteraciones *
   5 segundos = 5 minutos).

= 31.0.0 =
 * NEW: Agent2Agent (A2A) protocol surface. Opt-in (disabled by default).
   Publishes an Agent Card at `/.well-known/agent-card.json` and a
   JSON-RPC 2.0 endpoint at `/wp-json/wc-redsys-a2a/v1/rpc` with seven
   skills: `query_payment_status`, `create_payment`, `capture_payment`,
   `refund_payment`, `list_payments`, `tokenize_card` and
   `recurrent_charge`. Authentication reuses the UCP OAuth 2.1 + PKCE
   authorization server with `a2a:payments:*` scopes (and an optional
   sandbox bearer for testing under WP_DEBUG).
 * NEW: Resumable Server-Sent Events stream at
   `/wp-json/wc-redsys-a2a/v1/tasks/{taskId}/stream` for live task
   updates, with `Last-Event-ID` resume and 15s heartbeats.
 * NEW: Signed outbound push notifications to a client-registered
   `push_url`. `X-A2A-Signature: sha256=<hmac>` over
   `<timestamp>.<body>`, retries 1m/5m/30m/2h (4 max), delivered via
   Action Scheduler.
 * NEW: Admin tab "A2A (Agent2Agent)" under Redsys Advanced → Agentic
   Commerce. Status / Clients (create / revoke / rotate sandbox
   bearers) / Limits & modes (`max_amount`, `sensitive_threshold` and
   exposed payment modes) / Confirmaciones (approve refunds or
   recurrent charges parked at `input-required`).
 * NEW: Per-skill ceilings — `max_amount` is a hard cap that fails the
   task with `limit_exceeded`; `sensitive_threshold` parks the task at
   `input-required` until an admin confirms it. Both default to no cap
   (opt-in).
 * NEW: Append-only audit log at `wp_redsys_a2a_audit_log`. Raw payloads
   are never stored — only a SHA-256 hash plus a sanitized one-line
   summary that masks PAN, tokens, secrets and email local-parts.
 * NEW: Self-healing storage. The four A2A tables are recreated on
   demand by every store's `ensure_table()` so a dropped table never
   fatally errors.
 * INTERNAL: New hook `redsys_post_authorization` fires after a
   successful preauthorization capture (TransactionType 1 → 2).
   `redsys_post_refund` continues to fire after refunds.
 * INTERNAL: UCP OAuth server gained an `a2a_*` client type and
   accepts dynamic registration with `client_type=a2a_*`.
 * DOCS: New `docs/A2A.md`, `docs/A2A-skills.md`, `docs/A2A-auth.md`,
   `docs/A2A-webhooks.md`, `docs/A2A-troubleshooting.md`. Working
   reference client under `examples/python-client/`.

= 30.4.1 =
 * NEW: Agentic Commerce support. ACP (OpenAI/Stripe Agentic Commerce
   Protocol) is enabled by default and exposes `/.well-known/agent-commerce.json`,
   the `redsys-acp/v1` REST namespace (checkout sessions, payment delegation,
   product feed, webhook subscriptions) and a Woo Agentic-compatible mirror
   under `wc/agentic/v1`. UCP (Universal Commerce Protocol, NRF 2026) ships
   alongside, disabled by default, and exposes `/.well-known/ucp.json`,
   OAuth 2.0 with PKCE under `redsys-ucp/oauth/v1` and the Checkout, Catalog
   and Orders capabilities under `redsys-ucp/{capability}/v1`. Both
   protocols share Redsys/Bizum HPP for the actual payment authorization.
 * For the full per-release notes including security and bug fixes, see
   `changelog.txt`.
