Biblioteca PHP para integrar la pasarela de pagos Redsys/Sermepa (Santander, Sabadell, LaCaixa, BBVA, etc.)
- PHP 7.1.3+ a 8.2
- ext-curl
- ext-openssl
- ext-json
composer require sermepa/sermepause Sermepa\Tpv\Tpv;
$redsys = new Tpv();
$redsys->setAmount(2500) // 25.00€
->setOrder('1234AB')
->setMerchantcode('999008881')
->setCurrency('978') // Euros
->setTransactiontype('0') // Autorización
->setTerminal('1')
->setMethod('C') // Solo tarjeta
->setNotification('https://tusitio.com/notificacion')
->setUrlOk('https://tusitio.com/ok')
->setUrlKo('https://tusitio.com/ko')
->setEnvironment('test'); // Entorno de pruebas
$signature = $redsys->generateMerchantSignature('TU_CLAVE_SECRETA');
$redsys->setMerchantSignature($signature);
echo $redsys->createForm();| Entorno | Descripción |
|---|---|
test |
Pruebas (SIS Sandbox) |
live |
Producción |
restTest |
API REST Pruebas |
restLive |
API REST Producción |
insiteSandbox |
InSite Pruebas (JS + REST) |
insiteLive |
InSite Producción (JS + REST) |
insiteRestSandbox |
InSite REST Pruebas (solo pago) |
insiteRestLive |
InSite REST Producción (solo pago) |
| Código | Descripción |
|---|---|
C o T |
Tarjeta |
R |
Transferencia |
D |
Domiciliación |
z |
Bizum |
p |
PayPal |
N |
Masterpass |
xpay |
GooglePay / ApplePay |
| Código | Descripción |
|---|---|
0 |
Autorización |
1 |
Preautorización |
2 |
Confirmación de preautorización |
3 |
Anulación de preautorización |
4 |
Devolución |
5 |
Cancelación |
7 |
Preautorización extendida |
8 |
Confirmación preautorización extendida |
9 |
Baja de preautorización extendida |
El banco devuelve un código en Ds_Response. Los principales:
| Código | Significado |
|---|---|
0000 a 0099 |
Pago aprobado |
0100 a 0199 |
Operatoria OK, verificación CVV obligatoria |
0200 |
Error de formato |
0201 |
Error de firma |
0204 |
Error de datos |
0209 |
Error de tajeta |
0214 |
Fecha de caducidad errónea |
0215 |
Error en importe mínimo (no usado) |
0290 |
Tarjeta no autorizada |
0401 |
Error en posición de tarjeta |
0404 |
Error de configuración de comercio |
0501 |
Operaciones pendientes |
0904 |
Comercio no operativo |
0912 |
Emisor no disponible |
9912 |
Emisor no disponible |
9913 |
Error en comunicación |
9914 |
Fallo al conectar con CA |
9919 |
Error de cryptograma |
9929 |
Error de deslinde |
9932 |
Error de bin |
9933 |
Error de BS. Cambio de dinámico a estático |
9934 |
Error de BS. Cambio de estático a dinámico |
9951 |
Operación de ingreso OK |
9952 |
Operación de ingreso NO OK |
9953 |
Devolución OK |
9954 |
Devolución NO OK |
9955 |
Anulación OK |
9956 |
Anulación NO OK |
9957 |
Ajuste OK |
9958 |
Ajuste NO OK |
9961 |
Error en cierre batch |
9962 |
Error en apertura batch |
9963 |
Error de operativa |
9992 |
Petición cancelada |
9993 |
Operatoria abandonada por el usuario |
9995 |
Operatoria abandonada - tiempo excedido |
9996 |
Error de conexión |
9997 |
Error de timeout |
9998 |
Error de validación |
9999 |
Error general |
Nota: Para pagos exitosos, el código debe estar entre
0000y0099. Convertir a entero para comparar:$DsResponse = (int) $parameters['Ds_Response'];
En tu URL de notificación (setNotification), debes validar la respuesta del banco:
use Sermepa\Tpv\Tpv;
use Sermepa\Tpv\TpvException;
try {
$redsys = new Tpv();
$key = 'TU_CLAVE_SECRETA';
// Decodificar parámetros recibidos
$parameters = $redsys->getMerchantParameters($_POST['Ds_MerchantParameters']);
$DsResponse = (int) $parameters['Ds_Response'];
// Validar firma y respuesta
if ($redsys->check($key, $_POST) && $DsResponse <= 99) {
// Pago correcto -Ds_Response = 0000 a 0099
// Aquí: actualizar pedido, enviar email, etc.
} else {
// Pago fallido
}
} catch (TpvException $e) {
error_log('Error TPV: ' . $e->getMessage());
}Guardar la tarjeta del cliente para pagos futuros:
// 1. Crear referencia (primer pago)
$redsys->setIdentifier(); // Sin parámetros - indica "REQUIRED"
// Respuesta del banco contendrá:
// $parameters['Ds_Merchant_Identifier']
// $parameters['Ds_ExpiryDate']// 2. Usar referencia (pagos recurrentes)
$redsys->setIdentifier('IDENTIFICADOR_GUARDADO');
$redsys->setMerchantDirectPayment(true); // Pago directo sin autenticación 3D SecurePuedes enviar los datos de la tarjeta para que el usuario no tenga que introducirlos en la pasarela:
$redsys->setPan('4548812049400004'); // Número de tarjeta
$redsys->setExpiryDate('1228'); // Caducidad (AAMM)
$redsys->setCVV2('123'); // CVV2Nota: Esta opción requiere que tu comercio tenga autorización del banco para enviar datos de tarjeta directamente.
Para cobros directos sin redirección del usuario:
use Sermepa\Tpv\Tpv;
use Sermepa\Tpv\TpvException;
try {
$key = 'TU_CLAVE_SECRETA';
$redsys = new Tpv();
$redsys->setAmount(2500)
->setOrder('1234AB')
->setMerchantcode('999008881')
->setCurrency('978')
->setTransactiontype('0')
->setTerminal('1')
->setIdentifier('IDENTIFICADOR_GUARDADO')
->setMerchantDirectPayment(true)
->setVersion('HMAC_SHA256_V1')
->setEnvironment('restTest')
->setMerchantCofIni('N');
$signature = $redsys->generateMerchantSignature($key);
$redsys->setMerchantSignature($signature);
$response = json_decode($redsys->send(), true);
// Verificar error en respuesta
if (isset($response['errorCode'])) {
throw new Exception("Error: " . $response['errorCode']);
}
// Decodificar respuesta
$parameters = $redsys->getMerchantParameters($response['Ds_MerchantParameters']);
$DsResponse = (int) $parameters['Ds_Response'];
if ($redsys->check($key, $response) && $DsResponse <= 99) {
// Cobro correcto
}
} catch (TpvException $e) {
echo 'Error TPV: ' . $e->getMessage();
}InSite permite incrustar el formulario de pago directamente en tu página web mediante un iframe, sin redirigir al usuario a la pasarela de Redsys. Los datos de tarjeta nunca pasan por tu servidor (cumplimiento PCI DSS).
Nota: Tu dominio debe estar registrado en Redsys para usar InSite. Configura los dominios permitidos en el Portal de Administración del TPV Virtual. Contacta con tu banco o soporte de Redsys.
- Generar formulario InSite - Renderiza el iframe en tu página
- Usuario completa pago - Introduce datos en el iframe de Redsys
- Obtener ID de operación - Redsys retorna un
idOper(válido por 30 minutos) - Ejecutar pago - Envía el
idOpermediante REST API
El modo unificado genera un iframe completo con todos los campos de pago:
use Sermepa\Tpv\Tpv;
use Redsys\Merchant\MerchantInsiteLanguage;
$redsys = new Tpv();
$redsys->setEnvironment('insiteSandbox') // o 'insiteLive' para producción
->setOrder('1234AB')
->setMerchantcode('999008881') // Tu código de comercio (FUC)
->setTerminal('1');
// Generar HTML del formulario InSite
$htmlForm = $redsys->createInSiteForm(
'card-form', // ID del contenedor
'background: #007bff;', // Estilo del botón
'color: white;', // Estilo del cuerpo
'padding: 10px;', // Estilo de la caja de datos
'font-size: 14px;', // Estilo de los inputs
'Pagar', // Texto del botón (HTML encoded: 'Botón' para Botón)
MerchantInsiteLanguage::ISO_ES, // Idioma: 'ES', '1', etc.
true, // Mostrar logo de entidad
false, // Estilo reducido
'inline' // Estilo InSite: 'inline' o 'twoRows'
);
echo $htmlForm;Para mayor flexibilidad, usa el método JSON:
$htmlForm = $redsys->createInSiteFormJSON([
'id' => 'card-form',
'fuc' => '999008881',
'terminal' => '1',
'order' => '1234AB',
'styleButton' => 'background: #007bff; color: white;',
'styleBody' => 'font-family: Arial;',
'styleBox' => 'padding: 10px;',
'buttonValue' => 'Pagar ahora',
'idiomaInsite' => 'ES',
'mostrarLogoInsite' => true,
'estiloReducidoInsite' => false,
'estiloInsite' => 'inline' // o 'twoRows'
]);| Parámetro | Obligatorio | Descripción |
|---|---|---|
id / $containerId |
Sí | ID del contenedor div |
fuc / merchantCode |
Sí | Código de comercio (FUC) |
terminal |
Sí | Número de terminal |
order |
Sí | Número de pedido (4-12 caracteres) |
styleButton |
No | CSS para el botón de pago |
styleBody |
No | CSS para el cuerpo del formulario |
styleBox |
No | CSS para la caja de datos |
styleBoxText |
No | CSS para el texto de los inputs |
buttonValue |
No | Texto del botón (HTML encoded) |
idiomaInsite |
No | Código de idioma (ver tabla) |
mostrarLogoInsite |
No | Mostrar logo de entidad (default: true) |
estiloReducidoInsite |
No | Usar estilo reducido (default: false) |
estiloInsite |
No | 'inline' o 'twoRows' (default: 'inline') |
| Idioma | Código SIS | ISO 639-1 |
|---|---|---|
| Español | 1 | ES |
| Inglés | 2 | EN |
| Catalán | 3 | CA |
| Francés | 4 | FR |
| Alemán | 5 | DE |
| Italiano | 7 | IT |
| Portugués | 9 | PT |
| ... | ... | ... |
Usa las constantes de MerchantInsiteLanguage:
use Redsys\Merchant\MerchantInsiteLanguage;
$redsys->createInSiteForm(..., MerchantInsiteLanguage::SPANISH, ...);
// o
$redsys->createInSiteForm(..., MerchantInsiteLanguage::ISO_ES, ...);El formulario incluye automáticamente un listener que almacena el idOper en un campo hidden:
<input type="hidden" id="token" name="token" value="">
<input type="hidden" id="errorCode" name="errorCode" value="">Puedes personalizar la validación:
function merchantValidation() {
// Tu validación personalizada
return true; // true para continuar, false para cancelar
}
window.addEventListener("message", function receiveMessage(event) {
storeIdOper(event, "token", "errorCode", merchantValidation);
});| Código | Descripción |
|---|---|
| msg1 | Ha de rellenar los datos de la tarjeta |
| msg2 | La tarjeta es obligatoria |
| msg3 | La tarjeta ha de ser numérica |
| msg15 | La longitud de la tarjeta no es correcta |
| msg16 | Debe introducir un número de tarjeta válido |
| msg17 | Validación incorrecta por parte del comercio |
| msg18 | Error de inicialización de dominio |
Usa MerchantInsiteError para obtener descripciones:
use Redsys\Merchant\MerchantInsiteError;
$errorDescription = MerchantInsiteError::getDescription('msg1');use Sermepa\Tpv\Tpv;
use Sermepa\Tpv\TpvException;
try {
$key = 'TU_CLAVE_SECRETA';
$idOper = $_POST['token']; // ID recibido del iframe
$order = '1234AB'; // Mismo pedido usado en el formulario
$redsys = new Tpv();
$redsys->setEnvironment('insiteRestLive') // o 'insiteRestSandbox'
->setAmount(2500)
->setOrder($order) // DEBE ser el mismo que en createInSiteForm
->setMerchantcode('999008881')
->setCurrency('978')
->setTransactiontype('0')
->setTerminal('1');
// Ejecutar pago con el ID de operación
$response = json_decode($redsys->sendInSite($idOper, $key), true);
// Decodificar respuesta
$parameters = $redsys->getMerchantParameters($response['Ds_MerchantParameters']);
$DsResponse = (int) $parameters['Ds_Response'];
if ($DsResponse >= 0 && $DsResponse <= 99) {
// Pago aprobado
} else {
// Pago denegado
}
}
} catch (TpvException $e) {
echo 'Error TPV: ' . $e->getMessage();
}| Método | Descripción |
|---|---|
setInSite(bool) |
Habilitar/deshabilitar modo InSite |
getInSiteMode() |
Obtener estado del modo InSite |
getInSiteJsUrl() |
Obtener URL del JavaScript de InSite |
createInSiteForm(string $containerId, string $buttonStyle, string $bodyStyle) |
Generar HTML del formulario embebido |
sendInSite(string $idOper, string $key) |
Ejecutar pago con ID de operación |
// Generar formulario con estilos personalizados
$html = $redsys->createInSiteForm(
'card-form', // ID del contenedor
'background-color: #28a745; color: white; padding: 15px; border: none; border-radius: 5px;', // Estilo botón
'font-family: Arial, sans-serif;' // Estilo cuerpo
);Para redirección automática sin mostrar el botón:
$redsys->executeRedirection();
// Opcional: obtener HTML
$html = $redsys->executeRedirection(true);La biblioteca incluye un método estático para obtener la ruta del JavaScript de Redsys:
use Sermepa\Tpv\Tpv;
// Obtener URL del script
$jsUrl = Tpv::getJsPath('test', '3'); // Entorno test, versión 3
// https://sis-t.redsys.es:25443/sis/NC/sandbox/redsysV3.js| Versión | Descripción |
|---|---|
2 |
Redsys Classic |
3 |
Redsys API (soporte NFC, Apple Pay, Google Pay) |
Usa las constantes predefinidas para evitar errores de transcripción:
use Redsys\Merchant\MerchantCurrencies;
use Redsys\Merchant\MerchantTransactionTypes;
use Redsys\Merchant\MerchantConsumerLanguages;
use Redsys\Merchant\MerchantPaymethods;
// Monedas
$redsys->setCurrency(MerchantCurrencies::EUR); // 978
$redsys->setCurrency(MerchantCurrencies::USD); // 840
$redsys->setCurrency(MerchantCurrencies::GBP); // 826
// Tipos de transacción
$redsys->setTransactiontype(MerchantTransactionTypes::AUTHORIZATION); // 0
$redsys->setTransactiontype(MerchantTransactionTypes::PREAUTHORIZATION); // 1
// Idiomas
$redsys->setLanguage(MerchantConsumerLanguages::SPANISH); // 001
$redsys->setLanguage(MerchantConsumerLanguages::ENGLISH); // 002
$redsys->setLanguage(MerchantConsumerLanguages::CATALAN); // 003
// Métodos de pago
$redsys->setMethod(MerchantPaymethods::CARD); // C
$redsys->setMethod(MerchantPaymethods::BIZUM); // zLos códigos ISO 4217. Los más comunes:
978- Euro (EUR)840- Dólar estadounidense (USD)826- Libra esterlina (GBP)392- Yen japonés (JPY)
| Código | Idioma |
|---|---|
| 001 | Castellano |
| 002 | Inglés |
| 003 | Catalán |
| 004 | Francés |
| 005 | Alemán |
| 006 | Holandés |
| 007 | Italiano |
| 008 | Sueco |
| 009 | Portugués |
| 010 | Valenciano |
| 011 | Polaco |
| 012 | Gallego |
| 013 | Euskera |
| Método | Descripción | Requerido |
|---|---|---|
setAmount(float) |
Importe (se convierte a céntimos) | Sí |
setOrder(string) |
Número de pedido (4-12 caracteres, primeros 4 numéricos) | Sí |
setMerchantcode(string) |
Código de comercio (FUC) | Sí |
setCurrency(string) |
Código de moneda ISO | Sí |
setTransactiontype(string) |
Tipo de transacción | Sí |
setTerminal(string) |
Número de terminal | Sí |
setMethod(string) |
Método de pago | No |
setNotification(string) |
URL de notificación (callback) | Recomendado |
setUrlOk(string) |
URL si pago exitoso | Recomendado |
setUrlKo(string) |
URL si pago fallido | Recomendado |
setEnvironment(string) |
Entorno de conexión | No |
setVersion(string) |
Versión de firma | No |
setTradeName(string) |
Nombre del comercio | No |
setTitular(string) |
Titular del pago | No |
setProductDescription(string) |
Descripción del producto | No |
| Método | Descripción |
|---|---|
generateMerchantSignature(string $key) |
Genera firma HMAC-SHA256 |
setMerchantSignature(string) |
Asigna la firma calculada |
createForm() |
Genera formulario HTML |
send() |
Envía petición REST (devuelve JSON) |
executeRedirection() |
Redirección automática con JavaScript |
| Método | Descripción |
|---|---|
check(string $key, array $postData) |
Valida firma de respuesta del banco |
getMerchantParameters(string) |
Decodifica Ds_MerchantParameters |
| Método | Descripción |
|---|---|
setInSite(bool) |
Habilitar modo InSite |
getInSiteMode() |
Obtener estado del modo InSite |
getInSiteJsUrl() |
Obtener URL del JavaScript de InSite |
createInSiteForm(...) |
Genera formulario embebido (modo unificado) |
createInSiteFormJSON(array) |
Genera formulario embebido (modo JSON) |
sendInSite(string $idOper, string $key) |
Ejecuta pago con ID de operación |
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
$containerId |
string | No | ID del contenedor div (default: 'card-form') |
$buttonStyle |
string | No | CSS para el botón de pago |
$bodyStyle |
string | No | CSS para el cuerpo del formulario |
$boxStyle |
string | No | CSS para la caja de datos |
$inputStyle |
string | No | CSS para los inputs |
$buttonText |
string | No | Texto del botón (HTML encoded) |
$language |
string | No | Código de idioma (default: 'ES') |
$showLogo |
bool | No | Mostrar logo de entidad (default: true) |
$reducedStyle |
bool | No | Usar estilo reducido (default: false) |
$insiteStyle |
string | No | 'inline' o 'twoRows' (default: 'inline') |
| Método | Descripción |
|---|---|
setNameForm(string) |
Nombre del formulario |
setIdForm(string) |
ID del formulario |
setAttributesSubmit(...) |
Personalizar botón submit |
setLanguage(string) |
Idioma de la pasarela |
setParameters(array) |
Parámetros adicionales |
setIdentifier(string) |
Referencia de tarjeta guardada |
setMerchantDirectPayment(bool) |
Pago directo sin autenticación |
setPan(string) |
Número de tarjeta |
setExpiryDate(string) |
Caducidad (AAMM) |
setCVV2(string) |
Código CVV2 |
setMerchantData(string) |
Datos adicionales del comercio |
getOrder() |
Obtener número de pedido |
getParameters() |
Obtener todos los parámetros |
getVersion() |
Obtener versión de firma |
getMerchantSignature() |
Obtener firma actual |
getEnvironment() |
Obtener URL del entorno |
getJsPath(string, string) |
Obtener ruta JS para integración moderna |
createOrderNumber(int) |
Generar número de pedido válido |
$redsys->setMerchantCofIni('S'); // Inicio de COF
$redsys->setMerchantCofType('R'); // Tipo: R=Recurrente, I=Cuotas
$redsys->setMerchantCofTxnid('123456789'); // ID de transacción
$redsys->setSumtotal(50000); // Importe total
$redsys->setChargeExpiryDate('2025-12-31'); // Fecha expiración
$redsys->setDateFrecuency(30); // Frecuencia en díasAlgunos bancos requieren parámetros adicionales:
$parameters = ['DS_MERCHANT_EXCEP_SCA' => 'MIT'];
$redsys->setParameters($parameters);<?php
use Sermepa\Tpv\Tpv;
use Sermepa\Tpv\TpvException;
try {
$key = 'sq7HjrUOBfKmC576ILgskD5srU870gJ7';
$redsys = new Tpv();
$redsys->setAmount(25.50)
->setOrder(date('YmdHis')) // 20240215120000
->setMerchantcode('999008881')
->setCurrency('978')
->setTransactiontype('0')
->setTerminal('1')
->setMethod('C')
->setNotification('https://tusitio.com/notificacion')
->setUrlOk('https://tusitio.com/ok')
->setUrlKo('https://tusitio.com/ko')
->setVersion('HMAC_SHA256_V1')
->setTradeName('Mi Tienda')
->setTitular('Cliente Ejemplo')
->setProductDescription('Compra en Mi Tienda')
->setEnvironment('test');
$signature = $redsys->generateMerchantSignature($key);
$redsys->setMerchantSignature($signature);
echo $redsys->createForm();
} catch (TpvException $e) {
echo 'Error: ' . $e->getMessage();
}<?php
use Sermepa\Tpv\Tpv;
use Sermepa\Tpv\TpvException;
try {
$key = 'sq7HjrUOBfKmC576ILgskD5srU870gJ7';
$redsys = new Tpv();
// Decodificar parámetros
$parameters = $redsys->getMerchantParameters($_POST['Ds_MerchantParameters']);
// Obtener código de respuesta
$DsResponse = (int) $parameters['Ds_Response'];
$DsOrder = $parameters['Ds_Order'];
// Validar firma y respuesta
if ($redsys->check($key, $_POST)) {
if ($DsResponse >= 0 && $DsResponse <= 99) {
// Pago aprobado
// Aquí: actualizar pedido en BDD, enviar confirmación, etc.
error_log("Pago exitoso - Pedido: $DsOrder, Respuesta: $DsResponse");
} else {
// Pago denegado
error_log("Pago denegado - Pedido: $DsOrder, Respuesta: $DsResponse");
}
} else {
// Firma inválida - posible fraude
error_log("Firma inválida - Pedido: $DsOrder");
}
} catch (TpvException $e) {
error_log('Error TPV: ' . $e->getMessage());
http_response_code(500);
}Ver CHANGELOG.md para más detalles.
MIT - Ver LICENSE.md
- Eduardo D - Autor original
- jaumecornado - Redirección automática
- markitosgv - Validación de respuesta
¿Te gustaría apoyar este proyecto? ¡Gracias por tu aprecio!
¿Necesitas ayuda? Abre un issue
