Actores

Hay dos actores en cada integración con Chytapay. Distinguirlos es clave para evitar errores.

RolQuién esQué hace
Administrador de integraciónVos — la empresa o producto que integra con Chytapay.Tu backend habla con la Integration API usando clientId/clientSecret. Tu equipo configura el portal admin (webhook, redirect URIs).
Cuenta de comercioEl usuario Chytapay (cliente final) que recibe los pagos.Autoriza tu integración vía OAuth una vez. Su identidad es email + password de Chytapay. Después, vos podés crear payment requests en su nombre usando el refreshToken que guardaste.
Confundir estos dos es el error más frecuente — intentar OAuth con tu cuenta de admin del portal en vez de con la cuenta de comercio del merchant. La cuenta de comercio es la que recibe los pagos; vos sos el que orquesta.

Vinculación inicial vs uso recurrente

Hay dos flujos distintos. Confundirlos es el error más frecuente en integraciones nuevas.

Flujo de vinculación (una sola vez)
Error: Object.hasOwn is not a function

Después de vincular una vez, cada cobro sigue este flujo más simple — todo server-side, sin intervención del usuario.

Uso recurrente (cada cobro)
Error: Object.hasOwn is not a function
pseudo-code — oauth-callback handler
// GET /oauth/callback  (your redirect_uri — Chytapay redirects here with ?code)
async function oauthCallback(req, res) {
  const { code } = req.query;

  const tokens = await authApi.post('/integration/oauth2/token', {
    code,
    clientId: CLIENT_ID,
    clientSecret: CLIENT_SECRET,
    redirectUri: REDIRECT_URI,
  });

  // Persist the refreshToken linked to the user
  await db.saveTokens(userId, tokens.idToken, tokens.refreshToken);

  res.redirect('/success');
}
pseudo-code — recurrent use (payment-request)
// Every time you need to charge the user
async function chargeUser(userId, amount) {
  let { idToken, refreshToken } = await db.getTokens(userId);

  // If idToken is expired (every 1h), refresh it
  if (isExpired(idToken)) {
    const refreshed = await authApi.post('/integration/oauth2/refresh', {
      refreshToken,
      clientId: CLIENT_ID,
      clientSecret: CLIENT_SECRET,
    });
    idToken = refreshed.idToken;
    await db.saveIdToken(userId, idToken);
  }

  // Create the charge with the current token
  return integrationApi.post('/payment-request', {
    referenceId: 'cuota-feb-2025',
    amount,
    description: 'Cuota mensual',
    dueDates: ['2025-02-15'],
    sendWhatsappNotification: true,
    sendEmailNotification: true,
    customer: {
      name: 'Juan Pérez',
      phoneNumber: { countryCode: '+54', number: '1112345678' },
      email: '[email protected]',
    },
  }, { headers: { Authorization: `Bearer ${idToken}` } });
}
El redirect_uri solo importa en la vinculación inicial. En el flujo recurrente nunca abrís el navegador — es todo server-side.

Sedes (tenants)

Una integración puede subdividirse en sedes: unidades distintas —sucursales, locales, franquicias— cada una con su propio webhook, para que cada pago llegue solo a la sede que lo generó. No es un actor aparte: las sedes las administra el mismo admin de integración.

Ver la guía Multi-sede →

Modelos de integración

Antes de escribir código, elegí cómo se mapea tu producto contra Chytapay. Hay dos formas de armarlo, y la elección define cuántas integraciones y webhooks vas a mantener.

Modelo plataforma

1 integración + N cuentas de comercio autorizadas por OAuth + 1 webhook. Distinguís cada cobro por su referenceId. Vos orquestás todo desde un solo lugar y cada usuario final autoriza una vez con su cuenta de comercio.

Cuándo: un SaaS que cobra en nombre de muchos usuarios finales desde una misma aplicación.

Modelo distribuido

1 integración por sistema o cliente. Cada instalación tiene sus propias credenciales, su propio webhook y su propio ciclo de vida, sin nada compartido entre ellas.

Cuándo: instalaciones separadas que no comparten backend ni base de datos.

Si tenés un solo backend que cobra por muchos usuarios, usá el modelo plataforma: una integración, un webhook y el referenceId como llave para separar cada cobro. Si cada cliente corre su propia instalación aislada, usá el modelo distribuido: una integración por cada una, así nunca se cruzan credenciales ni eventos.

Rate limits

La Integration API limita cuántos payment requests podés crear por día, por integración y por cuenta de comercio.

  • Tope: 200 creaciones de payment request por día (POST /payment-request y POST /payment-request/bulk).
  • Una llamada a POST /payment-request/bulk cuenta como UNA sola request, sin importar cuántos items tenga el batch (1 a 100). No se cobra 1 request por item.
  • El contador es por integración + cuenta de comercio, y se reinicia a las 00:00 hora Argentina.
  • Al superar el tope, la respuesta es 429 con { message, availableAt, maxRequestsPerDay } — availableAt es la fecha/hora (Argentina) desde la que podés volver a crear cobros.
Si necesitás crear muchos cobros, usá POST /payment-request/bulk: agrupa hasta 100 payment requests en una sola request contra tu tope diario. Ver la sección Cobros masivos (bulk) en Crear cobros.

Glosario

TérminoQué es
IntegraciónTu empresa o producto conectado a Chytapay; habla con la Integration API con clientId/clientSecret.
Cuenta de comercio (CVU)El usuario Chytapay que recibe los pagos; su CVU es la caja de banco a la que entra la plata.
Admin del portalLa persona de tu equipo que administra la integración desde el Portal (webhook, redirect URIs, sedes).
Pagador (customer)Quien paga el cobro. Sus datos van en el campo customer del payment request.
Sede / tenantCada unidad de negocio de una integración, con su propio webhook, para aislar los avisos por sucursal.
referenceIdTu llave de reconciliación para cada cobro; única y elegida por vos.
WebhookLa URL tuya a la que Chytapay hace POST cuando cambia el estado de un cobro.