Saltar al contenido
ESENIr al Portal

Conectar comercios

Cómo una cuenta de comercio autoriza a tu integración con OAuth, y cómo usar, renovar y revocar sus tokens.

Al terminar tenés el idToken de cada comercio vinculado, para crear cobros a su nombre, y sabés renovarlo, revocarlo y desvincular.

El flujo OAuth entre tu backend, el navegador del comercio y la Auth API.

  1. Tu backend redirige a GET /integration/oauth2/authorize con un state aleatorio.
  2. El comercio inicia sesión en Chytapay y autoriza.
  3. La Auth API vuelve a tu redirect_uri con code y state.
  4. Tu backend valida el state.
  5. Tu backend manda code, clientId y clientSecret a POST /integration/oauth2/token.
  6. La Auth API responde el idToken y el refreshToken del comercio.

Antes de empezar

Todo del mismo ambiente, Sandbox o Producción:

  • El clientId y el clientSecret, en el Portal, en Credenciales. El clientSecret va solo de tu backend a la Auth API (/token, /refresh y /revoke).
  • Una redirect_uri https registrada en el Portal, en Configuración.
  • Una cuenta de comercio: la que recibe los pagos, no tu usuario de admin de la integración.

El flujo corre contra la Auth API, en /integration/oauth2/: https://auth-api.test.chytapay.com.ar en Sandbox y https://auth-api.chytapay.com.ar en Producción.

1Mandá al comercio a autorizar

En tu backend, generá un state aleatorio, guardalo en la sesión y redirigí el navegador a GET /integration/oauth2/authorize:

Parámetro Obligatorio Valor
response_type Sí code
client_id Sí El clientId de tu integración
redirect_uri Sí Una redirect_uri registrada, idéntica
state No, pero usalo siempre Aleatorio, uno por intento
scope No Por defecto, openid email profile

También se aceptan los nombres en camelCase (clientId, redirectUri, responseType), pero en código nuevo usá snake_case.

Node
import crypto from 'node:crypto';

const AUTH_API = 'https://auth-api.test.chytapay.com.ar';
const REDIRECT_URI = 'https://tuapp.com/oauth/callback';

// Handler de Express: el comercio hace clic en "Conectar con Chytapay".
app.get('/conectar-chytapay', (req, res) => {
  const state = crypto.randomBytes(32).toString('base64url');
  req.session.chytapayState = state;

  const url = new URL(`${AUTH_API}/integration/oauth2/authorize`);
  url.search = new URLSearchParams({
    response_type: 'code',
    client_id: process.env.CHYTAPAY_CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    state,
  }).toString();

  res.redirect(url.toString());
});

La pantalla de autorización muestra el nombre y el logo de tu integración, y la comisión si tenés una oferta activa. Ahí inicia sesión la cuenta de comercio, nunca el admin de la integración. Usa POST /integration/oauth2/complete-login, que tu integración no llama.

2Recibí el code y validá el state

Cuando el comercio autoriza, Chytapay redirige su navegador a tu redirect_uri:

Código
https://tuapp.com/oauth/callback?code=<code>&state=<el state que mandaste>

El state evita que un atacante vincule a tu usuario con una cuenta ajena.

  1. Compará el state de la query, que Chytapay devuelve sin cambios, con el de la sesión. Si falta o no coincide, cortá el flujo.
  2. Borrá el state de la sesión: vale para un solo intento.
  3. Cambiá el code por tokens antes de que venza (10 minutos, un solo uso).

3Cambiá el code por tokens

Desde tu backend, POST /integration/oauth2/token con grantType: "authorization_code", code, clientId, clientSecret y la misma redirectUri de /authorize:

cURL

curl -X POST https://auth-api.test.chytapay.com.ar/integration/oauth2/token \
  -H "Content-Type: application/json" \
  -d '{
    "grantType": "authorization_code",
    "code": "'"$CODE"'",
    "clientId": "'"$CHYTAPAY_CLIENT_ID"'",
    "clientSecret": "'"$CHYTAPAY_CLIENT_SECRET"'",
    "redirectUri": "https://tuapp.com/oauth/callback"
  }'

Respuesta

{
  "idToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIn0...",
  "tokenType": "Bearer",
  "expiresIn": 3600
}

El comercio queda vinculado. Guardá su refreshToken: te deja operar sin que autorice de nuevo. Si vuelve a autorizar, recibís tokens nuevos.

Nota: En Sandbox, podés saltear OAuth con un comercio de prueba. No existe en Producción.

4Confirmá qué comercio autorizó

Con el idToken recién obtenido, llamá a GET /me en la Integration API para saber a qué cuenta de comercio pertenece, y guardá su merchantUserId junto al vínculo en tu sistema:

cURL

curl https://integration-api.test.chytapay.com.ar/me \
  -H "Authorization: Bearer $ID_TOKEN"

Respuesta

{
  "merchantUserId": "3f8a1c52-7d4e-4b19-9a60-2c5e8d1f0b47",
  "email": "[email protected]",
  "fantasyName": "Papelería del Sur",
  "accountHolderName": "Del Sur Papelería S.R.L.",
  "accountHolderTaxId": "30123456781",
  "integration": { "name": "Mi integración" },
  "tenantKey": "sede-centro",
  "linkedAt": "2026-05-20T14:30:00.000Z"
}
Campo Qué es
merchantUserId El identificador de la cuenta de comercio en Chytapay. Es estable: guardalo para reconocerla.
email El mail de la cuenta de comercio.
fantasyName El nombre de fantasía del comercio.
accountHolderName El titular de la cuenta: la razón social si es persona jurídica, o nombre y apellido si es persona física.
accountHolderTaxId El CUIT o CUIL del titular, sin guiones.
integration.name El nombre de tu integración, el que ve el comercio al autorizar.
tenantKey La clave de la sede a la que está vinculada la cuenta. No viene si la cuenta no está vinculada a una sede.
linkedAt La fecha, en ISO 8601 UTC, de la primera autorización. Si el comercio se desvincula y autoriza de nuevo, no cambia.

La identidad sale del idToken: GET /me no recibe parámetros y ignora los que le mandes. Una cuenta de comercio no vinculada o desvinculada recibe 403.

Autenticá las llamadas a la API

Cada request a la Integration API lleva el idToken en Authorization:

cURL
curl https://integration-api.test.chytapay.com.ar/payment-request/factura-1001 \
  -H "Authorization: Bearer $ID_TOKEN"

Su claim aud es tu clientId y su sub, la cuenta de comercio: así sabe Chytapay a nombre de quién operás. /token no devuelve access token: usás el idToken. El token del admin de la integración da 401, y una cuenta de comercio no vinculada, 403.

Token Dura Cómo se reemplaza
code 10 minutos, un solo uso El comercio autoriza de nuevo
idToken 1 hora (expiresIn: 3600) POST /integration/oauth2/refresh
refreshToken 180 días El comercio autoriza de nuevo

Antes de que venza el idToken, o ante un 401, pedí uno nuevo con POST /integration/oauth2/refresh: grantType: "refresh_token", el refreshToken del comercio, clientId y clientSecret. El comercio no interviene. La respuesta trae solo un idToken nuevo: el refresh token no rota, seguí usando el mismo hasta que lo revoques o venza.

Revocá un refresh token

Cuando dejás de operar para un comercio, revocá su refresh token con POST /integration/oauth2/revoke:

cURL
curl -X POST https://auth-api.test.chytapay.com.ar/integration/oauth2/revoke \
  -H "Content-Type: application/json" \
  -d '{
    "token": "'"$REFRESH_TOKEN"'",
    "tokenTypeHint": "refresh_token",
    "clientId": "'"$CHYTAPAY_CLIENT_ID"'",
    "clientSecret": "'"$CHYTAPAY_CLIENT_SECRET"'"
  }'

Responde 200 sin cuerpo. tokenTypeHint es opcional y vale refresh_token. Revocar no desvincula: el comercio puede volver a autorizar. Para cortar el vínculo, desvinculalo.

Desvinculá un comercio

En el Portal, en Cuentas de comercio, usá Desvincular en la fila del comercio. Desde ahí, sus tokens reciben 403 en la Integration API hasta que el comercio autorice de nuevo. Si la cuenta ya estaba desvinculada, el Portal avisa que no está vinculada.

Atención: Desvincular no cancela los cobros pendientes. Se pueden seguir pagando y la plata llega al comercio, pero tu integración deja de verlos. Si no querés que se paguen, cancelalos antes con POST /payment-request/{referenceId}/cancel.

Sin logo, el comercio ve solo el nombre de tu integración. Subilo en el Portal, en Configuración, sección Logo. Acepta PNG, JPEG o WEBP de hasta 5 MB (no SVG), lo reduce a 512 px del lado más largo y cada subida reemplaza la anterior.

La redirect_uri no es la URL de webhook

redirect_uri URL de webhook
Para qué Recibir el code cuando un comercio autoriza Recibir los eventos de pago
Quién llega El navegador del comercio, con un GET El backend de Chytapay, con un POST
Cuándo Una vez por autorización Cada vez que pasa algo con un cobro

Configurá la URL de webhook en Webhooks.

Errores

Los errores traen code, message e id, que sirve para rastrear el caso. Los demás errores de la API están en Operación y errores.

Ver los errores de OAuth
Dónde HTTP code Causa Qué hacer
/authorize 400 InvalidRequestError Falta client_id, redirect_uri o response_type, o la redirect_uri no es idéntica (no existe redirect_uri_mismatch). Compará protocolo, dominio, path y barra final.
/authorize 401 InvalidClientError client_id de otro ambiente. Usá el de Credenciales del mismo ambiente.
Pantalla de autorización 400 InvalidGrantError "No encontramos una cuenta con ese email.": entró el admin o una cuenta de otro ambiente. Que autorice la cuenta de comercio.
Pantalla de autorización 401 InvalidCredentialsError Contraseña incorrecta. Reintentar.
/token 400 InvalidGrantError code vencido, usado o de otro clientId, o redirectUri distinta. Autorizar de nuevo con la misma redirect_uri.
/token, /refresh, /revoke 401 InvalidClientError clientId o clientSecret incorrectos. Revisá Credenciales.
/token 500 IntegrationSetupError No se completó la vinculación; el code ya se consumió. Reiniciá desde /authorize.
/refresh 400 InvalidGrantError Refresh token revocado o inválido. Autorizar de nuevo.
Integration API 401 Token ausente, vencido o que no es de una cuenta de comercio. Renová el idToken.
Integration API 403 Cuenta de comercio no vinculada o desvinculada. Autorizar.
GET /me 403 La cuenta de comercio no está vinculada a tu integración. El cuerpo es texto plano, no JSON. Autorizar.