⚙️ Paynch Manual Integration

v2.0 - Updated 2026

Complete API Documentation for Custom Implementations

📋 Overview

The Paynch Manual Integration gives you full control over the payment flow, allowing you to customize the checkout experience while maintaining security and reliability.

💡 Key Difference:
Button Integration: Single <script> tag, zero configuration
Manual Integration (this doc): Full control, custom UI, advanced features

🎯 When to Use Manual Integration

🎨 Custom Design

You want complete control over the checkout UI and user experience

🔄 Complex Flows

Multi-step checkout, cart systems, or subscription management

📊 Advanced Tracking

Custom analytics, conversion tracking, or A/B testing

🔗 System Integration

Deep integration with existing ERP, CRM, or inventory systems

🏗️ System Architecture

Paynch operates as a 100% on-chain, frontend-only payment system:

⚠️ Important: There is NO centralized Paynch backend, no API keys, and no webhooks. All execution happens directly in the user's browser via Web3.js connecting to the blockchain.

Payment Flow

1
User accesses checkout page
URL contains: shop, orderId, amount
2
Paynch script loads in browser
Reads parameters, initializes Web3, prepares transaction
3
User clicks "Connect Wallet"
MetaMask/Trust Wallet/WalletConnect connects to BSC network
4
User clicks "Pay"
USDT transfer transaction is submitted to blockchain
5
Auto-verification starts
Frontend polls Paynch API every 3-5 seconds
6
Payment confirmed on blockchain
API returns count > 0 with transaction details
7
Backend validates payment
PHP/Node.js validates amount, updates database, releases product
8
User redirected to success page
Order marked as "confirmed", email sent, product delivered

📦 Initial Setup

1. Database Structure

Create a table to store orders. Here's a recommended MySQL schema:

CREATE TABLE `pedidos` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `order_id` VARCHAR(50) UNIQUE NOT NULL,
  `produto` VARCHAR(255) NOT NULL,
  `valor_usdt` DECIMAL(18,2) NOT NULL,
  `status` ENUM('pendente', 'confirmado', 'cancelado') DEFAULT 'pendente',
  `contract_loja` VARCHAR(42) NOT NULL,
  `tx_code` VARCHAR(100) DEFAULT NULL,
  `payer` VARCHAR(50) DEFAULT NULL,
  `amount_recebido` DECIMAL(18,2) DEFAULT NULL,
  `ip_cliente` VARCHAR(45),
  `user_agent` TEXT,
  `criado_em` DATETIME DEFAULT CURRENT_TIMESTAMP,
  `pago_em` DATETIME DEFAULT NULL,
  INDEX idx_order_id (order_id),
  INDEX idx_status (status),
  INDEX idx_criado_em (criado_em)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2. Generate Your Shop Contract

📍 Get your contract address:
Visit the Paynch Dashboard and create a new shop. You'll receive an Ethereum address like:
0xE5fF9d546278a7CE0DF261EB85945Df2F0Dcc3c6

3. Required Environment

Requirement Description Notes
PHP 7.4+ Backend language (or Node.js) PDO, cURL required
MySQL 5.7+ Database for orders MariaDB also supported
HTTPS SSL Certificate Required for MetaMask
Modern Browser Chrome, Firefox, Edge, Safari Web3 support needed

🎨 Frontend Integration

1. Required Libraries

Add these scripts before closing </body> tag:

<!-- Process polyfill for Web3 v4 -->
<script type="module">
  window.process = { browser: true, env: { ENVIRONMENT: 'BROWSER' } };
</script>

<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

<!-- Web3.js v4 -->
<script src="https://unpkg.com/web3@4.0.1/dist/web3.min.js"></script>

<!-- Paynch Payment Script -->
<script type="module" src="https://cdn.jsdelivr.net/gh/Paynch-Payments/teste@main/paynch-connect.js"></script>

2. URL Parameters Required

Your checkout page MUST contain these GET parameters:

Parameter Type Description Example
shop String Store contract address (42 chars) 0xE5fF9d546278a7CE0DF261EB85945Df2F0Dcc3c6
orderId String Unique order ID (8-20 chars) ORD_1738368123_a1b2c3
amount Number USDT amount (use dot decimal) 15.00 or 99.90

Example URL:

https://yourstore.com/checkout.php?shop=0xE5fF9d546278a7CE0DF261EB85945Df2F0Dcc3c6&orderId=ORD_123456&amount=15.00

3. Auto-inject URL Parameters (JavaScript)

Use this script to ensure parameters are always present:

<script>
const url = new URL(window.location);
let changed = false;

const params = {
  shop: '<?= $contract ?>',
  orderId: '<?= $orderId ?>',
  amount: '<?= $valor ?>'
};

Object.keys(params).forEach(key => {
  if (!url.searchParams.has(key)) {
    url.searchParams.set(key, params[key]);
    changed = true;
  }
});

if (changed) {
  history.replaceState(null, '', url.toString());
}
</script>

4. Required HTML Elements

The Paynch script expects these specific element IDs:

<!-- Hidden inputs for configuration -->
<input type="hidden" id="amount-input" value="<?= $valor ?>">
<input type="hidden" id="order-id-input" value="<?= $orderId ?>">
<input type="hidden" id="contract-input" value="<?= $contract ?>">

<!-- Payment buttons (IDs are MANDATORY) -->
<button id="connect-wallet">🔌 Connect Wallet</button>
<button id="pay-button">🚀 Pay <?= $valor ?> USDT</button>

<!-- Status display -->
<div id="pay-status">
  💳 Connect your wallet to continue
</div>
❌ DO NOT change these IDs:
connect-wallet
pay-button
pay-status
amount-input
order-id-input
contract-input

The Paynch script depends on these exact IDs to function.

5. Auto-Verification System (Frontend)

Implement automatic payment verification after transaction submission:

<script>
// Configuration
const CONFIG = {
  CONTRACT: '<?= $contract ?>',
  ORDER_ID: '<?= $orderId ?>',
  VALOR: <?= $valor ?>,
  MAX_TENTATIVAS: 20,         // 20 attempts
  INTERVALO_MS: 5000,         // Every 5 seconds
  DELAY_INICIAL_MS: 3000      // Start after 3s
};

let autoCheckInterval = null;
let tentativasAuto = 0;
let pagamentoConfirmado = false;

// Main verification function
async function verificarPagamento(options = {}) {
  const { isAuto = false, showLoading = true } = options;
  
  const statusEl = document.getElementById('pay-status');
  
  if (showLoading && isAuto) {
    statusEl.innerHTML = `
      <div class="loading-spinner"></div>
      <div>Waiting for blockchain confirmation...</div>
      <div>Attempt ${tentativasAuto}/${CONFIG.MAX_TENTATIVAS}</div>
    `;
  }
  
  try {
    const formData = new FormData();
    formData.append('orderId', CONFIG.ORDER_ID);
    formData.append('contract', CONFIG.CONTRACT);
    
    const response = await fetch('atualizar-status.php', {
      method: 'POST',
      body: formData
    });
    
    const data = await response.json();
    
    if (data.pago) {
      pagamentoConfirmado = true;
      pararVerificacaoAutomatica();
      
      statusEl.innerHTML = `
        <div style="color: #10b981; font-size: 1.2em;">
          ✅ Payment Confirmed!
        </div>
        <div>Amount: ${data.dados.valor_recebido} USDT</div>
        <div>TX: ${data.dados.tx_code}</div>
      `;
      
      // Redirect after 3 seconds
      setTimeout(() => {
        window.location.href = 'success.php?orderId=' + CONFIG.ORDER_ID;
      }, 3000);
      
      return true;
    } else {
      if (!isAuto) {
        statusEl.innerHTML = '⚠️ Payment not detected yet. Please wait...';
      }
      return false;
    }
  } catch (error) {
    console.error('Verification error:', error);
    return false;
  }
}

// Start automatic verification
function iniciarVerificacaoAutomatica() {
  if (pagamentoConfirmado || autoCheckInterval) return;
  
  setTimeout(() => {
    tentativasAuto = 0;
    
    autoCheckInterval = setInterval(async () => {
      tentativasAuto++;
      
      const confirmado = await verificarPagamento({ isAuto: true });
      
      if (confirmado || tentativasAuto >= CONFIG.MAX_TENTATIVAS) {
        pararVerificacaoAutomatica();
      }
    }, CONFIG.INTERVALO_MS);
  }, CONFIG.DELAY_INICIAL_MS);
}

function pararVerificacaoAutomatica() {
  if (autoCheckInterval) {
    clearInterval(autoCheckInterval);
    autoCheckInterval = null;
  }
}

// Trigger verification after payment button click
document.getElementById('pay-button')?.addEventListener('click', () => {
  setTimeout(() => {
    if (!pagamentoConfirmado) {
      iniciarVerificacaoAutomatica();
    }
  }, 2000);
});

// Cleanup on page unload
window.addEventListener('beforeunload', pararVerificacaoAutomatica);
</script>

⚙️ Backend Integration

1. Configuration File (config.php)

<?php
// Security headers
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');

// Start secure session
if (session_status() === PHP_SESSION_NONE) {
    ini_set('session.cookie_httponly', 1);
    ini_set('session.cookie_secure', 1);
    session_start();
}

// Database credentials
$host   = getenv('DB_HOST') ?: 'localhost';
$dbname = getenv('DB_NAME') ?: 'your_database';
$user   = getenv('DB_USER') ?: 'your_user';
$pass   = getenv('DB_PASS') ?: 'your_password';

// PDO connection
try {
    $pdo = new PDO(
        "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
        $user,
        $pass,
        [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false
        ]
    );
} catch (PDOException $e) {
    error_log("Database connection error: " . $e->getMessage());
    die(json_encode(['success' => false, 'error' => 'Database error']));
}

// Helper functions
function validarContrato($contract) {
    return preg_match('/^0x[a-fA-F0-9]{40}$/', $contract);
}

function validarOrderId($orderId) {
    return preg_match('/^[A-Z0-9]{8,20}$/', $orderId);
}

function validarValor($valor) {
    $valor = floatval($valor);
    return $valor > 0 && $valor <= 1000000;
}

function consultarPaynchApi($contract, $orderId) {
    $url = "https://pay.paynch.app/paynch.php?" . http_build_query([
        'contract' => $contract,
        'orderId' => $orderId
    ]);
    
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode !== 200) return false;
    
    $data = json_decode($response, true);
    return (json_last_error() === JSON_ERROR_NONE) ? $data : false;
}

// Constants
define('TOLERANCIA_PERCENTUAL', 1.2); // 1.2% tolerance for platform fees
?>

2. Order Creation (checkout.php)

<?php
require_once 'config.php';

// Get and validate parameters
$produto = $_GET['produto'] ?? 'Unknown Product';
$valor = floatval($_GET['amount'] ?? 0);
$contract = $_GET['shop'] ?? '';

if (!validarValor($valor) || !validarContrato($contract)) {
    die('Invalid parameters');
}

// Generate unique Order ID
function generateOrderId($length = 12) {
    $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
    $id = '';
    for ($i = 0; $i < $length; $i++) {
        $id .= $chars[random_int(0, strlen($chars) - 1)];
    }
    return $id;
}

// Check for existing pending order in session
$orderId = null;
if (isset($_SESSION['current_order_id'])) {
    $stmt = $pdo->prepare("
        SELECT order_id 
        FROM pedidos 
        WHERE order_id = ? 
        AND status = 'pendente'
        AND criado_em > DATE_SUB(NOW(), INTERVAL 30 MINUTE)
    ");
    $stmt->execute([$_SESSION['current_order_id']]);
    
    if ($stmt->fetch()) {
        $orderId = $_SESSION['current_order_id'];
    }
}

// Create new order if none exists
if (!$orderId) {
    $orderId = generateOrderId();
    
    $stmt = $pdo->prepare("
        INSERT INTO pedidos (
            order_id, produto, valor_usdt, status,
            contract_loja, ip_cliente, user_agent, criado_em
        ) VALUES (?, ?, ?, 'pendente', ?, ?, ?, NOW())
    ");
    
    $stmt->execute([
        $orderId,
        $produto,
        $valor,
        $contract,
        $_SERVER['REMOTE_ADDR'] ?? 'unknown',
        $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
    ]);
    
    $_SESSION['current_order_id'] = $orderId;
}
?>

<!-- Your checkout HTML here -->

3. Payment Validation (atualizar-status.php)

<?php
require_once 'config.php';

header('Content-Type: application/json; charset=utf-8');

// Get parameters
$orderId = $_REQUEST['orderId'] ?? '';
$contract = $_REQUEST['contract'] ?? '';

if (!validarOrderId($orderId) || !validarContrato($contract)) {
    http_response_code(400);
    echo json_encode(['pago' => false, 'mensagem' => 'Invalid parameters']);
    exit;
}

try {
    $pdo->beginTransaction();
    
    // Get order with lock
    $stmt = $pdo->prepare("
        SELECT id, valor_usdt, status, produto
        FROM pedidos 
        WHERE order_id = ?
        FOR UPDATE
    ");
    $stmt->execute([$orderId]);
    $pedido = $stmt->fetch();
    
    if (!$pedido) {
        $pdo->rollBack();
        echo json_encode(['pago' => false, 'mensagem' => 'Order not found']);
        exit;
    }
    
    if ($pedido['status'] === 'confirmado') {
        $pdo->rollBack();
        echo json_encode([
            'pago' => true,
            'mensagem' => 'Already confirmed',
            'status' => 'confirmado'
        ]);
        exit;
    }
    
    // Query Paynch API
    $paynchData = consultarPaynchApi($contract, $orderId);
    
    if (!$paynchData || !$paynchData['success'] || $paynchData['count'] == 0) {
        $pdo->rollBack();
        echo json_encode([
            'pago' => false,
            'mensagem' => 'Payment not detected yet'
        ]);
        exit;
    }
    
    // Get most recent payment
    usort($paynchData['payments'], function($a, $b) {
        return ($b['timestamp'] ?? 0) - ($a['timestamp'] ?? 0);
    });
    
    $pagamento = $paynchData['payments'][0];
    
    // Validate amount
    $valorEsperado = floatval($pedido['valor_usdt']);
    $valorRecebido = floatval($pagamento['amountHuman']);
    $tolerancia = $valorEsperado * (TOLERANCIA_PERCENTUAL / 100);
    
    if ($valorRecebido < ($valorEsperado - $tolerancia)) {
        $pdo->rollBack();
        echo json_encode([
            'pago' => false,
            'mensagem' => 'Insufficient amount',
            'esperado' => $valorEsperado,
            'recebido' => $valorRecebido
        ]);
        exit;
    }
    
    // Update order as confirmed
    $updateStmt = $pdo->prepare("
        UPDATE pedidos 
        SET status = 'confirmado',
            tx_code = ?,
            payer = ?,
            amount_recebido = ?,
            pago_em = NOW()
        WHERE id = ? AND status = 'pendente'
    ");
    
    $updateStmt->execute([
        substr($pagamento['code'], 0, 100),
        substr($pagamento['customer'], 0, 50),
        $valorRecebido,
        $pedido['id']
    ]);
    
    $pdo->commit();
    
    // Clear session
    if (isset($_SESSION['current_order_id'])) {
        unset($_SESSION['current_order_id']);
    }
    
    // Success response
    echo json_encode([
        'pago' => true,
        'mensagem' => 'Payment confirmed!',
        'dados' => [
            'order_id' => $orderId,
            'produto' => $pedido['produto'],
            'valor_recebido' => $valorRecebido,
            'tx_code' => $pagamento['code'],
            'customer' => $pagamento['customer']
        ]
    ]);
    
} catch (Exception $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    
    http_response_code(500);
    echo json_encode([
        'pago' => false,
        'mensagem' => 'Internal error'
    ]);
}
?>

🔌 API Reference

Paynch Verification API

Endpoint: GET https://pay.paynch.app/paynch.php

Request Parameters

Parameter Type Required Description
contract String ✅ Yes Store contract address (42 chars)
orderId String ✅ Yes Unique order identifier

Example Request

GET https://pay.paynch.app/paynch.php?contract=0xE5fF9d546278a7CE0DF261EB85945Df2F0Dcc3c6&orderId=ORD_123456

Response - Payment NOT Found

{
  "success": true,
  "count": 0,
  "payments": []
}

Response - Payment CONFIRMED

{
  "success": true,
  "count": 1,
  "payments": [
    {
      "customer": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
      "amount": "15000000",
      "amountHuman": "15.00",
      "code": "0xa1b2c3d4e5f6...",
      "orderId": "ORD_123456",
      "timestamp": 1738368000,
      "timestampFormatted": "2026-01-31 15:30:00"
    }
  ]
}

Response Fields

Field Type Description
success Boolean Query status (true = success)
count Integer Number of payments found (0 = pending)
payments Array Array of payment objects
customer String Wallet address that made payment
amount String Amount in wei (6 decimals for USDT)
amountHuman String Human-readable amount (e.g., "15.00")
code String Transaction hash
orderId String Order ID from the payment
timestamp Integer Unix timestamp
timestampFormatted String Formatted date (YYYY-MM-DD HH:MM:SS)

🛡️ Security Best Practices

1. Order ID Generation

❌ NEVER DO THIS:
// Predictable IDs - VERY INSECURE!
$orderId = "order-1";
$orderId = "order-" . time(); // Still predictable
$orderId = $_GET['id']; // User-controlled - DANGEROUS!
✅ SECURE ORDER ID GENERATION:
// Good
$orderId = uniqid('ORD_', true);

// Better
$orderId = 'ORD_' . time() . '_' . bin2hex(random_bytes(8));

// Excellent (used in example code)
function generateOrderId($length = 12) {
    $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
    $id = '';
    for ($i = 0; $i < $length; $i++) {
        $id .= $chars[random_int(0, strlen($chars) - 1)];
    }
    return $id;
}

2. Amount Validation with Tolerance

The Paynch platform may charge a small fee (1.1%) if you do not have the Premium plan, so implement tolerance:

// Define tolerance (1.2% for platform fees)
define('TOLERANCIA_PERCENTUAL', 1.2);

$valorEsperado = 15.00; // Expected
$valorRecebido = 14.82; // Received (after fees)

$tolerancia = $valorEsperado * (TOLERANCIA_PERCENTUAL / 100);
$valorMinimo = $valorEsperado - $tolerancia;

if ($valorRecebido >= $valorMinimo) {
    // ✅ Valid payment (considering fees)
    liberarProduto($orderId);
} else {
    // ❌ Insufficient payment
    logError("Insufficient: expected {$valorEsperado}, got {$valorRecebido}");
}

3. Database Transaction Locking

Prevent race conditions with database locks:

try {
    $pdo->beginTransaction();
    
    // Lock the row with FOR UPDATE
    $stmt = $pdo->prepare("
        SELECT * FROM pedidos 
        WHERE order_id = ?
        FOR UPDATE
    ");
    $stmt->execute([$orderId]);
    $pedido = $stmt->fetch();
    
    // Validate payment and update
    // ...
    
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    throw $e;
}

4. Prevent Double-Processing

// Check if already confirmed
if ($pedido['status'] === 'confirmado') {
    echo json_encode([
        'pago' => true,
        'mensagem' => 'Already confirmed',
        'warning' => 'This order was already processed'
    ]);
    exit;
}

// Only update if still pending
$stmt = $pdo->prepare("
    UPDATE pedidos 
    SET status = 'confirmado'
    WHERE id = ? AND status = 'pendente'
");

if ($stmt->rowCount() === 0) {
    // Order was already confirmed by another request
    echo json_encode(['pago' => true, 'mensagem' => 'Already processed']);
    exit;
}

5. Input Sanitization

// Always escape output
<?= htmlspecialchars($produto, ENT_QUOTES, 'UTF-8') ?>

// Validate contract format
function validarContrato($contract) {
    return preg_match('/^0x[a-fA-F0-9]{40}$/', $contract);
}

// Validate order ID format
function validarOrderId($orderId) {
    return preg_match('/^[A-Z0-9]{8,20}$/', $orderId);
}

// Validate amount
function validarValor($valor) {
    $valor = floatval($valor);
    return $valor > 0 && $valor <= 1000000;
}

6. Rate Limiting

$ip = $_SERVER['REMOTE_ADDR'];
$rateKey = "rate_limit_$ip";

if (!isset($_SESSION[$rateKey])) {
    $_SESSION[$rateKey] = ['count' => 0, 'time' => time()];
}

// Reset after 1 minute
if (time() - $_SESSION[$rateKey]['time'] > 60) {
    $_SESSION[$rateKey] = ['count' => 0, 'time' => time()];
}

// Block if too many requests
if ($_SESSION[$rateKey]['count'] > 30) {
    http_response_code(429);
    echo json_encode(['error' => 'Too many requests. Wait 1 minute.']);
    exit;
}

$_SESSION[$rateKey]['count']++;

7. Logging and Auditing

function logEvento($tipo, $mensagem, $dados = []) {
    $log = [
        'timestamp' => date('Y-m-d H:i:s'),
        'tipo' => $tipo,
        'mensagem' => $mensagem,
        'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
        'dados' => $dados
    ];
    
    $logFile = __DIR__ . '/logs/paynch_' . date('Y-m-d') . '.log';
    error_log(json_encode($log) . PHP_EOL, 3, $logFile);
}

// Usage
logEvento('PAGAMENTO_CONFIRMADO', 'Payment validated', [
    'orderId' => $orderId,
    'valor' => $valorRecebido,
    'tx' => $txCode
]);

💻 Complete Working Examples

Example 1: Simple Product Page

<!-- index.php - Product Listing -->
<?php
$contractPadrao = '0xE5fF9d546278a7CE0DF261EB85945Df2F0Dcc3c6';
?>

<h1>Premium Course</h1>
<p>Price: 49.90 USDT</p>

<a href="checkout.php?produto=Premium%20Course&amount=49.90&shop=<?= $contractPadrao ?>">
  Buy Now
</a>

Example 2: E-commerce with Cart

<?php
// cart-checkout.php
require_once 'config.php';

// Get cart from session
$cart = $_SESSION['cart'] ?? [];
$total = array_sum(array_column($cart, 'price'));

// Create order
$orderId = 'ORD_' . time() . '_' . bin2hex(random_bytes(6));

$stmt = $pdo->prepare("
    INSERT INTO pedidos (order_id, produto, valor_usdt, status)
    VALUES (?, ?, ?, 'pendente')
");

$stmt->execute([
    $orderId,
    json_encode($cart), // Store cart as JSON
    $total
]);

$_SESSION['current_order_id'] = $orderId;

// Redirect to checkout
$contract = '0xE5fF9d546278a7CE0DF261EB85945Df2F0Dcc3c6';
$redirectUrl = "checkout.php?shop={$contract}&orderId={$orderId}&amount={$total}";

header("Location: {$redirectUrl}");
exit;
?>

Example 3: Success Page with Backend Validation

<?php
// success.php
require_once 'config.php';

$orderId = $_GET['orderId'] ?? null;

if (!$orderId || !validarOrderId($orderId)) {
    die('Invalid Order ID');
}

// Get order from database
$stmt = $pdo->prepare("SELECT * FROM pedidos WHERE order_id = ?");
$stmt->execute([$orderId]);
$pedido = $stmt->fetch();

if (!$pedido) {
    die('Order not found');
}

if ($pedido['status'] !== 'confirmado') {
    // Re-validate with Paynch API
    $paynchData = consultarPaynchApi($pedido['contract_loja'], $orderId);
    
    if ($paynchData['success'] && $paynchData['count'] > 0) {
        // Payment found! Update database
        $pagamento = $paynchData['payments'][0];
        
        $stmt = $pdo->prepare("
            UPDATE pedidos 
            SET status = 'confirmado',
                tx_code = ?,
                payer = ?,
                pago_em = NOW()
            WHERE order_id = ?
        ");
        
        $stmt->execute([
            $pagamento['code'],
            $pagamento['customer'],
            $orderId
        ]);
        
        $pedido['status'] = 'confirmado';
    } else {
        echo '<h1>⏳ Payment Pending</h1>';
        echo '<p>We have not detected your payment yet. Please wait...</p>';
        echo '<script>setTimeout(() => location.reload(), 5000);</script>';
        exit;
    }
}

// Payment confirmed!
echo '<h1>✅ Payment Confirmed!</h1>';
echo '<p>Thank you for your purchase!</p>';
echo '<p>Order ID: ' . htmlspecialchars($orderId) . '</p>';
echo '<p>Product: ' . htmlspecialchars($pedido['produto']) . '</p>';
echo '<p>Amount: ' . number_format($pedido['valor_usdt'], 2) . ' USDT</p>';

// Send confirmation email
mail(
    $customerEmail,
    'Payment Confirmed - Order ' . $orderId,
    "Your payment has been confirmed!\n\nOrder: {$orderId}\nAmount: {$pedido['valor_usdt']} USDT"
);

// Clear session
unset($_SESSION['current_order_id']);
?>

🚨 Troubleshooting

Problem: "Connect Wallet" button not working

Solutions:
  • ✅ Check if MetaMask is installed
  • ✅ Verify button ID is exactly connect-wallet
  • ✅ Open browser console (F12) and look for errors
  • ✅ Ensure Web3.js script loaded before Paynch script
  • ✅ Check if page is served over HTTPS (required)

Problem: Payment not detected

Solutions:
  • ✅ Wait up to 5-10 minutes (blockchain confirmation time)
  • ✅ Verify transaction on BscScan
  • ✅ Check if Order ID matches exactly
  • ✅ Manually query API: paynch.php?contract=...&orderId=...
  • ✅ Verify user is on BSC network (not Ethereum mainnet)

Problem: "Insufficient amount" error

Cause: Platform fees were deducted from payment

Solution: Increase tolerance in your validation:
// Increase from 1.2% to 2%
define('TOLERANCIA_PERCENTUAL', 2.0);

Problem: Scripts not loading

Solutions:
  • ✅ Check browser console for CORS errors
  • ✅ Verify script URLs are correct
  • ✅ Ensure scripts are loaded in correct order (jQuery → Web3 → Paynch)
  • ✅ Try loading from CDN alternatives

Problem: Database errors

Solutions:
  • ✅ Check database credentials in config.php
  • ✅ Verify table structure matches schema
  • ✅ Check PHP error logs: /var/log/php-fpm/
  • ✅ Ensure PDO extension is enabled

Debug Mode

Add this to your verification script for detailed logging:

// Add after API response
console.log('=== PAYNCH DEBUG ===');
console.log('OrderID:', CONFIG.ORDER_ID);
console.log('Contract:', CONFIG.CONTRACT);
console.log('Expected Amount:', CONFIG.VALOR);
console.log('API Response:', data);
console.log('Payment Count:', data.count);
if (data.payments && data.payments.length > 0) {
    console.log('Payment Data:', data.payments[0]);
    console.log('Amount Received:', data.payments[0].amountHuman);
    console.log('Customer:', data.payments[0].customer);
    console.log('TX Hash:', data.payments[0].code);
}
console.log('===================');

✅ Production Deployment Checklist

Before going live, verify all items:

  • Created database table with proper schema
  • Generated shop contract in Paynch dashboard
  • Updated config.php with production credentials
  • Implemented secure Order ID generation
  • Added input validation for all user inputs
  • Configured proper amount tolerance (1-2%)
  • Implemented database transaction locking
  • Added rate limiting for API calls
  • Set up logging system for auditing
  • Tested full payment flow on testnet
  • Verified auto-verification works correctly
  • Implemented proper error handling
  • Added email notifications for customers
  • Set up HTTPS/SSL certificate
  • Tested on mobile devices
  • Configured proper PHP error reporting
  • Created backup/restore procedures
  • Documented contract address used
  • Set up monitoring/alerts for failures
  • Performed security audit of code

📚 Additional Resources

🤖 AI Support

24/7 AI assistant for integration help

Open AI Chat

📖 Embed Payment Button

Easily integrate a secure crypto payment button into your website with just a few lines of code.

View Full Documentation

🎮 Dashboard

Manage contracts and view transactions

Go to Dashboard

💬 Community

Join our developer community

Join Now