Skip to main content

Security

1. Protect Your API Keys

Never expose API keys in frontend code or public repositories!
# .env file (add to .gitignore)
ONECLICK_API_KEY=your_key_here
const apiKey = process.env.ONECLICK_API_KEY;

2. Backend Only

Always call OCPay API from your backend, never from frontend:
// ❌ WRONG - Frontend
fetch("https://api.oneclickdz.com/v3/ocpay/createLink", {
  headers: { "X-Access-Token": "YOUR_KEY" }, // Exposed to users!
});

// βœ… CORRECT - Backend
// Frontend calls YOUR API, your backend calls OneClick
fetch("/api/create-payment", { method: "POST" });

3. Validate Inputs

function validateOrderData(data) {
  if (!Number.isInteger(data.amount)) {
    throw new Error("Amount must be integer");
  }

  if (data.amount < 500 || data.amount > 500000) {
    throw new Error("Amount must be between 500 and 500,000 DZD");
  }

  if (!data.title || data.title.trim().length === 0) {
    throw new Error("Title is required");
  }

  return data;
}

4. Verify Before Fulfillment

Always verify payment status on backend before fulfilling orders!
async function fulfillOrder(orderId) {
  const order = await db.orders.findOne({ id: orderId });

  // CRITICAL: Verify payment status
  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();

  if (data.data.status !== "CONFIRMED") {
    throw new Error("Payment not confirmed");
  }

  // Safe to fulfill
  await processOrder(order);
}

Error Handling

Handle API Errors

async function createPaymentSafe(orderId, amount, title) {
  try {
    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, amount },
        }),
      }
    );

    if (!response.ok) {
      if (response.status === 403) {
        throw new Error("Merchant not validated");
      }
      throw new Error("API request failed");
    }

    const data = await response.json();

    if (!data.success) {
      throw new Error(data.error?.message || "Unknown error");
    }

    return data.data;
  } catch (error) {
    console.error("Payment creation failed:", error);
    throw error;
  }
}

Testing

Use Sandbox

Test with sandbox keys before production:
# Development
ONECLICK_API_KEY=sk_sandbox_abc123...

# Production
ONECLICK_API_KEY=sk_live_xyz789...

Test Scenarios

Test these cases:
  • βœ… Successful payment
  • βœ… Failed payment
  • βœ… Expired payment link (20 min)
  • βœ… Customer returns without paying
  • βœ… API errors

Production Checklist

Before going live:

Security

  • API keys in environment variables
  • No frontend API calls
  • Input validation implemented
  • HTTPS enabled

Functionality

  • Order creation works
  • Payment links generate correctly
  • Status checking works
  • Order fulfillment only on CONFIRMED
  • Cron job running

Testing

  • Sandbox testing complete
  • All edge cases tested
  • Error handling verified

Monitoring

  • Error logging enabled
  • Can view pending orders
  • Can manually check status

Common Mistakes

Avoid these:
  • ❌ Hardcoding API keys
  • ❌ Calling API from frontend
  • ❌ Not saving paymentRef
  • ❌ Fulfilling without verification
  • ❌ No error handling
  • ❌ Checking too frequently (respect rate limits)
  • ❌ Not testing in sandbox first

Database Best Practices

Add indexes for performance:
-- Speed up pending order queries
CREATE INDEX idx_orders_pending
ON orders(status, payment_ref)
WHERE status = 'PENDING';

-- Speed up paymentRef lookups
CREATE INDEX idx_payment_ref ON orders(payment_ref);

Logging

Log important events:
// Log payment creation
console.log("[Payment] Created", {
  orderId,
  paymentRef,
  amount,
  timestamp: new Date(),
});

// Log status changes
console.log("[Payment] Status changed", {
  orderId,
  oldStatus: "PENDING",
  newStatus: "PAID",
  timestamp: new Date(),
});

Next Steps

You’re Ready! πŸŽ‰

You now have everything to accept payments with OCPay. Start with sandbox, test thoroughly, then go live!