Accept MobileCoin payments

Add a Buy Now button to your website. Three lines of code. No backend required.

Quick start

Copy this into any HTML page. It works immediately if the customer has Kyoto installed.

HTML + JavaScript
 1<button id="buy-btn">Buy Now — 1 MOB</button>
 2
 3<script>
 4document.getElementById('buy-btn').addEventListener('click', async () => {
 5  // Check if Kyoto wallet is installed
 6  if (!window.kyoto?.installed) {
 7    alert('Please install Kyoto wallet to pay with MobileCoin');
 8    window.open('https://kyoto.antelopeswap.com', '_blank');
 9    return;
10  }
11
12  // Request payment
13  const result = await window.kyoto.requestPayment({
14    recipientB58: 'YOUR_MOB_ADDRESS_HERE',
15    amount: '1000000000000',  // 1 MOB in picoMOB
16    memo: 'Purchase #1234'
17  });
18
19  if (result.ok) {
20    // Payment approved — listen for confirmation
21    window.addEventListener('kyoto-payment-update', (e) => {
22      if (e.detail.status === 'confirmed') {
23        document.getElementById('buy-btn').textContent = 'Paid ✓';
24      }
25    });
26  }
27});
28</script>

Try it

Live demo

How it works

The payment flow happens entirely in the browser. No server integration needed.

01
Customer clicks
Your "Buy Now" button calls requestPayment()
02
Kyoto popup opens
Shows amount, memo, and your verified domain
03
Customer approves
Authenticates with PIN or biometric
04
Payment confirmed
Your page gets a callback in ~5 seconds

API Reference

The complete window.kyoto payment API.

Detection

JavaScript
1// Check if Kyoto is installed
2const hasKyoto = typeof window.kyoto !== 'undefined'
3  && window.kyoto.installed === true;

window.kyoto.requestPayment(request)

Request a MobileCoin payment from the user. Opens the Kyoto approval popup.

Parameters

ParameterTypeRequiredDescription
recipientB58 string Yes Your MobileCoin b58 address where funds will be sent
amount string Yes Amount in picoMOB. 1 MOB = 1,000,000,000,000 picoMOB
memo string No Memo attached to the transaction (visible to sender and recipient)
merchantRef string No Your reference ID. Used for idempotency — duplicate requests with the same ref from the same tab are deduplicated

Returns: Promise<{ ok: boolean, intentId?: string, error?: string }>

JavaScript
1const result = await window.kyoto.requestPayment({
2  recipientB58: 'b58addr...',
3  amount: '5000000000000',    // 5 MOB
4  memo: 'Order #12345',
5  merchantRef: 'order-12345'
6});
7
8if (result.ok) {
9  console.log('Payment intent:', result.intentId);
10} else {
11  console.error('Payment failed:', result.error);
12}

Events

JavaScript
1// Payment status updates
2window.addEventListener('kyoto-payment-update', (event) => {
3  const { intentId, status, txId } = event.detail;
4  // status: 'confirmed' | 'rejected' | 'failed' | 'expired'
5});
6
7// Intent registered (fires after requestPayment resolves)
8window.addEventListener('kyoto-payment-registered', (event) => {
9  const { intentId } = event.detail;
10});

Status lifecycle

StatusMeaningWhen it fires
confirmedPayment confirmed on-chain~5–10 seconds after approval
rejectedUser declined in the popupImmediately
failedTransaction failed (network error, insufficient funds)Immediately
expiredTab was closed or navigated awayOn tab close

Meta tag method

For sites that prefer declarative payment requests:

HTML
1<meta name="mobilecoin-payment"
2      content="mob://pay?to=b58addr&amount=1000000000000&memo=Order+12345&ref=order-12345">

Kyoto detects these tags automatically via MutationObserver. Works with SPAs that add meta tags dynamically.

Amount conversion

MobileCoin amounts are specified in picoMOB (1012 picoMOB = 1 MOB).

MOBpicoMOBString value
0.0110,000,000,000"10000000000"
0.1100,000,000,000"100000000000"
11,000,000,000,000"1000000000000"
1010,000,000,000,000"10000000000000"
100100,000,000,000,000"100000000000000"
JavaScript
1function mobToPico(mob) {
2  return String(BigInt(Math.round(mob * 1e12)));
3}
4
5// Usage
6const fiveMob = mobToPico(5);  // "5000000000000"

Always pass amounts as strings, not numbers. JavaScript numbers lose precision above 253. picoMOB values for amounts over ~9 MOB exceed this limit.

Complete examples

Simple donation button

HTML + JavaScript
 1<button onclick="donate()">Donate 1 MOB</button>
 2<p id="status"></p>
 3
 4<script>
 5async function donate() {
 6  const status = document.getElementById('status');
 7
 8  if (!window.kyoto?.installed) {
 9    status.textContent = 'Please install Kyoto wallet';
10    return;
11  }
12
13  status.textContent = 'Opening wallet...';
14
15  const result = await window.kyoto.requestPayment({
16    recipientB58: 'YOUR_ADDRESS',
17    amount: '1000000000000',
18    memo: 'Donation'
19  });
20
21  if (result.ok) {
22    status.textContent = 'Confirming...';
23    window.addEventListener('kyoto-payment-update', (e) => {
24      if (e.detail.status === 'confirmed') {
25        status.textContent = 'Thank you for your donation!';
26      } else if (e.detail.status === 'failed') {
27        status.textContent = 'Transaction failed. Please try again.';
28      }
29    }, { once: true });
30  } else {
31    status.textContent = result.error || 'Payment cancelled';
32  }
33}
34</script>

E-commerce checkout with order tracking

JavaScript
 1async function checkout(orderId, amount, description) {
 2  if (!window.kyoto?.installed) {
 3    window.location.href = 'https://kyoto.antelopeswap.com';
 4    return;
 5  }
 6
 7  const result = await window.kyoto.requestPayment({
 8    recipientB58: STORE_MOB_ADDRESS,
 9    amount: mobToPico(amount),
10    memo: description,
11    merchantRef: orderId  // Prevents double-payment on refresh
12  });
13
14  if (!result.ok) {
15    showError(result.error);
16    return;
17  }
18
19  showStatus('Payment submitted. Waiting for confirmation...');
20
21  window.addEventListener('kyoto-payment-update', async (e) => {
22    const { status, txId } = e.detail;
23
24    switch (status) {
25      case 'confirmed':
26        showStatus('Payment confirmed!');
27        // Notify your backend
28        await fetch('/api/orders/' + orderId + '/paid', {
29          method: 'POST',
30          headers: { 'Content-Type': 'application/json' },
31          body: JSON.stringify({ txId, amount, currency: 'MOB' })
32        });
33        window.location.href = '/order/' + orderId + '/confirmation';
34        break;
35
36      case 'failed':
37        showError('Payment failed. Please try again.');
38        break;
39
40      case 'rejected':
41        showStatus('Payment cancelled.');
42        break;
43    }
44  }, { once: true });
45}

Detecting Kyoto and adapting UI

JavaScript
 1// Adapt UI based on wallet availability
 2function initPaymentOptions() {
 3  const kyotoButton = document.getElementById('pay-with-kyoto');
 4  const installBanner = document.getElementById('kyoto-install');
 5
 6  if (window.kyoto?.installed) {
 7    kyotoButton.style.display = 'block';
 8    installBanner.style.display = 'none';
 9  } else {
10    kyotoButton.style.display = 'none';
11    installBanner.style.display = 'block';
12  }
13}
14
15window.addEventListener('DOMContentLoaded', initPaymentOptions);

Security

FAQ

What does the user see?
A Kyoto popup showing your domain name, the amount, the memo, their current balance, and the network fee. They approve with their PIN or biometric (Touch ID / Face ID).
How fast is confirmation?
MobileCoin blocks are ~5 seconds. The confirmed event fires as soon as the transaction is included in a block.
Are transactions reversible?
No. MobileCoin transactions are final and irreversible once confirmed.
What if the user doesn't have Kyoto?
Check window.kyoto?.installed. If false, show an install link or offer an alternative payment method.
Is there a fee?
The MobileCoin network fee is 0.0004 MOB (less than $0.01). There is no Kyoto processing fee.
Can I verify payments server-side?
Not directly through Kyoto. Your backend would need to monitor the MobileCoin blockchain or trust the frontend confirmation callback. For high-value transactions, server-side verification is recommended.
Does this work on mobile?
Kyoto is currently a Chrome desktop extension. Mobile support is planned.