Accept payments on any website.
One function call, one event listener. No SDK to install, no API key, no backend required.
Detect the wallet
Fire Wallet injects a JavaScript API into every page. Check for it before requesting a payment.
if (window.fire?.installed) {
console.log('Fire Wallet v' + window.fire.version);
}
window.fire.installed is true when the extension is active. The API object is frozen and tamper-proof — no page script can overwrite it.
Because content scripts may load after your page, poll for the wallet if you need it on page load.
function waitForWallet(maxMs) {
if (window.fire?.installed)
return Promise.resolve(true);
return new Promise(function(resolve) {
var elapsed = 0;
var timer = setInterval(function() {
elapsed += 100;
if (window.fire?.installed) {
clearInterval(timer);
resolve(true);
} else if (elapsed >= (maxMs || 3000)) {
clearInterval(timer);
resolve(false);
}
}, 100);
});
}
The default timeout is 3 seconds. On slow connections or cold extension starts, the content script may take a moment to inject.
Pay button (2 minutes)
The shortest possible integration. One function call, one event listener.
<button id="pay">Pay 1 eUSD</button>
<script>
document.getElementById('pay').addEventListener('click', async () => {
const result = await window.fire.requestPayment({
recipientB58: '3Fhv7x...',
amount: '1000000',
tokenId: 1,
memo: 'Coffee',
merchantRef: 'order-42'
});
if (result.ok) {
console.log('Intent registered:', result.intentId);
} else {
console.error(result.error);
}
});
window.addEventListener('fire-payment-update', (e) => {
const { intentId, status, txId, merchantRef } = e.detail;
if (status === 'confirmed') {
showReceipt(txId);
}
});
</script>
That is a complete checkout. The wallet handles approval UI, transaction signing, and on-chain confirmation.
Payment API
window.fire.requestPayment(request)
Registers a payment intent and opens the wallet approval screen.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
recipientB58 |
string |
Yes* | MobileCoin b58 address |
amount |
string |
Yes | Amount in smallest unit (picoMOB or micro-eUSD) |
tokenId |
number |
No | 0 = MOB (default), 1 = eUSD |
memo |
string |
No | Payment memo shown to the user |
merchantRef |
string |
No | Your order/payment reference (enables idempotency) |
resolveUrl |
string |
Yes* | HTTPS endpoint for server-side address resolution |
intentToken |
string |
Yes* | Opaque token for your resolve endpoint |
*Either recipientB58 or resolveUrl + intentToken is required. If both are present, recipientB58 takes precedence. For the resolve protocol, see the Resolve Protocol page.
Returns: Promise<{ ok: boolean; intentId?: string; error?: string }>
Timeout: 10 seconds. If the wallet does not respond, returns { ok: false, error: 'Fire Wallet did not respond' }.
Units
| Token | Smallest Unit | Decimals | Example |
|---|---|---|---|
| MOB | picoMOB | 12 | 1000000000000 = 1 MOB |
| eUSD | micro-eUSD | 6 | 1000000 = 1 eUSD |
picoMOB conversion helper
This function converts decimal MOB values to picoMOB strings without floating point errors.
function mobToPico(mob) {
const parts = String(mob).split('.');
const whole = parts[0] || '0';
const frac = (parts[1] || '').padEnd(12, '0').slice(0, 12);
let raw = whole + frac;
raw = raw.replace(/^0+/, '') || '0';
return BigInt(raw).toString();
}
mobToPico(0.01) // → '10000000000'
Payment events
fire-payment-update
Fired on window whenever a payment intent changes status.
window.addEventListener('fire-payment-update', (e) => {
const { intentId, status, txId, merchantRef } = e.detail;
});
Status lifecycle
pending → confirming → confirmed
│ │
▼ ▼
rejected failed
| Status | Meaning |
|---|---|
pending |
Intent registered, awaiting user approval |
confirming |
User approved, transaction submitted to network |
confirmed |
Transaction settled on-chain. txId is set. |
rejected |
User declined the payment |
failed |
Transaction failed (network error, insufficient funds, resolve failure) |
fire-payment-registered
Fired when a meta-tag payment is detected and registered.
window.addEventListener('fire-payment-registered', (e) => {
const { intentId, merchantRef } = e.detail;
});
Meta tag payments
For static pages or server-rendered checkout, embed a <meta> tag instead of calling JavaScript.
<meta
name="mobilecoin-payment"
content="mob://pay?to=3Fhv7x...&amount=1000000&token_id=1&memo=Coffee&ref=order-42"
/>
The wallet detects the tag automatically. A MutationObserver watches for dynamically added tags, so this works with single-page apps too.
URI format
mob://pay?to=<b58-address>&amount=<smallest-unit>&token_id=<0|1>&memo=<text>&ref=<merchant-ref>
| Parameter | Required | Description |
|---|---|---|
to |
Yes | MobileCoin b58 address |
amount |
Yes | Amount in smallest unit |
token_id |
No | 0 = MOB, 1 = eUSD |
memo |
No | Payment memo |
ref |
No | Merchant reference ID |
UX patterns
Three patterns worth keeping in any payment integration.
Cooldown after payment
After a payment completes (success, reject, or fail), disable the button for 3 seconds to prevent accidental double-payments.
function resetButton(btn) {
btn.disabled = true;
setTimeout(() => {
btn.disabled = false;
btn.textContent = 'Pay 1 eUSD';
}, 3000);
}
Timeout
If the user walks away or the wallet stops responding, a 2-minute timeout catches it. Use a settled flag to prevent the timeout from firing after a successful payment.
Listener cleanup
Remove event listeners after they fire. If the timeout fires first, remove both the message and fire-payment-update listeners.