Overview
You need to check payment status because bank processing is asynchronous. Use two simple methods:- Cron job: Check pending orders every 20 minutes
- Manual check: When customer views their order
Method 1: Cron Job (Every 20 Minutes)
const cron = require("node-cron");
// Run every 20 minutes
cron.schedule("*/20 * * * *", async () => {
console.log("Checking pending payments...");
try {
// Get pending orders from last 24 hours
const pendingOrders = await db.orders.find({
status: "PENDING",
paymentRef: { $exists: true },
createdAt: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) },
});
for (const order of pendingOrders) {
await checkAndUpdateOrder(order.id, order.paymentRef);
await sleep(100); // Small delay between requests
}
console.log(`Checked ${pendingOrders.length} orders`);
} catch (error) {
console.error("Cron job error:", error);
}
});
async function checkAndUpdateOrder(orderId, paymentRef) {
try {
const response = await fetch(
`https://api.oneclickdz.com/v3/ocpay/checkPayment/${paymentRef}`,
{
headers: { "X-Access-Token": process.env.ONECLICK_API_KEY },
}
);
const data = await response.json();
const status = data.data.status;
if (status === "CONFIRMED") {
await db.orders.update(orderId, { status: "PAID" });
console.log(`✅ Order ${orderId} marked as PAID`);
} else if (status === "FAILED") {
await db.orders.update(orderId, { status: "FAILED" });
console.log(`❌ Order ${orderId} marked as FAILED`);
}
} catch (error) {
console.error(`Error checking order ${orderId}:`, error);
}
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
from apscheduler.schedulers.background import BackgroundScheduler
import requests
import os
from datetime import datetime, timedelta
import time
scheduler = BackgroundScheduler()
# Run every 20 minutes
@scheduler.scheduled_job('interval', minutes=20)
def check_pending_payments():
print('Checking pending payments...')
try:
# Get pending orders from last 24 hours
yesterday = datetime.now() - timedelta(days=1)
pending_orders = db.orders.find({
'status': 'PENDING',
'paymentRef': {'$exists': True},
'createdAt': {'$gte': yesterday}
})
count = 0
for order in pending_orders:
check_and_update_order(order['id'], order['paymentRef'])
time.sleep(0.1) # Small delay between requests
count += 1
print(f'Checked {count} orders')
except Exception as e:
print(f'Cron job error: {e}')
def check_and_update_order(order_id, payment_ref):
try:
response = requests.get(
f'https://api.oneclickdz.com/v3/ocpay/checkPayment/{payment_ref}',
headers={'X-Access-Token': os.getenv('ONECLICK_API_KEY')}
)
data = response.json()
status = data['data']['status']
if status == 'CONFIRMED':
db.orders.update(order_id, {'status': 'PAID'})
print(f'✅ Order {order_id} marked as PAID')
elif status == 'FAILED':
db.orders.update(order_id, {'status': 'FAILED'})
print(f'❌ Order {order_id} marked as FAILED')
except Exception as e:
print(f'Error checking order {order_id}: {e}')
# Start scheduler
scheduler.start()
<?php
// cron-check-payments.php
// Add to crontab: */20 * * * * php /path/to/cron-check-payments.php
require_once 'config.php';
echo "Checking pending payments...\n";
try {
// Get pending orders from last 24 hours
$yesterday = date('Y-m-d H:i:s', strtotime('-24 hours'));
$stmt = $db->prepare("
SELECT id, payment_ref
FROM orders
WHERE status = 'PENDING'
AND payment_ref IS NOT NULL
AND created_at >= ?
");
$stmt->execute([$yesterday]);
$pendingOrders = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($pendingOrders as $order) {
checkAndUpdateOrder($order['id'], $order['payment_ref']);
usleep(100000); // 100ms delay
}
echo "Checked " . count($pendingOrders) . " orders\n";
} catch (Exception $e) {
echo "Cron job error: " . $e->getMessage() . "\n";
}
function checkAndUpdateOrder($orderId, $paymentRef) {
global $db;
try {
$ch = curl_init("https://api.oneclickdz.com/v3/ocpay/checkPayment/{$paymentRef}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Access-Token: ' . getenv('ONECLICK_API_KEY')
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
$status = $data['data']['status'];
if ($status === 'CONFIRMED') {
$stmt = $db->prepare("UPDATE orders SET status = 'PAID' WHERE id = ?");
$stmt->execute([$orderId]);
echo "✅ Order {$orderId} marked as PAID\n";
} elseif ($status === 'FAILED') {
$stmt = $db->prepare("UPDATE orders SET status = 'FAILED' WHERE id = ?");
$stmt->execute([$orderId]);
echo "❌ Order {$orderId} marked as FAILED\n";
}
} catch (Exception $e) {
echo "Error checking order {$orderId}: " . $e->getMessage() . "\n";
}
}
?>
Setup Cron Job
Add to your crontab (Linux/Unix):# Edit crontab
crontab -e
# Add line to run every 20 minutes
*/20 * * * * /usr/bin/node /path/to/cron-job.js >> /var/log/payment-check.log 2>&1
# Or for Python
*/20 * * * * /usr/bin/python3 /path/to/cron-job.py >> /var/log/payment-check.log 2>&1
# Or for PHP
*/20 * * * * /usr/bin/php /path/to/cron-check-payments.php >> /var/log/payment-check.log 2>&1
Method 2: Manual Check (When Customer Views Order)
Already implemented in Step 2! When customer lands on order page, it automatically checks once. Optional: Add a button to check on demand (checks every 1 minute max):<!DOCTYPE html>
<html>
<head>
<title>Order Status</title>
</head>
<body>
<h1>Order Status</h1>
<p>Status: <span id="status">PENDING</span></p>
<button id="checkBtn" onclick="startChecking()">
Check Payment Status
</button>
<p id="message"></p>
<script>
let checking = false;
let checkInterval = null;
const orderId = window.location.pathname.split("/").pop();
async function checkPayment() {
const response = await fetch(`/orders/${orderId}/check`, {
method: "POST",
});
const data = await response.json();
document.getElementById("status").textContent = data.status;
if (data.status === "CONFIRMED") {
document.getElementById("message").textContent =
"✅ Payment confirmed!";
stopChecking();
setTimeout(() => location.reload(), 2000);
} else if (data.status === "FAILED") {
document.getElementById("message").textContent = "❌ Payment failed";
stopChecking();
} else {
document.getElementById("message").textContent =
"⏳ Still pending...";
}
}
function startChecking() {
if (checking) return;
checking = true;
document.getElementById("checkBtn").disabled = true;
// Check immediately
checkPayment();
// Then check every 1 minute
checkInterval = setInterval(checkPayment, 60000);
// Stop after 10 minutes
setTimeout(stopChecking, 10 * 60000);
}
function stopChecking() {
checking = false;
document.getElementById("checkBtn").disabled = false;
if (checkInterval) {
clearInterval(checkInterval);
checkInterval = null;
}
}
// Auto-check when page loads (if pending)
if (document.getElementById("status").textContent === "PENDING") {
checkPayment();
}
</script>
</body>
</html>
Summary
Two simple methods:
- ✅ Cron job every 20 minutes for all pending orders
- ✅ Manual check when customer views order (max every 1 minute)
- ✅ Auto-check once when order page loads
Next Steps
Continue to Best Practices
Learn essential security and error handling

