Payment Links - Your Reusable Payment Superpower!

Welcome to Payment Links - one of the coolest features in ZendFi! Think of payment links as your personal payment URLs that you can share anywhere and use multiple times. Perfect for social media, invoices, or anywhere you need a quick way to accept crypto payments. Let's dive in!

Error Responses

400 Bad Request

Link is invalid or inactive.

{
  "error": "Invalid payment link parameters",
  "details": "Amount must be greater than 0"
}

404 Not Found

Payment link doesn't exist.

{
  "error": "Payment link not found",
  "details": "No payment link found with code: pyl_invalid123"
}

410 Gone

Payment link has expired or reached max uses.

{
  "error": "Payment link no longer available",
  "details": "This payment link has expired or reached its usage limit"
}

Expired Link:

{
  "error": "Payment link expired",
  "details": "This link expired on 2025-11-30T23:59:59Z"
}

Max Uses Reached:

{
  "error": "Payment link capacity reached",
  "details": "This link has been used 100 times (maximum allowed)"
}

500 Internal Server Error

Something went wrong on our end.

{
  "error": "Internal server error",
  "details": "Failed to create payment from link. Please try again."
}

Hosted Checkout Pages

Every payment link comes with a beautiful, mobile-optimized checkout page automatically! No frontend development needed. 🎨

Accessing Hosted Pages

Use the hosted_page_url from your payment link response:

https://zendfi.tech/checkout/pyl_hk3n7x9m2q

What's Included?

  • Mobile-optimized design: Perfect on all devices
  • Professional UI: Beautiful, trustworthy checkout experience
  • QR code display: Scan with Phantom, Solflare, or any Solana wallet
  • Live countdown timer: Shows 15-minute expiration
  • Payment instructions: Clear steps for customers
  • Real-time status updates: Automatically updates when payment confirms
  • Multi-network support: Works on mainnet and devnet
  • Secure: All transactions on Solana blockchain

Sharing Options

Direct Link

Share the hosted_page_url directly: https://zendfi.tech/checkout/pyl_hk3n7x9m2q

QR Code

Generate a QR code pointing to your hosted page for:

  • Physical products (print on packaging)
  • Marketing materials (flyers, posters)
  • Event tickets (print on entrance)
  • Restaurant menus (table tents)

Embed in Email

<a href="https://zendfi.tech/checkout/pyl_hk3n7x9m2q">
  Click here to complete your payment
</a>

Social Media

Perfect for Instagram/Twitter bio links, TikTok profiles, YouTube descriptions!

Use Cases & Best Practices

E-Commerce Store

Scenario: You sell digital products and want one link per product.

Implementation:

  1. Create a payment link for each product
  2. Store the link_code in your database
  3. Share the hosted_page_url on your product page
  4. Listen for payment.confirmed webhooks
  5. Deliver digital product automatically

Best Practice:

  • Use unlimited max_uses for digital products
  • Include product details in metadata
  • Set description to product name
  • Use USDC for price stability

Social Media Creator

Scenario: You want fans to support you via tips.

Implementation:

  1. Create payment link with small amount (e.g., $5)
  2. Add hosted_page_url to Instagram/Twitter bio
  3. Monitor uses_count to track support
  4. Thank supporters via webhook data

Best Practice:

  • Keep amount low ($1-$10) for accessibility
  • Use friendly description like "☕ Buy me a coffee!"
  • No max_uses or expires_at limits
  • Consider USDC for stability, SOL for crypto fans

Event Tickets

Scenario: Sell tickets for a conference with limited capacity.

Implementation:

  1. Create payment link with max_uses = ticket capacity
  2. Set expires_at to day before event
  3. Share hosted page on event website
  4. Monitor uses_count for capacity tracking
  5. Send ticket confirmation via webhook

Best Practice:

  • Always set max_uses to prevent overselling
  • Set expires_at to prevent late purchases
  • Include event details in metadata
  • Use descriptive description with date/time

Professional Invoicing

Scenario: Send payment requests to clients.

Implementation:

  1. Create payment link with max_uses: 1 (one-time use)
  2. Include client details in metadata
  3. Email the hosted_page_url to client
  4. Set expires_at to payment due date
  5. Track payment via webhook

Best Practice:

  • Use max_uses: 1 for single invoices
  • Set expires_at to payment deadline
  • Include invoice number in metadata
  • Use USDC for business payments

Donations & Fundraising

Scenario: Accept ongoing donations for a cause.

Implementation:

  1. Create payment link with suggested donation amount
  2. No max_uses or expires_at limits
  3. Share everywhere: website, social media, email
  4. Track total donations via uses_count
  5. Thank donors via webhook data

Best Practice:

  • Keep amount flexible (consider PWYW in future)
  • Use inspiring description
  • No limits on uses or expiration
  • USDC recommended for donor simplicity

Code Examples

Node.js/Express: Create & Share Payment Link

const express = require('express');
const axios = require('axios');

const app = express();
const ZENDFI_API_KEY = process.env.ZENDFI_API_KEY;

// Create payment link for a product
app.post('/products/:id/payment-link', async (req, res) => {
  const { id } = req.params;
  const product = await getProductById(id); // Your DB function

  try {
    const response = await axios.post(
      'https://api.zendfi.tech/api/v1/payment-links',
      {
        amount: product.price,
        currency: 'USD',
        token: 'USDC',
        description: `${product.name} - ${product.description}`,
        metadata: {
          product_id: product.id,
          sku: product.sku,
          category: product.category
        }
      },
      {
        headers: {
          'Authorization': `Bearer ${ZENDFI_API_KEY}`,
          'Content-Type': 'application/json'
        }
      }
    );

    const link = response.data;

    // Save link_code to database for future reference
    await savePaymentLink(product.id, link.link_code);

    res.json({
      success: true,
      payment_link: link.hosted_page_url,
      link_code: link.link_code,
      message: 'Share this link with your customers!'
    });
  } catch (error) {
    console.error('Payment link creation failed:', error.response?.data || error.message);
    res.status(500).json({ error: 'Failed to create payment link' });
  }
});

// Get payment link stats
app.get('/payment-links/:code/stats', async (req, res) => {
  const { code } = req.params;

  try {
    const response = await axios.get(
      `https://api.zendfi.tech/api/v1/payment-links/${code}`
    );

    const link = response.data;

    res.json({
      link_code: code,
      uses_count: link.uses_count,
      max_uses: link.max_uses,
      remaining_uses: link.max_uses ? link.max_uses - link.uses_count : 'unlimited',
      is_active: link.is_active,
      expires_at: link.expires_at,
      created_at: link.created_at
    });
  } catch (error) {
    console.error('Failed to get link stats:', error.response?.data || error.message);
    res.status(404).json({ error: 'Payment link not found' });
  }
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Python/Flask: Event Ticketing with Payment Links

from flask import Flask, request, jsonify
import requests
import os
from datetime import datetime, timedelta

app = Flask(__name__)
ZENDFI_API_KEY = os.getenv('ZENDFI_API_KEY')

@app.route('/events/<event_id>/create-ticket-link', methods=['POST'])
def create_ticket_link(event_id):
    """Create a payment link for event tickets"""
    event = get_event_by_id(event_id)  # Your DB function
    
    # Calculate expiration (day before event)
    event_date = datetime.fromisoformat(event['date'])
    expires_at = event_date - timedelta(days=1)
    
    response = requests.post(
        'https://api.zendfi.tech/api/v1/payment-links',
        headers={
            'Authorization': f'Bearer {ZENDFI_API_KEY}',
            'Content-Type': 'application/json'
        },
        json={
            'amount': event['ticket_price'],
            'currency': 'USD',
            'token': 'USDC',
            'description': f"{event['name']} - General Admission",
            'max_uses': event['capacity'],
            'expires_at': expires_at.isoformat() + 'Z',
            'metadata': {
                'event_id': event['id'],
                'event_name': event['name'],
                'event_date': event['date'],
                'ticket_type': 'general_admission',
                'venue': event['venue']
            }
        }
    )
    
    if response.status_code == 200:
        link = response.json()
        
        # Save to database
        save_ticket_link(event_id, link['link_code'], link['id'])
        
        return jsonify({
            'success': True,
            'hosted_page': link['hosted_page_url'],
            'link_code': link['link_code'],
            'capacity': link['max_uses'],
            'expires_at': link['expires_at'],
            'message': f"Ticket link created! Share this link to sell {link['max_uses']} tickets."
        }), 200
    else:
        return jsonify({
            'error': 'Failed to create ticket link',
            'details': response.json()
        }), 500

@app.route('/events/<event_id>/ticket-sales', methods=['GET'])
def get_ticket_sales(event_id):
    """Check how many tickets have been sold"""
    link_code = get_ticket_link_code(event_id)  # Your DB function
    
    response = requests.get(
        f'https://api.zendfi.tech/api/v1/payment-links/{link_code}'
    )
    
    if response.status_code == 200:
        link = response.json()
        sold = link['uses_count']
        capacity = link['max_uses']
        remaining = capacity - sold if capacity else 'unlimited'
        
        return jsonify({
            'event_id': event_id,
            'tickets_sold': sold,
            'total_capacity': capacity,
            'tickets_remaining': remaining,
            'is_sold_out': sold >= capacity if capacity else False,
            'link_active': link['is_active'],
            'expires_at': link['expires_at']
        }), 200
    else:
        return jsonify({'error': 'Ticket link not found'}), 404

if __name__ == '__main__':
    app.run(debug=True, port=5000)

Summary & Next Steps

Congratulations! 🎉 You now know how to create and use payment links like a pro!

What You Learned:

  • Create reusable payment links with optional limits and expiration
  • Accept USDC, SOL, or USDT payments
  • Get beautiful hosted checkout pages automatically
  • Track usage counts and monitor capacity
  • Implement various use cases (e-commerce, events, donations, etc.)

Next Steps:

  1. Read Webhooks Guide to handle payment notifications
  2. Explore Wallet Management to check balances
  3. Check out Advanced Features for payment splits

Need Help?

  • Email: support@zendfi.tech
  • Discord: discord.gg/zendfi
  • Docs: https://docs.zendfi.tech

Happy building!