Embed Checkout
Embed DonutMe checkout directly into your website using an iframe — no SDK installation required.
Quick Start
Add this snippet anywhere in your HTML to embed a checkout session:
HTML
<iframe
src="https://donutme.xyz/pay/PLAN_ID"
width="100%"
height="700"
style="border: none; border-radius: 12px"
allow="payment; clipboard-write"
></iframe>Replace PLAN_ID with your actual payment plan ID from the dashboard.
Listening for Events
The checkout iframe communicates with the parent page via postMessage. Subscribe to these events to react to payment status changes:
JavaScript
window.addEventListener("message", (event) => {
// Always verify the origin in production
if (event.origin !== "https://donutme.xyz") return;
switch (event.data?.type) {
case "donutme:payment-completed":
console.log("Payment successful:", event.data.payload);
// { paidAmount, redirectUrl }
break;
case "donutme:checkout-session-updated":
console.log("Session changed:", event.data.payload);
// { status, selectedOptionId }
break;
case "donutme:checkout-session-terminated":
console.log("Session ended:", event.data.payload);
// { reason: "expired" | "failed" | "invalid" }
break;
}
});Event Reference
| Event | Payload | Description |
|---|---|---|
donutme:payment-completed | { paidAmount, redirectUrl } | Payment confirmed on-chain |
donutme:checkout-session-updated | { status, selectedOptionId } | Customer changed payment option or wallet |
donutme:checkout-session-terminated | { reason } | Session ended (reason: "expired" | "failed" | "invalid") |
Framework Examples
React
TSX
import { useEffect } from "react";
export function DonutMeCheckout() {
useEffect(() => {
function handler(event: MessageEvent) {
if (event.origin !== "https://donutme.xyz") return;
if (event.data?.type === "donutme:payment-completed") {
console.log("Paid:", event.data.payload);
}
}
window.addEventListener("message", handler);
return () => window.removeEventListener("message", handler);
}, []);
return (
<iframe
src="https://donutme.xyz/pay/PLAN_ID"
width="100%"
height={700}
style={{ border: "none", borderRadius: 12 }}
allow="payment; clipboard-write"
/>
);
}Vue
Vue
<script setup lang="ts">
import { onMounted, onUnmounted } from "vue";
function handler(event: MessageEvent) {
if (event.origin !== "https://donutme.xyz") return;
if (event.data?.type === "donutme:payment-completed") {
console.log("Paid:", event.data.payload);
}
}
onMounted(() => window.addEventListener("message", handler));
onUnmounted(() => window.removeEventListener("message", handler));
</script>
<template>
<iframe
src="https://donutme.xyz/pay/PLAN_ID"
width="100%"
height="700"
style="border: none; border-radius: 12px"
allow="payment; clipboard-write"
/>
</template>Customization
The embedded checkout automatically inherits the checkout appearance settings from your project (banner, cover, accent color). Configure these in:
Dashboard → Project Settings → Checkout AppearanceSecurity Notes
- Always validate
event.originagainsthttps://donutme.xyzbefore processing messages - Never trust the iframe
postMessagedata for server-side fulfillment — always verify via the Webhooks callback - The
allow="payment; clipboard-write"attribute enables wallet interactions within the iframe
