Skip to content

PayPal

PayPal usa Orders v2 con captura: la librería crea una orden, tú rediriges al usuario a la página de aprobación de PayPal y, al volver, la librería captura el pago. La fuente de verdad es el webhook, aunque la vuelta del navegador también confirma la captura.

Configuración

ts
import { PayPalGateway } from '@kaizen/payments-gateway'

PayPalGateway.register({
  client_id: process.env.PAYPAL_CLIENT_ID!,
  client_secret: process.env.PAYPAL_CLIENT_SECRET!,
  webhook_id: process.env.PAYPAL_WEBHOOK_ID!,
  environment: 'sandbox',                         // 'sandbox' | 'production'
  public_base_url: process.env.PAYPAL_PUBLIC_BASE_URL!,
})
CampoTipoRequeridoDescripción
client_idstringClient ID de tu app REST de PayPal
client_secretstringClient Secret de tu app REST de PayPal
webhook_idstringID del webhook, para verificar la firma de las notificaciones
environment'sandbox' | 'production'Selecciona el endpoint de la API de PayPal
public_base_urlstringURL pública de tu API; PayPal devuelve al usuario a …/payments/paypal/return

Variables de entorno

Los valores los aportas tú desde tu propio entorno y se los pasas a PayPalGateway.register():

ini
PAYPAL_CLIENT_ID=AeA1QIZ...
PAYPAL_CLIENT_SECRET=EGnHDxD...
PAYPAL_WEBHOOK_ID=8SB63863JN...
PAYPAL_ENVIRONMENT=sandbox           # 'sandbox' | 'production'
PAYPAL_PUBLIC_BASE_URL=https://tu-dominio.com
VariableCampoRequeridoDescripción
PAYPAL_CLIENT_IDclient_idClient ID de la app REST
PAYPAL_CLIENT_SECRETclient_secretClient Secret de la app REST
PAYPAL_WEBHOOK_IDwebhook_idID del webhook para verificar la firma
PAYPAL_ENVIRONMENTenvironmentsandbox o production (elige el endpoint)
PAYPAL_PUBLIC_BASE_URLpublic_base_urlURL pública de tu API para la vuelta del navegador

Crear un cobro

bash
curl -X POST http://localhost:3000/payments/charge \
  -H 'content-type: application/json' \
  -d '{
    "gateway": "paypal",
    "amount": 4900,
    "currency": "EUR",
    "reference": "pedido-001",
    "redirect": {
      "success_url": "http://localhost:3000/ok",
      "cancel_url": "http://localhost:3000/ko"
    }
  }'
  • redirect.success_url y redirect.cancel_url son obligatorios en PayPal. Sin ellos → 400.
  • amount va en la unidad mínima de la moneda (4900 = 49,00 €).
  • La respuesta trae un redirect_url con la página de aprobación de PayPal: manda ahí al usuario.

La respuesta tiene el mismo formato que el resto de pasarelas:

json
{
  "payment_id": "b9c1…",
  "gateway": "paypal",
  "status": "pending",
  "redirect_url": "https://www.sandbox.paypal.com/checkoutnow?token=…"
}

Cómo se paga

  1. Rediriges al usuario al redirect_url (la página de aprobación de PayPal).
  2. El usuario aprueba el pago en PayPal.
  3. PayPal lo devuelve a …/payments/paypal/return (una ruta que monta la propia librería a partir de tu public_base_url). Ahí la librería captura la orden y luego redirige el navegador a tu success_url (o a cancel_url si no se capturó).

TIP

Tú no implementas la ruta de vuelta: la librería la expone y PayPal la usa como return_url. Tú solo aportas success_url / cancel_url en el cuerpo del cobro y public_base_url en la config.

Notificación y confirmación

Además de la captura en la vuelta del navegador, PayPal envía un webhook a POST /webhooks/paypal. La librería:

  1. Verifica la firma llamando a la API de PayPal con tu webhook_id. Inválida → 400.
  2. Lee el event_type:
    • PAYMENT.CAPTURE.COMPLETEDcapturado → emite payment.captured.
    • PAYMENT.CAPTURE.DENIEDfallido → emite payment.failed.

El gateway_ref que casa la notificación con el Payment es el id de la orden de PayPal, generado por la librería al crear el cobro. La idempotencia por (gateway, event_id) evita que la vuelta del navegador y el webhook se procesen dos veces.