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).