Cobros únicos y servicios

Cobra un monto que defines tú a un cliente concreto, con su propia página de pago y fecha límite.

Para quien cobra servicios y no vende un catálogo cerrado: un freelancer que factura horas, una agencia que cobra un presupuesto aprobado o una clínica que cobra una consulta. El ejemplo es una consulta de USD 30,00 que el paciente paga desde un enlace.

El modelo

En tu aplicaciónEn Billete
El paciente pac_81Un cliente con externalId: "pac_81" y su correo
Lo que te debe por una citaUn Cobro (invoice) con sus líneas
El enlace que le envíasLa url del Cobro, su página de pago
La cita en tu agendametadata del Cobro, para encontrarla al recibir el pago
  • Un Cobro por cosa que se debe. Cada Cobro tiene su número, su página y su estado (open, paid, void, uncollectible), así que tu aplicación solo guarda su ID junto a la cita y lee el resto de Billete.
  • Las líneas las describes tú. Una línea puede ser libre (description y unitAmount) o un producto de tu catálogo a su precio. No hace falta crear un producto por cada servicio que cobras una sola vez.
  • El cliente es obligatorio. El Cobro va dirigido a alguien: Billete le envía el enlace por correo y le recuerda pagarlo mientras siga abierto. Por eso el cliente necesita email.
  • No es una factura fiscal. Es la deuda y el registro de su pago. Si tu negocio debe emitir factura, hazlo en tu sistema con los datos del Cobro.

Cobro, checkout o enlace de pago

NecesitasUsaPor qué
Pedirle a una persona concreta un monto que defines tú, con fecha límiteUn CobroAdmite líneas libres, se envía por correo y queda abierto hasta que se paga o lo anulas.
Vender productos del catálogo o un plan a quien está comprando ahora en tu aplicaciónUn checkoutSolo vende catálogo, admite cupones, pide los datos de entrega de productos físicos, un plan crea su suscripción y vence si no se paga.
Una URL fija para redes o WhatsApp que cualquiera puede pagar, sin códigoUn enlace de pagoAbre un checkout nuevo para cada comprador.

Un Cobro no admite cupones ni crea entregas: si vendes algo que se retira, se lleva o se envía, usa un checkout.

1. Crea el Cobro

const invoice = await billete.invoices.create({
  customer: { externalId: patient.id, email: patient.email, name: patient.name },
  currency: "USD",
  lines: [{ description: "Consulta de medicina general", unitAmount: 3000 }],
  daysUntilDue: 3,
  description: "Cita del 24 de octubre con la Dra. Pérez",
  metadata: { appointmentId: appointment.id },
}, { idempotencyKey: `cita-${appointment.id}` });

await db.appointments.update(appointment.id, { invoiceId: invoice.id, payUrl: invoice.url });

customer como objeto crea el cliente o reutiliza el que ya tiene ese externalId. quantity es 1 si no la envías. La clave de idempotencia evita cobrar dos veces la misma cita si tu servidor reintenta.

Un precio en dólares o euros se paga en bolívares a tasa BCV, como cualquier otro: mira precios en dólares. taxBehavior dice si los montos ya incluyen el IVA de tu negocio (inclusive, por defecto) o se le suma.

Para cobrar productos del catálogo, envía { product, variant?, quantity? } en vez de una línea libre. El producto debe tener la moneda del Cobro y, si tu negocio cobra IVA, su tratamiento del IVA (si no, currency_mismatch o tax_behavior_mismatch), y sus existencias se descuentan al pagarse.

2. Comparte el enlace

Billete le envía el Cobro por correo a tu cliente al crearlo, con el botón para pagar. Si lo atiendes por otro canal, comparte la misma invoice.url: su página muestra las líneas, el total y los métodos de pago, y el monto en bolívares si el precio está en otra moneda.

daysUntilDue pone la fecha límite (dueAt), que el correo le indica a tu cliente. Pasada esa fecha el Cobro no se cierra solo: sigue abierto y se puede pagar, y el dashboard lo muestra vencido. Sin daysUntilDue, no tiene fecha límite.

3. Marca la cita como pagada

Los eventos de Cobros también llegan por los que registran una compra en un checkout (reason: "checkout") y por las renovaciones de suscripciones (subscription_cycle). Atiende solo los tuyos, manual:

// En tu webhook, ya verificado:
switch (event.type) {
  case "invoice.paid":
  case "invoice.voided":
  case "invoice.uncollectible": {
    const invoice = await billete.invoices.retrieve(event.data.id);
    if (invoice.reason !== "manual") break;
    await db.appointments.update(invoice.metadata.appointmentId, {
      paymentStatus: invoice.status,
      paymentId: invoice.paymentId,
    });
  }
}
EventoQué pasóQué hace tu aplicación
invoice.createdSe emitió el Cobro y salió el correo a tu cliente.Nada: ya guardaste su ID al crearlo.
invoice.paidTu cliente pagó. paymentId es su pago.Marca la cita como pagada y confirma la reserva.
invoice.voidedLo anulaste.Quita el enlace de pago de la cita.
invoice.uncollectibleTu negocio lo marcó como incobrable desde el dashboard.Márcalo como no cobrado en tu sistema.

4. Anula lo que ya no se debe

Si la cita se cancela antes del pago, anula su Cobro. Tu cliente recibe un correo que le dice que ya no necesita pagarlo, y su página deja de aceptar pagos:

await billete.invoices.void(appointment.invoiceId);

Solo se anula un Cobro abierto y sin un pago en verificación (invoice_not_voidable): si tu cliente está pagando en ese momento, espera el resultado. Un Cobro ya pagado no se anula: si la cita se cancela después, reembólsalo. Los Cobros de renovación no se anulan aquí, sino cancelando su suscripción (renewal_invoice).

Para reenviar el correo o marcar un Cobro como incobrable, usa el dashboard (Cobros); la API no tiene esas operaciones.

Referencia de Cobros · Reglas de Cobros · Pagos