DevelopersAPI ReferenceIndex

Suzaa API - Complete Documentation

Table of Contents

  1. Overview
  2. What is Suzaa?
  3. API Architecture
  4. Authentication
  5. Endpoints
  6. Payment Request Lifecycle
  7. Integration Examples
  8. Error Handling
  9. Best Practices
  10. Webhooks (Future)
  11. Security
  12. Rate Limits
  13. Testing

Overview

The Suzaa API is a RESTful API that allows merchants to create non-custodial cryptocurrency payment requests (invoices). It generates payment links with QR codes that display:

  • Merchant’s wallet address
  • Payment amount (converted to cryptocurrency)
  • Order reference/description
  • Expiration time

Important: Suzaa is NOT a payment processor, gateway, or custodial service. It’s a payment request/invoicing tool. All cryptocurrency payments go directly to the merchant’s wallet on the blockchain.

Base URL

Production: https://api.suzaa.com

API Version

Current Version: v1 (included in base URL path)

What is Suzaa?

What Suzaa Does

Suzaa is a payment request generation service that:

  1. Creates Payment Requests - Generates unique payment links with embedded payment information
  2. Displays QR Codes - Shows QR codes containing wallet address and payment amount
  3. Tracks Status - Records when customers claim they’ve paid
  4. Provides UI - Offers a clean payment page for customers
  5. Manages Expiration - Automatically expires old payment requests

What Suzaa Does NOT Do

  • ❌ Does NOT process payments
  • ❌ Does NOT hold cryptocurrency
  • ❌ Does NOT act as a custodian
  • ❌ Does NOT provide wallets
  • ❌ Does NOT verify blockchain transactions (merchant’s responsibility)
  • ❌ Does NOT handle refunds
  • ❌ Does NOT require customer accounts

The Flow

Merchant System → Suzaa API → Payment Request Created

                           Customer Shown Payment Link

                           QR Code with Wallet Address

                    Customer Sends Payment on Blockchain

                           Customer Clicks "I Paid"

                         Redirects Back to Merchant

              Merchant Verifies Transaction on Blockchain

                            Fulfills Order if Valid

Key Point: The actual payment happens on the blockchain, between the customer’s wallet and the merchant’s wallet. Suzaa just facilitates the request and provides the interface.


API Architecture

RESTful Design

  • HTTP Methods: POST, GET, PUT, DELETE
  • Data Format: JSON
  • Authentication: Bearer tokens (API keys)
  • Response Format: JSON with standardized structure

Standard Response Structure

All API responses follow this structure:

{
  "success": true | false,
  "data": {
    // Response data here
  },
  "error": {
    // Only present if success: false
    "code": "ERROR_CODE",
    "message": "Human-readable error message"
  }
}

Authentication

API Keys

Suzaa uses API key authentication with Bearer tokens.

Obtaining an API Key

  1. Create a Suzaa merchant account at https://app.suzaa.com
  2. Navigate to Settings → API Keys
  3. Click “Generate New API Key”
  4. Copy your API key (starts with sza_live_ or sza_test_)

Key Types

  • Live Keys - sza_live_* - For production
  • Test Keys - sza_test_* - For development/testing

Using API Keys

Include the API key in the Authorization header:

Authorization: Bearer sza_live_b20a7aa260a229853af02fd73fffdcd8a71ede41eaf775f5228fda2f265ef506

Example Request

curl -X POST https://api.suzaa.com/payments/requests \
  -H "Authorization: Bearer sza_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount": "100.00", "description": "Order #123"}'

Security Best Practices

  • ✅ Never expose API keys in client-side code
  • ✅ Store keys in environment variables
  • ✅ Use different keys for development and production
  • ✅ Rotate keys periodically
  • ✅ Revoke compromised keys immediately

Endpoints

1. Create Payment Request

Create a new payment request (invoice) for a customer.

Endpoint:

POST /payments/requests

Headers:

Content-Type: application/json
Authorization: Bearer YOUR_API_KEY

Request Body:

{
  "amount": "100.00",              // Required: Amount in USD (string or number)
  "description": "Order #12345",   // Required: Description/reference (max 500 chars)
  "expiryTime": 60,                // Required: Expiration in minutes (1-1440)
  "redirectUrl": "https://..."     // Optional: Where to redirect after payment
}

Field Details:

FieldTypeRequiredDescription
amountstring/numberYesPayment amount in USD. Can be string or number. Examples: "100.00", 100, 99.99
descriptionstringYesOrder description or reference. Shown to customer. Max 500 characters.
expiryTimeintegerYesTime until payment request expires, in minutes. Min: 1, Max: 1440 (24 hours). Default: 60
redirectUrlstringNoAbsolute URL (starting with http:// or https://) where customer will be redirected after clicking “I Paid” or “Cancel”. Max 500 characters.

Response (Success - 201 Created):

{
  "success": true,
  "data": {
    "success": true,
    "paymentRequestId": "fd74f812-268c-43bc-9e42-924ce36ce1ff",
    "linkId": "825757/20251213/0007",
    "paymentUrl": "https://app.suzaa.com/recipient/825757/20251213/0007",
    "expiresAt": "2025-12-13T15:52:28.353Z",
    "message": "Payment request created successfully"
  }
}

Response Fields:

FieldTypeDescription
paymentRequestIdUUIDUnique identifier for this payment request (internal database ID)
linkIdstringHuman-readable payment link ID. Format: {merchantId}/{date}/{sequenceNumber}
paymentUrlstringFull URL to payment page. Share this with customer or redirect them here.
expiresAtISO 8601Timestamp when this payment request will expire
messagestringHuman-readable success message

Response (Error - 400 Bad Request):

{
  "success": false,
  "error": {
    "code": "INVALID_AMOUNT",
    "message": "Amount must be a positive number"
  }
}

Response (Error - 401 Unauthorized):

{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid or missing API key"
  }
}

Example (Node.js):

const axios = require('axios');
 
async function createPaymentRequest() {
  try {
    const response = await axios.post('https://api.suzaa.com/payments/requests', {
      amount: '150.00',
      description: 'Premium Subscription - 1 Year',
      expiryTime: 120,
      redirectUrl: 'https://mysite.com/payment-return'
    }, {
      headers: {
        'Authorization': 'Bearer sza_live_YOUR_API_KEY',
        'Content-Type': 'application/json'
      }
    });
    
    console.log('Payment URL:', response.data.data.paymentUrl);
    console.log('Link ID:', response.data.data.linkId);
    
    return response.data.data;
  } catch (error) {
    console.error('Error:', error.response.data);
    throw error;
  }
}

Example (PHP):

<?php
 
function createPaymentRequest($apiKey, $amount, $description, $expiryTime = 60, $redirectUrl = null) {
    $data = [
        'amount' => $amount,
        'description' => $description,
        'expiryTime' => $expiryTime
    ];
    
    if ($redirectUrl) {
        $data['redirectUrl'] = $redirectUrl;
    }
    
    $ch = curl_init('https://api.suzaa.com/payments/requests');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json'
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode !== 201) {
        throw new Exception('API Error: ' . $response);
    }
    
    $result = json_decode($response, true);
    return $result['data'];
}
 
// Usage
try {
    $payment = createPaymentRequest(
        'sza_live_YOUR_API_KEY',
        '100.00',
        'Order #12345',
        60,
        'https://mysite.com/return'
    );
    
    echo "Payment URL: " . $payment['paymentUrl'] . "\n";
    echo "Link ID: " . $payment['linkId'] . "\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Example (Python):

import requests
import json
 
def create_payment_request(api_key, amount, description, expiry_time=60, redirect_url=None):
    url = 'https://api.suzaa.com/payments/requests'
    
    headers = {
        'Authorization': f'Bearer {api_key}',
        'Content-Type': 'application/json'
    }
    
    data = {
        'amount': str(amount),
        'description': description,
        'expiryTime': expiry_time
    }
    
    if redirect_url:
        data['redirectUrl'] = redirect_url
    
    response = requests.post(url, headers=headers, json=data)
    
    if response.status_code != 201:
        raise Exception(f'API Error: {response.text}')
    
    result = response.json()
    return result['data']
 
# Usage
try:
    payment = create_payment_request(
        api_key='sza_live_YOUR_API_KEY',
        amount=100.00,
        description='Order #12345',
        expiry_time=60,
        redirect_url='https://mysite.com/return'
    )
    
    print(f"Payment URL: {payment['paymentUrl']}")
    print(f"Link ID: {payment['linkId']}")
except Exception as e:
    print(f"Error: {e}")

2. Get Payment Request Details

Retrieve details about a specific payment request.

Endpoint:

GET /payments/requests/{paymentRequestId}

Headers:

Authorization: Bearer YOUR_API_KEY

Path Parameters:

ParameterTypeDescription
paymentRequestIdUUIDThe payment request ID returned when creating the request

Response (Success - 200 OK):

{
  "success": true,
  "data": {
    "paymentRequestId": "fd74f812-268c-43bc-9e42-924ce36ce1ff",
    "linkId": "825757/20251213/0007",
    "amount": "100.00",
    "description": "Order #12345",
    "status": "pending",
    "paymentUrl": "https://app.suzaa.com/recipient/825757/20251213/0007",
    "redirectUrl": "https://mysite.com/return",
    "expiryTime": 60,
    "expiresAt": "2025-12-13T15:52:28.353Z",
    "createdAt": "2025-12-13T14:52:28.353Z",
    "claimedAt": null
  }
}

Status Values:

StatusDescription
pendingPayment request created, waiting for customer
claimed_paidCustomer clicked “I Paid” button
canceledCustomer clicked “Cancel” button
expiredPayment request expired before completion

Example (cURL):

curl -X GET https://api.suzaa.com/payments/requests/fd74f812-268c-43bc-9e42-924ce36ce1ff \
  -H "Authorization: Bearer sza_live_YOUR_API_KEY"

3. List Payment Requests

Get a paginated list of your payment requests.

Endpoint:

GET /payments/requests

Headers:

Authorization: Bearer YOUR_API_KEY

Query Parameters:

ParameterTypeDefaultDescription
pageinteger1Page number (1-indexed)
limitinteger20Items per page (1-100)
statusstringallFilter by status: pending, claimed_paid, canceled, expired, all
sortstringcreated_descSort order: created_asc, created_desc, amount_asc, amount_desc

Response (Success - 200 OK):

{
  "success": true,
  "data": {
    "requests": [
      {
        "paymentRequestId": "fd74f812-268c-43bc-9e42-924ce36ce1ff",
        "linkId": "825757/20251213/0007",
        "amount": "100.00",
        "description": "Order #12345",
        "status": "claimed_paid",
        "createdAt": "2025-12-13T14:52:28.353Z",
        "expiresAt": "2025-12-13T15:52:28.353Z"
      }
      // ... more requests
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 156,
      "totalPages": 8
    }
  }
}

Example:

curl -X GET "https://api.suzaa.com/payments/requests?page=1&limit=10&status=claimed_paid" \
  -H "Authorization: Bearer sza_live_YOUR_API_KEY"

4. Cancel Payment Request

Cancel a pending payment request.

Endpoint:

DELETE /payments/requests/{paymentRequestId}

Headers:

Authorization: Bearer YOUR_API_KEY

Response (Success - 200 OK):

{
  "success": true,
  "data": {
    "message": "Payment request cancelled successfully",
    "paymentRequestId": "fd74f812-268c-43bc-9e42-924ce36ce1ff",
    "status": "canceled"
  }
}

Example:

curl -X DELETE https://api.suzaa.com/payments/requests/fd74f812-268c-43bc-9e42-924ce36ce1ff \
  -H "Authorization: Bearer sza_live_YOUR_API_KEY"

5. Get Public Payment Request

Get payment request details without authentication (for displaying payment page).

Endpoint:

GET /public/payment/{linkId}

No authentication required - This is a public endpoint used by the payment page.

Path Parameters:

ParameterTypeDescription
linkIdstringThe link ID (e.g., 825757/20251213/0007)

Response (Success - 200 OK):

{
  "success": true,
  "data": {
    "linkId": "825757/20251213/0007",
    "amount": "100.00",
    "amountCrypto": "0.00234567",
    "cryptocurrency": "BTC",
    "walletAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "description": "Order #12345",
    "qrCodeUrl": "https://app.suzaa.com/qr/825757/20251213/0007.png",
    "status": "pending",
    "expiresAt": "2025-12-13T15:52:28.353Z",
    "redirectUrl": "https://mysite.com/return",
    "merchantName": "My Store"
  }
}

This endpoint is called by:

  • The payment page frontend to display payment details
  • Your integration to check payment status without authentication

Payment Request Lifecycle

State Diagram

        CREATE

      [PENDING] ──────────────────→ [EXPIRED]
          ↓                              ↑
    Customer Action                  Timeout

   ┌──────┴──────┐
   ↓             ↓
[CLAIMED_PAID] [CANCELED]

Merchant Verifies
on Blockchain

Status Flow

  1. Created (pending)

    • Payment request just created
    • Customer hasn’t taken action yet
    • Waiting for customer to pay
  2. Customer Claims Payment (claimed_paid)

    • Customer clicked “I Paid” button
    • Does NOT mean payment verified
    • Merchant should verify on blockchain
  3. Customer Cancels (canceled)

    • Customer clicked “Cancel” button
    • Can create new payment request if needed
  4. Expires (expired)

    • expiryTime elapsed without action
    • Cannot be used anymore
    • Merchant should create new request

Important Notes

Status claimed_paid does NOT mean verified payment!

When status is claimed_paid:

  • Customer CLAIMS they paid
  • Merchant MUST verify on blockchain
  • Check your wallet for incoming transaction
  • Match amount and timestamp
  • Only fulfill order after blockchain confirmation

This is the merchant’s responsibility - Suzaa doesn’t verify blockchain transactions.


Integration Examples

E-Commerce Integration

// 1. Customer clicks "Pay with Crypto"
app.post('/checkout', async (req, res) => {
  const order = await createOrder(req.body);
  
  // 2. Create Suzaa payment request
  const payment = await suzaa.createPaymentRequest({
    amount: order.total,
    description: `Order #${order.id}`,
    expiryTime: 60,
    redirectUrl: `https://mystore.com/order/${order.id}/confirm`
  });
  
  // 3. Store payment link ID with order
  await updateOrder(order.id, {
    suzaa_link_id: payment.linkId,
    suzaa_payment_url: payment.paymentUrl
  });
  
  // 4. Redirect customer to Suzaa payment page
  res.redirect(payment.paymentUrl);
});
 
// 5. Customer returns after clicking "I Paid"
app.get('/order/:id/confirm', async (req, res) => {
  const status = req.query.status; // claimed_paid or canceled
  const paymentId = req.query.paymentId;
  
  if (status === 'claimed_paid') {
    // Mark order as "pending verification"
    await updateOrder(req.params.id, {
      status: 'pending_verification',
      suzaa_payment_id: paymentId
    });
    
    res.render('thank-you', {
      message: 'Payment claimed! We will verify and process your order soon.'
    });
  } else {
    res.render('cancelled', {
      message: 'Payment cancelled. Please try again or choose another method.'
    });
  }
});
 
// 6. Admin manually verifies blockchain transaction
app.post('/admin/orders/:id/verify-payment', async (req, res) => {
  const order = await getOrder(req.params.id);
  
  // Admin checks blockchain and confirms transaction exists
  // Then marks order as paid
  await updateOrder(req.params.id, {
    status: 'paid',
    verified_at: new Date()
  });
  
  // Ship the order
  await shipOrder(req.params.id);
});

Subscription Service Integration

# Create recurring payment request
def create_subscription_payment(user_id, plan):
    payment = suzaa_api.create_payment_request(
        amount=plan.price,
        description=f"Subscription: {plan.name} - Month {get_current_month()}",
        expiry_time=1440,  # 24 hours
        redirect_url=f"https://myapp.com/subscription/callback/{user_id}"
    )
    
    # Store payment details
    db.subscriptions.update(user_id, {
        'payment_link_id': payment['linkId'],
        'payment_url': payment['paymentUrl'],
        'status': 'awaiting_payment'
    })
    
    # Email payment link to user
    send_email(
        to=user.email,
        subject="Your Monthly Subscription Payment",
        body=f"Please complete your payment: {payment['paymentUrl']}"
    )
    
    return payment

Invoice System Integration

<?php
// Generate invoice and payment request
function createInvoice($customerId, $items) {
    $total = calculateTotal($items);
    
    // Create payment request
    $payment = SuzaaAPI::createPaymentRequest([
        'amount' => $total,
        'description' => "Invoice #" . generateInvoiceNumber(),
        'expiryTime' => 2880, // 48 hours
        'redirectUrl' => "https://myapp.com/invoices/paid"
    ]);
    
    // Create invoice record
    $invoice = Invoice::create([
        'customer_id' => $customerId,
        'items' => json_encode($items),
        'total' => $total,
        'suzaa_link_id' => $payment['linkId'],
        'suzaa_payment_url' => $payment['paymentUrl'],
        'status' => 'unpaid'
    ]);
    
    // Send invoice email with payment link
    sendInvoiceEmail($customerId, $invoice, $payment['paymentUrl']);
    
    return $invoice;
}

Error Handling

Error Response Structure

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description",
    "field": "fieldName" // Optional: which field caused the error
  }
}

Common Error Codes

HTTP StatusError CodeDescriptionSolution
400INVALID_AMOUNTAmount is not a valid number or is negativeSend positive number as string or number
400INVALID_DESCRIPTIONDescription is missing or too longProvide description under 500 chars
400INVALID_EXPIRY_TIMEExpiry time not in valid range (1-1440)Use minutes between 1 and 1440
400INVALID_REDIRECT_URLURL format is invalidUse absolute URL with http:// or https://
400MISSING_REQUIRED_FIELDRequired field not providedCheck request body has all required fields
401INVALID_API_KEYAPI key is missing or invalidCheck Authorization header and API key
401EXPIRED_API_KEYAPI key has expiredGenerate new API key
403INSUFFICIENT_PERMISSIONSAPI key doesn’t have required permissionsCheck API key permissions in dashboard
404PAYMENT_REQUEST_NOT_FOUNDPayment request ID doesn’t existVerify the payment request ID
409PAYMENT_REQUEST_ALREADY_COMPLETEDCannot modify completed payment requestCreate new payment request
429RATE_LIMIT_EXCEEDEDToo many requestsWait and retry with exponential backoff
500INTERNAL_SERVER_ERRORServer errorRetry request, contact support if persists
503SERVICE_UNAVAILABLEService temporarily unavailableRetry with exponential backoff

Error Handling Best Practices

async function createPaymentWithRetry(data, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await suzaaAPI.createPaymentRequest(data);
      return response.data;
    } catch (error) {
      if (error.response) {
        const status = error.response.status;
        const errorCode = error.response.data.error?.code;
        
        // Don't retry client errors (4xx)
        if (status >= 400 && status < 500) {
          if (errorCode === 'RATE_LIMIT_EXCEEDED' && attempt < maxRetries) {
            // Wait before retry
            await sleep(2000 * attempt);
            continue;
          }
          
          // Other 4xx errors - don't retry
          throw new Error(`API Error: ${error.response.data.error.message}`);
        }
        
        // Retry server errors (5xx)
        if (status >= 500 && attempt < maxRetries) {
          await sleep(1000 * Math.pow(2, attempt)); // Exponential backoff
          continue;
        }
      }
      
      // Network error or max retries reached
      if (attempt === maxRetries) {
        throw error;
      }
      
      await sleep(1000 * attempt);
    }
  }
}

Best Practices

1. Store Payment Details

Always store these fields from the API response:

{
  paymentRequestId: "fd74f812...",  // For API queries
  linkId: "825757/20251213/0007",   // Human-readable reference
  paymentUrl: "https://app.suzaa.com/...",  // For customer
  expiresAt: "2025-12-13T15:52:28.353Z"  // To check expiration
}

2. Handle Expiration

// Check if payment request expired
function isExpired(expiresAt) {
  return new Date(expiresAt) < new Date();
}
 
// If expired, create new payment request
if (isExpired(payment.expiresAt)) {
  payment = await createNewPaymentRequest(order);
}

3. Verify Blockchain Transactions

CRITICAL: Always verify transactions on the blockchain before fulfilling orders!

// Example verification workflow
async function verifyPayment(order, linkId) {
  // 1. Check your wallet for incoming transactions
  const wallet = await getWalletTransactions(YOUR_WALLET_ADDRESS);
  
  // 2. Find transaction matching:
  //    - Amount (within acceptable range due to fees)
  //    - Timestamp (around when customer claimed payment)
  const payment = wallet.transactions.find(tx => {
    const amountMatch = Math.abs(tx.amount - order.total_crypto) < 0.0001;
    const timeMatch = Math.abs(tx.timestamp - order.claimed_at) < 3600; // 1 hour
    return amountMatch && timeMatch;
  });
  
  // 3. Verify confirmations
  if (payment && payment.confirmations >= REQUIRED_CONFIRMATIONS) {
    // Payment verified!
    return { verified: true, txHash: payment.hash };
  }
  
  return { verified: false };
}

4. Handle redirectUrl Properly

// Build complete redirect URL with all necessary info
const redirectUrl = new URL('https://mysite.com/payment-callback');
redirectUrl.searchParams.set('order_id', order.id);
redirectUrl.searchParams.set('session', session.id);
// Don't add payment status here - Suzaa will add it
 
const payment = await suzaa.createPaymentRequest({
  amount: order.total,
  description: `Order #${order.id}`,
  expiryTime: 60,
  redirectUrl: redirectUrl.toString()
});

5. Provide Clear Customer Communication

// After customer claims payment
async function handlePaymentClaimed(order) {
  // Update order status
  order.status = 'pending_verification';
  await order.save();
  
  // Send confirmation email
  await sendEmail(order.customer.email, {
    subject: 'Payment Received - Awaiting Confirmation',
    body: `
      Thank you for your payment!
      
      We've received notification that you sent payment for Order #${order.id}.
      
      We're now verifying your transaction on the blockchain. This usually 
      takes a few minutes to a few hours depending on network conditions.
      
      Once verified, we'll send you a shipping confirmation.
      
      Payment Details:
      - Amount: ${order.total} USD
      - Suzaa Payment ID: ${order.suzaa_link_id}
    `
  });
}

6. Implement Timeout Handling

// Set timeout for blockchain verification
setTimeout(async () => {
  const order = await getOrder(orderId);
  
  if (order.status === 'pending_verification') {
    // Still not verified after X hours
    await sendEmail(order.customer.email, {
      subject: 'Payment Verification Delayed',
      body: `
        We're still waiting to see your payment on the blockchain.
        
        Please ensure you:
        1. Sent the correct amount
        2. Sent to the correct address
        3. Paid the network fee
        
        If you need assistance, please reply to this email with your 
        transaction hash.
      `
    });
  }
}, 6 * 60 * 60 * 1000); // 6 hours

7. Use Idempotency

// Prevent duplicate payment requests for same order
async function getOrCreatePaymentRequest(order) {
  // Check if payment request already exists
  if (order.suzaa_link_id) {
    const existing = await suzaa.getPaymentRequest(order.suzaa_payment_request_id);
    
    // Reuse if not expired
    if (new Date(existing.expiresAt) > new Date()) {
      return existing;
    }
  }
  
  // Create new payment request
  const payment = await suzaa.createPaymentRequest({
    amount: order.total,
    description: `Order #${order.id}`,
    expiryTime: 60,
    redirectUrl: getRedirectUrl(order.id)
  });
  
  // Store with order
  order.suzaa_payment_request_id = payment.paymentRequestId;
  order.suzaa_link_id = payment.linkId;
  order.suzaa_payment_url = payment.paymentUrl;
  await order.save();
  
  return payment;
}

Webhooks (Future)

Note: Webhooks are not currently implemented but are planned for future releases.

Planned Webhook Events

// When customer claims payment
{
  "event": "payment.claimed",
  "paymentRequestId": "fd74f812...",
  "linkId": "825757/20251213/0007",
  "timestamp": "2025-12-13T15:52:28.353Z"
}
 
// When payment request expires
{
  "event": "payment.expired",
  "paymentRequestId": "fd74f812...",
  "linkId": "825757/20251213/0007",
  "timestamp": "2025-12-13T16:52:28.353Z"
}

Currently, use the redirect URL mechanism for callbacks.


Security

API Key Security

# Good: Environment variable
export SUZAA_API_KEY="sza_live_YOUR_KEY"
 
# Bad: Hardcoded
const apiKey = "sza_live_YOUR_KEY"; // DON'T DO THIS
 
# Good: Use in code
const apiKey = process.env.SUZAA_API_KEY;

HTTPS Only

Always use HTTPS in production. Suzaa API rejects HTTP requests.

Input Validation

// Validate amounts
function validateAmount(amount) {
  const num = parseFloat(amount);
  if (isNaN(num) || num <= 0 || num > 1000000) {
    throw new Error('Invalid amount');
  }
  return num.toFixed(2);
}
 
// Validate redirect URLs
function validateRedirectUrl(url) {
  try {
    const parsed = new URL(url);
    if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
      throw new Error('Invalid protocol');
    }
    return url;
  } catch (e) {
    throw new Error('Invalid URL');
  }
}

Preventing Double-Spending

Customers might try to use the same payment for multiple orders:

// Track which payment links have been used
async function checkPaymentNotUsed(linkId) {
  const existingOrder = await Order.findOne({ suzaa_link_id: linkId });
  
  if (existingOrder && existingOrder.status !== 'cancelled') {
    throw new Error('Payment link already used');
  }
}

Rate Limits

Current Limits

EndpointRate LimitWindow
POST /payments/requests100 requestsper minute
GET /payments/requests300 requestsper minute
GET /payments/requests/{id}300 requestsper minute
DELETE /payments/requests/{id}50 requestsper minute
GET /public/payment/{linkId}1000 requestsper minute

Rate Limit Headers

Responses include rate limit information:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1702468800

Handling Rate Limits

async function createPaymentWithRateLimit(data) {
  try {
    return await suzaa.createPaymentRequest(data);
  } catch (error) {
    if (error.response?.status === 429) {
      const resetTime = error.response.headers['x-ratelimit-reset'];
      const waitTime = (resetTime * 1000) - Date.now();
      
      console.log(`Rate limited. Waiting ${waitTime}ms`);
      await sleep(waitTime);
      
      // Retry
      return await suzaa.createPaymentRequest(data);
    }
    throw error;
  }
}

Testing

Test API Keys

Use test API keys (starting with sza_test_) for development:

const apiKey = process.env.NODE_ENV === 'production' 
  ? process.env.SUZAA_LIVE_KEY 
  : process.env.SUZAA_TEST_KEY;

Test Mode Behavior

Test mode:

  • Uses test wallet addresses
  • Payments don’t require real cryptocurrency
  • All features work identically to production
  • Data isolated from production

Example Test

describe('Suzaa Payment Integration', () => {
  it('should create payment request', async () => {
    const payment = await suzaa.createPaymentRequest({
      amount: '100.00',
      description: 'Test Order',
      expiryTime: 60
    });
    
    expect(payment).toHaveProperty('paymentUrl');
    expect(payment).toHaveProperty('linkId');
    expect(payment.paymentUrl).toContain('app.suzaa.com');
  });
  
  it('should handle expired payment', async () => {
    const payment = await suzaa.createPaymentRequest({
      amount: '50.00',
      description: 'Test',
      expiryTime: 1 // 1 minute
    });
    
    // Wait for expiration
    await sleep(65000); // 65 seconds
    
    const details = await suzaa.getPaymentRequest(payment.paymentRequestId);
    expect(details.status).toBe('expired');
  });
});

Appendix

Complete TypeScript Interface

// Request Types
interface CreatePaymentRequest {
  amount: string | number;
  description: string;
  expiryTime: number;
  redirectUrl?: string;
}
 
// Response Types
interface PaymentRequestResponse {
  success: true;
  data: {
    success: true;
    paymentRequestId: string;
    linkId: string;
    paymentUrl: string;
    expiresAt: string;
    message: string;
  };
}
 
interface PaymentRequestDetails {
  paymentRequestId: string;
  linkId: string;
  amount: string;
  description: string;
  status: 'pending' | 'claimed_paid' | 'canceled' | 'expired';
  paymentUrl: string;
  redirectUrl: string | null;
  expiryTime: number;
  expiresAt: string;
  createdAt: string;
  claimedAt: string | null;
}
 
interface ErrorResponse {
  success: false;
  error: {
    code: string;
    message: string;
    field?: string;
  };
}
 
// API Client
class SuzaaAPI {
  constructor(private apiKey: string, private baseUrl: string = 'https://api.suzaa.com') {}
  
  async createPaymentRequest(data: CreatePaymentRequest): Promise<PaymentRequestResponse['data']> {
    const response = await fetch(`${this.baseUrl}/payments/requests`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });
    
    const result = await response.json();
    
    if (!result.success) {
      throw new Error(result.error.message);
    }
    
    return result.data;
  }
  
  async getPaymentRequest(id: string): Promise<PaymentRequestDetails> {
    const response = await fetch(`${this.baseUrl}/payments/requests/${id}`, {
      headers: {
        'Authorization': `Bearer ${this.apiKey}`
      }
    });
    
    const result = await response.json();
    
    if (!result.success) {
      throw new Error(result.error.message);
    }
    
    return result.data;
  }
}

Support

Getting Help

Reporting Issues

When reporting API issues, include:

  1. Endpoint used
  2. Request body (remove sensitive data)
  3. Response received
  4. Expected behavior
  5. Timestamp of request
  6. Your merchant ID (not API key!)

Last Updated: December 13, 2024
API Version: v1
Document Version: 1.0.0