Quickstart Guide
Build a complete payment flow: create a payment in your backend, trigger it from your frontend, and verify it via on-chain events to unlock access.
In this guide, you'll:
- Create a payment in your backend with metadata
- Trigger a ZynPay payment from your frontend
- Verify it via on-chain events / API and unlock access
Prerequisites
- Node.js 18 or later (for backend) or Python 3.8+ (for Python backend)
- A MetaMask wallet installed in your browser
- Testnet USDC on Base Sepolia
1
Install the SDK
Install the ZynPay SDK in your project.
bash
npm install @zyntrialabs/zynpay-sdk ethers2
Create a payment in your backend
In your backend API, create a payment with metadata. This generates a payment ID that you'll use to track the transaction.
typescript
import { ZynPayClient, Environment } from '@zyntrialabs/zynpay-sdk'
// In your API route or backend service
export async function createPayment(orderId: string, amount: number) {
const client = new ZynPayClient({
merchantWallet: process.env.MERCHANT_WALLET_ADDRESS!,
environment: Environment.TESTNET,
defaultChain: 'base',
})
// Create payment with metadata
const payment = client.createPayment(amount, 'base', {
orderId: orderId,
description: `Order #${orderId}`,
metadata: {
userId: 'user_123',
productId: 'prod_456',
// Add any custom metadata you need
},
})
// Return payment ID to frontend
return {
paymentId: payment.id,
amount: payment.amount,
merchantAddress: payment.merchantAddress,
}
}Payment Metadata
The metadata you attach to a payment is stored on-chain and can be used to identify which order, user, or product the payment is for. This enables automatic access unlocking and order fulfillment.
3
Trigger payment from your frontend
In your frontend, use the payment data from your backend to trigger the ZynPay payment flow with MetaMask.
typescript
import { ZynPayClient, Environment } from '@zyntrialabs/zynpay-sdk'
import { BrowserProvider } from 'ethers'
async function handlePayment(paymentData: { paymentId: string; amount: number; merchantAddress: string }) {
// Connect to MetaMask
if (!window.ethereum) {
throw new Error('Please install MetaMask')
}
const provider = new BrowserProvider(window.ethereum)
const signer = await provider.getSigner()
// Initialize ZynPay client
const client = new ZynPayClient({
merchantWallet: paymentData.merchantAddress,
environment: Environment.TESTNET,
defaultChain: 'base',
})
// Create payment object (matches backend)
const payment = client.createPayment(paymentData.amount, 'base', {
paymentId: paymentData.paymentId,
})
try {
// Trigger payment - user approves in MetaMask
const txHash = await client.pay(payment, signer, true)
console.log('Payment transaction:', txHash)
// Wait for confirmation
const receipt = await provider.waitForTransaction(txHash)
console.log('Payment confirmed!', receipt)
// Notify your backend that payment is complete
await fetch('/api/payments/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentId: paymentData.paymentId, txHash }),
})
return { success: true, txHash }
} catch (error: any) {
if (error.code === 'ACTION_REJECTED') {
throw new Error('Payment cancelled by user')
}
throw error
}
}4
Verify payment and unlock access
In your backend, verify the payment via on-chain events or transaction hash, then unlock access, deliver files, or upgrade accounts.
typescript
import { ZynPayClient, Environment } from '@zyntrialabs/zynpay-sdk'
import { JsonRpcProvider } from 'ethers'
export async function verifyPayment(paymentId: string, txHash: string) {
const client = new ZynPayClient({
merchantWallet: process.env.MERCHANT_WALLET_ADDRESS!,
environment: Environment.TESTNET,
defaultChain: 'base',
})
// Option 1: Verify via transaction hash
const provider = new JsonRpcProvider('https://sepolia.base.org')
const receipt = await provider.getTransactionReceipt(txHash)
if (!receipt || receipt.status !== 1) {
throw new Error('Transaction failed or not found')
}
// Option 2: Listen for on-chain events
const payment = await client.getPayment(paymentId)
if (payment.status === 'completed') {
// Payment verified! Now unlock access
// Example: Unlock premium features
await unlockUserAccess(payment.metadata.userId)
// Example: Mark order as paid
await updateOrderStatus(payment.metadata.orderId, 'paid')
// Example: Deliver digital product
await sendDownloadLink(payment.metadata.userId, payment.metadata.productId)
return { verified: true, payment }
}
return { verified: false }
}
async function unlockUserAccess(userId: string) {
// Your business logic here
// e.g., update database, send webhook, etc.
}
async function updateOrderStatus(orderId: string, status: string) {
// Your business logic here
}
async function sendDownloadLink(userId: string, productId: string) {
// Your business logic here
}Automatic Verification
You can also set up webhooks or event listeners to automatically verify payments as they happen on-chain, without requiring the frontend to call your API. See Events & Webhooks for more details.
Next Steps
Protecting an API with payments
Learn how to gate API access behind payments
Fee splitting & events
Understand automatic fee splitting and event handling