Developer notes
Technical reference for developers who customize Redsys for CS-Cart and Multi-Vendor.
Add-on identity
Section titled “Add-on identity”| Item | Value |
|---|---|
| Add-on id | kv_redsys |
| Function prefix | fn_kv_redsys_ |
| Language variables | kv_redsys.* |
| CSS scope | .kv-redsys |
| PHP namespace | Tygh\Addons\KvRedsys |
| Preset | payment (generated by kv new --kind integration) |
Structure
Section titled “Structure”| Class | Role |
|---|---|
Signature |
Message signing, pure functions: HMAC_SHA512_V2 and HMAC_SHA256_V1, per-order key, Base64 / Base64URL, verification of notifications and REST answers |
Codes |
Ds_Response and SIS error texts, ISO 4217 numeric currency table, Ds_Merchant_ConsumerLanguage values |
Client\BaseClient |
HTTP (cURL, TLS verification, timeouts), JSON, error mapping, masked logging (no OAuth for Redsys: $oauth = false) |
Client\Api |
The Redsys calls: signed redirection fields, notification verification, REST refund (type 3), connection probe |
PaymentService |
Order number, redirect parameters, notification handling (applyNotification), order status, refunds (refund, resolveRefund) |
Client |
Client per company, the connection test |
Accounts, Secret |
Merchant code, terminal numbers and signature version per account; terminal keys sealed with AES-256-GCM; one account per store owner or per vendor |
Config, Access |
Settings, owner mode, who may see and manage what |
Queue, Jobs, JobException |
Job queue: back-off 5 min / 15 min / 1 h / 3 h / 12 h, per-job isolation, overlap lock; handlers in Jobs::handle() |
Log |
Event log with masking, filters, CSV, retention |
Webhook |
Reads the notification form fields, idempotency id, the fields that are kept (no card data) |
Payment flow
Section titled “Payment flow”payments/kv_redsys.php(start):PaymentService::initialize()creates a unique 12-character Redsys order number, stores the transaction row (statuspending) and only then prints a self-submitting POST form (fn_create_payment_form) to.../sis/realizarPagowithDs_SignatureVersion,Ds_MerchantParameters,Ds_Signature.- Redsys calls the notification URL (
kv_redsys.webhook, HTTP POST, form encoded). The controller finds the transaction byDs_Order, takes the terminal key of the account that owns it, verifies the signature and callsPaymentService::applyNotification()(per-transaction lock, never downgrades a paid payment, amount and currency of an authorization must match). If processing fails the verified data is queued as awebhookjob. - The customer returns on
Ds_Merchant_URLOKorDs_Merchant_URLKO(payment_notification.return|cancel). These carry nothing that is trusted (the guide says never to validate an order from them). The handler waits up to 5 s for the notification; when the payment is still pending it empties the cart and the saved cart rows (?:user_session_products) and shows the order, so a signed-in customer cannot pay the same cart twice. - Refunds:
PaymentService::refund()locks per payment, stores a refund row (creating) before the REST call, and sends operation type 3 with the order number of the payment. A clear Redsys refusal (errorCode, orDs_Responseother than 900) is recorded asfailed; a timeout, an unreadable answer orDs_Response912 is recorded asunconfirmed: the amount stays reserved, nothing repeats it, and an administrator confirms in the order tab whether it was executed (after checking the Redsys portal).
Official sources and every field
Section titled “Official sources and every field”All documents were downloaded from the Redsys developer area (pagosonline.redsys.es/desarrolladores-inicio/integrate-con-nosotros/area-de-descargas-y-documentacion/) on 2026-11-07:
| Short name | Document |
|---|---|
| REDIR | Guia de Integracion por Redireccion, ref. RS.TE.CEL.MAN.0039, v4.2 (04/02/2026) |
| REST | Guia de integracion por REST, v4.0.1 (Annex 2 = HMAC_SHA256_V1) |
| BIZUM | Guia de Integracion BIZUM comercios SIS, ref. RS.TE.CEL.MAN.0033, v2.6 |
| IO | “Parametros de entrada y salida” page of the developer area (the spreadsheet itself is not public) |
| TEST | “Tarjetas y entornos de prueba” page of the developer area |
| REFUND | “Devolver o anular un pago” page of the developer area |
| PHPLIB | Redsys PHP integration library 2.0.1: read only to confirm algorithm details and the REST request shape. Its licence forbids redistribution, so none of its code is shipped; Signature is written from the guides. |
Endpoints
Section titled “Endpoints”| Use | URL | Source |
|---|---|---|
| Redirection, test | https://sis-t.redsys.es:25443/sis/realizarPago |
REDIR section “Entorno de pruebas”, TEST |
| Redirection, live | https://sis.redsys.es/sis/realizarPago |
PHPLIB Endpoints, “Autorizacion” page of the developer area |
| REST refund, test | https://sis-t.redsys.es:25443/sis/rest/trataPeticionREST |
REST “Transacciones directas”, REFUND |
| REST refund, live | https://sis.redsys.es/sis/rest/trataPeticionREST |
REST, REFUND |
Redirection form (HTTP POST from the customer’s browser)
Section titled “Redirection form (HTTP POST from the customer’s browser)”| Field | Value | Source |
|---|---|---|
Ds_SignatureVersion |
HMAC_SHA512_V2 (default) or HMAC_SHA256_V1 |
REDIR section 5 (V2 is “el valor actual”); REST Annex 2 (V1) |
Ds_MerchantParameters |
JSON of the DS_MERCHANT_* parameters, UTF-8, Base64URL without padding (V2) or standard Base64 (V1) |
REDIR section 6.1.4 and “Error SIS0430”; REST Annex 2 step 2 |
Ds_Signature |
see “Signature” | REDIR 6.1.3; REST Annex 2 |
Parameters sent (Ds_MerchantParameters)
Section titled “Parameters sent (Ds_MerchantParameters)”| Parameter | What the add-on sends | Source |
|---|---|---|
DS_MERCHANT_AMOUNT |
Minor units as an integer string (43.45 EUR = 4345); decimals per currency (0 for JPY, 3 for KWD …) via Money |
IO |
DS_MERCHANT_ORDER |
12 characters: 4 digits (order id modulo 10000) + 8 random base-36 characters, new for every attempt | IO (“first 4 characters numeric, the rest ASCII letters or digits, max 12”); BIZUM ^[a-zA-Z0-9]{4,12}$; REDIR “SIS0051” (never reuse a number, also not for unfinished attempts) |
DS_MERCHANT_MERCHANTCODE |
Merchant code (FUC), up to 9 digits | IO, TEST |
DS_MERCHANT_TERMINAL |
Terminal number as configured (default 1) |
IO, TEST (“001” and “1” both appear in the guides) |
DS_MERCHANT_CURRENCY |
ISO 4217 numeric code (978 EUR, 840 USD …) from the table in Codes (140 ISO 4217 currency codes taken from List One, published 2026-09-17, not every code of the list; a currency outside the table is refused; the amount uses the ISO exponent from Money: 0 for XOF, XAF, XPF, PYG, JPY, KRW …, 3 for BHD, IQD, JOD, KWD, LYD, OMR, TND) |
IO |
DS_MERCHANT_TRANSACTIONTYPE |
0 (authorization) |
IO, BIZUM 2.2.1 |
DS_MERCHANT_MERCHANTURL |
Notification URL, https, max 250 characters | IO, REDIR section 7 |
DS_MERCHANT_URLOK / DS_MERCHANT_URLKO |
Return URLs, max 250 characters | IO, REDIR “Mostrar Recibo” |
DS_MERCHANT_PAYMETHODS |
z (lower case) only for the Bizum payment method; omitted otherwise |
BIZUM 2.2.1; IO |
DS_MERCHANT_PRODUCTDESCRIPTION |
Payment description + #order id, cut to 125 characters |
name: PHPLIB Parameters; length 125 not verified in a public document |
DS_MERCHANT_CONSUMERLANGUAGE |
001 es, 002 en, 003 ca, 010 va, 012 gl, 013 eu; other languages omit it (Redsys detects the browser language) |
name: REDIR page; values: PHPLIB Utils; the full table is in the non-public spreadsheet |
DS_MERCHANT_EMV3DS |
{"email": ..., "cardholderName": ...} when the setting is on |
REDIR 6.1.4 example (keys email, cardholderName); field lengths not verified (name cut at 45, e-mail at 254 as in EMV 3DS) |
Signature
Section titled “Signature”| Step | HMAC_SHA512_V2 (REDIR 6.1.2, 6.1.3) | HMAC_SHA256_V1 (REST Annex 2) |
|---|---|---|
| Terminal key | first 16 characters of the key text, right-padded with 0 |
Base64-decode the key (24 bytes) |
| Per-order key | AES-128-CBC, zero IV, encrypt the order number with that key; the Base64 string of the result is the HMAC key | 3DES-CBC, zero IV, encrypt the order number zero-padded to a multiple of 8 bytes |
| Signature | HMAC-SHA512 over the Ds_MerchantParameters string, Base64URL without padding |
HMAC-SHA256 over the Ds_MerchantParameters string, Base64 |
| Verification of a notification / answer | Decode Ds_MerchantParameters only to read Ds_Order; sign the string exactly as received; compare (REDIR 6.1.3 “Respuesta”; REST “Estructura de respuesta”) |
same |
The comparison ignores the Base64 / Base64URL spelling and padding (Signature::same) and uses hash_equals.
Test vectors (unit tests)
Section titled “Test vectors (unit tests)”| Vector | Value | Source |
|---|---|---|
| Test terminal key | sq7HjrUOBfKmC576ILgskD5srU870gJ7 (merchant 999008881, terminal 001) |
TEST, REDIR |
V2 derived key, order 1234567890 |
RWt3/IPTzYRMXsQtkiGRKg== |
REDIR 6.1.2 |
V2 signature of the guide’s Ds_MerchantParameters |
Vjo02eSWq249IeZZp3R-ArFnGLhKY0OuzDDlx1BuVtZDC2yhczA7_11uZhsYzLZBCMFAz8u8uzGDX3AErHKmmw |
REDIR 6.1.3 |
V1 derived key (hex), order 06080232580 |
a5334014a4f010c8779cef789886c123 |
REST Annex 2 |
| V1 signature of the guide’s refund request | GmHwTovthyrztLs7D77GflclBzsANderHe3zFF6JiZQ= |
REST Annex 2 |
All four are asserted in tests/unit.php; the mock server (an independent JavaScript implementation) verifies the PHP signatures of both versions.
Notification (Redsys -> Ds_Merchant_MerchantURL, HTTP POST)
Section titled “Notification (Redsys -> Ds_Merchant_MerchantURL, HTTP POST)”| Field | Use | Source |
|---|---|---|
Ds_SignatureVersion, Ds_MerchantParameters, Ds_Signature |
verified as above | REDIR section 7 |
Ds_Order |
finds the transaction (the number we generated) | IO |
Ds_Response |
0000 to 0099 = authorized payment; 9915 = cancelled by the customer (Codes, PHPLIB response table); 8210, 8220, 9998, 9999 = operation still under way (not a failure); anything else = denied |
REDIR “Resultado de la Operacion”; PHPLIB response table |
Ds_Amount, Ds_Currency |
must equal the amount and numeric currency sent, otherwise the authorization is not accepted | IO |
Ds_TransactionType |
only type 0 changes the payment; others are logged and ignored |
IO |
Ds_AuthorisationCode, Ds_SecurePayment, Ds_ProcessedPayMethod |
stored / logged; no card data is kept | IO |
The guide states that the result must be taken only from this POST and never from the data of the HTTP GET on the OK/KO URLs.
REST refund (operation type 3)
Section titled “REST refund (operation type 3)”Request Content-Type: application/json, body {"Ds_SignatureVersion", "Ds_MerchantParameters", "Ds_Signature"} (REST “Estructura de una peticion REST”; the key names are the same in the PHPLIB RequestData). Parameters: DS_MERCHANT_ORDER (the order number of the payment), DS_MERCHANT_MERCHANTCODE, DS_MERCHANT_TERMINAL, DS_MERCHANT_TRANSACTIONTYPE=3, DS_MERCHANT_CURRENCY, DS_MERCHANT_AMOUNT (REFUND, REST “Peticion de Confirmacion/Devolucion/Anulacion”). Total or partial; the sum of refunds never exceeds the authorized amount (REFUND). Ds_Response 0900 = refunded (REFUND, REDIR). Bizum payments are refunded the same way (BIZUM 2.3). A failure is {"errorCode":"SIS...."} (REST). Timeout: Redsys answers within 30 s (code 912 / 9912 “issuer unavailable”); the client timeout is 50 s; “for refunds the request may be repeated” (REST “Timeout”), which is why the add-on never repeats it by itself but asks the administrator to check the portal.
SIS errors the add-on reads
Section titled “SIS errors the add-on reads”SIS0042 signature, SIS0054 no operation exists for the refund, SIS0057 refund above the limit, SIS0051 repeated order number, SIS0026 merchant / terminal unknown, SIS0027 currency, SIS0430 parameters not decodable (texts from the PHPLIB resource file, rewritten in Codes::sisText).
Connection probe (“Test connection”)
Section titled “Connection probe (“Test connection”)”Redsys has no read-only credentials call. The probe sends a type-3 refund of the non-existing order 0000KVPROBE0 for 0.01: Redsys answers SIS0054 when merchant code, terminal and signature are accepted, SIS0042 / SIS0026 / SIS0027 otherwise. The meaning of SIS0054 for this probe is inferred from the error table, not documented as a connection test. The daily automatic probe runs only in test mode.
Flagged: not verifiable from public documents
Section titled “Flagged: not verifiable from public documents”- The meaning of
Ds_Merchant_PayMethodsvalues other thanz(the table is not public); the “card only” choice is therefore not offered, “all methods enabled on the terminal” omits the parameter. - Field lengths of
DS_MERCHANT_PRODUCTDESCRIPTIONandDS_MERCHANT_EMV3DS; language codes other than the six listed. - Whether the live
sis.redsys.esaccepts both signature versions for a terminal: it depends on the terminal configuration at the bank; the guides call V2 current. Use V1 only for older terminals. - Bizum test data: BIZUM and TEST give user
700000000, e-mail[email protected], PIN1234, SMS code123456, amounts up to 10 EUR; not exercised here because the sandbox is not reachable from CI. - REST refunds are signed with the account’s signature version. The REST guide 4.0.1 documents HMAC_SHA256_V1 (Annex 2); if a terminal’s REST interface refuses V2 (Test connection answers SIS0042 with V2), switch the account to HMAC_SHA256_V1.
- Which IP addresses Redsys notifies from (not published): do not IP-filter the notification URL; the signature is the protection. The URL must use the standard HTTPS port.
Database tables
Section titled “Database tables”| Table | Purpose |
|---|---|
?:kv_redsys_accounts |
Merchant code, sealed terminal keys, terminals and signature version (in extra), last connection check |
?:kv_redsys_log |
Log |
?:kv_redsys_jobs |
Queue |
?:kv_redsys_transactions |
One row per payment attempt: Redsys order number (provider_ref, unique), status, answer code, authorization code, method (card / bizum), the order total at the start (order_total, compared when the money arrives). Multi-Vendor owner mode: a cart with several vendors is one row on the parent order |
?:kv_redsys_refunds |
Refund rows: creating, done, failed, unconfirmed |
Hooks used
Section titled “Hooks used”| Hook | Why |
|---|---|
prepare_checkout_payment_methods |
hides the method while the add-on is disabled, unlicensed or has no credentials |
change_order_status_post |
optional full refund when an order reaches the configured status (never from a vendor in owner mode) |
init_templater_post |
upgrade synchronizer |
Template hooks orders:tabs_extra (admin), addons:manage, addons:tabs_content, index:styles |
order tab, add-on badge and styles |
Test the HTTP layer without Redsys
Section titled “Test the HTTP layer without Redsys”The base URL can be overridden for tests in config.local.php (development only):
$config['kv_redsys_base_url'] = 'http://127.0.0.1:11830';node tests/mock-server/server.js 11830 starts a local stand-in of the TPV Virtual that verifies both signature versions, refuses repeated order numbers, posts signed notifications on request and answers REST refunds; node tests/e2e/e2e.mjs runs the HTTP end-to-end test against it; php tests/unit.php runs the unit tests. This proves the add-on against the documentation, not against the Redsys test environment.
Template overrides
Section titled “Template overrides”Admin templates live in design/backend/templates/addons/kv_redsys/. Do not edit them in a store; they are replaced on update.
Trademarks
Section titled “Trademarks”Redsys is a trademark of Redsys Servicios de Procesamiento, S.L.; Bizum is a trademark of Bizum. This add-on is not affiliated with or endorsed by either. No Redsys or Bizum logo is bundled: the Bizum guide provides official buttons in the “Botones Bizum” package of the merchant administration portal, which a store owner may upload as the payment method image.