phpguzzle.org
enrudees
Stripe – Docs

Errores comunes de Stripe en PHP y cómo solucionarlos

Cada llamada fallida al API de Stripe lanza una excepción, pero el tipo de excepción por sí solo no dice mucho. CardException engloba desde fondos insuficientes hasta una tarjeta marcada como robada, y el mensaje no distingue entre ambos casos. Aparte están los errores que ni siquiera vienen de Stripe: cURL error 60 es un problema de configuración SSL local, pero aparece al hacer peticiones a Stripe porque suelen ser las primeras llamadas HTTPS que hace una aplicación PHP.

Stripe PHP no funciona: por dónde empezar

Antes de buscar el error concreto, una lista de verificación rápida:

  1. Paquete instalado: composer show stripe/stripe-php
  2. Autoloader incluido: require __DIR__ . '/vendor/autoload.php';
  3. PHP 8.0+ recomendado: php -v (el SDK admite 7.2+, los ejemplos usan sintaxis de PHP 8)
  4. Extensión cURL habilitada: php -m | grep curl
  5. Claves API configuradas: echo getenv('STRIPE_SECRET_KEY');
  6. Claves del mismo modo: sk_test_ con pk_test_, o sk_live_ con pk_live_

Si todo está en orden y Stripe sigue sin funcionar, las secciones siguientes cubren cada error habitual.

Manejo de excepciones con try/catch

El SDK organiza las excepciones en una jerarquía con \Stripe\Exception\ApiErrorException como base, y la referencia del API enumera cada clase de la jerarquía. Capturar solo la clase base funciona, pero pierdes información sobre qué salió mal. No capturar nada significa un Fatal Error en la primera tarjeta rechazada.

Un bloque try/catch que cubre cada tipo:

$stripe = new \Stripe\StripeClient('sk_test_...');

try {
    $intent = $stripe->paymentIntents->create([
        'amount' => 3500,
        'currency' => 'mxn',
        'payment_method' => $paymentMethodId,
        'confirm' => true,
    ]);
} catch (\Stripe\Exception\CardException $e) {
    // Tarjeta rechazada: fondos insuficientes, expirada, CVC incorrecto, fraude
    $decline = $e->getError()?->decline_code;
    error_log("Tarjeta rechazada [{$decline}]: " . $e->getMessage());

} catch (\Stripe\Exception\AuthenticationException $e) {
    // Clave API inválida o revocada
    error_log('Fallo de autenticación Stripe: ' . $e->getMessage());

} catch (\Stripe\Exception\InvalidRequestException $e) {
    // Parámetros incorrectos: objeto inexistente, formato erróneo, modo equivocado
    $param = $e->getError()?->param;
    error_log("Parámetro inválido '{$param}': " . $e->getMessage());

} catch (\Stripe\Exception\ApiConnectionException $e) {
    // Problema de red entre tu servidor y Stripe
    // Se puede reintentar con clave de idempotencia
    error_log('Error de conexión con Stripe: ' . $e->getMessage());

} catch (\Stripe\Exception\RateLimitException $e) {
    // Demasiadas peticiones, necesita backoff
    error_log('Rate limit de Stripe alcanzado');

} catch (\Stripe\Exception\PermissionException $e) {
    // La restricted key no tiene permisos para esta operación
    error_log('Permiso denegado en Stripe: ' . $e->getMessage());

} catch (\Stripe\Exception\ApiErrorException $e) {
    // Error 500 del lado de Stripe (raro). Consulta status.stripe.com
    error_log('Error de Stripe: ' . $e->getMessage());
}

El operador nullsafe (?->) aparece porque getError() devuelve null para ciertos tipos de excepción.

De cada excepción puedes extraer estos datos:

$status = $e->getHttpStatus();      // 402, 400, 401, 429...
$type = $e->getError()?->type;      // card_error, invalid_request_error...
$code = $e->getError()?->code;      // expired_card, resource_missing...
$param = $e->getError()?->param;    // qué parámetro causó el error
$message = $e->getError()?->message; // descripción técnica en inglés

La relación entre códigos HTTP y excepciones:

HTTPExcepciónSignificado
400InvalidRequestExceptionParámetros incorrectos, objeto inexistente
401AuthenticationExceptionClave API inválida o revocada
402CardExceptionTarjeta rechazada
403PermissionExceptionLa restricted key no tiene permisos
429RateLimitExceptionDemasiadas peticiones
500+ApiErrorExceptionError del lado de Stripe (raro)

El $e->getMessage() y $e->getError()?->message contienen descripciones técnicas pensadas para desarrolladores. Nunca muestres estos textos al usuario final.

cURL error 60: certificado SSL

El error más frecuente al configurar Stripe por primera vez en un servidor local. PHP no puede verificar el certificado SSL porque no encuentra el bundle de certificados raíz:

cURL error 60: SSL certificate problem: unable to get local issuer certificate

No es un error de Stripe. Pasa con cualquier petición HTTPS que haga cURL. En XAMPP, WAMP, MAMP e instalaciones de PHP en Windows el bundle de certificados no viene configurado. En Linux con gestores de paquetes rara vez ocurre porque apt y yum instalan ca-certificates automáticamente.

Para solucionarlo:

  1. Descarga el cacert.pem más reciente de https://curl.se/docs/caextract.html

  2. Coloca el archivo en una ruta estable. En Windows: C:\php\extras\ssl\cacert.pem. En macOS/Linux: /etc/ssl/certs/cacert.pem

  3. Apunta ambas directivas en php.ini:

curl.cainfo = "C:\php\extras\ssl\cacert.pem"
openssl.cafile = "C:\php\extras\ssl\cacert.pem"
  1. Reinicia el servidor web (Apache, nginx + php-fpm)

Una trampa habitual en XAMPP: PHP carga un php.ini diferente al que estás editando. Verifica cuál se está usando:

echo php_ini_loaded_file();

Comprobación rápida después del arreglo:

$ch = curl_init('https://api.stripe.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
if (false === $result) {
    echo 'Error cURL: ' . curl_error($ch);
} else {
    echo 'SSL funciona correctamente';
}
curl_close($ch);

No desactives la verificación SSL con CURLOPT_SSL_VERIFYPEER = false. Muchas respuestas en Stack Overflow lo sugieren. En procesamiento de pagos, desactivar la verificación abre la puerta a ataques man-in-the-middle, donde los datos de la tarjeta pueden ser interceptados.

Modo test vs modo live: No such token

Un clásico al pasar a producción:

No such token: tok_xxx; a similar object exists in test mode,
but a live mode key was used to make this request.

La clave publicable en el frontend empieza con pk_test_, mientras que la secreta en el backend empieza con sk_live_ (o al revés). Un token creado con una clave de pruebas es invisible para una clave de producción. Ambas claves tienen que ser del mismo modo.

Diagnóstico rápido:

$pubKey = getenv('STRIPE_PUBLISHABLE_KEY');
$secKey = getenv('STRIPE_SECRET_KEY');

$pubMode = str_starts_with($pubKey, 'pk_live_') ? 'live' : 'test';
$secMode = str_starts_with($secKey, 'sk_live_') ? 'live' : 'test';

if ($pubMode !== $secMode) {
    throw new \RuntimeException(
        "Claves Stripe no coinciden: publicable es {$pubMode}, secreta es {$secMode}"
    );
}

Menos obvio: los IDs de objetos (cus_, pm_, pi_, sub_) pertenecen a un modo. Un cliente creado en test no existe en live. Al migrar a producción, no puedes reutilizar IDs de tu base de datos de pruebas; cada objeto se crea desde cero.

En plataformas como Heroku y Railway, las variables de entorno actualizadas en el panel no se aplican hasta que el proceso se reinicia. Las variables se leen al arrancar el proceso.

No se puede usar un token más de una vez

Los tokens (tok_) son de un solo uso:

You cannot use a Stripe token more than once

Suele pasar por un envío doble de formulario. El usuario hace clic en pagar dos veces, la segunda petición intenta reutilizar el token. En el frontend, desactiva el botón tras el primer clic. En el backend, usa una clave de idempotencia:

$stripe->paymentIntents->create(
    [
        'amount' => 1500,
        'currency' => 'usd',
        'payment_method' => $paymentMethodId,
        'confirm' => true,
    ],
    ['idempotency_key' => 'order_' . $orderId]
);

La clave se guarda durante 24 horas. Usar el ID de la orden es práctico porque es único y está vinculado a la operación. Pero enviar la misma clave con parámetros distintos (por ejemplo, el monto cambió) lanza IdempotencyException (HTTP 400):

try {
    $stripe->paymentIntents->create($params, ['idempotency_key' => $key]);
} catch (\Stripe\Exception\IdempotencyException $e) {
    // Misma clave con parámetros distintos, generar una nueva
    $stripe->paymentIntents->create($params, ['idempotency_key' => $key . '_v2']);
}

Una clave, un conjunto de parámetros.

Los tokens (tok_) son el mecanismo antiguo. El flujo Charges API + Token ha sido reemplazado por PaymentMethod (pm_) + PaymentIntent. Un PaymentMethod no es de un solo uso: puedes asociarlo a un cliente y reutilizarlo.

Monto en unidades incorrectas

Stripe recibe amount en la unidad menor de la moneda (centavos): para MXN, 5000 significa $50.00. Si pasas 50, cobras $0.50. No hay excepción ni advertencia, solo un cliente confundido.

// Mal: cobra $0.50 MXN en vez de $50
$stripe->paymentIntents->create([
    'amount' => 50,
    'currency' => 'mxn',
    'payment_method' => $paymentMethodId,
    'confirm' => true,
]);

// Bien: monto en centavos
$stripe->paymentIntents->create([
    'amount' => 50 * 100,
    'currency' => 'mxn',
    'payment_method' => $paymentMethodId,
    'confirm' => true,
]);

Las monedas sin decimales (JPY, KRW, VND) son la excepción: se pasa el monto tal cual sin multiplicar. El mínimo para USD es 50 centavos; cualquier valor menor devuelve amount_too_small. La lista completa de monedas sin decimales está en la documentación de Stripe.

Códigos de rechazo: qué mostrar al usuario

CardException incluye un decline_code. Mostrar el código en crudo al usuario no sirve, y ciertos códigos como stolen_card deben ocultarse.

function mensajeRechazo(string $code): string
{
    return match ($code) {
        'insufficient_funds' => 'Fondos insuficientes. Intenta con otra tarjeta.',
        'expired_card' => 'Esta tarjeta ha expirado.',
        'incorrect_cvc' => 'El código de seguridad (CVC) es incorrecto.',
        'incorrect_number' => 'El número de tarjeta es incorrecto.',
        'incorrect_pin' => 'El PIN es incorrecto.',
        'card_velocity_exceeded' => 'Demasiadas transacciones con esta tarjeta. Intenta más tarde.',
        'lost_card', 'stolen_card', 'pickup_card'
            => 'Esta tarjeta no se puede utilizar. Contacta a tu banco.',
        'generic_decline', 'do_not_honor'
            => 'El banco rechazó el pago. Intenta con otra tarjeta.',
        'processing_error' => 'Ocurrió un error de procesamiento. Intenta en un minuto.',
        default => 'Pago rechazado. Intenta con otra tarjeta o método de pago.',
    };
}

En la práctica, alrededor del 80% de los rechazos son generic_decline, insufficient_funds y expired_card. La lista completa está en la referencia de códigos de rechazo.

Tarjetas de prueba para reproducir rechazos:

  • 4000000000000002 – rechazo genérico
  • 4000000000009995 – fondos insuficientes
  • 4000000000000069 – tarjeta expirada
  • 4000000000000127 – CVC incorrecto

Un caso aparte es authentication_required. No es un rechazo sino una solicitud de 3D Secure: el banco necesita que el titular se autentique. El PaymentIntent pasa a requires_action y el frontend debe completar la verificación:

if ('requires_action' === $intent->status) {
    echo json_encode([
        'requires_action' => true,
        'client_secret' => $intent->client_secret,
    ]);
    return;
}

En el frontend, stripe.confirmCardPayment(clientSecret) presenta el formulario 3DS del banco. En Europa y en algunos países de América Latina esto ocurre con frecuencia por PSD2 y regulaciones locales.

3D Secure y autenticación reforzada

PSD2 en Europa obliga a que los pagos en línea pasen por autenticación reforzada (SCA). En la práctica esto se traduce en 3D Secure: verificación mediante código SMS, notificación push o biometría. Stripe activa 3DS automáticamente cuando el banco emisor lo exige.

Para el desarrollador PHP, el efecto es que un PaymentIntent no pasa directamente a succeeded tras la confirmación. Se queda en requires_action. Si no manejas este estado, el pago queda incompleto.

On-session: el cliente está en tu sitio

El flujo de checkout estándar:

$intent = $stripe->paymentIntents->create([
    'amount' => 4900,
    'currency' => 'eur',
    'payment_method' => $paymentMethodId,
    'confirm' => true,
    'return_url' => 'https://example.com/checkout/complete',
]);

if ('requires_action' === $intent->status) {
    // Enviar client_secret al frontend para completar 3DS
    echo json_encode([
        'status' => 'requires_action',
        'client_secret' => $intent->client_secret,
    ]);
    return;
}

if ('succeeded' === $intent->status) {
    fulfillOrder($intent);
}

El frontend llama a stripe.confirmCardPayment(clientSecret), Stripe muestra un iframe o redirige a la página del banco. Tras la verificación, llega el webhook payment_intent.succeeded.

Off-session: cobros sin el cliente presente

Suscripciones, cobros diferidos, pagos recurrentes: el cliente no está en tu sitio, no hay forma de mostrarle un formulario 3DS. Si el banco exige autenticación, el PaymentIntent no se queda en requires_action; en su lugar lanza CardException con el código authentication_required:

try {
    $intent = $stripe->paymentIntents->create([
        'amount' => 4900,
        'currency' => 'eur',
        'customer' => $customerId,
        'payment_method' => $paymentMethodId,
        'off_session' => true,
        'confirm' => true,
    ]);
} catch (\Stripe\Exception\CardException $e) {
    if ('authentication_required' === $e->getError()?->code) {
        $intentId = $e->getError()?->payment_intent?->id;
        // Enviar un email al cliente con enlace para completar el pago
        enviarEmailAutenticacion($customerId, $intentId);
    }
}

El cliente abre el enlace, el frontend obtiene el client_secret del PaymentIntent y llama a stripe.confirmCardPayment. Después de pasar 3DS el pago se completa.

Si prefieres que Stripe lance una excepción en vez de devolver requires_action, pasa error_on_requires_action:

$intent = $stripe->paymentIntents->create([
    'amount' => 4900,
    'currency' => 'eur',
    'customer' => $customerId,
    'payment_method' => $paymentMethodId,
    'off_session' => true,
    'confirm' => true,
    'error_on_requires_action' => true,
]);

Con error_on_requires_action activo, un intento off-session que necesite autenticación lanza CardException directamente, sin dejar el PaymentIntent en un estado intermedio.

Para reducir la frecuencia con la que esto ocurre, pasa usage: 'off_session' al crear el SetupIntent cuando guardas la tarjeta por primera vez. Stripe le pedirá al banco que permita cobros futuros sin autenticación adicional.

Tarjetas de prueba para 3DS

  • 4000000000003220 – autenticación requerida, el cliente confirma
  • 4000000000003063 – autenticación requerida, el cliente rechaza
  • 4000000000003055 – autenticación admitida pero el banco no la exige

3DS funciona en localhost. Stripe muestra un formulario de prueba sin redirigir a un banco real.

Webhook: SignatureVerificationException

Stripe firma cada petición de webhook. Si la firma no coincide, constructEvent lanza SignatureVerificationException. Casi siempre la causa es un secreto de firma incorrecto, no un intento de manipulación.

$payload = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$endpointSecret = getenv('STRIPE_WEBHOOK_SECRET');

try {
    $event = \Stripe\Webhook::constructEvent(
        $payload,
        $sigHeader,
        $endpointSecret
    );
} catch (\UnexpectedValueException $e) {
    // JSON inválido en el payload
    http_response_code(400);
    exit;
} catch (\Stripe\Exception\SignatureVerificationException $e) {
    // Firma no coincide
    http_response_code(400);
    error_log('Firma de webhook incorrecta: ' . $e->getMessage());
    exit;
}

switch ($event->type) {
    case 'payment_intent.succeeded':
        $intent = $event->data->object;
        break;
    case 'payment_intent.payment_failed':
        $intent = $event->data->object;
        $error = $intent->last_payment_error;
        error_log("Pago fallido: {$error?->message}");
        break;
}

http_response_code(200);

Causas por las que la firma no coincide:

  • Secreto equivocado. Cada endpoint de webhook tiene su propio whsec_. Los endpoints de test y live tienen secretos diferentes.
  • El framework modificó el body. constructEvent verifica la firma contra la cadena en crudo. Si tu framework ya parseó el JSON, la firma no coincidirá. En Laravel, usa $request->getContent(), no $request->all() ni $request->json().
  • Secreto de test vs live. Los eventos de test se firman con el secreto de test. Los de producción con el de live.

Para desarrollo local, los webhooks no llegan a localhost. Stripe CLI reenvía los eventos:

stripe listen --forward-to localhost:8080/webhook

La CLI imprime un secreto temporal. Úsalo en lugar del whsec_ del panel.

Tu endpoint de webhook debe devolver HTTP 200 rápidamente. Si el procesamiento toma tiempo (envío de emails, escritura en base de datos), encola el evento, devuelve 200 de inmediato y procesa la tarea pesada de forma asíncrona. Si no, Stripe considera la entrega fallida y empieza a reintentar con intervalos crecientes durante hasta 3 días.

Errores HTTP en webhooks

Si tu endpoint devuelve un código distinto de 2xx, Stripe lo registra como entrega fallida:

  • 307 – tu servidor redirige la petición POST (frecuente con reglas de trailing-slash en Nginx o Apache). La redirección descarta el body, así que el payload del webhook nunca llega. Corrige la URL en el panel de Stripe para que coincida con la ruta real del endpoint, o desactiva la reescritura para esa ruta.
  • 400 – generalmente indica que la verificación de firma falló o que el payload no se pudo parsear.
  • 403 – problemas de permisos o firewall bloqueando la petición.
  • 404 – URL incorrecta configurada en el panel.
  • 405 – tu servidor no acepta POST en esa ruta.
  • 500 – tu código de manejo lanzó una excepción no capturada.

Errores TLS. Stripe requiere TLS 1.2+ en tu endpoint de webhook. Si tu servidor usa un certificado expirado o autofirmado, Stripe no puede entregar el evento. No verás ni un intento fallido en el panel porque la conexión se rechaza antes de HTTP. Verifica tu certificado con openssl s_client -connect tudominio.com:443 y asegúrate de que la cadena sea válida. Los certificados de Let’s Encrypt funcionan bien, solo mantén el cron de renovación activo.

Revisa los intentos de entrega de webhooks en el panel de Stripe en Developers > Webhooks. Cada intento muestra el código de respuesta y el body devuelto. Esta sección trata solo los errores; la guía completa de webhooks cubre el receptor de principio a fin: raw body, idempotencia y despacho a colas.

Stripe Checkout no funciona

Montar Checkout desde cero es tarea de la guía de Checkout Sessions; esta sección trata de lo que se rompe. Lo primero que suele fallar es una URL faltante:

You must provide `success_url` for Checkout Sessions in `payment` mode.

Hosted Checkout requiere tanto success_url como cancel_url. Puedes incluir el ID de sesión en la URL de éxito para consultar el pago después:

$session = $stripe->checkout->sessions->create([
    'line_items' => [['price' => 'price_xxx', 'quantity' => 1]],
    'mode' => 'payment',
    'success_url' => 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
    'cancel_url' => 'https://example.com/cancel',
]);

header('Location: ' . $session->url);
exit; // sin exit la redirección puede no ejecutarse

Sesión expirada. Una Checkout Session vive 24 horas. Verifica el estado:

$session = $stripe->checkout->sessions->retrieve($sessionId);

if ('expired' === $session->status) {
    // Crear una nueva sesión
}

La página de Checkout no carga. Si usas embedded Checkout, los errores CORS suelen indicar un dominio mal configurado en el panel de Stripe. El dominio que sirve la página de checkout tiene que coincidir con el registrado en la configuración de tu cuenta Stripe.

El webhook checkout.session.completed no llega. La URL del webhook no es accesible desde Internet (localhost, firewall) o devuelve un código distinto de 200. Usa Stripe CLI para desarrollo. En producción, tu endpoint debe devolver HTTP 200 sin demora.

Tarjetas de prueba para Checkout: 4242424242424242 (pago exitoso), 4000000000000002 (rechazo).

Rate limit y errores de red

El modo live permite 100 peticiones por segundo, el modo test 25. Excederlo lanza RateLimitException con HTTP 429. Un problema diferente es ApiConnectionException: timeouts de red, fallos temporales de DNS, conexiones rotas. Ambos errores son transitorios y seguros de reintentar.

Un reintento inmediato sin pausa empeora las cosas. Usa backoff exponencial:

function stripeConReintento(\Stripe\StripeClient $stripe, callable $operacion, int $maxReintentos = 3): mixed
{
    for ($intento = 0; $intento <= $maxReintentos; $intento++) {
        try {
            return $operacion($stripe);
        } catch (\Stripe\Exception\RateLimitException|\Stripe\Exception\ApiConnectionException $e) {
            if ($maxReintentos === $intento) {
                throw $e;
            }
            // Pausa exponencial + jitter aleatorio
            usleep((int) (pow(2, $intento) * 1_000_000 + random_int(100_000, 500_000)));
        }
    }
}

$customer = stripeConReintento($stripe, fn(\Stripe\StripeClient $s) => $s->customers->create([
    'email' => '[email protected]',
]));

El jitter (la adición aleatoria) evita que todos tus workers golpeen el API al mismo tiempo después de la pausa.

Con ApiConnectionException, Stripe no garantiza que la petición no haya pasado. Usa una clave de idempotencia para operaciones que modifican datos (create, update) para que un reintento no genere duplicados. Las operaciones de lectura (retrieve, list) no necesitan clave.

Para pagos de suscripciones, Stripe cuenta con Smart Retries que reintentan automáticamente los cobros fallidos en los momentos óptimos. No necesitas implementar lógica de reintento para cobros recurrentes: configúralo en el panel bajo Billing > Revenue recovery.

Errores de entorno

No tienen que ver con el API, pero te frenarán antes de empezar.

Class ‘Stripe\StripeClient’ not found. El paquete no está instalado o no se incluyó el autoloader:

composer require stripe/stripe-php
require __DIR__ . '/vendor/autoload.php';

En Laravel y Symfony el autoloader ya está cableado. Si aparece este error significa que el paquete no está instalado: composer show stripe/stripe-php.

Código de un tutorial antiguo no funciona. Antes de 2020 todos los ejemplos usaban el API estático: \Stripe\Stripe::setApiKey('sk_...') seguido de \Stripe\Charge::create(...). Este enfoque aún funciona, pero la documentación actual y los ejemplos (incluyendo este artículo) usan new \Stripe\StripeClient('sk_...'). Con StripeClient los métodos se llaman a través de propiedades ($stripe->paymentIntents->create(...)), mientras que el API estático usa clases directamente (\Stripe\PaymentIntent::create(...)). Puedes mezclar ambos en un mismo proyecto, pero se recomienda StripeClient: es thread-safe y permite usar diferentes claves API para distintas operaciones.

Undefined type ‘Stripe\StripeClient’. Una queja del IDE (PhpStorm, VS Code), no un error de ejecución. El IDE no ha detectado los mapeos del autoload. composer dump-autoload suele resolverlo.

Versión de PHP. El SDK admite PHP 7.2+, pero el soporte para 7.2-7.3 se eliminará pronto. Los ejemplos de este artículo usan sintaxis de PHP 8.0+ (match, str_starts_with, ?->). Si estás en PHP 7.x, reemplaza match con switch y ?-> con verificaciones explícitas de null.

ext-curl no encontrado. El SDK depende de cURL. Para instalarlo en Debian/Ubuntu: sudo apt-get install php-curl && sudo systemctl restart php8.2-fpm. En macOS cURL viene incluido por defecto.

Registro seguro de errores

Al registrar errores de Stripe, nunca registres datos de tarjeta (PCI DSS). El $e->getMessage() es seguro: no contiene números de tarjeta. Pero si estás registrando las peticiones POST entrantes completas, estas pueden contener datos de tarjeta del frontend.

error_log(json_encode([
    'stripe_error' => $e->getError()?->code,
    'decline_code' => $e->getError()?->decline_code,
    'http_status' => $e->getHttpStatus(),
    'param' => $e->getError()?->param,
    'request_id' => $e->getRequestId(), // req_xxx, útil al contactar soporte de Stripe
]));

El request_id empieza con req_. El soporte de Stripe puede buscar la petición exacta en sus logs con este ID. Guárdalo con cada error.

Si tu aplicación tiene fallos en pagos y quieres verificar si Stripe está teniendo problemas, consulta status.stripe.com.

¿Has visto algo inexacto en esta página?

Reportar un error