Actores
Hay dos actores en cada integración con Chytapay. Distinguirlos es clave para evitar errores.
| Rol | Quién es | Qué hace |
|---|
| Administrador de integración | Vos — 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 comercio | El 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.