Skip to main content
The Stripe Payments API implements a consistent error handling system that provides meaningful error messages and appropriate HTTP status codes.

Error Response Format

All error responses follow a consistent structure:
{
  "status": false,
  "message": "Error description",
  "code": "ERROR_CODE"
}
Response Fields:
  • status: Always false for errors
  • message: Human-readable error description
  • code: Optional error code (e.g., Stripe error codes)
In development mode (NODE_ENV !== 'production'), error responses include a stack field with the full stack trace.

Error Handling Implementation

The API uses a centralized error handling system built on three components:

1. ApiError Class

Custom error class for operational errors:
class ApiError extends Error {
  constructor(statusCode, message, isOperational = true, stack = '') {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = isOperational;
    if (stack) {
      this.stack = stack;
    } else {
      Error.captureStackTrace(this, this.constructor);
    }
  }
}
Usage in Controllers:
if (!email || !name || !phone) {
  throw new ApiError(400, 'email, name y phone son requeridos');
}

2. catchAsync Middleware

Wrapper function that catches async errors and passes them to error handlers:
export const catchAsync = (fn) => (req, res, next) => {
  fn(req, res, next).catch(next);
};
Usage:
router.post('/customers', catchAsync(createCustomer));
This eliminates the need for try-catch blocks in every controller.

3. Global Error Middleware

Centralized error handler that formats all errors:
export const apiError = (err, req, res, next) => {
  const statusCode = err.statusCode || err.raw?.statusCode || 500;
  const message = err.message || 'Internal Server Error';
  const payload = {
    status: false,
    message,
  };

  if (err.code) {
    payload.code = err.code;
  }

  if (process.env.NODE_ENV !== 'production') {
    payload.stack = err.stack;
  }

  res.status(statusCode).json(payload);
};

HTTP Status Codes

The API uses standard HTTP status codes:
Status CodeMeaningWhen Used
200OKSuccessful GET, PUT, PATCH requests
201CreatedSuccessful POST requests (resource created)
400Bad RequestMissing required fields, invalid input
404Not FoundResource or route not found
500Internal Server ErrorUnexpected server errors

Common Error Scenarios

Missing Required Fields

Request:
curl -X POST http://localhost:3000/api/customers \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]"
  }'
Response (400):
{
  "status": false,
  "message": "email, name y phone son requeridos"
}

Invalid Payment Intent ID

Request:
curl -X POST http://localhost:3000/api/payments/confirm \
  -H "Content-Type: application/json" \
  -d '{}'
Response (400):
{
  "status": false,
  "message": "paymentId es requerido"
}

Invalid Card Token

Request:
curl -X POST http://localhost:3000/api/cards/assign \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "cus_invalid",
    "source": "tok_invalid"
  }'
Response (400-500):
{
  "status": false,
  "message": "No such token: tok_invalid",
  "code": "resource_missing"
}

Route Not Found

Request:
curl http://localhost:3000/api/invalid-route
Response (404):
{
  "status": false,
  "message": "Route not found: /api/invalid-route"
}

Invalid Refund Amount

Request:
curl -X POST http://localhost:3000/api/payments/refund \
  -H "Content-Type: application/json" \
  -d '{
    "chargeId": "ch_xxxxx",
    "amount": -10
  }'
Response (400):
{
  "status": false,
  "message": "amount debe ser un número mayor a 0"
}

Stripe Error Codes

When Stripe API calls fail, the error includes a Stripe error code:
{
  "status": false,
  "message": "Your card was declined",
  "code": "card_declined"
}

Common Stripe Error Codes

CodeDescriptionAction
card_declinedCard was declinedAsk customer to use different card
insufficient_fundsInsufficient fundsRequest payment with different method
expired_cardCard expiredRequest updated card details
incorrect_cvcInvalid CVCRequest correct CVC code
processing_errorPayment processing errorRetry the payment
resource_missingResource not foundVerify the ID is correct
invalid_request_errorInvalid API requestCheck request parameters

Error Handling in Client Code

Here’s how to handle errors in your client application:
try {
  const response = await fetch('http://localhost:3000/api/payments', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      customer_id: customerId,
      amount: 50.00,
      payment_method: paymentMethod
    })
  });

  const data = await response.json();

  if (!data.status) {
    // Handle error
    console.error('Payment failed:', data.message);
    
    // Check for specific error codes
    if (data.code === 'card_declined') {
      alert('Your card was declined. Please use a different card.');
    }
    
    return;
  }

  // Handle success
  console.log('Payment created:', data.data);
} catch (error) {
  // Handle network errors
  console.error('Network error:', error);
}

Best Practices

Check Response Status

Always check the status field in the response before processing data:
if (!response.status) {
  // Handle error
}

Handle Specific Error Codes

Use the code field to provide specific error messages to users:
if (data.code === 'card_declined') {
  // Show user-friendly message
}

Log Errors for Debugging

In development, use the stack trace for debugging:
if (process.env.NODE_ENV !== 'production' && data.stack) {
  console.error(data.stack);
}

Retry Logic

Implement retry logic for transient errors:
if (statusCode === 500 || statusCode === 503) {
  // Retry with exponential backoff
}

Webhook Error Handling

Webhook endpoints have specific error handling:

Missing Signature

{
  "status": false,
  "message": "Missing stripe-signature header"
}

Missing Webhook Secret

{
  "status": false,
  "message": "Missing STRIPE_WEBHOOK_SECRET in environment variables"
}

Signature Verification Failed

{
  "status": false,
  "message": "Webhook signature verification failed: No signatures found matching the expected signature for payload"
}
Always return a 200 status code for successfully received webhooks, even if your business logic fails. This prevents Stripe from retrying valid events.

Development vs Production

Development Mode

  • Includes full stack traces in error responses
  • More verbose error messages
  • Logs all errors to console
Example Response:
{
  "status": false,
  "message": "email, name y phone son requeridos",
  "stack": "Error: email, name y phone son requeridos\n    at createCustomer (...)\n    at ..."
}

Production Mode

  • No stack traces in responses
  • Sanitized error messages
  • Errors logged to monitoring systems
Example Response:
{
  "status": false,
  "message": "email, name y phone son requeridos"
}

Next Steps

Build docs developers (and LLMs) love