tutoriales.com

Procesamiento de Pagos Seguro y Escalable con Stripe, AWS Lambda y DynamoDB

Este tutorial te guía paso a paso en el diseño y despliegue de una plataforma de comercio electrónico totalmente serverless capaz de procesar pagos en tiempo real con alta disponibilidad, garantizando la seguridad de los datos sensibles y el almacenamiento eficiente de las transacciones.

Avanzado12 min de lectura30 views
Reportar error

Introducción al Procesamiento de Pagos Serverless

El comercio electrónico moderno exige arquitecturas capaces de responder instantáneamente ante picos de tráfico inesperados, sin descuidar la seguridad de la información financiera y optimizando los costos operativos. La computación serverless se ha convertido en el estándar de la industria para cumplir con estos requisitos, eliminando la necesidad de aprovisionar y administrar servidores tradicionales.

En este tutorial avanzado, implementaremos una solución completa para procesar pagos utilizando AWS Lambda como motor de ejecución, Amazon DynamoDB para el registro persistente de transacciones y la pasarela de pagos Stripe para gestionar de manera segura las tarjetas de crédito y las transacciones monetarias.

📌 Nota: Este tutorial asume que posees conocimientos básicos de AWS, Node.js y conceptos generales de APIs REST.

Arquitectura del Sistema

Antes de escribir código, es fundamental comprender cómo fluyen los datos entre el cliente, la API Gateway, nuestras funciones Lambda y los servicios externos como Stripe.

HTTP POST Invocar Pago Guardar Cliente API Gateway AWS Lambda Stripe API DynamoDB

Componentes Clave

  • API Gateway: El punto de entrada HTTP que expone nuestros endpoints seguros para el procesamiento de pagos.
  • AWS Lambda (Node.js): Contiene la lógica de negocio para validar las peticiones, comunicarse con Stripe y registrar los resultados.
  • Amazon DynamoDB: Base de datos NoSQL con diseño de clave de partición y ordenamiento para almacenar el historial de transacciones de manera ultrarrápida.
  • Stripe SDK: La biblioteca oficial que gestiona de forma segura los tokens de pago y las llamadas a la pasarela.

Preparación del Entorno de Desarrollo

Para seguir este tutorial con éxito, asegúrate de cumplir con los siguientes requisitos previos:

Paso 1: Tener una cuenta activa en AWS con permisos para crear funciones Lambda, tablas DynamoDB y APIs.
Paso 2: Tener una cuenta en Stripe y obtener tus claves API de prueba (Publishable y Secret Keys).
Paso 3: Instalar Node.js (versión 18.x o superior) y la CLI de AWS configurada en tu máquina local.

Instalación de Dependencias

Crea un nuevo directorio para tu proyecto e inicializa un proyecto Node.js:

mkdir serverless-payment-gateway
cd serverless-payment-gateway
npm init -y

A continuación, instala las dependencias necesarias para interactuar con Stripe y el SDK de AWS:

npm install stripe @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb uuid
npm install --save-dev serverless

Configuración de la Base de Datos con DynamoDB

Una gestión eficiente de los datos de pago requiere una tabla optimizada. Utilizaremos Amazon DynamoDB para almacenar cada intento de pago utilizando un identificador único (UUID).

Estructura de la Tabla

  • Nombre de la tabla: Transactions
  • Clave de partición: transactionId (String)
  • Atributos adicionales: userId, amount, currency, status, createdAt

Puedes desplegar esta tabla utilizando un archivo de configuración de Serverless Framework (serverless.yml). Creemos este archivo en la raíz del proyecto:

service: serverless-payment-gateway
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs18.x
  region: us-east-1
  environment:
    STRIPE_SECRET_KEY: ${env:STRIPE_SECRET_KEY}
    TRANSACTIONS_TABLE: ${self:custom.transactionsTable}
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:PutItem
            - dynamodb:GetItem
            - dynamodb:UpdateItem
          Resource: 
            - Fn::GetAtt: [TransactionsTable, Arn]

custom:
  transactionsTable: transactions-table-${sls:stage}

functions:
  processPayment:
    handler: handler.processPayment
    events:
      - httpApi:
          path: /pay
          method: post

resources:
  Resources:
    TransactionsTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:custom.transactionsTable}
        BillingMode: PAY_PER_REQUEST
        AttributeDefinitions:
          - AttributeName: transactionId
            AttributeType: S
        KeySchema:
          - AttributeName: transactionId
            KeyType: S
💡 Consejo: El modo de facturación `PAY_PER_REQUEST` (On-Demand) es ideal para arquitecturas serverless con patrones de tráfico impredecibles, ya que escala automáticamente sin necesidad de aprovisionar capacidad de lectura o escritura.

Desarrollo de la Función Lambda para Procesar el Pago

El núcleo de nuestra aplicación reside en la función Lambda. Esta función recibirá los datos del cliente y el monto, se comunicará con Stripe para realizar el cargo y finalmente guardará el registro en DynamoDB.

Crea un archivo llamado handler.js con el siguiente código:

const { v4: uuidv4 } = require('uuid');
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, PutCommand } = require('@aws-sdk/lib-dynamodb');

const client = new DynamoDBClient({});
const dynamoDb = DynamoDBDocumentClient.from(client);

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const TRANSACTIONS_TABLE = process.env.TRANSACTIONS_TABLE;

module.exports.processPayment = async (event) => {
  try {
    const body = JSON.parse(event.body);
    const { amount, currency, paymentMethodId, customerId } = body;

    if (!amount || !currency || !paymentMethodId) {
      return {
        statusCode: 400,
        body: JSON.stringify({ error: 'Faltan parámetros obligatorios en la solicitud' }),
      };
    }

    // 1. Crear el cargo utilizando Stripe
    const paymentIntent = await stripe.paymentIntents.create({
      amount,
      currency,
      payment_method: paymentMethodId,
      confirm: true,
      return_url: 'https://tusitio.com/checkout/complete',
    });

    const transactionId = uuidv4();
    const timestamp = new Date().toISOString();

    // 2. Preparar el objeto de transacción para DynamoDB
    const transactionRecord = {
      transactionId,
      customerId: customerId || 'guest',
      amount,
      currency,
      status: paymentIntent.status,
      stripePaymentIntentId: paymentIntent.id,
      createdAt: timestamp,
    };

    // 3. Guardar en DynamoDB
    await dynamoDb.send(
      new PutCommand({
        TableName: TRANSACTIONS_TABLE,
        Item: transactionRecord,
      })
    );

    return {
      statusCode: 200,
      body: JSON.stringify({
        message: 'Pago procesado exitosamente',
        transactionId,
        status: paymentIntent.status,
      }),
    };
  } catch (error) {
    console.error('Error al procesar el pago:', error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: error.message }),
    };
  }
};

Seguridad y Buenas Prácticas en Pasarelas de Pago

Cuando trabajamos con sistemas financieros, la seguridad no es opcional. Debemos cumplir estrictamente con normativas internacionales como PCI-DSS.

⚠️ Advertencia: Nunca proceses, almacenes ni transmitas números completos de tarjetas de crédito (PAN) ni códigos CVV a través de tus propias funciones Lambda o bases de datos DynamoDB. Utiliza siempre los tokens seguros que provee Stripe.js en el frontend.

Recomendaciones Clave

  • Gestión de Secretos: Utiliza AWS Secrets Manager o variables de entorno cifradas para almacenar tu STRIPE_SECRET_KEY. No hardcodees credenciales en el código fuente.
  • Validación de Entradas: Asegúrate de validar y sanitizar todos los datos entrantes en la función Lambda antes de interactuar con la pasarela de pagos.
  • Idempotencia: Implementa mecanismos de idempotencia utilizando identificadores únicos enviados desde el cliente para evitar cargos duplicados en caso de reintentos de red.

Despliegue y Pruebas de la Solución

Una vez que hayas configurado tu código y el archivo serverless.yml, es momento de desplegar la infraestructura en tu cuenta de AWS.

Pasos para el Despliegue

  1. Configura tu clave secreta de Stripe como variable de entorno en tu terminal:
export STRIPE_SECRET_KEY=sk_test_tu_clave_secreta_aqui
  1. Ejecuta el comando de despliegue mediante Serverless Framework:
npx serverless deploy --verbose
  1. Copia la URL de la API HTTP devuelta por la consola al finalizar el despliegue.

Probando el Endpoint con cURL

Puedes realizar una prueba simulando una petición HTTP POST utilizando curl:

curl -X POST https://tu-api-gateway-url.amazonaws.com/pay \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 2000,
    "currency": "usd",
    "paymentMethodId": "pm_card_visa",
    "customerId": "cust_12345"
  }'
🔥 Importante: El identificador `pm_card_visa` es un token de prueba proporcionado por Stripe que simula una tarjeta de crédito válida en entornos de desarrollo.

Preguntas Frecuentes (FAQ)

¿Cómo manejo los webhooks de Stripe para pagos asíncronos? Para métodos de pago que requieren confirmación adicional (como 3D Secure o transferencias bancarias), debes crear una segunda función Lambda expuesta a través de API Gateway para escuchar los eventos de webhook de Stripe (como `payment_intent.succeeded` o `payment_intent.payment_failed`) y actualizar el estado en DynamoDB de forma asíncrona.
¿Qué pasa si ocurre un fallo de red durante el registro en DynamoDB después de cobrar en Stripe? Esta es una situación crítica conocida como inconsistencia transaccional. Para mitigarla, puedes implementar una función de reconciliación nocturna utilizando AWS EventBridge y AWS Lambda que compare los cargos registrados en Stripe con los almacenados en DynamoDB, o bien utilizar transacciones distribuidas y colas de reintento con Amazon SQS.

Conclusión

Has construido con éxito una arquitectura serverless robusta, escalable y segura para el procesamiento de pagos utilizando AWS Lambda, DynamoDB y Stripe. Este diseño te permite crecer desde cero transacciones hasta millones de operaciones mensuales sin preocuparte por la infraestructura subyacente, manteniendo un costo estrictamente ligado al consumo real.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!