Pagos con PayPal
El flujo de pago usa la API REST de PayPal directamente (sin el SDK de servidor). El worker actúa como intermediario para mantener las credenciales de PayPal fuera del navegador.
Ambos endpoints requieren autenticación (Authorization: Bearer <token>).
Flujo completo
Frontend Worker PayPal API
│ │ │
├─ POST /create-order ──►│ │
│ ├─ POST /oauth2/token ────►│
│ │◄─ access_token ──────────┤
│ ├─ POST /v2/orders ────────►│
│ │◄─ { id: "ORDER-ID" } ────┤
│◄─ { orderID } ─────────┤ │
│ │ │
│ [usuario aprueba en la UI de PayPal] │
│ │ │
├─ POST /capture-order ─►│ │
│ { orderID, ├─ POST /orders/:id/ │
│ reservationId } │ capture ──────────────►│
│ │◄─ { status: COMPLETED } ─┤
│ │ │
│ ├─ UPDATE reservations (Supabase)
│ ├─ POST email de confirmación (Resend)
│◄─ { status: completed }┤Endpoints
POST /api/paypal/create-order
Crea una orden de pago en PayPal.
Body:
{
"amount": 800,
"currency": "MXN"
}Respuesta exitosa (200):
{
"data": {
"orderID": "PAYPAL-ORDER-ID"
}
}El orderID se pasa al componente <PayPalButtons> de @paypal/react-paypal-js via la función createOrder.
POST /api/paypal/capture-order
Captura el pago de una orden aprobada. Este endpoint también actualiza la reservación en Supabase y envía el email de confirmación.
Body:
{
"orderID": "PAYPAL-ORDER-ID",
"reservationId": "uuid-de-la-reservacion"
}Respuesta exitosa (200):
{
"data": {
"orderID": "PAYPAL-ORDER-ID",
"status": "completed"
}
}Si PayPal rechaza el pago:
{
"data": {
"orderID": "PAYPAL-ORDER-ID",
"status": "failed"
}
}En ambos casos la reservación se actualiza en Supabase con el estado correspondiente.
Variables de entorno
| Variable | Dev | Prod |
|---|---|---|
PAYPAL_CLIENT_ID | Sandbox client ID | Live client ID |
PAYPAL_CLIENT_SECRET | Sandbox secret | Live secret |
PAYPAL_API_URL | https://api-m.sandbox.paypal.com | https://api-m.paypal.com |
PAYPAL_API_URL se configura en wrangler.toml por entorno (no es un secret).
Email de confirmación
Al capturar un pago exitoso, el worker:
- Consulta la reservación → obtiene
clerk_user_idyevent_id - Consulta el usuario → obtiene
full_nameyemail - Consulta el evento → obtiene
titleyevent_date - Envía email via Resend con template HTML
El template está en apps/worker/src/email.ts como función que retorna HTML puro (sin dependencias de React — incompatible con el edge runtime).
Remitente: AsamaMX <hello@asamamx.com>
Asunto: Tu lugar en {eventTitle} está confirmado