Online Checkout Integration
Accept stablecoin payments in your e-commerce checkout flow, mobile app, or any web-based payment experience.
Online checkout integration follows the same API pattern as POS terminals. The only difference is where the QR code is rendered — on a webpage or in a mobile app instead of a physical screen.
Overview
- Customer clicks “Pay with Crypto” at checkout
- Your server creates a payment intent
- Your frontend displays the QR code
- Customer scans with their mobile wallet and confirms
- Your server receives the webhook and fulfills the order
Step 1: Create Payment Intent (Server-Side)
// Your checkout API endpoint
app.post('/api/checkout/crypto', async (req, res) => {
const { orderId, amount } = req.body;
const response = await fetch('https://api.stablegenius.co/v1/payment-intents', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.STABLEGENIUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount,
currency: 'usd',
merchant_id: process.env.MERCHANT_ID,
metadata: { order_id: orderId },
ttl: 600, // 10 minute timeout for online checkout
}),
});
const paymentIntent = await response.json();
res.json({
payment_intent_id: paymentIntent.id,
qr_payload: paymentIntent.qr_payload,
qr_image_url: paymentIntent.qr_image_url,
expires_at: paymentIntent.expires_at,
});
});
Always create payment intents server-side. Never expose your API key in client-side JavaScript.
Step 2: Display QR Code (Client-Side)
<!-- Checkout page -->
<div id="crypto-payment">
<h3>Scan to pay with USDC</h3>
<img id="qr-code" alt="Payment QR Code" />
<p id="status">Waiting for payment...</p>
<p id="timer"></p>
</div>
<script>
async function startCryptoPayment(orderId, amount) {
// Create payment intent via your server
const res = await fetch('/api/checkout/crypto', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orderId, amount }),
});
const data = await res.json();
// Display QR code
document.getElementById('qr-code').src = data.qr_image_url;
// Start countdown timer
startTimer(data.expires_at);
// Poll for payment confirmation
pollForConfirmation(data.payment_intent_id);
}
async function pollForConfirmation(intentId) {
const interval = setInterval(async () => {
const res = await fetch(`/api/checkout/status?id=${intentId}`);
const { status } = await res.json();
if (status === 'confirmed') {
clearInterval(interval);
document.getElementById('status').textContent = 'Payment confirmed!';
window.location.href = '/order/success';
} else if (status === 'expired') {
clearInterval(interval);
document.getElementById('status').textContent = 'Payment expired. Please try again.';
}
}, 3000); // Poll every 3 seconds
}
</script>
For lower latency, replace polling with a WebSocket or Server-Sent Events connection. Your backend pushes the confirmation to the frontend as soon as the webhook arrives, reducing perceived wait time from 3s (polling interval) to under 100ms.
Step 3: Handle Webhook and Fulfill Order
app.post('/webhooks/stablegenius', (req, res) => {
res.status(200).json({ received: true });
const event = req.body;
if (event.type === 'payment_intent.confirmed') {
const { metadata, amount } = event.data;
// Fulfill the order
fulfillOrder(metadata.order_id, { amount });
// Send confirmation email
sendConfirmationEmail(metadata.order_id);
}
});
Mobile App Integration
For mobile apps, the same API works. Display the QR code in your app for the customer to scan with a separate wallet app, or use deep linking to open the customer’s wallet directly:
// React Native example
import { Linking } from 'react-native';
function openWalletForPayment(qrPayload) {
// Attempt to open the QR payload as a deep link
// Coinbase Wallet, MetaMask, and others handle EIP-681 URIs
Linking.openURL(qrPayload).catch(() => {
// Fallback: show QR code for manual scanning
showQRCode(qrPayload);
});
}
Shopify and WooCommerce plugins are on our roadmap. In the meantime, you can integrate via the API using a custom payment gateway. Contact us if you need help with platform-specific integration.