Dos formas de integrar
Botón de Pago Sin PCI
Rediriges al cliente a una página de cobro hospedada por Payefy. Tú no capturas la tarjeta. Ideal para salir rápido y sin alcance PCI. → Ver detalle
API E-commerce Checkout propio
Tu servidor envía la tarjeta al endpoint de cobro y recibes la respuesta síncrona. Control total del checkout; requiere manejar datos de tarjeta de forma segura. → Ver detalle
Ambientes
| Ambiente | Base URL | Uso |
|---|---|---|
| Sandbox | https://sandbox.payefy.me | Pruebas de formato, headers y firma. |
| Producción | https://botpv.payefy.me | Cobros reales (comercio activado por Payefy). |
Todos los endpoints cuelgan de la base del ambiente. No hay API keys ni sesión: cada petición transaccional se autentica con la firma TKA.
Credenciales y headers
Tus 3 credenciales
commerce-id | Número de afiliación |
commerce-name | Nombre del comercio |
| token de activación | TKR — base de la firma |
Las entrega Payefy al dar de alta tu comercio (una tríada por ambiente).
Headers de la petición
Content-Type | application/json |
commerce-id | Afiliación |
commerce-name | Nombre del comercio |
X-Time | Epoch en milisegundos |
X-Auth | TKA (ver Firma) |
Firma (TKA)
El header X-Auth lleva el TKA, calculado por petición a partir de tu token (TKR), el X-Time y la afiliación:
cad1 = OR(TKR, X-Time) // byte a byte, hasta el operando más corto cad2 = XOR(cad1, afiliación) // byte a byte; el resto de cad1 queda intacto cad_b64 = base64(cad2) // bytes crudos (latin1) → base64 ASCII TKA = bcrypt(cad_b64, cost = 11) // va en el header X-Auth
X-Time es epoch en milisegundos y debe ser exactamente el mismo valor que usaste para calcular el TKA. Calcúlalo una sola vez por petición. Segundos ≠ milisegundos.cad_b64 solo supera 72 si tu TKR mide > 54 bytes (no ocurre con un token normal), y aun así ambos lados cortan igual. No pre-hashees ni recortes a mano. El hash $2y$11$… de PHP se valida sin problema (bcrypt acepta $2a/$2b/$2y).Implementación de referencia
const bcrypt = require('bcryptjs'); function orBytes(tkr, ts){ const A = Buffer.from(tkr,'latin1'), B = Buffer.from(ts,'latin1'), out = Buffer.from(A); for (let i=0; i<Math.min(A.length,B.length); i++) out[i] = A[i] | B[i]; return out; } function xorBytes(cad1, afil){ const B = Buffer.from(afil,'latin1'), out = Buffer.from(cad1); for (let i=0; i<Math.min(cad1.length,B.length); i++) out[i] = cad1[i] ^ B[i]; return out; } function makeTKA(tkr, xTime, afiliacion){ const cadB64 = xorBytes(orBytes(tkr,xTime), afiliacion).toString('base64'); return bcrypt.hashSync(cadB64, 11); // X-Auth } const xTime = String(Date.now()); // X-Time (ms) — el MISMO valor
function tka_or_bytes($tkr, $ts){
$out = $tkr; $n = min(strlen($tkr), strlen($ts));
for ($i=0; $i<$n; $i++) $out[$i] = chr(ord($tkr[$i]) | ord($ts[$i]));
return $out;
}
function tka_xor_bytes($cad1, $afil){
$out = $cad1; $n = min(strlen($cad1), strlen($afil));
for ($i=0; $i<$n; $i++) $out[$i] = chr(ord($cad1[$i]) ^ ord($afil[$i]));
return $out;
}
function make_tka($tkr, $ts, $afil){
$cad_b64 = base64_encode(tka_xor_bytes(tka_or_bytes($tkr,$ts), $afil));
return password_hash($cad_b64, PASSWORD_BCRYPT, ['cost'=>11]); // X-Auth ($2y$11$…)
}
$xTime = (string)(int)round(microtime(true)*1000); // X-Time (ms)import base64, time, bcrypt
def _or(tkr, ts):
a = bytearray(tkr.encode('latin1')); b = ts.encode('latin1')
for i in range(min(len(a), len(b))): a[i] |= b[i]
return bytes(a)
def _xor(cad1, afil):
a = bytearray(cad1); b = afil.encode('latin1')
for i in range(min(len(a), len(b))): a[i] ^= b[i]
return bytes(a)
def make_tka(tkr, x_time, afiliacion):
cad_b64 = base64.b64encode(_xor(_or(tkr, x_time), afiliacion)) # bytes ASCII
return bcrypt.hashpw(cad_b64, bcrypt.gensalt(11)).decode() # X-Auth
x_time = str(int(time.time() * 1000)) # X-Time (ms)using System; using System.Text;
static byte[] OrBytes(string tkr, string ts){
var a = Encoding.Latin1.GetBytes(tkr); var b = Encoding.Latin1.GetBytes(ts);
for (int i=0; i<Math.Min(a.Length,b.Length); i++) a[i] = (byte)(a[i] | b[i]);
return a;
}
static byte[] XorBytes(byte[] cad1, string afil){
var b = Encoding.Latin1.GetBytes(afil);
for (int i=0; i<Math.Min(cad1.Length,b.Length); i++) cad1[i] = (byte)(cad1[i] ^ b[i]);
return cad1;
}
static string MakeTKA(string tkr, string xTime, string afil){
string cadB64 = Convert.ToBase64String(XorBytes(OrBytes(tkr,xTime), afil));
return BCrypt.Net.BCrypt.HashPassword(cadB64, workFactor: 11); // X-Auth
}
string xTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(); // X-Time (ms)Vectores de prueba
Antes de tocar la API, verifica que tu cad_b64 (base64 antes del bcrypt) coincide con estos:
| TKR | X-Time | afiliación | cad_b64 esperado |
|---|---|---|---|
1Mo6p53KiDeUVa# | 1700000000000 | 8262003 | CU1JBEAFAHt5dHV1dmEj |
MiTokenDeRegistro_ABC123 | 1717171717171 | 1234567890123 | TE1GS05BSE9MR0RFSnN0cm9fQUJDMTIz |
API E-commerce
POST {base}/streampay/v1/ecommerce/charge — cobro de tarjeta no presente. Resuelve de forma síncrona: la respuesta trae el resultado (resp_code).
Cuerpo de la petición
| Campo | Tipo | Oblig. | Descripción / valor 2D |
|---|---|---|---|
isoType | String | Sí | Siempre "AGRS" al integrar vía Payefy (agregador). |
amount | Decimal (string) | Sí | Monto, p.ej. "150.00". |
currency | String | Sí | Solo "MXN" (ISO 4217 alfabético). USD no soportado en este rail. |
reference | String (12) | Sí | Numérico de 12 dígitos. Tolera ceros a la izquierda. Único por intento (idempotencia). |
cardInformation | Objeto | Sí | Ver abajo. |
holder_name | String | Sí | Sin acentos. |
card_number | Numérico (16) | Sí | 16 dígitos. |
last4 / bin | Numérico (4 / 6) | Sí | Últimos 4 y primeros 6 dígitos. |
expiration_year | Numérico (2) | Sí | YY (2 dígitos). |
expiration_month | Numérico (2) | Sí | MM (2 dígitos, con cero a la izquierda: enero = "01"). |
cvv | Numérico (3-4) | Sí | Código de verificación. |
entry_mode | String | Sí | "manual" para tarjeta no presente. |
authentication | String | Sí | "unknown" para 2D (sin 3DS). Otros: signature/pin/mail_tel/qps. |
draftCapture | String (1) | Sí | "1" = autorizar y capturar (liquidación inmediata). "0" = solo autorizar. |
tpv | Boolean | Sí | false en e-commerce (true solo en TPV física). |
periodic | Boolean | No | false salvo cargo recurrente. |
email, billingAddress, clientip | — | 3DS/AVS | Datos del tarjetahabiente para AVS/antifraude. No disparan 3DS por sí solos (ver 3D Secure). |
{
"isoType": "AGRS", "amount": "150.00", "currency": "MXN",
"reference": "423456789485",
"cardInformation": {
"holder_name": "JUAN PEREZ",
"card_number": "4110760000000008", "last4": "0008", "bin": "411076",
"expiration_year": "27", "expiration_month": "09", "cvv": "123"
},
"entry_mode": "manual", "authentication": "unknown",
"draftCapture": "1", "tpv": false, "periodic": false
}3D Secure
El cobro resuelve síncrono y devuelve resp_code. Para 2D puro: envía authentication:"unknown" y no incluyas los campos 3DS_XID / 3DS_UCAF / 3DS_ECI; tu comercio se aprovisiona con un perfil e-commerce sin 3DS. Enviar email/billingAddress/clientip es AVS/antifraude y no gatilla un challenge. Si tu comercio usa 3DS interno, entonces el cobro puede devolver una urlRedirect para el challenge.
Respuestas
Aprobada · HTTP 200
{
"request_status": true,
"resp_code": "00",
"description": "APROBADA",
"authorization": "012773",
"id": "160765279732",
"trace_id": "140679",
"binInformation": {
"bin": "557910", "bank": "SANTANDER",
"type": "DEBITO", "brand": "MASTERCARD"
}
}Declinada · HTTP 200
{
"request_status": true,
"resp_code": "05", // ≠ "00"
"description": "DECLINADA",
"id": "…", "trace_id": "…"
// sin campo "authorization"
}Error técnico (firma/formato inválidos) → HTTP ≥ 400 con request_status:false, no un resp_code de negocio.
Regla de mapeo
Éxito:resp_code === "00" (y aprobatorios 08/11/76–81/M1). · Declinada: HTTP 200 con cualquier otro resp_code. · Error técnico: HTTP ≥ 400 o request_status:false.| Campo | Qué es |
|---|---|
id | Identificador de la transacción (Numérico 12). Se usa para cancelar/reversar. Puede diferir de reference. |
authorization | Código de autorización bancario (Numérico 6). Solo en aprobadas. |
trace_id | Identificador del mensaje. |
binInformation | Datos del BIN: banco, tipo (crédito/débito), marca. No se devuelve ARN. |
resp_code / description | Resultado de la transacción (ver Códigos). |
Códigos de respuesta
Catálogo completo (ISO 8583 → descripción del gateway). Aprobatorios: 00 y 08, 11, 76–81, M1, R0–R2; el resto es declinación o error.
| Código | Descripción ISO | Descripción Gateway |
|---|
Botón de Pago
GET {base}/api/pay con parámetros en el query string. Rediriges al cliente a esa URL; Payefy hospeda la página de cobro y regresa a tu url_respuesta.
{base}/api/pay
?commerce_id={afiliación}
&commerce_name={nombre urlencoded}
&concepto={texto urlencoded}
&monto={12 dígitos en centavos} // $50.00 → 000000005000
&url_respuesta={url urlencoded}
&checksum={SHA-256}
&x_time={epoch ms}
&x_auth={TKA}SHA256("commerce_id=" + afiliación + "monto=" + monto + ".token=" + TKR). x_auth = TKA (mismo algoritmo que E-commerce).
Nota: el
checksum es exclusivo del Botón de Pago. La API E-commerce no lo usa — se autentica solo con X-Auth + X-Time en headers.Ambiente de pruebas
resp_code 05) aunque tu firma, headers y formato estén perfectos. Es el comportamiento esperado y sirve para validar tu integración extremo a extremo. Para obtener aprobaciones de prueba, solicítanos la ruta habilitada. No interpretes el 05 como que tu integración falla.Tarjetas de prueba
| Marca | Número | Exp (YY/MM) | CVV |
|---|---|---|---|
| Mastercard | 5579100155521006 | 21 / 01 | 111 |
| Visa | 4110760000000008 | 23 / 12 | 123 |
El set completo para forzar aprobada/rechazo/reembolso se entrega junto con tus credenciales de sandbox.
Operación y errores
- Timeout: conexión 10 s, respuesta 30–40 s (la autorización puede tardar 15–25 s).
- Sin reintentos automáticos por timeout (riesgo de doble cobro): ante un timeout, consulta el estado por
referenceantes de reintentar. - Idempotencia: usa un
referenceúnico por intento (derívalo de tu order-id). Un reenvío puede regresar09/94 = TRANSACCIÓN DUPLICADA. - Whitelist de IP: no se requiere del lado del comercio.
Checklist a producción
- El auto-test de vectores del TKA pasa en tu lenguaje.
X-Timeen milisegundos y reutilizado en la firma.- Manejas los 3 desenlaces: aprobada / declinada (
resp_code≠00) / error técnico (HTTP≥400). - Mapeas los
resp_codea mensajes al usuario (catálogo completo). referenceúnico por intento; timeouts configurados; sin reintentos ciegos.- Validaste en Sandbox (recuerda: E-commerce declina por defecto).
- Nos solicitaste las credenciales de Producción y la activación del comercio.
¿Dudas de integración? soporte@payefy.me. Payefy entrega bajo solicitud: la referencia del TKA en tu lenguaje, el set completo de tarjetas de prueba y ejemplos capturados de respuestas.
© Payefy · Documentación Técnica