Documentation Index
Fetch the complete documentation index at: https://docs.swiftpay.finance/llms.txt
Use this file to discover all available pages before exploring further.
The Embedded iFrame integration embeds SwiftPay’s checkout directly into your webpage. Users never leave your page, and you can customize the surrounding UX while maintaining full payment security.
When to Use
- Seamless experience — Users stay on your domain
- Custom checkout flow — Control the page layout and styling
- Single-page apps — Integrate without page redirects
- Cart checkout — Add checkout to a cart summary page
- Modal overlays — Display checkout in a modal dialog
- White-label — Branded checkout experience
How It Works
- Create invoice on your backend
- Embed iframe in your page with the invoice ID
- User completes payment in the iframe
- Listen for events — iFrame sends payment status via postMessage
Implementation
Step 1: Create Invoice
Create an invoice on your backend:
import { SwiftPay } from '@swiftpayfi/api-client';
const client = new SwiftPay({
secretKey: process.env.SWIFTPAY_SECRET_KEY,
});
const { invoice } = await client.invoices.create({
amount: '100.00',
token: 'USDC',
network: 'ethereum',
recipients: { evm: '0xYourWallet...' },
externalRef: 'order_12345',
metadata: {
userId: 'user_456',
productId: 'product_789',
},
});
// Return invoice ID to frontend
res.json({ invoiceId: invoice.id });
Step 2: Embed iFrame
Insert the checkout iframe in your HTML:
<div id="checkout-container">
<iframe
src="https://pay.swiftpay.finance/inv_xxxxxxxxxxxxx?embed=true"
style="width: 100%; height: 600px; border: none; border-radius: 8px;"
allow="clipboard-read clipboard-write"
sandbox="allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox"
></iframe>
</div>
Step 3: Listen for Events
Handle payment status events via postMessage:
window.addEventListener('message', (event) => {
// Verify origin (security)
if (event.origin !== 'https://pay.swiftpay.finance') {
return;
}
const { type, data } = event.data;
if (type === 'checkout.ready') {
console.log('✅ Checkout loaded');
}
if (type === 'payment.pending') {
console.log('⏳ Payment initiated:', data.invoiceId);
showSpinner();
}
if (type === 'payment.completed') {
console.log('✅ Payment completed:', data.invoiceId);
showConfirmation();
fulfillOrder(data.invoiceId);
}
if (type === 'payment.failed') {
console.error('❌ Payment failed:', data.error);
showError(data.error);
}
if (type === 'checkout.close') {
console.log('User closed checkout');
closeModal();
}
});
Step 4: Verify Payment
Verify payment completion on your backend:
// After receiving payment.completed event
const invoice = await client.invoices.get(invoiceId);
if (invoice.status === 'paid' || invoice.status === 'completed') {
// Payment confirmed
await fulfillOrder(invoiceId);
res.json({ success: true });
}
React Integration
The Checkout SDK includes a React hook for iFrame integration:
import { useSwiftPayCheckout } from '@swiftpayfi/checkout-sdk/react';
export function CheckoutPage({ product }) {
const {
createInvoice,
open,
isLoading,
session,
error,
} = useSwiftPayCheckout({
key: 'pk_live_xxx',
token: 'USDC',
chains: ['ethereum'],
mode: 'iframe',
sandbox: false,
onSuccess: ({ invoice }) => {
console.log('Payment complete:', invoice);
showConfirmation();
},
});
const handleCheckout = async () => {
const session = await createInvoice({
amount: product.price.toString(),
reference: product.id,
metadata: { productId: product.id },
});
if (session) {
await open(); // Opens iframe modal
}
};
return (
<div>
<h2>{product.name}</h2>
<p>${product.price}</p>
<button onClick={handleCheckout} disabled={isLoading}>
{isLoading ? 'Loading...' : 'Pay Now'}
</button>
{error && <p style={{ color: 'red' }}>{error.message}</p>}
</div>
);
}
PostMessage Events
Outbound Events (from iframe to your page)
interface CheckoutEvent {
type: 'checkout.ready' | 'payment.pending' | 'payment.completed'
| 'payment.failed' | 'checkout.close';
data?: {
invoiceId?: string;
status?: 'pending' | 'partial' | 'paid' | 'completed';
error?: string;
transactionHash?: string;
chain?: string;
};
}
Event Details
| Event | Meaning | Next Step |
|---|
checkout.ready | Iframe loaded | Show checkout UI |
payment.pending | User confirmed payment | Show spinner, poll API |
payment.completed | Payment confirmed on-chain | Fulfill order, show confirmation |
payment.failed | Payment failed or rejected | Show error message, allow retry |
checkout.close | User clicked close/back | Clean up modal/UI |
Modal Checkout
Combine iframe with a modal for a better UX:
function CheckoutModal({ invoiceId, onClose, onSuccess }) {
const [status, setStatus] = useState('pending');
useEffect(() => {
const handleMessage = (event) => {
if (event.origin !== 'https://pay.swiftpay.finance') return;
const { type, data } = event.data;
if (type === 'payment.completed') {
setStatus('success');
setTimeout(() => {
onSuccess(data.invoiceId);
onClose();
}, 2000);
}
if (type === 'payment.failed') {
setStatus('error');
}
if (type === 'checkout.close') {
onClose();
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h2>Complete Payment</h2>
<button onClick={onClose}>✕</button>
</div>
{status === 'pending' && (
<iframe
src={`https://pay.swiftpay.finance/${invoiceId}?embed=true`}
style={{ width: '100%', height: '600px', border: 'none' }}
/>
)}
{status === 'success' && (
<div className="success-message">
<p>✅ Payment successful!</p>
</div>
)}
{status === 'error' && (
<div className="error-message">
<p>❌ Payment failed. Please try again.</p>
<button onClick={() => setStatus('pending')}>Retry</button>
</div>
)}
</div>
</div>
);
}
Security Considerations
Origin Verification
Always verify the origin of postMessage events:
window.addEventListener('message', (event) => {
if (event.origin !== 'https://pay.swiftpay.finance') {
console.warn('Unexpected origin:', event.origin);
return; // Ignore
}
// Process event
});
Sandbox Attributes
Use appropriate sandbox attributes to restrict iframe capabilities:
<iframe
src="https://pay.swiftpay.finance/inv_xxx?embed=true"
sandbox="
allow-same-origin
allow-scripts
allow-popups
allow-popups-to-escape-sandbox
"
></iframe>
This allows:
- ✅ Scripts and popups (for wallet connections)
- ❌ Form submission, plugins, top-level navigation
- ❌ Access to your page’s cookies/localStorage
HTTPS Only
Always use HTTPS in production. Mixed content (HTTP iframe in HTTPS page) is blocked by browsers.
Styling & Customization
The checkout UI is fixed, but you can customize the surrounding page:
#checkout-container {
max-width: 500px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
border-radius: 12px;
}
iframe {
width: 100%;
height: 600px;
border: none;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
Fallback Handling
Handle browsers that don’t support postMessage (rare):
// Fallback: poll API for payment status every 2 seconds
let pollCount = 0;
const pollInterval = setInterval(async () => {
const invoice = await fetch(`/api/invoices/${invoiceId}`).then(r => r.json());
if (invoice.status === 'completed') {
clearInterval(pollInterval);
fulfillOrder(invoiceId);
}
if (pollCount++ > 300) { // 10 minutes
clearInterval(pollInterval);
showTimeoutError();
}
}, 2000);
Comparison
See Integration Options for a comparison of iFrame vs. other integration methods.