Andorpay
Navegación de la documentación

Documentación para desarrolladores

Next.js

Mantén la API key en el servidor, crea una sesión desde un Route Handler y redirige al comprador al checkout alojado.

Antes de empezar

Necesitas un proyecto Next.js con una ruta de servidor, una cuenta AndorPay activa, una API key de pruebas y el identificador de un producto activo. El ejemplo usa App Router y runtime Node.js.

Configuración

Define estas variables en .env.local durante desarrollo y en el gestor de secretos de tu entorno al desplegar. No uses el prefijo NEXT_PUBLIC_.

.env.local
dotenv
ANDORPAY_API_KEY=ap_test_replace_me
ANDORPAY_API_URL=https://api.andorpay.com
ANDORPAY_WEBHOOK_SECRET=whsec_replace_me
lib/andorpay.ts
typescript
import "server-only"

type CheckoutInput = {
  productId: string
  externalCustomerId: string
  customer: {
    email: string
    name?: string
  }
  successUrl?: string
  failUrl?: string
}

type CheckoutResponse = {
  id: string
  sessionId: string
  hostedUrl: string
  expiresAt: string
  request_id: string
}

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

export async function createAndorpayCheckout(input: CheckoutInput) {
  const apiKey = process.env.ANDORPAY_API_KEY
  if (!apiKey) throw new Error("ANDORPAY_API_KEY is not configured")

  const response = await fetch(`${apiUrl}/v1/checkouts`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Andorpay-Version": "2026-01-01",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
    cache: "no-store",
  })

  const payload = await response.json()
  if (!response.ok) {
    throw Object.assign(new Error(payload.message ?? "AndorPay request failed"), payload)
  }

  return payload as CheckoutResponse
}

Crear un pago

El Route Handler recibe sólo los datos que tu aplicación ya validó. La API key identifica al comercio; no envíesmerchantId, priceId ni Idempotency-Key a POST /v1/checkouts.

app/api/andorpay/checkout/route.ts
typescript
import { NextResponse } from "next/server"
import { createAndorpayCheckout } from "@/lib/andorpay"

export const runtime = "nodejs"

export async function POST(request: Request) {
  const input = await request.json()
  const checkout = await createAndorpayCheckout({
    productId: input.productId,
    externalCustomerId: input.customerId,
    customer: {
      email: input.email,
      name: input.name,
    },
    successUrl: new URL("/payment/success", request.url).toString(),
    failUrl: new URL("/payment/failed", request.url).toString(),
  })

  return NextResponse.json({ hostedUrl: checkout.hostedUrl })
}

Desde tu componente cliente, llama a la ruta local y abre exactamente el hostedUrl devuelto.

Redirigir desde el navegador
typescript
const response = await fetch("/api/andorpay/checkout", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ productId, customerId, email, name }),
})

const payload = await response.json()
if (!response.ok) throw new Error(payload.message ?? "Checkout failed")

window.location.assign(payload.hostedUrl)

Probar la integración

  1. 1

    Arranca Next.js con ap_test_

    Confirma que ANDORPAY_API_KEY sólo existe en el proceso servidor y no en el HTML o los bundles del navegador.
  2. 2

    Crea una sesión

    La ruta local debe responder con hostedUrl y el backend de AndorPay con estado 201.
  3. 3

    Completa un pago sandbox

    Prueba autorización, 3DS y rechazo con las credenciales publicadas en la guía de pruebas.
  4. 4

    Comprueba el evento

    payment.succeeded debe llegar firmado y producir el efecto de negocio una sola vez.

Configurar webhooks

Crea una segunda ruta Node.js. Lee request.text() antes de JSON, verificaAndorpay-Signature y conecta el evento ya validado con una función de tu dominio.

app/api/andorpay/webhook/route.ts
typescript
import { createHmac, timingSafeEqual } from "node:crypto"
import { NextResponse } from "next/server"

export const runtime = "nodejs"

function validSignature(rawBody: string, header: string, secret: string) {
  const values = new Map(header.split(",").map((part) => part.trim().split("=", 2)))
  const timestamp = values.get("t")
  const signature = values.get("v1")

  if (!timestamp || !signature || !/^[a-f0-9]{64}$/i.test(signature)) return false
  const timestampSeconds = Number(timestamp)
  if (!Number.isFinite(timestampSeconds)) return false
  if (Math.abs(Date.now() / 1000 - timestampSeconds) > 300) return false

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

  return receivedBuffer.length === expectedBuffer.length && timingSafeEqual(receivedBuffer, expectedBuffer)
}

export async function POST(request: Request) {
  const rawBody = await request.text()
  const signature = request.headers.get("Andorpay-Signature")
  const secret = process.env.ANDORPAY_WEBHOOK_SECRET

  if (!signature || !secret || !validSignature(rawBody, signature, secret)) {
    return NextResponse.json({ code: "invalid_signature" }, { status: 400 })
  }

  const event = JSON.parse(rawBody) as { id: string; type: string }
  await acceptVerifiedAndorpayEvent(event)
  return NextResponse.json({ received: true })
}

Configura la URL final en el dashboard. Una respuesta 2xx acepta la entrega; otros estados y timeouts provocan reintento con el mismo event.id.

Errores frecuentes

SíntomaCausa y corrección
401 api_key_invalidLa clave falta, está revocada o no se cargó en el proceso servidor.
400 checkout_idempotency_key_not_allowedElimina Idempotency-Key de la creación de checkout.
404 product_not_foundCopia un productId activo que pertenezca al comercio de la API key.
El navegador ve la API keyElimina NEXT_PUBLIC_, mueve la llamada a un módulo server-only y vuelve a desplegar.
hostedUrl no abreNo reconstruyas la URL. Usa el valor completo de la respuesta antes de expiresAt.
Firma inválidaVerifica el texto exacto, el secreto activo y la marca temporal antes de JSON.parse.

Pasar a producción

Configura las credenciales Redsys productivas, genera una clave ap_live_ y actualiza el secreto de servidor. Mantén la misma frontera server-only y valida un pago real de importe reducido.

Comprueba el resultado en Redsys, AndorPay y tu aplicación. Las suscripciones requieren tokenización o COF y MIT habilitados por el banco para el terminal productivo.

Siguiente paso

    Usamos cookies necesarias para que la web funcione y, si aceptas, cookies de analítica para mejorarla. Consulta nuestra Política de cookies.