Overview
The payment flow is simple:- Customer creates order → Save as
PENDING - Create payment link → Get
paymentRef - Save
paymentRefwith order (important!) - Redirect customer to payment page
- Customer pays and returns
- Check payment status and update order
Complete Implementation
Step 1: Order Creation
When a customer places an order, save it with aPENDING status:
Copy
async function createOrder(customerData, items, total) {
// Generate unique order ID
const orderId = generateUniqueId(); // e.g., "ORD-2025-001234"
// Save order to database
const order = await db.orders.create({
id: orderId,
customerId: customerData.id,
items: items,
totalAmount: total,
status: "PENDING", // Important: Start as PENDING
paymentRef: null, // Will be set after creating payment link
createdAt: new Date(),
updatedAt: new Date(),
});
return order;
}
Critical: Every order must have a unique ID and must start with status
PENDINGStep 2: Create Payment Link
Copy
const express = require("express");
const fetch = require("node-fetch");
const app = express();
app.use(express.json());
// 1. Create order and redirect to payment
app.post("/checkout", async (req, res) => {
const { items, total, customerId } = req.body;
try {
// Create order in database
const orderId = generateOrderId();
await db.orders.create({
id: orderId,
customerId: customerId,
items: items,
total: total,
status: "PENDING",
});
// Create payment link
const response = await fetch(
"https://api.oneclickdz.com/v3/ocpay/createLink",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Access-Token": process.env.ONECLICK_API_KEY,
},
body: JSON.stringify({
productInfo: {
title: `Order ${orderId}`,
amount: total,
},
redirectUrl: `https://yoursite.com/orders/${orderId}`,
}),
}
);
const data = await response.json();
// IMPORTANT: Save paymentRef with order
await db.orders.update(orderId, {
paymentRef: data.data.paymentRef,
});
// Redirect to payment page
res.redirect(data.data.paymentUrl);
} catch (error) {
console.error("Checkout error:", error);
res.status(500).json({ error: "Checkout failed" });
}
});
// 2. Order status page
app.get("/orders/:orderId", async (req, res) => {
const { orderId } = req.params;
try {
const order = await db.orders.findOne({ id: orderId });
if (!order) {
return res.status(404).send("Order not found");
}
// Check payment if still pending
if (order.status === "PENDING" && order.paymentRef) {
const statusResponse = await fetch(
`https://api.oneclickdz.com/v3/ocpay/checkPayment/${order.paymentRef}`,
{
headers: { "X-Access-Token": process.env.ONECLICK_API_KEY },
}
);
const statusData = await statusResponse.json();
if (statusData.data.status === "CONFIRMED") {
await db.orders.update(orderId, { status: "PAID" });
order.status = "PAID";
} else if (statusData.data.status === "FAILED") {
await db.orders.update(orderId, { status: "FAILED" });
order.status = "FAILED";
}
}
res.render("order-status", { order });
} catch (error) {
console.error("Error:", error);
res.status(500).send("Error loading order");
}
});
// 3. Manual payment check endpoint
app.post("/orders/:orderId/check", async (req, res) => {
const { orderId } = req.params;
try {
const order = await db.orders.findOne({ id: orderId });
if (!order || !order.paymentRef) {
return res.status(404).json({ error: "Order not found" });
}
const response = await fetch(
`https://api.oneclickdz.com/v3/ocpay/checkPayment/${order.paymentRef}`,
{
headers: { "X-Access-Token": process.env.ONECLICK_API_KEY },
}
);
const data = await response.json();
// Update order status
if (data.data.status === "CONFIRMED") {
await db.orders.update(orderId, { status: "PAID" });
} else if (data.data.status === "FAILED") {
await db.orders.update(orderId, { status: "FAILED" });
}
res.json({ status: data.data.status });
} catch (error) {
console.error("Check error:", error);
res.status(500).json({ error: "Check failed" });
}
});
function generateOrderId() {
return `ORD-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
app.listen(3000);
Frontend: Order Status Page
Simple HTML page with manual check button:Copy
<!DOCTYPE html>
<html>
<head>
<title>Order Status</title>
<style>
.status-pending {
color: #f59e0b;
}
.status-paid {
color: #10b981;
}
.status-failed {
color: #ef4444;
}
</style>
</head>
<body>
<h1>Order #<span id="orderId"></span></h1>
<h2>Status: <span id="status" class="status-pending">PENDING</span></h2>
<button id="checkBtn" onclick="checkPayment()">Check Payment Status</button>
<p id="message"></p>
<script>
const orderId = window.location.pathname.split("/").pop();
document.getElementById("orderId").textContent = orderId;
async function checkPayment() {
const btn = document.getElementById("checkBtn");
const msg = document.getElementById("message");
btn.disabled = true;
msg.textContent = "Checking...";
try {
const response = await fetch(`/orders/${orderId}/check`, {
method: "POST",
});
const data = await response.json();
const statusEl = document.getElementById("status");
statusEl.textContent = data.status;
statusEl.className = `status-${data.status.toLowerCase()}`;
if (data.status === "CONFIRMED") {
msg.textContent = "✅ Payment confirmed! Refreshing...";
setTimeout(() => location.reload(), 2000);
} else if (data.status === "FAILED") {
msg.textContent = "❌ Payment failed";
} else {
msg.textContent = "⏳ Payment still pending";
}
} catch (error) {
msg.textContent = "Error checking payment";
} finally {
btn.disabled = false;
}
}
// Auto-check when page loads
if (document.getElementById("status").textContent === "PENDING") {
checkPayment();
}
</script>
</body>
</html>
Database Schema
Simple schema for orders:Copy
CREATE TABLE orders (
id VARCHAR(50) PRIMARY KEY,
customer_id VARCHAR(50) NOT NULL,
total INTEGER NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
payment_ref VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_payment_ref (payment_ref),
INDEX idx_status (status)
);
Key Points
Important: - Always save
paymentRef with your order - Check payment
status when customer returns - Only fulfill orders with status CONFIRMED -
Payment links expire after 20 minutesNext Steps
Continue to Status Polling
Learn how to automate status checking with cron jobs

