Skip to main content

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

  1. Your backend creates a checkout session
  2. Customer is redirected to pay.zkp2p.xyz/checkout?session=...
  3. Customer completes payment on the hosted checkout page
  4. Customer is redirected back to your successUrl or cancelUrl

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

  1. Your page stays open in the original tab
  2. Checkout opens in a new browser tab
  3. Customer completes payment in the new tab
  4. On success/cancel, the new tab redirects to your successUrl/cancelUrl
  5. 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');
};
Demo App

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:

  1. Your app renders checkout in an iframe with ?embed=true
  2. Checkout runs session/start/payment/proof flow inside the iframe
  3. Checkout reports completion using postMessage events (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
Demo App

Try this integration in the SDK Demo App - click "Open Embedded" to launch the embedded checkout flow.


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.success
  • checkout.failed

Parent pages should listen for these events and then render success/failure UI or perform navigation.