Ir al contenido

Guías

Vender créditos

Créditos prepagados para una API o un servicio de IA: los compradores recargan por su cuenta y tu servidor gasta por llamada. De una recarga no se descuenta nada.

Ver como Markdown

Los créditos sirven para todo lo que se vende por uso: tokens, imágenes, minutos, búsquedas. El comprador paga un paquete en cripto; tú gastas de él en cada llamada; cuando se acaban los créditos, recarga en su propia página. Nada se cobra automáticamente (el comprador siempre paga él mismo) y Perly no se queda nada de la recarga.

Configuración

  1. 1

    Crea un producto y sus paquetes

    Dashboard → Products → New, luego New price → Credit pack: por ejemplo, 1 000 créditos por $10, 5 000 por $40. O pay.prices.create({ product, model: "credit_pack", unit_amount: 1000, credits: 1000 }).

  2. 2

    Elige cuándo se entera el comprador de su saldo

    En la página del producto, en Credits: el umbral de aviso, un correo al comprador y Any amount (N de un paquete), para que el comprador pueda recargar $25 y no solo un paquete fijo.

  3. 3

    Da a cada comprador un cliente

    Crea uno por usuario con tu id: pay.customers.create({ email, external_id: user.id }). A partir de ahí, el SDK acepta ese id.

Gastar, consultar, recargar

import { PerlyPay, InvalidRequestError } from "perly-pay";
const pay = new PerlyPay(process.env.PERLY_PAY_SECRET_KEY);

// Before the work: enough left?
const { balance, top_up_url } = await pay.credits.balance(user.id);

// After it: spend. The same idempotencyKey never spends twice.
try {
  await pay.credits.consume(user.id, 3, { action: "chat", idempotencyKey: request.id });
} catch (e) {
  if (e instanceof InvalidRequestError && e.code === "insufficient_credits") {
    return { error: "Out of credits", topUp: await pay.credits.topUpUrl(user.id) };
  }
  throw e;
}
  • El cliente es un id cus_…, un correo o tu propio id para él (external_id). Indica product si vendes créditos para varios productos.
  • El gasto es atómico y nunca baja de cero: dos gastos simultáneos no pasan con los mismos créditos. Si no alcanza, devuelve insufficient_credits (409) y no se gasta nada.
  • Pasa el id de tu solicitud como clave de idempotencia. Un reintento (tras un timeout, un fallo, incluso al día siguiente) devuelve el primer gasto en vez de gastar otra vez.
  • action es tu etiqueta (chat, image.hd, api/search). La página del producto suma el gasto por acción y por cliente.

La página de recarga

top_up_url es la página propia del comprador, siempre la misma: muestra el saldo y tus paquetes con el aspecto de tu página de pago, y abre una factura normal cuando el comprador elige un paquete. En cuanto se paga, los créditos se suman al saldo una sola vez. Muestra el enlace en tu app o en el error de «sin créditos», o envíalo: el portal del comprador también tiene un botón Recargar.

Cuando se acaban los créditos

EventoCuándo
credits.lowEl saldo bajó al umbral del producto. Una vez por cruce: vuelve a llegar solo después de que una recarga lo suba por encima.
credits.depletedEl saldo llegó a cero. Una vez por cruce.

Ambos llevan el cliente como data.object, además de data.product y data.balance. Si hay umbral y el correo está activado, el comprador recibe también un correo con el enlace de recarga, como mucho una vez al día. En modo de prueba, los correos solo van a tu equipo.

Los créditos de varios paquetes se suman; el gasto usa primero el paquete más antiguo.