Andorpay
Recursos/Documentación

Guías de integración

Revisa las guías principales para integrar Andorpay: checkout alojado, suscripciones, webhooks, reembolsos y prompts de implementación para IA.

Guías rápidas

Checkout alojado

Crea checkouts desde tu servidor con la clave de API del comercio y redirige siempre al hostedUrl devuelto.

Suscripciones

Los productos recurrentes crean suscripciones. Tu aplicación concede o retira acceso a partir de eventos verificados.

Webhooks

Verifica firmas, evita duplicados con event.id y trata la entrega como al menos una vez sin conceder acceso dos veces.

Documentación preparada para IA

Copia prompts para Codex, Claude, Cursor y Windsurf que obligan al agente a respetar esta documentación.

Modelo de integración

Los usuarios del panel y las claves de API del comercio son identidades distintas

El alta usa el JWT de Supabase porque una persona está configurando el comercio. Checkout, reembolsos y facturación usan la clave de API del comercio porque actúa un servidor en su nombre.

1. Completa el onboarding en Andorpay

El comercio inicia sesión, configura Redsys y recibe la clave de API inicial una sola vez.

2. Guarda la clave de API en el servidor

Guárdala en el gestor de secretos del servidor del comercio. No la incluyas en aplicaciones móviles ni en paquetes de navegador.

3. Crea checkouts desde tu backend

La clave de API determina el alcance del comercio. Envía productId, los datos del cliente y las URL de éxito y fallo para cada petición.

Inicio rápido

Crea un checkout alojado y envía al comprador al pago

El checkout alojado es la integración recomendada: tu servidor crea el checkout, Andorpay devuelve hostedUrl, tu aplicación redirige al comprador y Andorpay encapsula la interacción con Redsys.

Crear checkout alojado
curl
curl -X POST https://api.andorpay.com/v1/checkouts \
  -H "Authorization: Bearer $ANDORPAY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Andorpay-Version: 2026-01-01" \
  -H "Idempotency-Key: checkout:user_42:starter:1" \
  -d '{
    "externalCustomerId": "user_42",
    "customer": {
      "email": "ana@example.com",
      "name": "Ana Garcia",
      "billingDetails": {
        "line1": "Carrer Prat de la Creu 12",
        "city": "Andorra la Vella",
        "postal_code": "AD500",
        "country": "AD"
      },
      "tax": {
        "value": "F-123456-Z",
        "type": "andorra_nrt"
      }
    },
    "productId": "ap_prod_starter_monthly",
    "successUrl": "https://example.com/billing/success",
    "failUrl": "https://example.com/billing/cancelled"
  }'

Lista mínima para producción

  • Llama a Andorpay desde tu backend, no desde JavaScript en el navegador.
  • Fija Andorpay-Version y revísala en cambios de código.
  • Genera una clave de idempotencia por cada intento lógico de checkout.
  • Persiste checkout.id, orderId y paymentId antes de mostrar la interfaz de pago.
  • Redirige siempre al hostedUrl devuelto por Andorpay.
  • Entrega el producto desde webhooks payment.succeeded o subscription.created.
Respuesta de la sesión de checkout
json
{
  "id": "ap_chk_01HY8Q9A3Y9B5J7V2V0M9R4S8T",
  "sessionId": "ap_chk_01HY8Q9A3Y9B5J7V2V0M9R4S8T",
  "hostedUrl": "https://andorpay.com/checkout/ap_chk_01HY8Q9A3Y9B5J7V2V0M9R4S8T",
  "expiresAt": "2026-05-17T10:30:00.000Z",
  "request_id": "req_01HY8T2M0D6F4Q7F3K8A9V2N1C"
}
Función auxiliar en TypeScript
ts
type CheckoutCustomer = {
  email: string
  name?: string
  billingDetails?: {
    line1?: string
    city?: string
    postal_code?: string
    country?: string
  }
  tax?: {
    value: string
    type: string
  }
}

type CreateCheckoutSessionInput = {
  externalCustomerId: string
  productId: string
  customer: CheckoutCustomer
  successUrl: string
  failUrl: string
  amount?: number
}

const andorpayApiUrl = process.env.ANDORPAY_API_URL ?? "https://api.andorpay.com"

export async function createAndorpayCheckout(input: CreateCheckoutSessionInput, idempotencyKey: string) {
  const response = await fetch(`${andorpayApiUrl}/v1/checkouts`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.ANDORPAY_API_KEY}`,
      "Content-Type": "application/json",
      "Andorpay-Version": "2026-01-01",
      "Idempotency-Key": idempotencyKey,
    },
    body: JSON.stringify(input),
  })

  const payload = await response.json()

  if (!response.ok) {
    throw new Error(`No se pudo crear el checkout de Andorpay: ${payload.code ?? response.status}`)
  }

  return payload as {
    id: string
    sessionId: string
    hostedUrl: string
    expiresAt?: string
  }
}

export function continueToAndorpayCheckout(checkout: { hostedUrl: string }) {
  window.location.assign(checkout.hostedUrl)
}

Referencia de API

Endpoints principales y entrada del checkout alojado

La entrada del checkout es explícita para que personas y agentes puedan mapear comercio electrónico, SaaS y facturación al mismo contrato. La clave de API aporta el alcance del comercio.

MétodoRutaUso
POST/v1/checkoutsCrea un checkout alojado.
POST/v1/payment-methods/changeCrea un flujo alojado para actualizar el método de pago.
POST/v1/subscriptions/{id}/cancelCancela una suscripción del comercio autenticado por la clave de API.
GET/v1/productsLista los productos del comercio autenticado.
GET/v1/ordersLista los pedidos del comercio autenticado.
GET/v1/subscriptionsLista las suscripciones del comercio autenticado.
GET/v1/invoicesLista las facturas del comercio autenticado.
GET/v1/paymentsLista los pagos del comercio autenticado.
GET/v1/billing/dashboardDevuelve los contadores del panel de facturación.
POST/v1/refundsCrea un reembolso desde paymentId u orderId.
POST/v1/manual-chargesEncola un cargo manual para un pedido o importe existente.

Campos obligatorios del checkout

externalCustomerIdproductIdcustomer.emailsuccessUrlfailUrl

Campos opcionales habituales

amountpaymentMethodcustomer.namecustomer.billingDetailscustomer.tax

Webhooks

Verifica, evita duplicados y actualiza el estado

Los webhooks son la fuente de verdad para entregas y suscripciones. Las páginas de redirección son experiencia de usuario, no prueba de pago.

Carga útil de webhook
json
{
  "id": "evt_01HY8R3D4P7N7QGJ9M2K3W8X6B",
  "type": "payment.succeeded",
  "occurred_at": "2026-05-17T09:42:18.000Z",
  "merchant_id": "mer_01J0ANDORPAYMERCHANT",
  "customer": {
    "id": "cus_123",
    "external_customer_id": "user_42",
    "email": "ana@example.com"
  },
  "product": {
    "id": "ap_prod_starter_monthly",
    "external_reference": "starter-monthly"
  },
  "subscription": {
    "id": "ap_sub_123",
    "status": "active",
    "current_period_end": "2026-06-17T09:42:18.000Z"
  },
  "payment": {
    "id": "ap_pay_123",
    "rail": "redsys",
    "status": "succeeded"
  }
}

Eventos soportados

payment.succeededpayment.failedsubscription.createdsubscription.updatedsubscription.cancelledinvoice.createdinvoice.payment_failedrefund.createdpayment_method.updatedpayment_method.updated_requiredbilling.operational_alert

Nota sobre fallos del rail: payment.failed, payment_method.updated_required y billing.operational_alert significan cosas distintas. No trates todos los fallos del rail como una cancelación del usuario.

EventoCuándo se usaQué debe hacer tu sistema
payment.succeededUn pago ha sido autorizado y confirmado por el rail.Marca pedido o pago como cobrado, entrega el producto y activa el acceso si no depende de una suscripción.
payment.failedEl intento de pago ha fallado o ha sido rechazado.No entregues el producto. Libera reservas, muestra recuperación de pago y conserva el motivo operativo en logs.
subscription.createdSe ha creado una suscripción desde un checkout recurrente.Crea o enlaza la suscripción local y concede acceso según el estado recibido.
subscription.updatedLa suscripción ha cambiado de estado, periodo, plan o datos relevantes.Sincroniza permisos, fechas de renovación, estado past_due/active y cambios de plan.
subscription.cancelledLa suscripción ha sido cancelada o finalizada.Revoca o programa la retirada de acceso según la fecha efectiva y actualiza el estado local.
invoice.createdSe ha generado una factura o periodo de cobro.Registra la factura, muéstrala en el panel del cliente y espera eventos de pago para entrega o renovación.
invoice.payment_failedEl cobro asociado a una factura no ha podido completarse.Activa gracia, reintentos, aviso al cliente o flujo de actualización de método de pago.
refund.createdSe ha creado un reembolso total o parcial.Actualiza el saldo del pedido, estado de pago y soporte/contabilidad interna.
payment_method.updatedEl método de pago del cliente se ha actualizado correctamente.Confirma la recuperación de cobro o marca el método como válido para futuros cargos.
payment_method.updated_requiredEl cliente debe actualizar o autenticar su método de pago.Envía al cliente al flujo alojado de actualización y no canceles la suscripción automáticamente.
billing.operational_alertHay una incidencia operativa de billing, rail, credenciales, webhook o reintento.Alerta a operadores internos. No muestres el detalle técnico al cliente final.
Verificación de webhooks en Next.js
ts
import crypto from "node:crypto"
import { NextRequest, NextResponse } from "next/server"

function verifyAndorpaySignature(rawBody: string, signatureHeader: string, secret: string) {
  const parts = new Map(signatureHeader.split(",").map((part) => {
    const [key, value] = part.split("=")
    return [key, value]
  }))
  const timestamp = parts.get("t")
  const signature = parts.get("v1")

  if (!timestamp || !signature) {
    return false
  }

  const timestampMs = Number(timestamp) * 1000
  if (!Number.isFinite(timestampMs) || Math.abs(Date.now() - timestampMs) > 5 * 60 * 1000) {
    return false
  }

  const payload = `${timestamp}.${rawBody}`
  const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex")
  const signatureBuffer = Buffer.from(signature, "hex")
  const expectedBuffer = Buffer.from(expected, "hex")

  if (signatureBuffer.length !== expectedBuffer.length) {
    return false
  }

  return crypto.timingSafeEqual(signatureBuffer, expectedBuffer)
}

export async function POST(request: NextRequest) {
  const rawBody = await request.text()
  const signature = request.headers.get("Andorpay-Signature")

  if (!signature || !verifyAndorpaySignature(rawBody, signature, process.env.ANDORPAY_WEBHOOK_SECRET!)) {
    return NextResponse.json({ error: "Firma no válida" }, { status: 400 })
  }

  const event = JSON.parse(rawBody) as {
    id: string
    type: string
    customer?: { external_customer_id?: string }
    subscription?: { id?: string; status?: string; current_period_end?: string }
    payment?: { id?: string; status?: string }
  }

  // Guarda event.id antes de aplicar efectos. Ignora el evento si ya lo has procesado.
  switch (event.type) {
    case "payment.succeeded":
      // Marca el pago o pedido local como pagado. En suscripciones, actualiza el acceso desde eventos subscription.*.
      break
    case "payment.failed":
      // Mantén una política de gracia y pide al cliente que actualice o autentique el método de pago si hace falta.
      break
    case "subscription.created":
    case "subscription.updated":
    case "subscription.cancelled":
      break
    case "invoice.created":
    case "invoice.payment_failed":
      break
    case "refund.created":
    case "payment_method.updated":
    case "payment_method.updated_required":
    case "billing.operational_alert":
      break
    default:
      break
  }

  return NextResponse.json({ received: true })
}

Fiabilidad

Reintenta de forma segura con claves de idempotencia

La API evita duplicados en checkouts y reembolsos mediante Idempotency-Key. Reutiliza esa clave al reintentar la misma petición después de un tiempo de espera agotado, un límite de tasa o un error transitorio de la API.

Reglas para agentes

  • No crees una clave nueva al reintentar el mismo checkout o reembolso.
  • Sí debes crear una clave nueva para otro carrito, otro producto u otro reembolso.
  • Registra el request_id de Andorpay junto al id interno del pedido.
  • Trata el 409 como un desacople entre clave y cuerpo hasta demostrar lo contrario.
Función de reintentos
ts
async function retryAndorpayCreate(request: RequestInit, idempotencyKey: string) {
  for (let attempt = 0; attempt < 3; attempt += 1) {
    const response = await fetch(`${process.env.ANDORPAY_API_URL ?? "https://api.andorpay.com"}/v1/checkouts`, {
      ...request,
      headers: {
        ...request.headers,
        "Idempotency-Key": idempotencyKey,
      },
    })

    if (response.ok) {
      return response.json()
    }

    if (![408, 409, 429, 500, 502, 503, 504].includes(response.status)) {
      throw new Error(`Respuesta no reintentable de Andorpay: ${response.status}`)
    }

    await new Promise((resolve) => setTimeout(resolve, 250 * 2 ** attempt))
  }

  throw new Error("La petición a Andorpay falló después de varios reintentos")
}

Suscripciones

Crea mediante checkout y cancela mediante la API

Un producto recurrente crea una suscripción mediante checkout alojado. Tu aplicación debe actualizar el acceso desde webhooks de suscripción verificados, no desde una redirección optimista.

Cancelar suscripción
curl
curl -X POST https://api.andorpay.com/v1/subscriptions/sub_01HY8R62TFX01K5CM7Y4S0N6VA/cancel \
  -H "Authorization: Bearer $ANDORPAY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Andorpay-Version: 2026-01-01"

Sesión de checkout

createdrequires_actionsucceededfailed

Crea el checkout y envía al cliente a hostedUrl. Andorpay muestra la UI alojada y encapsula internamente cualquier paso firmado que Redsys pueda requerir. Trata las redirecciones solo como experiencia de usuario.

Pago

requires_actionprocessingsucceededfailedcancelledrefundedpartially_refunded

Entrega el producto en payment.succeeded. En payment.failed, libera reservas o muestra una recuperación de pago.

Suscripción

incompletetrialingactivepast_duecancelledexpiredpaused

Los permisos de acceso deben seguir subscription.created, subscription.updated y subscription.cancelled.

Factura

draftopencreatedvoiduncollectiblepayment_failed

Andorpay emite invoice.created e invoice.payment_failed. Mantén el estado de la factura separado del acceso del usuario.

Errores

Devuelve mensajes seguros y registra el detalle operativo

Muestra al cliente un mensaje de recuperación genérico y conserva en logs el code, message y request_id estructurados de Andorpay.

Forma de una respuesta de error
json
{
  "code": "product_not_found",
  "message": "Producto no encontrado o inactivo",
  "request_id": "req_01HY8T2M0D6F4Q7F3K8A9V2N1C"
}
EstadoTipoTratamiento
400checkout_amount_invalidCorrige la forma de la petición o el valor del parámetro antes de reintentar.
401andorpay_api_key_requiredEnvía Authorization: Bearer <ANDORPAY_API_KEY> desde el servidor.
403andorpay_merchant_forbiddenNo envíes otro merchantId; la clave de API define el alcance del comercio.
409idempotency_key_conflictReutiliza una clave solo para el mismo método, ruta y cuerpo.
404product_not_foundComprueba que el producto pertenece al comercio y está activo.
5xxcheckout_failedReintenta de forma segura, conserva la clave de idempotencia y registra request_id.

IA-DX

Pega un prompt protegido en tu herramienta de código

Estos prompts indican a las herramientas de IA que lean esta URL, respeten la superficie documentada de la API y pregunten cuando falte un detalle.

Prompt de integración generado

Pulsa una tarjeta de herramienta para generar y copiar un prompt adaptado.

Ruta legible para LLM

Hay un resumen compacto de estilo llms.txt dentro del espacio de documentación para agentes que prefieren un contrato en texto plano.

Abrir llms.txt

    Usamos cookies propias y de analítica para mejorar la web. Puedes revisar la política de cookies.