Ir al contenido

Quickstart

Esta guía te lleva desde cero hasta tu primera transacción en producción.

Contacta al equipo de Rivopay para recibir:

  • Tu clientId
  • Tu keyId (formato rivo_xxxxxxxxxx)
  • Instrucciones para registrar tu clave pública EC secp256k1

Paso 2 — Generar tu par de claves EC secp256k1

Sección titulada «Paso 2 — Generar tu par de claves EC secp256k1»
Ventana de terminal
# Generar clave privada EC secp256k1
openssl ecparam -name secp256k1 -genkey -noout -out private.pem
# Extraer la clave pública
openssl ec -in private.pem -pubout -out public.pem

Envía el contenido de public.pem a Rivopay.

Crea un endpoint en tu servidor que reciba POST requests:

app.post('/webhook/rivoplay', express.json(), async (req, res) => {
const eventType = req.headers['x-webhook-event'];
const payload = req.body;
const { txId, status } = payload;
switch (eventType) {
case 'payin.completed':
await confirmarPedido(txId, payload.netAmount);
break;
case 'payin.failed':
await cancelarPedido(txId);
break;
case 'payout.failed':
await notificarFalloPago(txId, payload.failureReason);
break;
case 'payout.reversed':
await procesarReversion(txId);
break;
}
res.status(200).json({ received: true });
});
const crypto = require('crypto');
const fs = require('fs');
const PRIVATE_KEY = fs.readFileSync('./private.pem', 'utf8');
const KEY_ID = 'rivo_abc123xyz';
const BASE_URL = 'https://api.rivoplay.com';
function buildAuthHeaders(method, path, query = '', bodyString = '') {
const timestamp = new Date().toISOString();
const nonce = crypto.randomBytes(16).toString('hex');
const signedData = JSON.stringify({ method, path, query, body: bodyString, timestamp, nonce });
const signature = crypto.sign('SHA256', Buffer.from(signedData), PRIVATE_KEY).toString('base64');
return {
'Content-Type': 'application/json',
'x-client-id': KEY_ID,
'x-timestamp': timestamp,
'x-nonce': nonce,
'x-signature': signature,
};
}
async function createPayIn(amount, description, txIdSource) {
const path = '/v1/br/payin/pix/instant';
const body = {
amount,
amountFormat: 'cents',
amountType: 'fixed',
expirationInSeconds: 3600,
description,
txIdSource,
};
const bodyString = JSON.stringify(body);
const response = await fetch(`${BASE_URL}${path}`, {
method: 'POST',
headers: buildAuthHeaders('POST', path, '', bodyString),
body: bodyString,
});
return response.json();
}
const result = await createPayIn(10000, 'Pedido #1234', 'ORD-20260318-001');
console.log('QR generado:', result.txId, result.copiaECola);
async function createPayOut(amount, pixKeyType, pixKeyValue) {
const path = '/v1/br/payout/pix/key';
const body = { pixKeyType, pixKeyValue, amount };
const bodyString = JSON.stringify(body);
const response = await fetch(`${BASE_URL}${path}`, {
method: 'POST',
headers: buildAuthHeaders('POST', path, '', bodyString),
body: bodyString,
});
const result = await response.json();
if (response.status === 402) {
console.error('Saldo insuficiente:', result.availableBalance);
return null;
}
return result;
}
const payout = await createPayOut(5000, 'CPF', '123.456.789-00');
// El resultado final llega via webhook: payout.completed o payout.failed

Paso 6 — Consultar el estado de una transacción

Sección titulada «Paso 6 — Consultar el estado de una transacción»
async function getPayInStatus(txId) {
const path = `/v1/br/payin/pix/${txId}`;
const response = await fetch(`${BASE_URL}${path}`, {
method: 'GET',
headers: buildAuthHeaders('GET', path),
});
return response.json();
}
Cliente en tu plataforma quiere pagar
POST /v1/br/payin/pix/instant
→ Guarda txId en tu base de datos
→ Muestra qrCode o copiaECola al cliente
Cliente paga con su app de banco
Webhook → payin.completed (header X-Webhook-Event)
→ payload incluye debtor (quien pagó) y creditor
→ Acredita el pedido en tu sistema
→ Notifica al cliente: "pago recibido"
(si no llega webhook)
GET /v1/br/payin/pix/{txId}
→ Consulta activa tras 5+ min en PENDING
→ Si sigue PENDING, el QR puede expirar
async function createPayInWithRetry(amount, description, txIdSource, maxRetries = 3) {
const idempotencyKey = crypto.randomUUID();
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const path = '/v1/br/payin/pix/instant';
const body = {
amount,
amountFormat: 'cents',
amountType: 'fixed',
expirationInSeconds: 3600,
description,
txIdSource
};
const bodyString = JSON.stringify(body);
const response = await fetch(`${BASE_URL}${path}`, {
method: 'POST',
headers: {
...buildAuthHeaders('POST', path, '', bodyString),
'x-idempotency-key': idempotencyKey,
},
body: bodyString,
});
if (response.status === 502 || response.status === 503) {
await sleep(Math.pow(2, attempt) * 1000);
continue;
}
return response.json();
} catch (err) {
if (attempt === maxRetries) throw err;
await sleep(Math.pow(2, attempt) * 1000);
}
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}