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 ethers
2

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,
  }
}
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
}

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