Integration Options
This guide explains the available integration methods for Peer and their trade-offs.
Supported: Full-Page Redirect
The primary integration method is a full-page redirect to the hosted checkout page. This is similar to Stripe Checkout.
import { createCheckoutSession } from '@zkp2p/pay-sdk';
const response = await createCheckoutSession(params, opts);
// Redirect the customer to checkout
window.location.href = response.checkoutUrl;
How It Works
- Your backend creates a checkout session
- Customer is redirected to
pay.zkp2p.xyz/checkout?session=... - Customer completes payment on the hosted checkout page
- Customer is redirected back to your
successUrlorcancelUrl
Benefits
- Zero frontend code required - Just redirect to the URL
- Always up-to-date - Automatically gets new features and payment platforms
- PCI/security compliant - Payment handling is fully hosted
- Browser extension integration - Works seamlessly with PeerAuth extension
Redirect Methods
// Option 1: Full-page redirect (recommended)
window.location.href = response.checkoutUrl;
// Option 2: Use SDK helper
import { redirectToCheckout } from '@zkp2p/pay-sdk';
redirectToCheckout(session.id, sessionToken, opts);
Supported: New Tab Checkout
If you want to keep your page visible while the customer completes checkout, open the checkout in a new tab:
// Open checkout in new tab - keeps your page in the background
window.open(response.checkoutUrl, '_blank');
How It Works
- Your page stays open in the original tab
- Checkout opens in a new browser tab
- Customer completes payment in the new tab
- On success/cancel, the new tab redirects to your
successUrl/cancelUrl - Customer can return to your original tab
Benefits
- Original page preserved - Customer doesn't lose their place
- Cart state maintained - No need to restore form data after redirect
- Less disruptive UX - Customer can switch between tabs
Example
const response = await createCheckoutSession({
merchantId: 'merchant_123',
amountUsdc: '50.00',
destinationChainId: 8453,
recipientAddress: '0xYourWallet',
// These URLs open in the new tab after checkout
successUrl: 'https://yoursite.com/payment/success',
cancelUrl: 'https://yoursite.com/payment/cancelled',
}, opts);
// Open in new tab when user clicks checkout button
document.getElementById('checkout-btn').onclick = () => {
window.open(response.checkoutUrl, '_blank');
};
Try this integration in the SDK Demo App - click "Open in New Tab" to see how it works.
Supported: Embedded Checkout (Iframe)
Embedded checkout works directly inside an iframe:
- Your app renders checkout in an iframe with
?embed=true - Checkout runs session/start/payment/proof flow inside the iframe
- Checkout reports completion using
postMessageevents (checkout.success/checkout.failed)
Iframe Integration Notes
- Use iframe sandbox attributes that still allow checkout + extension flow:
allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox
- In embedded mode, parent pages should handle completion events and decide navigation UX
Embedded Iframe Example (React)
import { useEffect, useRef } from 'react';
import {
EMBED_EVENT_CHANNEL,
ensureEmbedModeUrl,
} from '@zkp2p/pay-sdk/embedded';
export function EmbeddedCheckout({ checkoutUrl }: { checkoutUrl: string }) {
const iframeRef = useRef<HTMLIFrameElement | null>(null);
useEffect(() => {
const onMessage = (event: MessageEvent) => {
if (event.source !== iframeRef.current?.contentWindow) return;
if (!event.data || event.data.channel !== EMBED_EVENT_CHANNEL) return;
if (event.data.type === 'checkout.success') {
// Parent success handling (UI update, redirect, analytics, etc.)
}
if (event.data.type === 'checkout.failed') {
// Parent failure handling
}
};
window.addEventListener('message', onMessage);
return () => window.removeEventListener('message', onMessage);
}, []);
return (
<iframe
ref={iframeRef}
title="Embedded Peer Checkout"
src={ensureEmbedModeUrl(checkoutUrl)}
sandbox="allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox"
/>
);
}
For a complete integration example, see:
apps/demo/src/pages/DemoPage.tsx
Try this integration in the SDK Demo App - click "Open Embedded" to launch the embedded checkout flow.
Recommended Integration Pattern
For the best user experience, use redirects with proper success/cancel handling:
// 1. Create session on your backend
const response = await createCheckoutSession({
merchantId: 'merchant_123',
amountUsdc: '50.00',
destinationChainId: 8453,
recipientAddress: '0xYourWallet',
successUrl: 'https://yoursite.com/payment/success?session_id={SESSION_ID}',
cancelUrl: 'https://yoursite.com/payment/cancelled',
}, opts);
// 2. Store session ID in your database for tracking
await db.orders.update({
where: { id: orderId },
data: { checkoutSessionId: response.session.id }
});
// 3. Redirect customer
window.location.href = response.checkoutUrl;
Handling Success/Cancel
When the customer returns to your site:
// On your success page
const sessionId = new URLSearchParams(window.location.search).get('session_id');
// Verify payment status via webhook or API
// Don't trust the redirect alone - always verify server-side
Embedded Event Contract
When checkout runs with ?embed=true, it emits parent-window events on channel zkp2p_checkout_embed_v1:
checkout.successcheckout.failed
Parent pages should listen for these events and then render success/failure UI or perform navigation.