Skip to main content
Start with Add the widget for setup, the callback payload and funding submission. Use this page when implementing your server verification.

Server contract

Your frontend’s fundOrderOnce helper coordinates your backend and connected wallet. It is not an SDK method. Keep verification, reservation, storage and public API submission on your backend; run connected-wallet calls in your frontend. Together they must:
  1. Authenticate the order owner and verify the complete instruction’s signature, expiry, widget/workspace/environment, merchant/customer identity, sender, requested amount, asset/network and beneficiary against the stored order. If bank details are collected only in the widget, bind the signed beneficiary hash on first acceptance.
  2. Atomically reserve the order and paymentRef/nonce. Resume a recorded hash; return a waiting state if a transfer is running or its result is unknown. Never approve a second broadcast for the same operation.
  3. Complete/resume any swap, then transfer the signed funding amount. Preserve the wallet operation identity for recovery. Durably save the real hash and enqueue submission to Onstacks before acknowledging the widget.
  4. Submit and track from the server even after the widget closes. Retry a submission with the same hash; reconcile an unknown broadcast before another transfer.

Wallet cancellation and uncertain outcomes

Your frontend calls reply.rejected({ reason: "Wallet request cancelled" }) only after the wallet confirms no broadcast and your backend clears the reservation. Return { outcome: "cancelled_before_broadcast" } from fundOrderOnce for this case. The SDK then permits the widget’s retry callback for the same instruction. A wallet error code alone is not proof if an earlier broadcast may have succeeded. For a timeout, unknown broadcast or failed hash recording, retain the reservation and recover the existing transaction. Do not call reply.rejected, resend tokens or repeat a completed swap.

Verify the signature

Use the environment’s widget signing secret. This helper checks the HMAC; it does not replace the order, expiry or duplicate checks above. V2 uses the fields below; V3 appends requestedAmount and currency. Create services/verifyOnstacksTransfer.js in your backend. Keep the signing secret there.
services/verifyOnstacksTransfer.js · Backend
Preserve decimal amount strings exactly. The beneficiary hash is SHA256 of JSON.stringify([bankCode, accountNumber]), preserving leading zeros. Reject unknown versions and expired instructions. HMAC verification alone does not establish order ownership or prevent duplicate transfers.

Submit funding and track payout

Your backend submission worker sends this request to the public API. Set environment to the verified instruction’s sandbox or live:
Submit funding · Backend
GET the same URL with the same Authorization header to read status. Merchants use only https://api.onstacks.io; the Onstacks backend forwards internally. No core-service access or internal service key is needed. Keep the capability private. Browser callbacks and hash submission are not payout confirmation.

Response and payment states

POST and GET return the same public envelope (example; placeholders are not credentials):
Public API response
data.payoutStatus is the provider/local payment status and may be null. Use data.status for the overall delegated flow. HTTP 200 confirms the request was handled, not that funds reached the bank. An immediate POST can already return a later state if verification progressed. Other lifecycle states (creating, provider_requested, payout_claimed) mean work is underway: wait, do not fund again. retryable permits resuming intent preparation through the widget; first reconcile any wallet broadcast in your own ledger. HTTP 409 means a conflicting funding hash: reconcile rather than replace it. On transient server errors, retry the same saved hash or poll status.