Testing & Debugging
Complete guide to testing ZendFi integrations locally and debugging common issues.
Local Development Setup
1. Get API Keys
Create test mode API keys from your dashboard:
# .env.local or .env
ZENDFI_API_KEY=zfi_test_abc123...
ZENDFI_WEBHOOK_SECRET=whsec_test_xyz789...
API keys starting with zfi_test_ automatically use test mode - no real crypto transactions!
2. Install Dependencies
- npm
- yarn
- pnpm
npm install @zendfi/sdk
yarn add @zendfi/sdk
pnpm add @zendfi/sdk
3. Enable Debug Mode
- TypeScript SDK
- Environment Variable
import { ZendFiClient } from '@zendfi/sdk';
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_API_KEY,
debug: true // Logs all requests/responses
});
// Or use environment variable
// ZENDFI_DEBUG=true
# .env.local
ZENDFI_DEBUG=true
ZENDFI_API_KEY=zfi_test_abc123...
Debug mode logs:
- Request URL, method, headers
- Request body (sanitized)
- Response status, headers
- Response body
- Request timing
Testing Payments Locally
Create Test Payment
import { ZendFiClient } from '@zendfi/sdk';
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_API_KEY,
});
// Create test payment
const payment = await zendfi.createPayment({
amount: 10,
currency: 'USD',
description: 'Test Payment'
});
console.log('Payment URL:', payment.payment_url);
// Visit this URL to complete test payment
Test Payment with CLI
The CLI provides a quick way to test payments:
# Create test payment
zendfi payment create \
--amount 50 \
--description "Test order #123"
# Check payment status
zendfi payment status pay_abc123
Simulate Payment Completion
In test mode, you can simulate payment completion without real crypto:
// Create payment
const payment = await zendfi.createPayment({
amount: 25,
currency: 'USD'
});
// In test mode, visit the payment URL and click "Simulate Payment"
// Or use the dashboard to mark as paid
Testing Webhooks Locally
Webhooks need a public URL, but you're developing on localhost. Here are solutions:
Option 1: CLI Webhook Forwarding (Recommended)
The ZendFi CLI can forward webhooks to your local server:
# Forward webhooks to localhost:3000
zendfi webhooks --forward-to http://localhost:3000/api/webhooks/zendfi
# This creates a secure tunnel and automatically configures your webhook endpoint
Output:
✓ Tunnel created: https://abc123.zendfi.dev
✓ Webhook endpoint configured
✓ Forwarding to http://localhost:3000/api/webhooks/zendfi
Listening for webhooks...
Press Ctrl+C to stop
Now any webhook events will be forwarded to your local server!
Option 2: ngrok
# Install ngrok
npm install -g ngrok
# Start tunnel
ngrok http 3000
# Copy the HTTPS URL (e.g., https://abc123.ngrok.io)
# Add to dashboard: Settings → Webhooks → Add Endpoint
Option 3: Local Webhook Testing
Test webhook handler logic without external service:
import { ZendFiClient } from '@zendfi/sdk';
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_API_KEY,
});
// Your webhook handler
async function handleWebhook(body: any, signature: string) {
const isValid = zendfi.verifyWebhook({
payload: body,
signature,
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
});
if (!isValid) {
console.error('Invalid webhook signature');
return;
}
const event = typeof body === 'string' ? JSON.parse(body) : body;
console.log('Event:', event.event_type);
console.log('Data:', event.data);
}
// Simulate webhook payload
const mockPayload = {
event_type: 'PaymentConfirmed',
timestamp: new Date().toISOString(),
merchant_id: 'merch_test_123',
data: {
id: 'pay_test_123',
amount: 50,
status: 'confirmed'
}
};
// Test handler (skip signature verification for local testing)
await handleWebhook(mockPayload, 'mock_signature');
Verify Webhook Signatures
Always verify webhook signatures in production:
import { ZendFiClient } from '@zendfi/sdk';
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_API_KEY,
});
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('x-zendfi-signature');
if (!signature) {
return new Response('No signature', { status: 400 });
}
// Verify signature
const isValid = zendfi.verifyWebhook({
payload: body,
signature,
secret: process.env.ZENDFI_WEBHOOK_SECRET!
});
if (!isValid) {
return new Response('Invalid signature', { status: 401 });
}
// Parse and handle event
const event = JSON.parse(body);
console.log('Verified event:', event.event_type);
return new Response('Success', { status: 200 });
}
Common Issues & Solutions
Issue: "Invalid API key"
Symptoms:
Error: Invalid API key provided
Status: 401 Unauthorized
Solutions:
- Check API key is correct (no extra spaces)
- Verify environment variable is loaded:
console.log('API Key:', process.env.ZENDFI_API_KEY?.substring(0, 15) + '...');
- Restart dev server after changing
.envfile - Use
zfi_test_prefix for test mode
Issue: "Payment URL not working"
Symptoms:
- Payment link shows 404 or error page
- Cannot complete payment
Solutions:
- Verify payment was created successfully (check
payment.id) - Use
payment.payment_urlfield (snake_case, not camelCase) - Check payment hasn't expired
- Test mode payments require test wallet
Issue: "Webhooks not received"
Symptoms:
- Payment completes but webhook never fires
- No events logged
Solutions:
- Verify webhook endpoint is configured in dashboard
- Endpoint must be publicly accessible (use CLI webhook forwarding or ngrok for localhost)
- Endpoint must return 200 status code
- Check webhook logs in dashboard for delivery errors
- Verify signature is being validated correctly using
zendfi.verifyWebhook() - Check firewall isn't blocking ZendFi IPs
Issue: "Module not found: @zendfi/sdk"
Symptoms:
Error: Cannot find module '@zendfi/sdk'
Solutions:
- Install package:
npm install @zendfi/sdk - Restart TypeScript server (VS Code: Cmd+Shift+P → "Restart TS Server")
- Delete
node_modulesand reinstall:
rm -rf node_modules package-lock.json
npm install
Issue: "Type errors with SDK"
Symptoms:
// Property 'paymentUrl' does not exist on type 'Payment'
const url = payment.paymentUrl;
Solutions:
- Use snake_case field names:
payment.payment_url(notpayment.paymentUrl) - Update to latest SDK version:
npm install @zendfi/sdk@latest
- Check TypeScript version (requires 4.5+):
npx tsc --version
- Add proper imports:
import { ZendFiClient } from '@zendfi/sdk';
import type { Payment, CreatePaymentRequest } from '@zendfi/sdk';
Issue: "CORS errors in browser"
Symptoms:
Access to fetch blocked by CORS policy
Solutions:
Don't call API directly from browser - API keys would be exposed!
Create API route on your server:
- Next.js
- Express
// app/api/create-payment/route.ts
import { ZendFiClient } from '@zendfi/sdk';
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_API_KEY,
}); // Uses server-side env vars
export async function POST(request: Request) {
const { amount } = await request.json();
const payment = await zendfi.createPayment({
amount,
currency: 'USD'
});
return Response.json({ payment_url: payment.payment_url });
}
import express from 'express';
import { ZendFiClient } from '@zendfi/sdk';
const app = express();
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_API_KEY,
});
app.post('/api/create-payment', async (req, res) => {
const { amount } = req.body;
const payment = await zendfi.createPayment({
amount,
currency: 'USD'
});
res.json({ payment_url: payment.payment_url });
});
Issue: "Rate limit exceeded"
Symptoms:
Error: Rate limit exceeded
Status: 429 Too Many Requests
Retry-After: 60
Solutions:
- Implement exponential backoff:
import { ZendFiClient } from '@zendfi/sdk';
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_API_KEY,
retries: 3, // Auto-retry with backoff
});
- Cache responses when possible
- Batch requests instead of making individual calls
- Contact support for higher limits if needed
Issue: "Connection timeout"
Symptoms:
Error: Request timeout after 30000ms
Solutions:
- Increase timeout:
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_API_KEY,
timeout: 60000 // 60 seconds
});
- Check network connectivity
- Check ZendFi status page for outages
Debugging Checklist
Before asking for help, verify:
- Using latest SDK version (
npm install @zendfi/sdk@latest) - API key is correct and has
zfi_test_orzfi_live_prefix - Environment variables are loaded (restart dev server)
- Debug mode enabled (
debug: trueorZENDFI_DEBUG=true) - Webhook endpoint returns 200 status code
- Webhook endpoint is publicly accessible (not localhost)
- Webhook signature is verified using
zendfi.verifyWebhook() - Error messages are logged (check console/server logs)
- Check status page for outages
Testing Best Practices
1. Use Test Mode for Development
// .env.development
ZENDFI_API_KEY=zfi_test_abc123...
// .env.production
ZENDFI_API_KEY=zfi_live_xyz789...
2. Separate API Keys per Environment
- Development:
zfi_test_dev_... - Staging:
zfi_test_staging_... - Production:
zfi_live_prod_...
3. Write Integration Tests
import { ZendFiClient } from '@zendfi/sdk';
import { expect, test } from 'vitest';
test('creates payment successfully', async () => {
const zendfi = new ZendFiClient({
apiKey: process.env.ZENDFI_TEST_API_KEY
});
const payment = await zendfi.createPayment({
amount: 10,
currency: 'USD',
description: 'Test payment'
});
expect(payment.id).toMatch(/^pay_/);
expect(payment.amount).toBe(10);
expect(payment.status).toBe('pending');
});
4. Mock Webhooks in Tests
import { ZendFiClient } from '@zendfi/sdk';
test('handles payment confirmed webhook', async () => {
const mockEvent = {
event_type: 'PaymentConfirmed',
timestamp: new Date().toISOString(),
merchant_id: 'merch_test_123',
data: {
id: 'pay_test_123',
amount: 50,
status: 'confirmed'
}
};
const result = await handleWebhook(mockEvent);
expect(result).toBe('success');
});
5. Test Error Scenarios
import { ZendFiError } from '@zendfi/sdk';
test('handles payment creation failure', async () => {
const zendfi = new ZendFiClient({
apiKey: 'invalid_key'
});
try {
await zendfi.createPayment({ amount: 10, currency: 'USD' });
} catch (error) {
expect(error).toBeInstanceOf(ZendFiError);
expect(error.message).toContain('Invalid API key');
}
});
Monitoring in Production
1. Log All Payment Events
const payment = await zendfi.createPayment({
amount: 100,
currency: 'USD',
metadata: {
order_id: '12345',
user_id: 'user_789'
}
});
console.log('Payment created', {
payment_id: payment.id,
amount: payment.amount,
status: payment.status,
metadata: payment.metadata
});
2. Set Up Error Tracking
import * as Sentry from '@sentry/node';
import { ZendFiError } from '@zendfi/sdk';
try {
const payment = await zendfi.createPayment({...});
} catch (error) {
if (error instanceof ZendFiError) {
Sentry.captureException(error, {
tags: {
service: 'zendfi',
operation: 'create_payment',
error_type: error.type
},
extra: {
amount: 100,
currency: 'USD'
}
});
}
throw error;
}
3. Monitor Webhook Success Rate
export async function POST(request: Request) {
const startTime = Date.now();
try {
const body = await request.text();
const signature = request.headers.get('x-zendfi-signature');
const isValid = zendfi.verifyWebhook({
payload: body,
signature: signature!,
secret: process.env.ZENDFI_WEBHOOK_SECRET!
});
if (!isValid) {
console.warn('Invalid webhook signature', {
duration: Date.now() - startTime
});
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(body);
// Log success
console.log('Webhook processed', {
event: event.event_type,
duration: Date.now() - startTime
});
return new Response('OK', { status: 200 });
} catch (error) {
// Log failure
console.error('Webhook failed', {
error: error.message,
duration: Date.now() - startTime
});
return new Response('Error', { status: 500 });
}
}
CLI Debugging Commands
# Check API connection
zendfi status
# List recent payments
zendfi payment list --limit 10
# Get payment details
zendfi payment get pay_abc123
# Test webhook delivery
zendfi webhooks test
# Validate configuration
zendfi config validate
# Check CLI version
zendfi --version
Get Help
Still stuck?
- �� API Reference - Complete API documentation
- 💬 Discord Community - Get help from the community
- 📧 Email Support - Contact our team
- 🐛 GitHub Issues - Report bugs
When asking for help, include:
- SDK version (
npm list @zendfi/sdk) - Framework & version (Next.js 14, Express 4, etc.)
- Error message (full stack trace)
- Code snippet (sanitize API keys!)
- Expected vs actual behavior