Clientes y tarjeta guardada

Un cliente es una persona a la que le cobras más de una vez. Si registra su tarjeta una sola vez, después puedes cobrarle desde tu sistema sin que él tenga que hacer nada.

El celular es la identidad

Cada cliente se identifica por su celular, único dentro de tu cuenta. El correo es opcional. Si intentas crear un cliente con un celular que ya existe, recibes 409 customer_exists con el cliente existente en details — así puedes reutilizarlo en vez de duplicarlo.

Crear un cliente

POST/v1/customers

Cuerpo

CampoTipoDescripción
nameobligatoriostringNombre del cliente.
phoneobligatoriostringCelular. Lo normalizamos a solo dígitos, así que puedes mandarlo con formato.
emailstringCorreo, opcional.
notesstringNotas internas (máx. 500).
curl
curl -X POST https://go.paytiptap.com/api/v1/customers \
  -H "Authorization: Bearer tiptap_sk_TU_LLAVE" \
  -H "Content-Type: application/json" \
  -d '{ "name": "María Pérez", "phone": "+507 6000-0000", "email": "maria@ejemplo.com" }'
201 Created
{
  "object": "customer",
  "id": "cmqbcazfu000hjy7yb6bctj7v",
  "name": "María Pérez",
  "phone": "60000000",
  "email": "maria@ejemplo.com",
  "status": "ACTIVE",
  "source": "MERCHANT",
  "card": null,
  "created_at": "2026-07-26T00:31:00.000Z"
}

Cómo se guarda la tarjeta

Tu servidor nunca ve el número de tarjeta, así que no puedes mandarlo por API. La tarjeta se guarda de dos maneras, ambas del lado del cliente:

  1. Link de afiliación: https://go.paytiptap.com/join/TU-SLUG. El cliente pone su nombre, su celular y su tarjeta en un solo formulario. Se hace una verificación de $0 (no un cobro) y la tarjeta queda tokenizada. Si el celular ya existía, se le agrega la tarjeta al cliente que ya tenías. El link está en el panel, en Clientes → Link de afiliación.
  2. Al pagar un cobro suyo: si el link de pago está asociado a un cliente (porque lo creaste con POST /v1/customers/{id}/charges), al pagarlo la tarjeta queda guardada automáticamente.

Cuando el cliente tiene tarjeta, el objeto trae el bloque card con marca, últimos 4 y vencimiento:

json
"card": { "brand": "visa", "last4": "1111", "expiry": "10/2028" }

¿Cómo sé si puedo cobrarle directo?

Si card no es null, puedes usar use_saved_card: true. Si es null, mándale el link de afiliación para que registre una.

Mandar a tu usuario al link (sin que reescriba nada)

El link de afiliación acepta parámetros para que tu usuario no vuelva a teclear lo que tu sistema ya sabe, y para que sepas exactamente quién se afilió:

bash
https://go.paytiptap.com/join/TU-SLUG?ref=usuario_42&name=María%20Pérez&email=maria@correo.com&return_url=https://tu-sitio.com/gracias
  • ref — el id del usuario en tu sistema. Queda en el cliente como external_ref y lo puedes buscar con GET /v1/customers?external_ref=usuario_42. Sin esto tendrías que casar por celular, y un dígito mal tecleado rompe el vínculo.
  • name, email, phone — prellenan el formulario.
  • return_url — al terminar, se le pinta un botón para volver a tu sitio. Solo aceptamos https, y nunca redirigimos solos: el usuario ve a dónde va antes de hacer clic.

El ref es público: úsalo como identificador, no como contraseña

El link de afiliación es una página pública sin autenticación, así que cualquiera puede fabricar uno con el ref que quiera. Usa refs no adivinables (un UUID, no 42) y cruza el email o el celular antes de activarle algo valioso a alguien por el webhook.

Entérate sin preguntarle al usuario

Cuando la tarjeta queda guardada disparamos el webhook customer.card_saved con el cliente completo (incluido external_ref). Así activas el servicio en el momento, sin pedirle que regrese y confirme a mano.

Cobrarle a un cliente

POST/v1/customers/{id}/charges

Cuerpo

CampoTipoDescripción
amountobligatorionumberMonto a cobrar.
descriptionstringLo que ve el cliente. Por defecto, “Cobro a {nombre}”.
use_saved_cardbooleantrue: cobra al instante la tarjeta guardada. false o ausente: solo genera el link para que el cliente pague.

Cobro inmediato a la tarjeta guardada

curl
curl -X POST https://go.paytiptap.com/api/v1/customers/cmqbcazfu000hjy7yb6bctj7v/charges \
  -H "Authorization: Bearer tiptap_sk_TU_LLAVE" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 18.00, "description": "Mensualidad julio", "use_saved_card": true }'
201 Created
{
  "object": "charge",
  "customer_id": "cmqbcazfu000hjy7yb6bctj7v",
  "amount": 18,
  "currency": "USD",
  "paid": true,
  "payment_id": "cmqbbfsbd000fjy7yy29qmahh",
  "payment_url": "https://go.paytiptap.com/pay/odpDAk0FERxq",
  "payment_link": { "object": "payment_link", "status": "COMPLETED", "…": "…" }
}

Con paid: true el dinero ya está cobrado y entra a tu próxima liquidación. Si el banco rechaza la tarjeta, recibes 402 charge_declined — y el link igual queda creado en details.payment_link, para que puedas mandárselo al cliente y que pague con otra tarjeta.

Cobro por link

curl
curl -X POST https://go.paytiptap.com/api/v1/customers/cmqbcazfu000hjy7yb6bctj7v/charges \
  -H "Authorization: Bearer tiptap_sk_TU_LLAVE" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 18.00, "description": "Mensualidad julio" }'

Devuelve paid: false y payment_url. Mándasela al cliente por el canal que uses. Cuando pague, además del cobro, su tarjeta queda guardada para la próxima.

No es idempotente

Dos llamadas iguales crean dos cobros. Antes de reintentar por un timeout, consulta GET /v1/payments?customer_id=… para ver si el cobro ya existe.

Listar, consultar y actualizar

GET/v1/customers

Filtros: phone (busca por celular, con o sin formato) y status (ACTIVE | ARCHIVED).

Buscar por celular
curl "https://go.paytiptap.com/api/v1/customers?phone=6000-0000" \
  -H "Authorization: Bearer tiptap_sk_TU_LLAVE"
PATCH/v1/customers/{id}

Actualiza solo los campos que mandes. Para dar de baja a un cliente sin borrar su historial, mándale { "status": "ARCHIVED" }; un cliente archivado no admite cobros nuevos.

curl
curl -X PATCH https://go.paytiptap.com/api/v1/customers/cmqbcazfu000hjy7yb6bctj7v \
  -H "Authorization: Bearer tiptap_sk_TU_LLAVE" \
  -H "Content-Type: application/json" \
  -d '{ "email": "maria.perez@ejemplo.com" }'

Clientes que se registran solos

Los que llegan por el link de afiliación traen source: "SELF"; los que creas tú, source: "MERCHANT". Cada vez que alguien se registra por ese link te mandamos el webhook customer.created.