📋 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.
• 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:
Payment Flow
URL contains:
shop, orderId, amount
Reads parameters, initializes Web3, prepares transaction
MetaMask/Trust Wallet/WalletConnect connects to BSC network
USDT transfer transaction is submitted to blockchain
Frontend polls Paynch API every 3-5 seconds
API returns
count > 0 with transaction details
PHP/Node.js validates amount, updates database, releases product
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
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>
•
connect-wallet•
pay-button•
pay-status•
amount-input•
order-id-input•
contract-inputThe 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
// Predictable IDs - VERY INSECURE!
$orderId = "order-1";
$orderId = "order-" . time(); // Still predictable
$orderId = $_GET['id']; // User-controlled - DANGEROUS!
// 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
- ✅ 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
- ✅ 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
Solution: Increase tolerance in your validation:
// Increase from 1.2% to 2%
define('TOLERANCIA_PERCENTUAL', 2.0);
Problem: Scripts not loading
- ✅ 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
- ✅ 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
📖 Embed Payment Button
Easily integrate a secure crypto payment button into your website with just a few lines of code.
View Full Documentation