QR Code Payments
QR code payments shine when the user’s wallet lives on a different device, typically a phone. Generate a Solana Pay QR code, let them scan it with any compatible wallet app, and the transaction goes through. This approach feels especially natural for in-person scenarios or when someone is browsing on a laptop but paying from their mobile wallet.
Two approaches
Section titled “Two approaches”1. Built-in QR code (recommended)
Section titled “1. Built-in QR code (recommended)”This renders a QR code with the Tribe logo directly into a container element you specify:
import { Tribe } from "@tribecloud/sdk";
const payment = await tribe.renderPaymentQrCode({ mint: "SOL", amount: "0.5", memo: "Order #123", container: document.getElementById("qr-container"), width: 300, // optional, default 300 height: 300, // optional, default 300});2. Raw payment URL
Section titled “2. Raw payment URL”If you’d rather use your own QR library or do something custom with the URL, grab it directly:
const payment = await tribe.getPaymentUrl({ mint: "SOL", amount: "0.5", memo: "Order #123",});// payment.paymentUrl — a solana: URL to encode as a QR code// payment.id — use to check payment status// payment.expiresAt — payment expires after 10 minutesVerify payment
Section titled “Verify payment”Once the QR code is on screen, poll verifyPayment() to find out when the user actually pays:
const status = await tribe.verifyPayment(payment.id);// status.status — "pending" | "confirmed" | "expired"// status.txSignature — Solana transaction signature (when confirmed)Complete example
Section titled “Complete example”function PayWithQR({ mint, amount, memo }) { const containerRef = useRef(null); const [payment, setPayment] = useState(null); const [status, setStatus] = useState("pending");
useEffect(() => { if (!containerRef.current) return; tribe .renderPaymentQrCode({ mint, amount, memo, container: containerRef.current }) .then(setPayment); }, [mint, amount, memo]);
// Poll for confirmation useEffect(() => { if (!payment || status !== "pending") return; const interval = setInterval(async () => { const result = await tribe.verifyPayment(payment.id); if (result.status !== "pending") { setStatus(result.status); clearInterval(interval); } }, 3000); return () => clearInterval(interval); }, [payment, status]);
if (status === "confirmed") return <p>Payment confirmed!</p>; if (status === "expired") return <p>Payment expired. Please try again.</p>;
return ( <div> <div ref={containerRef} /> <p>Scan with a Solana wallet to pay</p> </div> );}<div id="qr-container"></div><p id="qr-status">Scan with a Solana wallet to pay</p>
<script src="https://api.tribe.utopian.build/sdk.js?site=YOUR_SITE_ID" defer></script><script defer> window.addEventListener("DOMContentLoaded", async () => { const payment = await Tribe.renderPaymentQrCode({ mint: "SOL", amount: "0.5", container: document.getElementById("qr-container"), });
const statusEl = document.getElementById("qr-status"); const interval = setInterval(async () => { const result = await Tribe.verifyPayment(payment.id); if (result.status === "confirmed") { statusEl.textContent = "Payment confirmed!"; clearInterval(interval); } else if (result.status === "expired") { statusEl.textContent = "Payment expired. Please refresh."; clearInterval(interval); } }, 3000); });</script>