Cómo se integró esta tienda
Tres piezas en el servidor de la tienda. La llave API nunca va al navegador.
1. Crear la orden, verificarla y mandar al cliente a pagar
La tienda recalcula la dirección de cada orden con su propia wallet de cobro: si alguien comprometiera Sadaya y devolviera otra dirección, la tienda no manda al cliente a pagar.
// En el servidor de la tienda, al hacer clic en pagar:
const r = await fetch('https://staging.sadaya.app/v1/orders', {
method: 'POST',
headers: { Authorization: 'Bearer ' + SADAYA_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: '15', currency: 'USD', // se paga con USDT, USDC, ETH o BNB
external_id: pedido.id, // el mismo id nunca crea dos cobros
redirect_url: 'https://tu-tienda.com/gracias/' + pedido.id,
}),
})
const orden = await r.json()
// orden.deposit_address: la dirección única de esta compra. Antes de mandar a pagar, la tienda la
// recalcula con SU wallet de cobro y SU comisión (de su configuración, nunca de la API):
verifyOrder(orden, { payout: MI_WALLET_DE_COBRO, feeBps: 100 }) // de @sadaya/node/verify; si no cuadra, lanza
return Response.redirect(orden.checkout_url, 303) // checkout con QR por red
Checkout embebido: pagar sin salir de la tienda
La página de pago de esta tienda es suya: el script de Sadaya va con su huella (integrity), así que si alguien lo cambiara el navegador no lo ejecuta, y recibe la dirección que el servidor ya verificó: si Sadaya respondiera otra, el checkout no muestra dónde pagar. La entrega se decide con el aviso firmado, nunca con el evento del navegador.
<!-- En la página de pago de la tienda: la orden la creó y la verificó el servidor -->
<div id="pago"></div>
<script src="https://staging.sadaya.app/embed/sadaya-2.0.0-f053eefc8d.js"
integrity="sha384-Rg5d0UqKXloBNeHOCuNpfqPGABT+glXV10MsBMZVPj5UjWDj4xACch2S1YYIDrye" crossorigin="anonymous"></script>
<script>
Sadaya.mount('#pago', {
order: ORDEN_ID, // orden.id
expectedAddress: DIRECCION, // orden.deposit_address, ya verificada con verifyOrder
onEvent: function (e) { if (e.type === 'order.paid') location.href = '/gracias/' + PEDIDO },
})
</script>
2. Recibir el aviso firmado y entregar
// POST /webhooks/sadaya: llega cuando cambia la orden
const raw = await request.text()
const [, t, v1] = request.headers.get('X-Sadaya-Signature').match(/t=(\d+),v1=(\w+)/)
const ok = hmacSha256Hex(SADAYA_WEBHOOK_SECRET, t + '.' + raw) === v1 // y que t sea reciente
if (!ok) return new Response('firma inválida', { status: 401 })
const evento = JSON.parse(raw)
if (evento.type === 'order.paid') entregarProducto(evento.data.external_id)
3. Probar sin dinero
Con la llave de prueba (sk_test_) las órdenes no tocan la blockchain: el checkout trae botones para simular el pago completo, uno incompleto o uno por la red equivocada, y llegan los mismos avisos con livemode: false.
4. Mostrar el resultado
La página de gracias consulta GET /v1/orders/{id} por si el aviso tarda. Otros avisos: order.confirming, order.underpaid, order.expired, order.misdirected (pago por otra red o moneda: lo resuelve Sadaya).