@zkp2p/pay-sdk
v7.0.0
Published
ZKP2P Pay checkout SDK
Readme
@zkp2p/pay-sdk
TypeScript SDK for creating checkout orders against the canonical orders API and creating and managing payouts.
- Package:
@zkp2p/pay-sdk - Runtime: Node.js with
fetchfor authenticated calls; browser helpers for navigation and embedding - Module format: ESM
Install
npm install @zkp2p/[email protected]Published artifacts
- Includes compiled ESM JavaScript and
.d.tstype declarations. - Does not publish declaration maps (
.d.ts.map) or JavaScript source maps (.js.map).
Recommended flow
Create checkout on your backend and return checkout.checkoutUrl to the browser.
Never bundle the merchant API key into a frontend. The redirect helper below
runs separately in the browser with public URL options only.
import {
createCheckout,
redirectToCheckout,
type CheckoutClientOptions,
} from '@zkp2p/pay-sdk';
const client: CheckoutClientOptions = {
apiBaseUrl: 'https://api.pay.peer.xyz',
checkoutBaseUrl: 'https://pay.peer.xyz',
apiKey: '<merchant-api-key>',
};
const checkout = await createCheckout(
{
requestedFiatAmount: '25.00',
requestedFiatCurrency: 'EUR',
destinationAddress: '0xYourRecipientAddress',
destinationToken: 'USDC',
destinationChainId: 8453,
successUrl: 'https://merchant.example/success',
cancelUrl: 'https://merchant.example/cancel',
notes: { cartId: 'cart_123' },
},
client,
);
// Browser code, after receiving orderId and orderToken from your backend:
redirectToCheckout(orderId, orderToken, {
apiBaseUrl: 'https://api.pay.peer.xyz',
checkoutBaseUrl: 'https://pay.peer.xyz',
});Functions
createCheckout(params, options)
Creates an order via POST /api/v1/orders and returns:
orderorderTokencheckoutUrl
params maps to the API schema:
- amount input XOR:
requestedUsdcAmountrequestedFiatAmount+requestedFiatCurrencyopenAmount:{ currency, minAmount?, maxAmount?, presets? }(the buyer chooses the amount at checkout)
idempotencyKey(optional, see below)destinationAddress(optional)destinationToken(optional)destinationChainId(optional)feePayer(optional):MERCHANT,PAYEE, orSPLITbuyerFeeShareBps: required with explicitSPLIT; buyer share of total fees in basis points (5000 = 50%, 0–10000 in steps of 1000)dynamicOrdersEnabled(optional; inherits merchant configuration when omitted)enabledRails(optional)successUrl(nullable, defaults tonull)cancelUrl(nullable, defaults tonull)notes(nullable, defaults tonull)
In fiat mode, the API converts to canonical USD/USDC amounts and persists order amounts in USDC fields.
Idempotent order creation
Pass idempotencyKey in params to reuse an order on retries. Use one stable key
per purchase, including after a timeout: 8–128 letters, digits, underscores or
hyphens. The API compares amount and mode; changing either returns
409 IDEMPOTENCY_KEY_CONFLICT. Other fields do not update the original order.
On replay, the API returns idempotentReplay: true and orderToken: null. The SDK
returns checkoutUrl: null and preserves the replay marker. Save the first
checkout URL on your backend; a replay does not recover a lost token or create a
new checkout link. createCheckoutAndRedirect returns a replay without navigating.
Handle the nullable URL before redirecting.
getCheckoutUrl(orderId, orderToken, options)
Builds the checkout URL using checkoutBaseUrl when provided and apiBaseUrl otherwise.
redirectToCheckout(orderId, orderToken, options)
Browser helper that redirects to the checkout URL.
createCheckoutAndRedirect(params, options)
Creates an order and immediately redirects.
getMerchant(options)
Fetches merchant profile data via GET /api/v1/merchants/me. The merchant is
determined by the API key in options — there is no merchant-id parameter.
checkQuoteAvailability(params, options)
Checks whether quotes exist for an amount before you create an order, and returns nearby
amounts that would fill when they do not. Calls
POST /api/v1/merchants/me/quotes/availability.
Server-side only. This sends your merchant API key. Never call it from browser code.
nearbySuggestions is null whenever available is true, and available: true is
advisory — the API reserves no liquidity, so order creation can still fail.
Pass the same enabledRails override as createCheckout to check the customer's
selected payment method. When omitted, any serviceable merchant rail can make
available true, even if a different rail selected for checkout is unavailable.
Forwarding enabledRails requires SDK 4.0.1 or later and an API deployment that
supports rail-scoped availability. Earlier SDK versions omit the field from the
request. Upgrade the package and redeploy your backend before relying on it.
import { checkQuoteAvailability, CheckoutMode } from '@zkp2p/pay-sdk';
const availability = await checkQuoteAvailability(
{
amount: '25.00',
quoteMode: CheckoutMode.EXACT_TOKEN,
enabledRails: ['venmo'],
destinationChainId: 8453,
destinationToken: 'USDC',
destinationAddress: '0xYourPayoutWallet',
},
{ apiBaseUrl, apiKey, signal: AbortSignal.timeout(8_000) },
);See the Quote Availability guide for the full field reference.
Payouts
Server-side only. Every payout call sends your merchant API key. Never call it from browser code.
createPayout(params, { idempotencyKey }, options)—POST /api/v1/payouts; returns aPayoutViewwithcheckoutUrlandidempotentReplay.getPayout(payoutId, options)—GET /api/v1/payouts/:id; returns aPayoutView.listPayouts(params, options)—GET /api/v1/payouts; accepts optionalcustomerEmail,status,merchantReference,pageandlimit, and returnsitems,page,limitandtotal, newest first.cancelPayout(payoutId, options)—POST /api/v1/payouts/:id/cancel; cancels a payout awaiting funding that has received nothing and returns itsPayoutView.
idempotencyKey is required: 8–128 letters, digits, _ or -, checked before sending.
Use one stable key per withdrawal. A retry with the same key and body returns the same
payout with idempotentReplay: true and its current checkoutUrl.
See the Payouts guide for funding, filters, cancellation rules and errors.
Legacy APIs
Legacy session helper APIs are not exported from the current SDK surface.
Client options
type CheckoutClientOptions = {
apiBaseUrl: string;
checkoutBaseUrl?: string;
apiKey?: string;
fetcher?: typeof fetch;
signal?: AbortSignal;
};Payout calls take PayoutClientOptions:
type PayoutClientOptions = {
apiBaseUrl: string;
apiKey: string;
fetcher?: typeof fetch;
signal?: AbortSignal;
};Payout webhooks
WebhookPayload is a union of OrderWebhookPayload and PayoutWebhookPayload.
After verifying the webhook signature, narrow with isPayoutWebhook before reading data:
import { PAYOUT_WEBHOOK_VERSION, isPayoutWebhook, type WebhookPayload } from '@zkp2p/pay-sdk';
function handleWebhook(payload: WebhookPayload) {
if (isPayoutWebhook(payload)) {
// payload.data is PayoutView; payload.version is PAYOUT_WEBHOOK_VERSION (2).
console.log(payload.data.payoutId, payload.data.status, PAYOUT_WEBHOOK_VERSION);
} else {
console.log(payload.data.order, payload.data.payment);
}
}Embedded checkout helpers
import {
EMBED_EVENT_CHANNEL,
isEmbeddedCheckout,
ensureEmbedModeUrl,
buildEmbedCheckoutEvent,
postEmbedCheckoutEvent,
} from '@zkp2p/pay-sdk/embedded';Mount checkout with ensureEmbedModeUrl, and if you sandbox the iframe include
allow-popups and allow-popups-to-escape-sandbox:
<iframe
title="Embedded Checkout"
src={ensureEmbedModeUrl(checkoutUrl)}
sandbox="allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox"
/>Embedded checkout opens payment apps in a new tab, because providers including Cash App
and PayPal serve X-Frame-Options: SAMEORIGIN and cannot load in-frame. Without
allow-popups the browser silently drops the Pay with … click and the payment attempt can time out. Omitting sandbox altogether is also fine — the requirement only applies once
you opt in to sandboxing.
Supported event types:
checkout.success— payment completedcheckout.failed— payment was attempted and failedcheckout.closed— customer dismissed checkout, e.g. no payment rail had liquidity for the amount. Close the iframe and do not run failure handling. The order can be reopened with the same checkout URL.
checkout.closedis not a statement that no funds were received. A partially paid order returns to method selection to pay its remaining balance, and can emitcheckout.closedfrom there. Treat your own order state — fetched byorder_id, or from webhooks — as authoritative before telling a customer nothing was charged.
Error handling
SDK helpers throw PayApiError (a subclass of Error) when:
- the HTTP response is not OK
- the API returns
{ success: false }
PayApiError carries statusCode, errorCode, responseObject, and — for
validation failures — fieldErrors / formErrors. Its message includes the
field-level detail, e.g. Invalid request: amount: Required.
A plain Error is thrown when required auth options are missing or the response
envelope is malformed. An invalid or missing payout idempotencyKey, or an empty
payoutId, throws a TypeError before any request is sent. A missing payout apiKey
throws an Error before sending. These local errors are not PayApiError.
Wrap SDK calls in try/catch.
