v0 to v1 Migration Guide
This guide covers what changed between the previous checkout integration (v0) and the current integration (v1).
Use it if your existing merchant integration already uses v0 SDK methods or v0 webhook events.
Required SDK Upgrade
Before applying the migration steps below, upgrade both packages to 1.0.0:
npm install @zkp2p/pay-sdk@1.0.0 @zkp2p/pay-shared@1.0.0
Summary
- The integration model is order-first in v1.
- The canonical SDK creation method is
createCheckout. - Fiat payments support multi-currency selection in checkout.
- Webhook events moved from dotted lowercase names to uppercase lifecycle events.
- Final order state handling should use
PAYMENT_SETTLED,PAYMENT_FAILED,PAYMENT_EXPIRED, andORDER_FULFILLED. - Crypto payout lifecycle is represented with
PAYMENT_BRIDGE_*events.
New Features in v1
1. Referral Split Support
- You can now configure referral splits directly in the checkout flow.
- Settlement processing applies referral allocations automatically.
- This makes net settlement and referral attribution easier to reconcile.
2. Partial Order Fulfillment
- Orders can now be partially settled and remain open with status
PARTIALLY_FULFILLED. - Additional payment attempts can continue until the order reaches
FULFILLED. - The checkout experience supports completing the remaining amount in follow-up attempts.
3. Fee Modes (MERCHANT vs PAYEE)
feePayer controls quote behavior and settlement contribution accounting:
MERCHANT:- Buyer pays the displayed fiat amount.
- Merchant absorbs fees.
PAYEE:- Buyer pays enough to cover both requested net and fees.
- Merchant receives net settlement amount.
4. Custom Webhook Headers
- Webhook endpoints now support merchant-defined custom headers.
- Up to 10 headers are supported per webhook.
- Header keys are normalized and validated as HTTP header names.
5. Dashboard Improvements
The merchant dashboard now includes:
- Improved order and volume analytics (including order trend views).
- Better webhook management UX (event selection, active toggles, header editing).
- Expanded settings surfaces for account/profile and wallet configuration.
6. Better Team Management
- Owners can invite team members directly by email from dashboard settings.
- Invite email delivery and resend flows are built in.
- Invited users can accept and join on first login without first creating their own separate merchant account.
Feature Changes
| Previous Integration (v0) | Current Integration (v1) | Migration Action |
|---|---|---|
Session-first flow (CheckoutSession, then order) | Order-first flow (CheckoutOrder, payments attached to order) | Store and reconcile by order.id |
| Exact-token and exact-fiat checkout modes | XOR create request (requestedUsdcAmount OR requestedFiatAmount + requestedFiatCurrency) | Migrate request payload builders to XOR amount mode |
| Session-level fiat currency defaults | Merchant config defaultPaymentCurrency + checkout geolocation fallback | Move default currency logic to merchant config + checkout |
enabledPaymentPlatforms request field | enabledRails request field | Rename the request field |
metadata request field | notes request field | Rename and update payload shape (Record<string, unknown>) |
URL shape /checkout?session=...&token=... | URL shape /?order=...&token=... | Update any URL parsing or validation logic |
SDK Integration Changes
Canonical SDK Methods
Use these as the only public integration methods:
createCheckoutgetMerchantgetCheckoutUrlredirectToCheckoutcreateCheckoutAndRedirect
If your integration still uses createCheckoutSession or createSessionAndRedirect, migrate to the v1 methods above.
Method Mapping
| Previous SDK (v0) | Current SDK (v1) |
|---|---|
createCheckoutSession(params, opts) | createCheckout(params, opts) |
createSessionAndRedirect(params, opts) | createCheckoutAndRedirect(params, opts) |
getMerchant(merchantId, opts) | getMerchant(opts) |
getCheckoutUrl(sessionId, opts, sessionToken) | getCheckoutUrl(orderId, orderToken, opts) |
redirectToCheckout(sessionId, sessionToken, opts) | redirectToCheckout(orderId, orderToken, opts) |
Request Parameter Mapping
createCheckoutSession (v0) -> createCheckout (v1)
| Previous field (v0) | Current field (v1) | Notes |
|---|---|---|
merchantId | Removed | Merchant context comes from apiKey |
amountUsdc | requestedUsdcAmount | Required in v1 |
recipientAddress | destinationAddress | Optional in v1 (merchant defaults can apply) |
destinationChainId | destinationChainId | Same meaning |
destinationToken | destinationToken | Same meaning (alias/address support) |
enabledPaymentPlatforms | enabledRails | Rename |
metadata | notes | Rename + broader value type |
successUrl | successUrl | Same field; nullable in API shape |
cancelUrl | cancelUrl | Same field; nullable in API shape |
checkoutMode | Removed | No exact-fiat request mode in v1 API |
fiatAmount + fiatCurrency | requestedFiatAmount + requestedFiatCurrency | Fiat create mode in v1 |
maxFeePercentage | Removed | Configure fee limits from the Merchant Dashboard settings |
Response Mapping
| Previous response (v0) | Current response (v1) |
|---|---|
session.id | order.id |
sessionToken | orderToken |
checkoutUrl | checkoutUrl (still returned) |
bridge | Bridge lifecycle represented in webhook events (PAYMENT_BRIDGE_*) |
API Endpoint Mapping
| Previous endpoint (v0) | Current endpoint (v1) |
|---|---|
POST /api/checkout/sessions | POST /api/v1/orders |
GET /api/merchants/{merchantId} | GET /api/v1/merchants/me |
Before/After SDK Example
// v0
import { createCheckoutSession } from '@zkp2p/pay-sdk';
const session = await createCheckoutSession(
{
merchantId: 'merchant_123',
amountUsdc: '50.00',
destinationChainId: 8453,
destinationToken: 'USDC',
recipientAddress: '0xYourWalletAddress',
metadata: { orderId: 'order_123' },
},
{ apiBaseUrl: 'https://api.pay.peer.xyz', apiKey: process.env.ZKPAY_API_KEY! }
);
// v1
import { createCheckout } from '@zkp2p/pay-sdk';
const checkout = await createCheckout(
{
requestedFiatAmount: '50.00',
requestedFiatCurrency: 'EUR',
destinationChainId: 8453,
destinationToken: 'USDC',
destinationAddress: '0xYourWalletAddress',
notes: { orderId: 'order_123' },
},
{ apiBaseUrl: 'https://api.pay.peer.xyz', apiKey: process.env.ZKPAY_API_KEY! }
);
Webhook Changes
Event Name Migration
| Previous event (v0) | Current event(s) (v1) | Notes |
|---|---|---|
order.created | ORDER_CREATED | Order lifecycle creation |
order.payment_sent | No direct equivalent (PAYMENT_CREATED is the closest lifecycle signal) | v1 does not expose a dedicated payment_sent event |
order.fulfilled | PAYMENT_SETTLED and ORDER_FULFILLED | Subscribe to both for final success handling |
order.failed | PAYMENT_FAILED | Terminal payment failure signal |
order.expired | PAYMENT_EXPIRED | Expiration signal; cancellation flows can also emit cancel events |
session.completed | ORDER_FULFILLED | Use ORDER_FULFILLED as the canonical checkout-complete event |
session.started | ORDER_CREATED | Use ORDER_CREATED as the canonical checkout-start event |
session.abandoned | No equivalent | We no longer expire orders |
payout.submitted | PAYMENT_BRIDGE_SUBMITTED | Crypto payout/bridge lifecycle |
payout.completed | PAYMENT_BRIDGE_COMPLETED | Crypto payout complete |
payout.failed | PAYMENT_BRIDGE_FAILED | Crypto payout failed |
Payload Shape Migration
| Previous payload path (v0) | Current payload path (v1) |
|---|---|
data.session.id | data.order.id (and data.payment.orderId when payment is present) |
data.session.status | data.order.status and data.payment.status |
data.order.paymentPlatform | data.payment.rail |
data.order.fiatCurrency | data.payment.currency |
data.order.fulfillTx | data.payment.fulfillTransaction |
data.order.errorCode / data.order.errorMessage | No direct payload field in v1 (use PAYMENT_FAILED / PAYMENT_EXPIRED and data.payment.status as failure signals) |
| Relay payout info on order/session objects | data.paymentBridge on PAYMENT_BRIDGE_* events |
Recommended v1 Webhook Subscriptions
[
"PAYMENT_SETTLED",
"PAYMENT_FAILED",
"PAYMENT_EXPIRED",
"ORDER_CREATED",
"ORDER_FULFILLED",
"ORDER_CANCELLED",
"PAYMENT_CREATED",
"PAYMENT_CANCELLED",
"PAYMENT_BRIDGE_PENDING",
"PAYMENT_BRIDGE_SUBMITTED",
"PAYMENT_BRIDGE_COMPLETED",
"PAYMENT_BRIDGE_FAILED"
]
For merchant order state transitions, treat PAYMENT_SETTLED, PAYMENT_FAILED, PAYMENT_EXPIRED, and ORDER_FULFILLED as the final-state event set.
Migration Checklist
- Replace
createCheckoutSessionwithcreateCheckout. - Replace
createSessionAndRedirectwithcreateCheckoutAndRedirect. - Update
getMerchantcalls togetMerchant(opts). - Rename request fields:
amountUsdc->requestedUsdcAmount,recipientAddress->destinationAddress,metadata->notes,enabledPaymentPlatforms->enabledRails. - Update create-order payload builders to use XOR amount mode (
requestedUsdcAmountORrequestedFiatAmount+requestedFiatCurrency). - Update webhook subscriptions from dotted v0 names to uppercase v1 event names.
- Update webhook handlers to use
data.order,data.payment, anddata.paymentBridge. - Verify one happy-path payment (
PAYMENT_SETTLED+ORDER_FULFILLED), one failed/expired path, and one bridge payout path (PAYMENT_BRIDGE_*).
Additional v1 Integrator Features
- For iframe-based checkout, use SDK embedded helpers from
@zkp2p/pay-sdk/embeddedplus parent-windowcheckout.success/checkout.failedevents. - For crypto payout tracking, subscribe to bridge lifecycle webhooks:
PAYMENT_BRIDGE_PENDING,PAYMENT_BRIDGE_SUBMITTED,PAYMENT_BRIDGE_COMPLETED,PAYMENT_BRIDGE_FAILED.