Skip to main content
Proof tracks the full lifecycle of every transaction. Use polling or WebSocket to stay updated.

Polling

curl https://DOMAIN/widget/transactions/{merchant_transaction_id} \
  -H "Authorization: Bearer <client_token>"
Response:
{
  "merchant_transaction_id": "550e8400-e29b-41d4-a716-446655440000",
  "partner_user_id": "user-123",
  "type": "buy",
  "status": "completed",
  "currency": "USDT",
  "network": "TRC20",
  "crypto_amount": "99.50",
  "fiat_currency": "EUR",
  "fiat_amount": "100.00",
  "created_at": "2026-04-01T10:00:00Z",
  "updated_at": "2026-04-01T10:03:45Z"
}
Recommended interval: every 5–10 seconds while status is pending or processing. Stop on terminal states.

Transaction Statuses

StatusDescription
pendingPayment initiated, waiting for card processor confirmation
processingPayment confirmed, crypto being allocated and sent
completedTransaction complete. Crypto delivered (on-ramp) or fiat sent (off-ramp).
failedPayment or processing failed. User can retry with a new session.
cancelledUser cancelled inside the widget
completed, failed, and cancelled are terminal states. Stop polling when you receive one of these.

WebSocket

For real-time updates without polling, connect to the WebSocket endpoint:
const ws = new WebSocket(
  "wss://DOMAIN/ws?token=<client_token>&user_id=<partner_user_id>"
);

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.event === "tx.update") {
    console.log("Transaction:", msg.merchant_transaction_id);
    console.log("Status:", msg.status);
  }
};

ws.onclose = () => {
  // Reconnect with exponential backoff
};
Message format:
{
  "event": "tx.update",
  "merchant_transaction_id": "550e8400-...",
  "partner_user_id": "user-123",
  "type": "buy",
  "status": "completed",
  "currency": "USDT",
  "fiat_currency": "EUR",
  "fiat_amount": "100.00",
  "crypto_amount": "99.50",
  "occurred_at": "2026-04-01T10:03:45Z"
}
Use WebSocket for the best user experience in web apps. Fall back to polling if the connection drops. Always implement reconnection with exponential backoff.

Widget onStatusChange Callback

The widget also fires status updates inside the browser as the user progresses:
proofWidget.run({
  // ... other params
  onStatusChange: function(data) {
    console.log(data.status);            // widget-level status
    console.log(data.merchantTransactionId);
  }
});
data.statusMeaning
newWidget opened, user has not started payment
pendingPayment initiated
paidPayment confirmed
order_failedPayment failed (card declined, 3DS failure)
completedTransaction complete
cancelledUser closed widget before completing
onStatusChange fires inside the browser and is informational only. For authoritative business logic, use the Proof Proxy API polling or WebSocket — do not rely solely on onStatusChange.

Retry Behavior

If a payment fails, the user can retry inside the widget without requesting a new session (as long as the init_token is still valid). Each retry may produce a new provider-level transaction identifier, but from your perspective the merchant_transaction_id stays the same throughout all retry attempts.