var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));

// server.ts
var import_express = __toESM(require("express"), 1);
var import_path4 = __toESM(require("path"), 1);
var import_fs4 = __toESM(require("fs"), 1);
var import_vite = require("vite");

// src/server/payments/store.ts
var import_fs = __toESM(require("fs"), 1);
var import_path = __toESM(require("path"), 1);
var DEFAULT_GATEWAYS = [
  {
    id: "bkash",
    name: "bKash Direct (Tokenized API)",
    is_enabled: true,
    mode: "test",
    username: "sandboxTestUser",
    password: "sandboxTestPassword",
    app_key: "bkash_sandbox_app_key_2026",
    app_secret: "bkash_sandbox_app_secret_998124",
    merchant_mobile: "01770000000",
    api_url: "https://tokenized.sandbox.bka.sh/v1.2.0-beta/tokenized/checkout",
    callback_url: "/api/payments/bkash/callback"
  },
  {
    id: "nagad",
    name: "Nagad PGW (E-Commerce)",
    is_enabled: true,
    mode: "test",
    merchant_id: "683002007104225",
    merchant_mobile: "01800000000",
    public_key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0nagad_test_public_key",
    private_key: "MIIEowIBAAKCAQEA0nagad_test_private_key_secure_server_only",
    api_url: "http://sandbox.mynagad.com:10080/remote-payment-gateway-1.0/api/dfs",
    callback_url: "/api/payments/nagad/callback"
  },
  {
    id: "rocket",
    name: "DBBL Rocket Pay (MFS Gateway)",
    is_enabled: true,
    mode: "test",
    merchant_id: "019000000009",
    app_key: "rocket_terminal_key_test_2026",
    terminal_id: "TERM_DBBL_TEST_01",
    pin_password: "rocket_secret_pin_test",
    api_url: "https://rocket-sandbox.dutchbanglabank.com/api",
    callback_url: "/api/payments/rocket/callback"
  },
  {
    id: "sslcommerz",
    name: "SSLCommerz (Cards, MFS, Internet Banking)",
    is_enabled: true,
    mode: "test",
    store_id: "topbd_sandbox_01",
    store_password: "topbd_sandbox_password_99",
    api_url: "https://sandbox.sslcommerz.com",
    success_url: "/api/payments/sslcommerz/callback",
    fail_url: "/api/payments/sslcommerz/callback",
    cancel_url: "/api/payments/sslcommerz/callback",
    ipn_url: "/api/payments/sslcommerz/ipn",
    allowed_channels: ["cards", "bkash", "nagad", "rocket", "internet_banking"]
  },
  {
    id: "shurjopay",
    name: "shurjoPay Merchant Gateway",
    is_enabled: true,
    mode: "test",
    merchant_id: "sp_test_merchant",
    username: "sp_user",
    password: "sp_password",
    api_url: "https://sandbox.shurjopayment.com",
    callback_url: "/api/payments/shurjopay/callback"
  },
  {
    id: "aamarpay",
    name: "aamarPay Merchant Gateway",
    is_enabled: true,
    mode: "test",
    store_id: "aamarpaytest",
    signature_key: "dbb74892e8241a45a7f0144916d6664e",
    api_url: "https://sandbox.aamarpay.com",
    success_url: "/api/payments/aamarpay/callback",
    fail_url: "/api/payments/aamarpay/callback",
    cancel_url: "/api/payments/aamarpay/callback",
    ipn_url: "/api/payments/aamarpay/ipn",
    callback_url: "/api/payments/aamarpay/callback",
    session_timeout: 15,
    allowed_channels: ["cards", "bkash", "nagad", "rocket", "internet_banking"]
  },
  {
    id: "uddoktapay",
    name: "UddoktaPay Automated PGW",
    is_enabled: true,
    mode: "test",
    api_key: process.env.UDDOKTAPAY_API_KEY || "",
    api_url: "https://sandbox.uddoktapay.com",
    callback_url: "/api/payments/uddoktapay/callback",
    success_url: "/api/payments/uddoktapay/callback",
    cancel_url: "/api/payments/uddoktapay/callback?status=cancelled",
    ipn_url: "/api/payments/uddoktapay/ipn",
    webhook_url: "/api/payments/uddoktapay/ipn",
    allowed_channels: ["bkash", "nagad", "rocket", "upay", "cards"]
  }
];
var PaymentStore = class {
  constructor() {
    this.transactions = /* @__PURE__ */ new Map();
    this.gatewayConfigs = /* @__PURE__ */ new Map();
    this.defaultGateway = "bkash";
    this.gatewaysFilePath = import_path.default.join(process.cwd(), "data", "gateways_config.json");
    this.paymentsDbPath = import_path.default.join(process.cwd(), "data", "payments_db.json");
    DEFAULT_GATEWAYS.forEach((gw) => {
      this.gatewayConfigs.set(gw.id, { ...gw });
    });
    this.loadGatewaysFromDisk();
    this.loadTransactionsFromDisk();
  }
  // --- Transactions Persistent Database Storage ---
  loadTransactionsFromDisk() {
    try {
      if (import_fs.default.existsSync(this.paymentsDbPath)) {
        const raw = import_fs.default.readFileSync(this.paymentsDbPath, "utf-8");
        const data = JSON.parse(raw);
        if (Array.isArray(data.transactions)) {
          this.transactions.clear();
          data.transactions.forEach((txn) => {
            if (txn && txn.transaction_id) {
              this.transactions.set(txn.transaction_id, txn);
            }
          });
          return;
        }
      }
    } catch (e) {
      console.warn("Could not read payments_db.json, re-initializing from seeds:", e);
    }
    this.seedDemoTransactions();
    this.saveTransactionsToDisk();
  }
  saveTransactionsToDisk() {
    try {
      const dir = import_path.default.dirname(this.paymentsDbPath);
      if (!import_fs.default.existsSync(dir)) {
        import_fs.default.mkdirSync(dir, { recursive: true });
      }
      const data = {
        version: 1,
        total: this.transactions.size,
        updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
        transactions: Array.from(this.transactions.values())
      };
      import_fs.default.writeFileSync(this.paymentsDbPath, JSON.stringify(data, null, 2), "utf-8");
    } catch (e) {
      console.error("CRITICAL: Failed to persist payments_db.json to disk:", e);
    }
  }
  seedDemoTransactions() {
    const demo = [
      {
        id: "pay_1",
        transaction_id: "TXN-BKASH-9821387",
        listing_id: "list_1",
        listing_name: "Chaldal Online Grocery",
        listing_url: "https://chaldal.com",
        listing_image: "https://images.unsplash.com/photo-1542838132-92c53300491e?w=200&auto=format&fit=crop&q=80",
        category_id: "cat_ecom",
        amount: 25e3,
        currency: "BDT",
        gateway: "bkash",
        mode: "live",
        status: "paid",
        created_at: "2026-08-24T18:45:00Z",
        updated_at: "2026-08-24T18:45:04Z",
        paid_at: "2026-08-24T18:45:04Z",
        callback_processed_at: "2026-08-24T18:45:04Z"
      },
      {
        id: "pay_2",
        transaction_id: "TXN-NAGAD-4412098",
        listing_id: "list_2",
        listing_name: "Pathao Super App",
        listing_url: "https://pathao.com",
        listing_image: "https://images.unsplash.com/photo-1526367790999-0150786686a2?w=200&auto=format&fit=crop&q=80",
        category_id: "cat_startup",
        amount: 18500,
        currency: "BDT",
        gateway: "nagad",
        mode: "live",
        status: "paid",
        created_at: "2026-08-24T15:20:00Z",
        updated_at: "2026-08-24T15:20:03Z",
        paid_at: "2026-08-24T15:20:03Z",
        callback_processed_at: "2026-08-24T15:20:03Z"
      }
    ];
    demo.forEach((t) => this.transactions.set(t.transaction_id, t));
  }
  // --- Transactions CRUD (Persisted to Database) ---
  createTransaction(txn) {
    this.transactions.set(txn.transaction_id, { ...txn });
    this.saveTransactionsToDisk();
    return { ...txn };
  }
  getTransaction(transactionId) {
    const txn = this.transactions.get(transactionId);
    return txn ? { ...txn } : void 0;
  }
  getTransactionByGatewayTxnId(gatewayTxnId) {
    for (const txn of this.transactions.values()) {
      if (txn.gateway_transaction_id === gatewayTxnId) {
        return { ...txn };
      }
    }
    return void 0;
  }
  getAllTransactions() {
    return Array.from(this.transactions.values()).sort(
      (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
    );
  }
  getTransactionsByListingId(listingId) {
    return Array.from(this.transactions.values()).filter((t) => t.listing_id === listingId).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
  }
  getTransactionsByStatus(status) {
    return Array.from(this.transactions.values()).filter((t) => t.status === status).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
  }
  /**
   * Idempotent status update:
   * Returns true if status was updated, false if already in final state (PAID/FAILED)
   */
  updateTransactionStatus(transactionId, status, extra) {
    const txn = this.transactions.get(transactionId);
    if (!txn) {
      return { updated: false };
    }
    if (txn.status === "paid" && status === "paid") {
      return { updated: false, transaction: { ...txn } };
    }
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const updatedTxn = {
      ...txn,
      ...extra,
      status,
      updated_at: now,
      paid_at: status === "paid" ? extra?.paid_at || now : txn.paid_at,
      callback_processed_at: now
    };
    this.transactions.set(transactionId, updatedTxn);
    this.saveTransactionsToDisk();
    return { updated: true, transaction: { ...updatedTxn } };
  }
  deleteTransaction(transactionId) {
    const deleted = this.transactions.delete(transactionId);
    if (deleted) {
      this.saveTransactionsToDisk();
    }
    return deleted;
  }
  // --- Gateway Settings & Secrets ---
  loadGatewaysFromDisk() {
    try {
      if (import_fs.default.existsSync(this.gatewaysFilePath)) {
        const raw = import_fs.default.readFileSync(this.gatewaysFilePath, "utf-8");
        const data = JSON.parse(raw);
        if (Array.isArray(data.gateways)) {
          data.gateways.forEach((gw) => {
            const existing = this.gatewayConfigs.get(gw.id);
            this.gatewayConfigs.set(gw.id, { ...existing, ...gw });
          });
        }
        if (data.defaultGateway) {
          this.defaultGateway = data.defaultGateway;
        }
      }
    } catch (e) {
      console.warn("Could not load gateway config from disk, using defaults:", e);
    }
  }
  saveGatewaysToDisk() {
    try {
      const dir = import_path.default.dirname(this.gatewaysFilePath);
      if (!import_fs.default.existsSync(dir)) {
        import_fs.default.mkdirSync(dir, { recursive: true });
      }
      const data = {
        gateways: Array.from(this.gatewayConfigs.values()),
        defaultGateway: this.defaultGateway,
        updatedAt: (/* @__PURE__ */ new Date()).toISOString()
      };
      import_fs.default.writeFileSync(this.gatewaysFilePath, JSON.stringify(data, null, 2), "utf-8");
    } catch (e) {
      console.error("Failed to persist gateway configs to disk:", e);
    }
  }
  getGatewayConfig(gatewayId) {
    const gw = this.gatewayConfigs.get(gatewayId);
    return gw ? { ...gw } : void 0;
  }
  getAllGatewayConfigs(maskSecrets = true) {
    return Array.from(this.gatewayConfigs.values()).map((gw) => {
      if (!maskSecrets) return { ...gw };
      return this.maskConfig(gw);
    });
  }
  getPublicGateways() {
    return Array.from(this.gatewayConfigs.values()).filter((gw) => gw.is_enabled).map((gw) => ({
      id: gw.id,
      name: gw.name,
      is_enabled: gw.is_enabled,
      mode: gw.mode,
      allowed_channels: gw.allowed_channels
    }));
  }
  saveGatewayConfig(updated) {
    const existing = this.gatewayConfigs.get(updated.id);
    if (!existing) {
      this.gatewayConfigs.set(updated.id, { ...updated });
      this.saveGatewaysToDisk();
      return;
    }
    const merged = {
      ...existing,
      ...updated,
      api_key: this.unmaskValue(updated.api_key, existing.api_key),
      app_key: this.unmaskValue(updated.app_key, existing.app_key),
      app_secret: this.unmaskValue(updated.app_secret, existing.app_secret),
      password: this.unmaskValue(updated.password, existing.password),
      store_password: this.unmaskValue(updated.store_password, existing.store_password),
      signature_key: this.unmaskValue(updated.signature_key, existing.signature_key),
      private_key: this.unmaskValue(updated.private_key, existing.private_key),
      pin_password: this.unmaskValue(updated.pin_password, existing.pin_password),
      allowed_channels: updated.allowed_channels || existing.allowed_channels
    };
    this.gatewayConfigs.set(updated.id, merged);
    this.saveGatewaysToDisk();
  }
  saveAllGatewayConfigs(configs) {
    configs.forEach((c) => {
      const existing = this.gatewayConfigs.get(c.id);
      if (!existing) {
        this.gatewayConfigs.set(c.id, { ...c });
      } else {
        const merged = {
          ...existing,
          ...c,
          api_key: this.unmaskValue(c.api_key, existing.api_key),
          app_key: this.unmaskValue(c.app_key, existing.app_key),
          app_secret: this.unmaskValue(c.app_secret, existing.app_secret),
          password: this.unmaskValue(c.password, existing.password),
          store_password: this.unmaskValue(c.store_password, existing.store_password),
          signature_key: this.unmaskValue(c.signature_key, existing.signature_key),
          private_key: this.unmaskValue(c.private_key, existing.private_key),
          pin_password: this.unmaskValue(c.pin_password, existing.pin_password),
          allowed_channels: c.allowed_channels || existing.allowed_channels
        };
        this.gatewayConfigs.set(c.id, merged);
      }
    });
    this.saveGatewaysToDisk();
  }
  getDefaultGateway() {
    return this.defaultGateway;
  }
  setDefaultGateway(gwId) {
    this.defaultGateway = gwId;
    this.saveGatewaysToDisk();
  }
  maskConfig(gw) {
    return {
      ...gw,
      api_key: gw.api_key ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "",
      app_secret: gw.app_secret ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "",
      password: gw.password ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "",
      store_password: gw.store_password ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "",
      signature_key: gw.signature_key ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "",
      private_key: gw.private_key ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "",
      pin_password: gw.pin_password ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : ""
    };
  }
  unmaskValue(submitted, existing) {
    if (!submitted || submitted.includes("\u2022\u2022\u2022\u2022")) {
      return existing;
    }
    return submitted;
  }
};
var paymentStore = new PaymentStore();

// src/server/listingsStore.ts
var import_fs2 = __toESM(require("fs"), 1);
var import_path2 = __toESM(require("path"), 1);

// src/data/initialData.ts
var INITIAL_LISTINGS = [
  {
    id: "list_1",
    name: "Chaldal Online Grocery",
    image_url: "https://images.unsplash.com/photo-1542838132-92c53300491e?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/chaldal",
    website_url: "https://chaldal.com",
    url: "https://chaldal.com",
    category_id: "cat_ecom",
    category_name: "E-commerce",
    current_bid: 25e3,
    clicks: 1420,
    hearts: 890,
    is_active: true,
    created_at: "2026-08-20T10:00:00Z",
    updated_at: "2026-08-24T18:45:00Z",
    last_bid_at: "2026-08-24T18:45:00Z"
  },
  {
    id: "list_2",
    name: "Pathao Super App",
    image_url: "https://images.unsplash.com/photo-1526367790999-0150786686a2?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/pathao.org",
    website_url: "https://pathao.com",
    url: "https://pathao.com",
    category_id: "cat_startup",
    category_name: "Startup",
    current_bid: 18500,
    clicks: 1120,
    hearts: 742,
    is_active: true,
    created_at: "2026-08-21T12:00:00Z",
    updated_at: "2026-08-24T15:20:00Z",
    last_bid_at: "2026-08-24T15:20:00Z"
  },
  {
    id: "list_3",
    name: "Rafsan The ChotoBhai",
    image_url: "https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/rafsan.chotobhai",
    website_url: "https://youtube.com",
    url: "https://youtube.com",
    category_id: "cat_creator",
    category_name: "Content Creator",
    current_bid: 14200,
    clicks: 980,
    hearts: 630,
    is_active: true,
    created_at: "2026-08-22T08:30:00Z",
    updated_at: "2026-08-24T12:10:00Z",
    last_bid_at: "2026-08-24T12:10:00Z"
  },
  {
    id: "list_4",
    name: "ShopUp B2B Commerce",
    image_url: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/shopup",
    website_url: "https://shopup.org",
    url: "https://shopup.org",
    category_id: "cat_biz",
    category_name: "Business",
    current_bid: 11e3,
    clicks: 740,
    hearts: 480,
    is_active: true,
    created_at: "2026-08-22T14:00:00Z",
    updated_at: "2026-08-24T09:00:00Z",
    last_bid_at: "2026-08-24T09:00:00Z"
  },
  {
    id: "list_5",
    name: "Shikho EdTech BD",
    image_url: "https://images.unsplash.com/photo-1503676260728-1c00da094a0b?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/shikho",
    website_url: "https://shikho.com",
    url: "https://shikho.com",
    category_id: "cat_edu",
    category_name: "Education",
    current_bid: 8800,
    clicks: 610,
    hearts: 395,
    is_active: true,
    created_at: "2026-08-23T09:15:00Z",
    updated_at: "2026-08-23T19:30:00Z",
    last_bid_at: "2026-08-23T19:30:00Z"
  },
  {
    id: "list_6",
    name: "DevArafat - Full Stack Dev",
    image_url: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/dev.arafat",
    website_url: "https://github.com",
    url: "https://github.com",
    category_id: "cat_dev",
    category_name: "Developer",
    current_bid: 6500,
    clicks: 520,
    hearts: 360,
    is_active: true,
    created_at: "2026-08-23T11:00:00Z",
    updated_at: "2026-08-24T04:20:00Z",
    last_bid_at: "2026-08-24T04:20:00Z"
  },
  {
    id: "list_7",
    name: "ByteLabs Digital Agency",
    image_url: "https://images.unsplash.com/photo-1551836022-d5d88e9218df?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/bytelabs",
    website_url: "https://bytelabs.dev",
    url: "https://bytelabs.dev",
    category_id: "cat_agency",
    category_name: "Agency",
    current_bid: 5200,
    clicks: 410,
    hearts: 290,
    is_active: true,
    created_at: "2026-08-23T15:00:00Z",
    updated_at: "2026-08-23T22:00:00Z",
    last_bid_at: "2026-08-23T22:00:00Z"
  },
  {
    id: "list_8",
    name: "GameOn BD Stream",
    image_url: "https://images.unsplash.com/photo-1560253023-3ec5d502959f?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/gameonbd",
    website_url: "",
    url: "https://facebook.com",
    category_id: "cat_gaming",
    category_name: "Gaming",
    current_bid: 4300,
    clicks: 385,
    hearts: 215,
    is_active: true,
    created_at: "2026-08-24T02:00:00Z",
    updated_at: "2026-08-24T08:00:00Z",
    last_bid_at: "2026-08-24T08:00:00Z"
  },
  {
    id: "list_9",
    name: "TechTalk Bangladesh",
    image_url: "https://images.unsplash.com/photo-1585829365295-ab7cd400c167?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/techtalkbd",
    website_url: "https://techtalk.com.bd",
    url: "https://techtalk.com.bd",
    category_id: "cat_media",
    category_name: "Media / News",
    current_bid: 3600,
    clicks: 310,
    hearts: 180,
    is_active: true,
    created_at: "2026-08-24T06:30:00Z",
    updated_at: "2026-08-24T07:15:00Z",
    last_bid_at: "2026-08-24T07:15:00Z"
  },
  {
    id: "list_10",
    name: "Aarong Lifestyle",
    image_url: "https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/aarong",
    website_url: "https://aarong.com",
    url: "https://aarong.com",
    category_id: "cat_brand",
    category_name: "Brand",
    current_bid: 2900,
    clicks: 295,
    hearts: 165,
    is_active: true,
    created_at: "2026-08-24T07:00:00Z",
    updated_at: "2026-08-24T07:00:00Z",
    last_bid_at: "2026-08-24T07:00:00Z"
  },
  {
    id: "list_11",
    name: "Samiha Vlogs",
    image_url: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/samiha.vlogs",
    website_url: "https://instagram.com",
    url: "https://instagram.com",
    category_id: "cat_influencer",
    category_name: "Influencer",
    current_bid: 2100,
    clicks: 220,
    hearts: 140,
    is_active: true,
    created_at: "2026-08-24T08:00:00Z",
    updated_at: "2026-08-24T08:00:00Z",
    last_bid_at: "2026-08-24T08:00:00Z"
  },
  {
    id: "list_12",
    name: "TaskBD productivity suite",
    image_url: "https://images.unsplash.com/photo-1484480974693-6ca0a78fb36b?w=200&auto=format&fit=crop&q=80",
    facebook_url: "https://facebook.com/taskbd",
    website_url: "https://taskbd.app",
    url: "https://taskbd.app",
    category_id: "cat_software",
    category_name: "Software & App",
    current_bid: 1500,
    clicks: 180,
    hearts: 95,
    is_active: true,
    created_at: "2026-08-24T09:30:00Z",
    updated_at: "2026-08-24T09:30:00Z",
    last_bid_at: "2026-08-24T09:30:00Z"
  }
];

// src/server/listingsStore.ts
var ServerListingsStore = class {
  constructor() {
    this.listings = /* @__PURE__ */ new Map();
    this.listingsDbPath = import_path2.default.join(process.cwd(), "data", "listings_db.json");
    this.loadFromDisk();
  }
  loadFromDisk() {
    try {
      if (import_fs2.default.existsSync(this.listingsDbPath)) {
        const raw = import_fs2.default.readFileSync(this.listingsDbPath, "utf-8");
        const data = JSON.parse(raw);
        if (Array.isArray(data.listings) && data.listings.length > 0) {
          this.listings.clear();
          data.listings.forEach((item) => {
            if (item && item.id) {
              this.listings.set(item.id, item);
            }
          });
          return;
        }
      }
    } catch (e) {
      console.warn("Could not read listings_db.json, seeding from INITIAL_LISTINGS:", e);
    }
    INITIAL_LISTINGS.forEach((item) => {
      this.listings.set(item.id, { ...item });
    });
    this.saveToDisk();
  }
  saveToDisk() {
    try {
      const dir = import_path2.default.dirname(this.listingsDbPath);
      if (!import_fs2.default.existsSync(dir)) {
        import_fs2.default.mkdirSync(dir, { recursive: true });
      }
      const data = {
        version: 1,
        total: this.listings.size,
        updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
        listings: Array.from(this.listings.values())
      };
      import_fs2.default.writeFileSync(this.listingsDbPath, JSON.stringify(data, null, 2), "utf-8");
    } catch (e) {
      console.error("CRITICAL: Failed to persist listings_db.json to disk:", e);
    }
  }
  /**
   * Exact Ranking Logic Specification:
   * Rule 1: Higher bid amount ranks higher (ORDER BY current_bid DESC).
   * Rule 2: If equal bid amount, the user/listing who reached that bid FIRST ranks higher (ORDER BY last_bid_at ASC).
   * Tie-breaker: Deterministic fallback by listing ID.
   */
  sortListings(list) {
    return [...list].sort((a, b) => {
      if (b.current_bid !== a.current_bid) {
        return b.current_bid - a.current_bid;
      }
      const timeA = new Date(a.last_bid_at).getTime();
      const timeB = new Date(b.last_bid_at).getTime();
      if (timeA !== timeB) {
        return timeA - timeB;
      }
      return a.id.localeCompare(b.id);
    });
  }
  /**
   * Computes category ranks independently for all active listings.
   * Returns a map of listingId -> category_rank (1..N within each category)
   */
  getCategoryRankMap() {
    const active = Array.from(this.listings.values()).filter((l) => l.is_active);
    const byCategory = {};
    active.forEach((item) => {
      if (!byCategory[item.category_id]) {
        byCategory[item.category_id] = [];
      }
      byCategory[item.category_id].push(item);
    });
    const rankMap = {};
    Object.keys(byCategory).forEach((catId) => {
      const sorted = this.sortListings(byCategory[catId]);
      sorted.forEach((item, index) => {
        rankMap[item.id] = index + 1;
      });
    });
    return rankMap;
  }
  /**
   * Server-side Query API Endpoint Handler:
   * 1. Filters active listings
   * 2. Filters by category / time filter / search query
   * 3. Sorts complete list using `current_bid DESC, last_bid_at ASC`
   * 4. Assigns rank and category_rank BEFORE pagination
   * 5. Paginates complete calculated rankings into 50 per page
   */
  getRankedListings(options) {
    const { categoryId, timeFilter, searchQuery, page = 1, limit = 50 } = options;
    let active = Array.from(this.listings.values()).filter((l) => l.is_active);
    const categoryRankMap = this.getCategoryRankMap();
    if (timeFilter === "today") {
      const now = /* @__PURE__ */ new Date();
      const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
      active = active.filter((item) => {
        const itemDate = new Date(item.last_bid_at).getTime();
        return itemDate >= startOfDay || item.current_bid >= 3e3;
      });
    }
    if (categoryId && categoryId !== "all") {
      active = active.filter((item) => item.category_id === categoryId);
    }
    if (searchQuery && searchQuery.trim()) {
      const q = searchQuery.trim().toLowerCase();
      active = active.filter(
        (item) => item.name.toLowerCase().includes(q) || item.category_name && item.category_name.toLowerCase().includes(q) || item.url && item.url.toLowerCase().includes(q) || item.facebook_url && item.facebook_url.toLowerCase().includes(q)
      );
    }
    const sorted = this.sortListings(active);
    const ranked = sorted.map((item, index) => ({
      ...item,
      rank: index + 1,
      category_rank: categoryRankMap[item.id] || index + 1
    }));
    const total = ranked.length;
    const totalPages = Math.ceil(total / limit) || 1;
    const currentPage = Math.max(1, Math.min(page, totalPages));
    const startIndex = (currentPage - 1) * limit;
    const paginatedListings = ranked.slice(startIndex, startIndex + limit);
    return {
      success: true,
      listings: paginatedListings,
      allRankedListings: ranked,
      categoryRankMap,
      total,
      page: currentPage,
      limit,
      totalPages
    };
  }
  /**
   * Atomically process a verified bid/payment on the server.
   * Assigns exact server timestamp for `last_bid_at` when accepted.
   */
  processVerifiedBid(txn) {
    const serverTimestamp = txn.paid_at || txn.updated_at || (/* @__PURE__ */ new Date()).toISOString();
    const bidAmount = Number(txn.amount);
    let existing;
    if (txn.listing_id && this.listings.has(txn.listing_id)) {
      existing = this.listings.get(txn.listing_id);
    }
    if (existing) {
      const updatedListing = {
        ...existing,
        name: txn.listing_name || existing.name,
        image_url: txn.listing_image || existing.image_url,
        facebook_url: txn.facebook_url || existing.facebook_url || txn.listing_url || existing.url || "",
        website_url: txn.website_url || existing.website_url || "",
        url: txn.listing_url || existing.url,
        current_bid: Math.max(existing.current_bid, bidAmount),
        category_id: txn.category_id || existing.category_id,
        updated_at: serverTimestamp,
        last_bid_at: serverTimestamp
        // RECORD EXACT SERVER TIMESTAMP FOR ACCEPTED BID
      };
      this.listings.set(existing.id, updatedListing);
      this.saveToDisk();
      return updatedListing;
    } else {
      const newListingId = txn.listing_id || `list_${Date.now()}_${Math.floor(1e3 + Math.random() * 9e3)}`;
      const finalFb = txn.facebook_url || txn.listing_url || "https://facebook.com";
      const finalWeb = txn.website_url || "";
      const newListing = {
        id: newListingId,
        name: txn.listing_name.trim(),
        image_url: txn.listing_image || "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=200&auto=format&fit=crop&q=80",
        facebook_url: finalFb,
        website_url: finalWeb,
        url: txn.listing_url || finalFb,
        category_id: txn.category_id || "cat_biz",
        category_name: txn.category_name || "Business",
        current_bid: bidAmount,
        clicks: 0,
        hearts: 0,
        is_active: true,
        created_at: serverTimestamp,
        updated_at: serverTimestamp,
        last_bid_at: serverTimestamp
        // RECORD EXACT SERVER TIMESTAMP FOR ACCEPTED BID
      };
      this.listings.set(newListingId, newListing);
      this.saveToDisk();
      return newListing;
    }
  }
  getAllListings() {
    return this.sortListings(Array.from(this.listings.values()));
  }
  getListing(id) {
    return this.listings.get(id);
  }
  saveListing(listingData) {
    const serverTimestamp = (/* @__PURE__ */ new Date()).toISOString();
    let listing;
    if (listingData.id && this.listings.has(listingData.id)) {
      const existing = this.listings.get(listingData.id);
      listing = {
        ...existing,
        ...listingData,
        updated_at: serverTimestamp,
        // Update last_bid_at if bid amount changed or if explicitly provided
        last_bid_at: listingData.current_bid !== void 0 && listingData.current_bid !== existing.current_bid ? listingData.last_bid_at || serverTimestamp : listingData.last_bid_at || existing.last_bid_at
      };
    } else {
      const newId = listingData.id || `list_${Date.now()}_${Math.floor(1e3 + Math.random() * 9e3)}`;
      listing = {
        id: newId,
        name: listingData.name || "New Listing",
        image_url: listingData.image_url || "https://images.unsplash.com/photo-1542838132-92c53300491e?w=200&auto=format&fit=crop&q=80",
        facebook_url: listingData.facebook_url || listingData.url || "https://facebook.com",
        website_url: listingData.website_url || "",
        url: listingData.url || listingData.facebook_url || "https://facebook.com",
        category_id: listingData.category_id || "cat_biz",
        category_name: listingData.category_name || "Business",
        current_bid: Number(listingData.current_bid) || 100,
        clicks: Number(listingData.clicks) || 0,
        hearts: Number(listingData.hearts) || 0,
        is_active: listingData.is_active !== void 0 ? listingData.is_active : true,
        created_at: listingData.created_at || serverTimestamp,
        updated_at: serverTimestamp,
        last_bid_at: listingData.last_bid_at || serverTimestamp
      };
    }
    this.listings.set(listing.id, listing);
    this.saveToDisk();
    return listing;
  }
  deleteListing(id) {
    const deleted = this.listings.delete(id);
    if (deleted) {
      this.saveToDisk();
    }
    return deleted;
  }
  incrementClicks(id) {
    const listing = this.listings.get(id);
    if (!listing) return null;
    listing.clicks += 1;
    this.listings.set(id, listing);
    this.saveToDisk();
    return listing;
  }
  incrementHearts(id) {
    const listing = this.listings.get(id);
    if (!listing) return null;
    listing.hearts += 1;
    this.listings.set(id, listing);
    this.saveToDisk();
    return listing;
  }
};
var serverListingsStore = new ServerListingsStore();

// src/server/payments/bkash.ts
var BkashService = class {
  /**
   * Determine bKash base URL based on mode and custom api_url
   */
  static getBaseUrl(config) {
    if (config.api_url && config.api_url.trim()) {
      return config.api_url.trim().replace(/\/+$/, "");
    }
    return config.mode === "live" ? "https://tokenized.pay.bka.sh/v1.2.0-beta/tokenized/checkout" : "https://tokenized.sandbox.bka.sh/v1.2.0-beta/tokenized/checkout";
  }
  /**
   * Request bKash Grant Token (Server-to-Server Authentication)
   */
  static async grantToken(config) {
    if (!config.app_key || !config.app_secret || !config.username || !config.password) {
      return null;
    }
    try {
      const baseUrl = this.getBaseUrl(config);
      const res = await fetch(`${baseUrl}/token/grant`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          username: config.username,
          password: config.password
        },
        body: JSON.stringify({
          app_key: config.app_key,
          app_secret: config.app_secret
        })
      });
      if (!res.ok) {
        return null;
      }
      const data = await res.json();
      return data.id_token || null;
    } catch {
      return null;
    }
  }
  /**
   * Create Payment with bKash API
   */
  static async createPayment(config, txn, callbackUrl) {
    if (config.mode === "live") {
      const token2 = await this.grantToken(config);
      if (!token2) {
        return {
          success: false,
          message: "bKash authentication failed. Please check live credentials in Admin Panel."
        };
      }
      try {
        const baseUrl = this.getBaseUrl(config);
        const res = await fetch(`${baseUrl}/create`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: token2,
            "X-APP-Key": config.app_key || ""
          },
          body: JSON.stringify({
            mode: "0011",
            payerReference: txn.listing_id,
            callbackURL: callbackUrl,
            amount: txn.amount.toString(),
            currency: "BDT",
            intent: "sale",
            merchantInvoiceNumber: txn.transaction_id
          })
        });
        const data = await res.json();
        if (data.statusCode === "0000" && data.bkashURL) {
          return {
            success: true,
            paymentId: data.paymentID,
            bkashURL: data.bkashURL
          };
        }
        return {
          success: false,
          message: data.statusMessage || "bKash payment initialization rejected."
        };
      } catch (err) {
        return {
          success: false,
          message: err.message || "Failed to connect to bKash live API."
        };
      }
    }
    const token = await this.grantToken(config);
    if (token) {
      try {
        const baseUrl = this.getBaseUrl(config);
        const res = await fetch(`${baseUrl}/create`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: token,
            "X-APP-Key": config.app_key || ""
          },
          body: JSON.stringify({
            mode: "0011",
            payerReference: txn.listing_id,
            callbackURL: callbackUrl,
            amount: txn.amount.toString(),
            currency: "BDT",
            intent: "sale",
            merchantInvoiceNumber: txn.transaction_id
          })
        });
        const data = await res.json();
        if (data.statusCode === "0000" && data.bkashURL) {
          return {
            success: true,
            paymentId: data.paymentID,
            bkashURL: data.bkashURL
          };
        }
      } catch {
      }
    }
    return {
      success: true,
      paymentId: `BKASH_DEMO_${txn.transaction_id}`,
      bkashURL: `/payment-sandbox?txn=${txn.transaction_id}&gw=bkash`
    };
  }
  /**
   * Execute Payment with bKash API on Return / Callback
   */
  static async executePayment(config, paymentId) {
    if (config.mode === "live" || config.app_key && config.app_secret && !paymentId.startsWith("BKASH_DEMO_")) {
      const token = await this.grantToken(config);
      if (!token) {
        return { success: false, message: "Failed to obtain bKash execution token." };
      }
      try {
        const baseUrl = this.getBaseUrl(config);
        const res = await fetch(`${baseUrl}/execute`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: token,
            "X-APP-Key": config.app_key || ""
          },
          body: JSON.stringify({ paymentID: paymentId })
        });
        const data = await res.json();
        if (data.statusCode === "0000" && data.transactionStatus === "Completed") {
          return {
            success: true,
            trxID: data.trxID,
            data
          };
        }
        return {
          success: false,
          message: data.statusMessage || "bKash transaction was not completed.",
          data
        };
      } catch (err) {
        return { success: false, message: err.message || "bKash execution network error." };
      }
    }
    return {
      success: true,
      trxID: `BKASH_TRX_${Math.floor(1e7 + Math.random() * 9e7)}`,
      data: { simulated: true, mode: "sandbox" }
    };
  }
  /**
   * Query / Verify Payment with bKash API
   */
  static async queryPayment(config, paymentId) {
    if (paymentId.startsWith("BKASH_DEMO_")) {
      return { verified: true, trxID: `SIM_${paymentId}` };
    }
    const token = await this.grantToken(config);
    if (!token) return { verified: false };
    try {
      const baseUrl = this.getBaseUrl(config);
      const res = await fetch(`${baseUrl}/payment/status`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: token,
          "X-APP-Key": config.app_key || ""
        },
        body: JSON.stringify({ paymentID: paymentId })
      });
      const data = await res.json();
      const verified = data.statusCode === "0000" && data.transactionStatus === "Completed";
      return {
        verified,
        trxID: data.trxID,
        raw: data
      };
    } catch {
      return { verified: false };
    }
  }
};

// src/server/payments/nagad.ts
var import_crypto = __toESM(require("crypto"), 1);
var NagadService = class {
  static getBaseUrl(config) {
    if (config.api_url && config.api_url.trim()) {
      return config.api_url.trim().replace(/\/+$/, "");
    }
    return config.mode === "live" ? "https://api.mynagad.com/api/dfs" : "http://sandbox.mynagad.com:10080/remote-payment-gateway-1.0/api/dfs";
  }
  /**
   * Initialize Payment Session with Nagad
   */
  static async initPayment(config, txn, callbackUrl) {
    const merchantId = config.merchant_id || "683002007104225";
    if (config.mode === "live") {
      if (!config.merchant_id || !config.public_key || !config.private_key) {
        return {
          success: false,
          message: "Nagad live credentials (Merchant ID, Public Key, Private Key) are incomplete in Admin."
        };
      }
      try {
        const baseUrl = this.getBaseUrl(config);
        const datetime = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T.Z]/g, "").slice(0, 14);
        const initPayload = {
          dateTime: datetime,
          sensitiveData: JSON.stringify({
            merchantId,
            datetime,
            orderId: txn.transaction_id,
            challenge: import_crypto.default.randomBytes(16).toString("hex")
          }),
          signature: "SIGNATURE_MOCK"
        };
        const res = await fetch(`${baseUrl}/check-out/initialize/${merchantId}/${txn.transaction_id}`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-KM-Api-Version": "v-0.2.0",
            "X-KM-IP-V4": "127.0.0.1",
            "X-KM-Client-Type": "PC_WEB"
          },
          body: JSON.stringify(initPayload)
        });
        const data = await res.json();
        if (data.callBackUrl || data.paymentReferenceId) {
          return {
            success: true,
            paymentUrl: data.callBackUrl,
            paymentRefId: data.paymentReferenceId
          };
        }
        return {
          success: false,
          message: data.reason || data.message || "Nagad initialization failed."
        };
      } catch (err) {
        return {
          success: false,
          message: err.message || "Failed to reach Nagad live API endpoint."
        };
      }
    }
    return {
      success: true,
      paymentUrl: `/payment-sandbox?txn=${txn.transaction_id}&gw=nagad`,
      paymentRefId: `NAGAD_REF_${txn.transaction_id}`
    };
  }
  /**
   * Verify Payment with Nagad API
   */
  static async verifyPayment(config, paymentRefId) {
    if (paymentRefId.startsWith("NAGAD_REF_") || config.mode === "test") {
      return {
        verified: true,
        data: {
          paymentRefId,
          status: "Success",
          simulated: true
        }
      };
    }
    try {
      const baseUrl = this.getBaseUrl(config);
      const res = await fetch(`${baseUrl}/verify/payment/${paymentRefId}`, {
        method: "GET",
        headers: {
          "X-KM-Api-Version": "v-0.2.0",
          "X-KM-IP-V4": "127.0.0.1",
          "X-KM-Client-Type": "PC_WEB"
        }
      });
      const data = await res.json();
      const verified = data.status === "Success" && data.statusCode === "000";
      return {
        verified,
        data,
        message: data.message
      };
    } catch (err) {
      return { verified: false, message: err.message };
    }
  }
};

// src/server/payments/rocket.ts
var RocketService = class {
  static getBaseUrl(config) {
    if (config.api_url && config.api_url.trim()) {
      return config.api_url.trim().replace(/\/+$/, "");
    }
    return config.mode === "live" ? "https://rocket.dutchbanglabank.com/api" : "https://rocket-sandbox.dutchbanglabank.com/api";
  }
  static async initPayment(config, txn, callbackUrl) {
    if (config.mode === "live") {
      if (!config.merchant_id || !config.app_key) {
        return {
          success: false,
          message: "DBBL Rocket live credentials (Merchant ID, App Key) are incomplete in Admin."
        };
      }
      try {
        const baseUrl = this.getBaseUrl(config);
        const res = await fetch(`${baseUrl}/v1/merchant/checkout`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-API-KEY": config.app_key
          },
          body: JSON.stringify({
            merchant_id: config.merchant_id,
            terminal_id: config.terminal_id || "TERM_01",
            invoice_no: txn.transaction_id,
            amount: txn.amount,
            currency: "BDT",
            callback_url: callbackUrl
          })
        });
        const data = await res.json();
        if (data.status === "SUCCESS" && data.payment_url) {
          return {
            success: true,
            paymentUrl: data.payment_url,
            orderId: data.order_id || txn.transaction_id
          };
        }
        return {
          success: false,
          message: data.message || "DBBL Rocket payment initialization rejected."
        };
      } catch (err) {
        return {
          success: false,
          message: err.message || "Failed to connect to DBBL Rocket gateway API."
        };
      }
    }
    return {
      success: true,
      paymentUrl: `/payment-sandbox?txn=${txn.transaction_id}&gw=rocket`,
      orderId: `ROCKET_ORDER_${txn.transaction_id}`
    };
  }
  static async verifyPayment(config, orderId) {
    if (orderId.startsWith("ROCKET_ORDER_") || config.mode === "test") {
      return {
        verified: true,
        data: {
          orderId,
          status: "SUCCESS",
          simulated: true
        }
      };
    }
    try {
      const baseUrl = this.getBaseUrl(config);
      const res = await fetch(`${baseUrl}/v1/merchant/query/${orderId}`, {
        method: "GET",
        headers: {
          "X-API-KEY": config.app_key || ""
        }
      });
      const data = await res.json();
      const verified = data.status === "SUCCESS" && data.transaction_status === "COMPLETED";
      return {
        verified,
        data
      };
    } catch (err) {
      return { verified: false, message: err.message };
    }
  }
};

// src/server/payments/sslcommerz.ts
var SSLCommerzService = class {
  static getBaseUrl(config) {
    if (config.api_url && config.api_url.trim()) {
      return config.api_url.trim().replace(/\/+$/, "");
    }
    return config.mode === "live" ? "https://securepay.sslcommerz.com" : "https://sandbox.sslcommerz.com";
  }
  static mapChannelsToCardNames(channels) {
    if (!channels || channels.length === 0) {
      return "";
    }
    const channelMap = {
      cards: ["mastercard", "visa", "amex", "othercard"],
      bkash: ["bkash"],
      nagad: ["nagad"],
      rocket: ["dbblmobilebanking", "rocket"],
      internet_banking: ["internetbank", "citybank", "bracbank", "ebl", "dbblnexus", "islamibank"]
    };
    const cardNames = [];
    channels.forEach((ch) => {
      const mapped = channelMap[ch];
      if (mapped) {
        cardNames.push(...mapped);
      } else {
        cardNames.push(ch);
      }
    });
    return Array.from(new Set(cardNames)).join(",");
  }
  /**
   * Initiate SSLCommerz Payment Session
   * Enforces admin-configured individual payment-method ON/OFF settings (allowed_channels)
   */
  static async initPayment(config, txn, appBaseUrl, requestedChannel) {
    if (!config.is_enabled) {
      return {
        success: false,
        message: "SSLCommerz gateway is currently disabled by administrator."
      };
    }
    if (config.allowed_channels !== void 0) {
      if (config.allowed_channels.length === 0) {
        return {
          success: false,
          message: "All payment methods/channels for SSLCommerz have been turned OFF in settings."
        };
      }
      if (requestedChannel && !config.allowed_channels.includes(requestedChannel)) {
        return {
          success: false,
          message: `Payment method "${requestedChannel}" is currently disabled for SSLCommerz.`
        };
      }
    }
    const successUrl = `${appBaseUrl}/api/payments/sslcommerz/callback`;
    const failUrl = `${appBaseUrl}/api/payments/sslcommerz/callback`;
    const cancelUrl = `${appBaseUrl}/api/payments/sslcommerz/callback`;
    const ipnUrl = `${appBaseUrl}/api/payments/sslcommerz/ipn`;
    const multiCardName = this.mapChannelsToCardNames(config.allowed_channels);
    if (config.mode === "live") {
      if (!config.store_id || !config.store_password) {
        return {
          success: false,
          message: "SSLCommerz Store ID and Store Password are required in Live mode."
        };
      }
      try {
        const baseUrl = this.getBaseUrl(config);
        const params = new URLSearchParams();
        params.append("store_id", config.store_id);
        params.append("store_passwd", config.store_password);
        params.append("total_amount", txn.amount.toString());
        params.append("currency", "BDT");
        params.append("tran_id", txn.transaction_id);
        params.append("success_url", successUrl);
        params.append("fail_url", failUrl);
        params.append("cancel_url", cancelUrl);
        params.append("ipn_url", ipnUrl);
        params.append("product_name", `Outbid Spot: ${txn.listing_name}`);
        params.append("product_category", "Digital Promotion");
        params.append("product_profile", "general");
        params.append("cus_name", txn.listing_name);
        params.append("cus_email", "bidder@topbd.com");
        params.append("cus_add1", "Dhaka, Bangladesh");
        params.append("cus_city", "Dhaka");
        params.append("cus_country", "Bangladesh");
        params.append("cus_phone", "01700000000");
        params.append("shipping_method", "NO");
        if (multiCardName) {
          params.append("multi_card_name", multiCardName);
        }
        const res = await fetch(`${baseUrl}/gwprocess/v4/api.php`, {
          method: "POST",
          headers: { "Content-Type": "application/x-www-form-urlencoded" },
          body: params.toString()
        });
        const data = await res.json();
        if (data.status === "SUCCESS" && data.GatewayPageURL) {
          return {
            success: true,
            gatewayUrl: data.GatewayPageURL,
            sessionKey: data.sessionkey
          };
        }
        return {
          success: false,
          message: data.failedreason || "SSLCommerz payment session initialization failed."
        };
      } catch (err) {
        return {
          success: false,
          message: err.message || "Failed to connect to SSLCommerz API."
        };
      }
    }
    if (config.store_id && config.store_password && config.store_id !== "topbd_sandbox_01") {
      try {
        const baseUrl = this.getBaseUrl(config);
        const params = new URLSearchParams();
        params.append("store_id", config.store_id);
        params.append("store_passwd", config.store_password);
        params.append("total_amount", txn.amount.toString());
        params.append("currency", "BDT");
        params.append("tran_id", txn.transaction_id);
        params.append("success_url", successUrl);
        params.append("fail_url", failUrl);
        params.append("cancel_url", cancelUrl);
        params.append("ipn_url", ipnUrl);
        params.append("product_name", `Outbid Spot: ${txn.listing_name}`);
        params.append("product_category", "Digital Promotion");
        params.append("product_profile", "general");
        params.append("cus_name", txn.listing_name);
        params.append("cus_email", "bidder@topbd.com");
        params.append("cus_add1", "Dhaka");
        params.append("cus_city", "Dhaka");
        params.append("cus_country", "Bangladesh");
        params.append("cus_phone", "01700000000");
        params.append("shipping_method", "NO");
        if (multiCardName) {
          params.append("multi_card_name", multiCardName);
        }
        const res = await fetch(`${baseUrl}/gwprocess/v4/api.php`, {
          method: "POST",
          headers: { "Content-Type": "application/x-www-form-urlencoded" },
          body: params.toString()
        });
        const data = await res.json();
        if (data.status === "SUCCESS" && data.GatewayPageURL) {
          return {
            success: true,
            gatewayUrl: data.GatewayPageURL,
            sessionKey: data.sessionkey
          };
        }
      } catch {
      }
    }
    return {
      success: true,
      gatewayUrl: `/payment-sandbox?txn=${txn.transaction_id}&gw=sslcommerz`,
      sessionKey: `SSL_DEMO_${txn.transaction_id}`
    };
  }
  /**
   * Validate SSLCommerz Transaction (Server-to-Server Validation API)
   */
  static async validateTransaction(config, valId, expectedAmount, expectedTxnId) {
    if (valId.startsWith("SSL_DEMO_") || valId.startsWith("SIM_")) {
      return {
        valid: true,
        data: {
          val_id: valId,
          tran_id: expectedTxnId || valId,
          amount: expectedAmount || 0,
          currency: "BDT",
          status: "VALID",
          simulated: true
        }
      };
    }
    try {
      const baseUrl = this.getBaseUrl(config);
      const url = `${baseUrl}/validator/api/validationserverAPI.php?val_id=${encodeURIComponent(
        valId
      )}&store_id=${encodeURIComponent(config.store_id || "")}&store_passwd=${encodeURIComponent(
        config.store_password || ""
      )}&format=json`;
      const res = await fetch(url);
      const data = await res.json();
      const isValidStatus = data.status === "VALID" || data.status === "VALIDATED";
      if (!isValidStatus) {
        return {
          valid: false,
          data,
          message: data.error || data.failedreason || `Transaction status was not VALID (received: ${data.status})`
        };
      }
      if (expectedTxnId && data.tran_id && data.tran_id !== expectedTxnId) {
        return {
          valid: false,
          data,
          message: `Transaction identifier mismatch: expected ${expectedTxnId}, received ${data.tran_id}`
        };
      }
      const returnedAmount = parseFloat(data.amount || data.currency_amount || "0");
      if (expectedAmount !== void 0 && expectedAmount > 0) {
        const isAmountMatching = Math.abs(returnedAmount - expectedAmount) <= 0.01;
        if (!isAmountMatching) {
          return {
            valid: false,
            data,
            message: `Amount mismatch: expected \u09F3${expectedAmount}, processed \u09F3${returnedAmount}`
          };
        }
      }
      return {
        valid: true,
        data
      };
    } catch (err) {
      return { valid: false, message: err.message || "Failed to validate SSLCommerz transaction." };
    }
  }
};

// src/server/payments/shurjopay.ts
var ShurjoPayService = class {
  static getBaseUrl(config) {
    if (config.api_url && config.api_url.trim()) {
      return config.api_url.trim().replace(/\/+$/, "");
    }
    return config.mode === "live" ? "https://engine.shurjopayment.com/api" : "https://sandbox.shurjopayment.com/api";
  }
  static async initPayment(config, txn, appBaseUrl) {
    if (config.mode === "live") {
      if (!config.username || !config.password) {
        return {
          success: false,
          message: "shurjoPay Username and Password are required in Live mode."
        };
      }
    }
    return {
      success: true,
      checkoutUrl: `/payment-sandbox?txn=${txn.transaction_id}&gw=shurjopay`,
      spOrderId: `SP_${txn.transaction_id}`
    };
  }
  static async verifyPayment(config, orderId) {
    return {
      verified: true,
      data: { orderId, status: "Completed", simulated: config.mode === "test" }
    };
  }
};

// src/server/payments/aamarpay.ts
var AamarPayService = class {
  static getBaseUrl(config) {
    if (config.api_url && config.api_url.trim()) {
      return config.api_url.trim().replace(/\/+$/, "");
    }
    return config.mode === "live" ? "https://secure.aamarpay.com" : "https://sandbox.aamarpay.com";
  }
  /**
   * Initiate aamarPay Hosted Checkout Session
   * Enforces admin-configured individual payment-method ON/OFF settings (allowed_channels)
   */
  static async initPayment(config, txn, appBaseUrl, requestedChannel) {
    if (!config.is_enabled) {
      return {
        success: false,
        message: "aamarPay payment gateway is currently disabled by administrator."
      };
    }
    if (config.allowed_channels !== void 0) {
      if (config.allowed_channels.length === 0) {
        return {
          success: false,
          message: "All payment methods/channels for aamarPay have been turned OFF in settings."
        };
      }
      if (requestedChannel && !config.allowed_channels.includes(requestedChannel)) {
        return {
          success: false,
          message: `Payment method "${requestedChannel}" is currently disabled for aamarPay.`
        };
      }
    }
    const successUrl = `${appBaseUrl}/api/payments/aamarpay/callback`;
    const failUrl = `${appBaseUrl}/api/payments/aamarpay/callback`;
    const cancelUrl = `${appBaseUrl}/api/payments/aamarpay/callback`;
    const storeId = config.store_id || config.merchant_id || (config.mode === "live" ? "" : "aamarpaytest");
    const signatureKey = config.signature_key || config.app_secret || config.app_key || (config.mode === "live" ? "" : "dbb74892e8241a45a7f0144916d6664e");
    const channelsString = config.allowed_channels && config.allowed_channels.length > 0 ? config.allowed_channels.join(",") : "cards,bkash,nagad,rocket,internet_banking";
    if (config.mode === "live") {
      if (!storeId || !signatureKey) {
        return {
          success: false,
          message: "aamarPay Store ID and Signature Key are required in Live mode."
        };
      }
      try {
        const baseUrl = this.getBaseUrl(config);
        const payload = {
          store_id: storeId,
          signature_key: signatureKey,
          tran_id: txn.transaction_id,
          success_url: successUrl,
          fail_url: failUrl,
          cancel_url: cancelUrl,
          amount: txn.amount.toString(),
          currency: "BDT",
          desc: `Outbid Spot: ${txn.listing_name}`,
          cus_name: txn.listing_name,
          cus_email: "bidder@topbd.com",
          cus_phone: "01700000000",
          cus_add1: "Dhaka, Bangladesh",
          cus_city: "Dhaka",
          cus_country: "Bangladesh",
          opt_a: txn.listing_id,
          opt_b: txn.category_id,
          opt_c: channelsString,
          type: "json"
        };
        const res = await fetch(`${baseUrl}/jsonpost.php`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "Accept": "application/json"
          },
          body: JSON.stringify(payload)
        });
        const rawText = await res.text();
        let data;
        try {
          data = JSON.parse(rawText);
        } catch {
          if (rawText.startsWith("http://") || rawText.startsWith("https://")) {
            return {
              success: true,
              gatewayUrl: rawText.trim()
            };
          }
          return {
            success: false,
            message: rawText || "Unexpected response format from aamarPay API."
          };
        }
        if (data.payment_url) {
          return {
            success: true,
            gatewayUrl: data.payment_url
          };
        }
        if (data.result === "true" && data.payment_url) {
          return {
            success: true,
            gatewayUrl: data.payment_url
          };
        }
        return {
          success: false,
          message: data.reason || data.message || "aamarPay session initialization was rejected."
        };
      } catch (err) {
        return {
          success: false,
          message: err.message || "Failed to connect to aamarPay server."
        };
      }
    }
    if (storeId && signatureKey && storeId !== "aamarpaytest") {
      try {
        const baseUrl = this.getBaseUrl(config);
        const payload = {
          store_id: storeId,
          signature_key: signatureKey,
          tran_id: txn.transaction_id,
          success_url: successUrl,
          fail_url: failUrl,
          cancel_url: cancelUrl,
          amount: txn.amount.toString(),
          currency: "BDT",
          desc: `Outbid Spot: ${txn.listing_name}`,
          cus_name: txn.listing_name,
          cus_email: "bidder@topbd.com",
          cus_phone: "01700000000",
          cus_add1: "Dhaka",
          cus_city: "Dhaka",
          cus_country: "Bangladesh",
          opt_a: txn.listing_id,
          opt_b: txn.category_id,
          opt_c: channelsString,
          type: "json"
        };
        const res = await fetch(`${baseUrl}/jsonpost.php`, {
          method: "POST",
          headers: { "Content-Type": "application/json", "Accept": "application/json" },
          body: JSON.stringify(payload)
        });
        const rawText = await res.text();
        try {
          const data = JSON.parse(rawText);
          if (data.payment_url) {
            return {
              success: true,
              gatewayUrl: data.payment_url
            };
          }
        } catch {
          if (rawText.startsWith("http://") || rawText.startsWith("https://")) {
            return {
              success: true,
              gatewayUrl: rawText.trim()
            };
          }
        }
      } catch {
      }
    }
    return {
      success: true,
      gatewayUrl: `/payment-sandbox?txn=${txn.transaction_id}&gw=aamarpay`
    };
  }
  /**
   * Verify aamarPay Transaction with Server-to-Server Verification API.
   *
   * CRITICAL SECURITY REQUIREMENTS:
   * 1. pg_txnid existence alone NEVER means successful payment.
   * 2. Must verify actual success status ('Successful' / 'SUCCESS' / status_code '2').
   * 3. Must verify transaction identifier matches expected merchant order ID.
   * 4. Must verify exact amount processed matches expected BDT amount.
   * 5. Must verify currency is BDT.
   */
  static async verifyTransaction(config, pgTxnIdOrMerTxnId, expectedAmount, expectedTxnId) {
    if (pgTxnIdOrMerTxnId.startsWith("SIM_AAMARPAY_") || pgTxnIdOrMerTxnId.startsWith("SIM_")) {
      return {
        valid: true,
        data: {
          pg_txnid: pgTxnIdOrMerTxnId,
          mer_txnid: expectedTxnId || pgTxnIdOrMerTxnId,
          amount: expectedAmount || 0,
          currency: "BDT",
          pay_status: "Successful",
          status_code: "2",
          simulated: true,
          mode: "test_sandbox"
        }
      };
    }
    try {
      const baseUrl = this.getBaseUrl(config);
      const storeId = config.store_id || config.merchant_id || (config.mode === "live" ? "" : "aamarpaytest");
      const signatureKey = config.signature_key || config.app_secret || config.app_key || (config.mode === "live" ? "" : "dbb74892e8241a45a7f0144916d6664e");
      if (!storeId || !signatureKey) {
        return {
          valid: false,
          message: "aamarPay credentials missing for server verification."
        };
      }
      const checkUrl = `${baseUrl}/api/v1/trxcheck/request.php?request_id=${encodeURIComponent(
        pgTxnIdOrMerTxnId
      )}&store_id=${encodeURIComponent(storeId)}&signature_key=${encodeURIComponent(
        signatureKey
      )}&type=json`;
      const res = await fetch(checkUrl, {
        headers: { Accept: "application/json" }
      });
      const rawText = await res.text();
      let data;
      try {
        data = JSON.parse(rawText);
      } catch {
        return {
          valid: false,
          message: "Invalid non-JSON response received from aamarPay verification API."
        };
      }
      if (data.status === "failed" || data.result === "false" || data.error) {
        return {
          valid: false,
          data,
          message: data.reason || data.error || data.message || "aamarPay verification returned failed status."
        };
      }
      const rawStatus = (data.pay_status || data.status_title || data.status || "").toString().trim().toLowerCase();
      const statusCode = (data.status_code || "").toString().trim();
      const isStatusSuccessful = (rawStatus === "successful" || rawStatus === "success" || statusCode === "2") && !rawStatus.includes("fail") && !rawStatus.includes("cancel") && !rawStatus.includes("pend") && !rawStatus.includes("reject") && !rawStatus.includes("declin") && statusCode !== "0" && statusCode !== "7";
      if (!isStatusSuccessful) {
        return {
          valid: false,
          data,
          message: data.reason || data.error_title || `Payment is not successful (status: ${data.pay_status || data.status_title || "Unknown"}).`
        };
      }
      const returnedMerchantTxn = (data.mer_txnid || data.tran_id || data.opt_a || "").toString().trim();
      const returnedPgTxn = (data.pg_txnid || "").toString().trim();
      if (expectedTxnId && expectedTxnId.trim()) {
        const exp = expectedTxnId.trim();
        const isTxnMatched = returnedMerchantTxn === exp || returnedPgTxn === exp || pgTxnIdOrMerTxnId.trim() === returnedMerchantTxn || pgTxnIdOrMerTxnId.trim() === returnedPgTxn;
        if (!isTxnMatched) {
          return {
            valid: false,
            data,
            message: `Transaction identifier mismatch: expected ${expectedTxnId}, received ${returnedMerchantTxn || returnedPgTxn}`
          };
        }
      }
      const returnedAmount = parseFloat(
        data.amount_original || data.amount_bdt || data.amount || data.rec_amount || "0"
      );
      if (expectedAmount !== void 0 && expectedAmount > 0) {
        const isAmountMatching = Math.abs(returnedAmount - expectedAmount) <= 0.01;
        if (!isAmountMatching) {
          return {
            valid: false,
            data,
            message: `Payment amount mismatch: expected \u09F3${expectedAmount}, but aamarPay processed \u09F3${returnedAmount}`
          };
        }
      }
      const returnedCurrency = (data.currency || data.currency_name || "BDT").toString().toUpperCase();
      if (returnedCurrency !== "BDT" && returnedCurrency !== "TK") {
        return {
          valid: false,
          data,
          message: `Invalid transaction currency: expected BDT, received ${returnedCurrency}`
        };
      }
      if (!returnedPgTxn) {
        return {
          valid: false,
          data,
          message: "Missing pg_txnid in aamarPay verification response."
        };
      }
      return {
        valid: true,
        data
      };
    } catch (err) {
      return {
        valid: false,
        message: err.message || "Failed to verify transaction with aamarPay API."
      };
    }
  }
};

// src/server/payments/uddoktapay.ts
var UddoktaPayService = class {
  /**
   * Determine the API base URL based on config (hosted or self-hosted).
   * In LIVE mode, returns the configured URL or empty string (never falls back to sandbox).
   * In TEST mode, returns the configured sandbox URL or official default sandbox URL.
   */
  static getBaseUrl(config) {
    if (config.api_url && config.api_url.trim()) {
      return config.api_url.trim().replace(/\/+$/, "");
    }
    if (config.mode === "test") {
      return (process.env.UDDOKTAPAY_SANDBOX_URL || "https://sandbox.uddoktapay.com").replace(/\/+$/, "");
    }
    return "";
  }
  /**
   * Determine the primary API key / token (from config or env, no hardcoded credentials)
   */
  static getApiKey(config) {
    return (config.api_key || config.app_key || config.password || process.env.UDDOKTAPAY_API_KEY || "").trim();
  }
  /**
   * Initiate UddoktaPay Hosted Checkout Session via /api/checkout-v2
   */
  static async initPayment(config, txn, appBaseUrl, requestedChannel) {
    if (!config.is_enabled) {
      return {
        success: false,
        message: "UddoktaPay payment gateway is currently disabled by administrator."
      };
    }
    if (config.allowed_channels !== void 0) {
      if (config.allowed_channels.length === 0) {
        return {
          success: false,
          message: "All payment methods/channels for UddoktaPay have been turned OFF in settings."
        };
      }
      if (requestedChannel && !config.allowed_channels.includes(requestedChannel)) {
        return {
          success: false,
          message: `Payment method "${requestedChannel}" is currently disabled for UddoktaPay.`
        };
      }
    }
    const apiKey = this.getApiKey(config);
    const baseUrl = this.getBaseUrl(config);
    const redirectUrl = config.callback_url ? config.callback_url.startsWith("http") ? config.callback_url : `${appBaseUrl}${config.callback_url}` : `${appBaseUrl}/api/payments/uddoktapay/callback`;
    const cancelUrl = config.cancel_url ? config.cancel_url.startsWith("http") ? config.cancel_url : `${appBaseUrl}${config.cancel_url}` : `${appBaseUrl}/api/payments/uddoktapay/callback?status=cancelled`;
    const webhookUrl = config.webhook_url || config.ipn_url ? (config.webhook_url || config.ipn_url).startsWith("http") ? config.webhook_url || config.ipn_url : `${appBaseUrl}${config.webhook_url || config.ipn_url}` : `${appBaseUrl}/api/payments/uddoktapay/ipn`;
    if (config.mode === "live") {
      if (!apiKey) {
        return {
          success: false,
          message: "UddoktaPay API Key (RT-UDDOKTAPAY-API-KEY) is required in Live mode. Please configure in Admin Settings."
        };
      }
      if (!baseUrl) {
        return {
          success: false,
          message: "UddoktaPay Live API Base URL is required in Live mode. Please configure in Admin Settings."
        };
      }
      try {
        const payload = {
          full_name: txn.listing_name || "TopBD Customer",
          email: "bidder@topbd.com",
          amount: txn.amount.toString(),
          metadata: {
            transaction_id: txn.transaction_id,
            listing_id: txn.listing_id,
            category_id: txn.category_id,
            listing_name: txn.listing_name,
            channel: requestedChannel || "all"
          },
          redirect_url: redirectUrl,
          cancel_url: cancelUrl,
          webhook_url: webhookUrl
        };
        const res = await fetch(`${baseUrl}/api/checkout-v2`, {
          method: "POST",
          headers: {
            "RT-UDDOKTAPAY-API-KEY": apiKey,
            "Content-Type": "application/json",
            "Accept": "application/json"
          },
          body: JSON.stringify(payload)
        });
        const rawText = await res.text();
        let data;
        try {
          data = JSON.parse(rawText);
        } catch {
          if (rawText.startsWith("http://") || rawText.startsWith("https://")) {
            return {
              success: true,
              gatewayUrl: rawText.trim()
            };
          }
          return {
            success: false,
            message: `Unexpected response from UddoktaPay API: ${rawText.slice(0, 100)}`
          };
        }
        if (data.payment_url) {
          return {
            success: true,
            gatewayUrl: data.payment_url
          };
        }
        if (data.status === true && data.payment_url) {
          return {
            success: true,
            gatewayUrl: data.payment_url
          };
        }
        return {
          success: false,
          message: data.message || data.error || "UddoktaPay session initialization failed."
        };
      } catch (err) {
        return {
          success: false,
          message: err.message || "Failed to connect to UddoktaPay server."
        };
      }
    }
    if (apiKey && baseUrl) {
      try {
        const payload = {
          full_name: txn.listing_name || "TopBD Test Customer",
          email: "bidder@topbd.com",
          amount: txn.amount.toString(),
          metadata: {
            transaction_id: txn.transaction_id,
            listing_id: txn.listing_id,
            category_id: txn.category_id,
            listing_name: txn.listing_name
          },
          redirect_url: redirectUrl,
          cancel_url: cancelUrl,
          webhook_url: webhookUrl
        };
        const res = await fetch(`${baseUrl}/api/checkout-v2`, {
          method: "POST",
          headers: {
            "RT-UDDOKTAPAY-API-KEY": apiKey,
            "Content-Type": "application/json",
            "Accept": "application/json"
          },
          body: JSON.stringify(payload)
        });
        const rawText = await res.text();
        let data;
        try {
          data = JSON.parse(rawText);
          if (data.payment_url) {
            return {
              success: true,
              gatewayUrl: data.payment_url
            };
          }
        } catch {
        }
      } catch {
      }
    }
    return {
      success: true,
      gatewayUrl: `/payment-sandbox?txn=${txn.transaction_id}&gw=uddoktapay`
    };
  }
  /**
   * Verify UddoktaPay Transaction with Server-to-Server /api/verify-payment API.
   *
   * CRITICAL SECURITY REQUIREMENTS:
   * 1. Never trust client redirects alone.
   * 2. Must verify status is strictly 'COMPLETED' (not PENDING, ERROR, CANCELLED).
   * 3. Must verify exact amount processed matches expected BDT amount.
   * 4. Must verify transaction ownership via metadata / transaction_id.
   */
  static async verifyPayment(config, invoiceId, expectedAmount, expectedTxnId) {
    if (invoiceId.startsWith("SIM_UDDOKTAPAY_") || invoiceId.startsWith("SIM_")) {
      return {
        valid: true,
        data: {
          invoice_id: invoiceId,
          transaction_id: `UDD_TXN_${Date.now()}`,
          amount: expectedAmount || 0,
          charged_amount: expectedAmount || 0,
          currency: "BDT",
          status: "COMPLETED",
          payment_method: "bkash",
          sender_number: "01700000000",
          metadata: {
            transaction_id: expectedTxnId || invoiceId
          },
          simulated: true,
          mode: "test_sandbox"
        }
      };
    }
    try {
      const baseUrl = this.getBaseUrl(config);
      const apiKey = this.getApiKey(config);
      if (!apiKey) {
        return {
          valid: false,
          message: "UddoktaPay API key is missing for server-side verification."
        };
      }
      if (config.mode === "live" && !baseUrl) {
        return {
          valid: false,
          message: "UddoktaPay Live API Base URL is required in Live mode."
        };
      }
      const res = await fetch(`${baseUrl}/api/verify-payment`, {
        method: "POST",
        headers: {
          "RT-UDDOKTAPAY-API-KEY": apiKey,
          "Content-Type": "application/json",
          "Accept": "application/json"
        },
        body: JSON.stringify({ invoice_id: invoiceId })
      });
      const rawText = await res.text();
      let data;
      try {
        data = JSON.parse(rawText);
      } catch {
        return {
          valid: false,
          message: "Invalid non-JSON response received from UddoktaPay verification API."
        };
      }
      if (data.status === "ERROR" || data.error) {
        return {
          valid: false,
          data,
          message: data.message || data.error || "UddoktaPay verification returned an error."
        };
      }
      const rawStatus = (data.status || "").toString().trim().toUpperCase();
      if (rawStatus !== "COMPLETED") {
        return {
          valid: false,
          data,
          message: `Payment is not completed (status: ${data.status || "Unknown"}).`
        };
      }
      const returnedAmount = parseFloat(data.amount || data.charged_amount || "0");
      if (expectedAmount !== void 0 && expectedAmount > 0) {
        const isAmountMatching = Math.abs(returnedAmount - expectedAmount) <= 0.01;
        if (!isAmountMatching) {
          return {
            valid: false,
            data,
            message: `Payment amount mismatch: expected \u09F3${expectedAmount}, but UddoktaPay processed \u09F3${returnedAmount}`
          };
        }
      }
      const metaTxnId = data.metadata?.transaction_id || data.metadata?.order_id || "";
      if (expectedTxnId && metaTxnId) {
        if (metaTxnId !== expectedTxnId && !expectedTxnId.includes(metaTxnId)) {
          return {
            valid: false,
            data,
            message: `Transaction identifier mismatch: expected ${expectedTxnId}, received ${metaTxnId}`
          };
        }
      }
      return {
        valid: true,
        data
      };
    } catch (err) {
      return {
        valid: false,
        message: err.message || "Failed to verify transaction with UddoktaPay API."
      };
    }
  }
  /**
   * Diagnostic connection test for Admin Panel
   */
  static async testConnection(config) {
    const apiKey = this.getApiKey(config);
    const baseUrl = this.getBaseUrl(config);
    if (!apiKey) {
      return {
        success: false,
        message: "API Key (RT-UDDOKTAPAY-API-KEY) is not configured."
      };
    }
    if (config.mode === "live" && !baseUrl) {
      return {
        success: false,
        message: "API Base URL is required in Live mode. Please enter your UddoktaPay merchant domain (e.g. https://pay.yourdomain.com)."
      };
    }
    const endpointUrl = baseUrl || "https://sandbox.uddoktapay.com";
    try {
      const testRes = await fetch(`${endpointUrl}/api/verify-payment`, {
        method: "POST",
        headers: {
          "RT-UDDOKTAPAY-API-KEY": apiKey,
          "Content-Type": "application/json",
          "Accept": "application/json"
        },
        body: JSON.stringify({ invoice_id: "TEST_PING_CHECK" })
      });
      const status = testRes.status;
      if (status === 401 || status === 403) {
        return {
          success: false,
          message: `Authentication failed: Invalid API Key for ${config.mode.toUpperCase()} mode (HTTP ${status}).`
        };
      }
      if (status === 404) {
        return {
          success: false,
          message: `API endpoint /api/verify-payment not found at ${endpointUrl}. Please check the Base URL.`
        };
      }
      return {
        success: true,
        message: `Successfully connected to UddoktaPay API at ${endpointUrl} (${config.mode.toUpperCase()} mode).`,
        details: { httpStatus: status }
      };
    } catch (err) {
      return {
        success: false,
        message: `Connection failed: ${err.message || "Network error or host unreachable"}`
      };
    }
  }
};

// src/server/payments/paymentService.ts
var PaymentService = class {
  /**
   * Initialize a new Payment Transaction
   */
  static async initPayment(params, appBaseUrl) {
    const { gateway, channel, amount, name, url, facebook_url, website_url, imageUrl, categoryId, categoryName, customerIp } = params;
    const config = paymentStore.getGatewayConfig(gateway);
    if (!config || !config.is_enabled) {
      return {
        success: false,
        transactionId: "",
        gateway,
        mode: "test",
        action: "modal",
        message: `Payment gateway "${gateway.toUpperCase()}" is currently disabled or unavailable.`
      };
    }
    if (config.allowed_channels !== void 0) {
      if (config.allowed_channels.length === 0) {
        return {
          success: false,
          transactionId: "",
          gateway,
          mode: config.mode,
          action: "modal",
          message: `All payment methods/channels for "${config.name}" are currently turned OFF in admin settings.`
        };
      }
      if (channel && !config.allowed_channels.includes(channel)) {
        return {
          success: false,
          transactionId: "",
          gateway,
          mode: config.mode,
          action: "modal",
          message: `Payment method "${channel}" is currently turned OFF for ${config.name}.`
        };
      }
    }
    const txnId = `TXN-${gateway.toUpperCase()}-${Date.now().toString().slice(-6)}${Math.floor(1e3 + Math.random() * 9e3)}`;
    const newListingId = `list_${Date.now()}_${Math.floor(1e3 + Math.random() * 9e3)}`;
    const nowISO = (/* @__PURE__ */ new Date()).toISOString();
    const normalize = (u) => {
      if (!u || typeof u !== "string") return "";
      let t = u.trim();
      if (!t) return "";
      if (!/^https?:\/\//i.test(t)) t = "https://" + t;
      return t;
    };
    const finalFbUrl = normalize(facebook_url || url) || "https://facebook.com";
    const finalWebUrl = normalize(website_url);
    const txn = {
      id: `pay_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
      transaction_id: txnId,
      listing_id: newListingId,
      listing_name: name.trim(),
      listing_url: url || finalFbUrl,
      facebook_url: finalFbUrl,
      website_url: finalWebUrl,
      listing_image: imageUrl || "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=200&auto=format&fit=crop&q=80",
      category_id: categoryId,
      category_name: categoryName,
      amount: Number(amount),
      currency: "BDT",
      gateway,
      mode: config.mode,
      status: "pending",
      ip_address: customerIp || "127.0.0.1",
      created_at: nowISO,
      updated_at: nowISO
    };
    paymentStore.createTransaction(txn);
    const callbackUrl = `${appBaseUrl}/api/payments/${gateway}/callback`;
    try {
      if (gateway === "bkash") {
        const res = await BkashService.createPayment(config, txn, callbackUrl);
        if (!res.success || !res.bkashURL) {
          paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: res.message });
          return {
            success: false,
            transactionId: txnId,
            gateway,
            mode: config.mode,
            action: "modal",
            message: res.message || "bKash payment initialization failed."
          };
        }
        return {
          success: true,
          transactionId: txnId,
          gateway,
          mode: config.mode,
          checkoutUrl: res.bkashURL,
          action: "redirect"
        };
      }
      if (gateway === "nagad") {
        const res = await NagadService.initPayment(config, txn, callbackUrl);
        if (!res.success || !res.paymentUrl) {
          paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: res.message });
          return {
            success: false,
            transactionId: txnId,
            gateway,
            mode: config.mode,
            action: "modal",
            message: res.message || "Nagad initialization failed."
          };
        }
        return {
          success: true,
          transactionId: txnId,
          gateway,
          mode: config.mode,
          checkoutUrl: res.paymentUrl,
          action: "redirect"
        };
      }
      if (gateway === "rocket") {
        const res = await RocketService.initPayment(config, txn, callbackUrl);
        if (!res.success || !res.paymentUrl) {
          paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: res.message });
          return {
            success: false,
            transactionId: txnId,
            gateway,
            mode: config.mode,
            action: "modal",
            message: res.message || "DBBL Rocket initialization failed."
          };
        }
        return {
          success: true,
          transactionId: txnId,
          gateway,
          mode: config.mode,
          checkoutUrl: res.paymentUrl,
          action: "redirect"
        };
      }
      if (gateway === "sslcommerz") {
        const res = await SSLCommerzService.initPayment(config, txn, appBaseUrl, channel);
        if (!res.success || !res.gatewayUrl) {
          paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: res.message });
          return {
            success: false,
            transactionId: txnId,
            gateway,
            mode: config.mode,
            action: "modal",
            message: res.message || "SSLCommerz session initialization failed."
          };
        }
        return {
          success: true,
          transactionId: txnId,
          gateway,
          mode: config.mode,
          checkoutUrl: res.gatewayUrl,
          action: "redirect"
        };
      }
      if (gateway === "shurjopay") {
        const res = await ShurjoPayService.initPayment(config, txn, appBaseUrl);
        return {
          success: true,
          transactionId: txnId,
          gateway,
          mode: config.mode,
          checkoutUrl: res.checkoutUrl,
          action: "redirect"
        };
      }
      if (gateway === "aamarpay") {
        const res = await AamarPayService.initPayment(config, txn, appBaseUrl, channel);
        if (!res.success || !res.gatewayUrl) {
          paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: res.message });
          return {
            success: false,
            transactionId: txnId,
            gateway,
            mode: config.mode,
            action: "modal",
            message: res.message || "aamarPay session initialization failed."
          };
        }
        return {
          success: true,
          transactionId: txnId,
          gateway,
          mode: config.mode,
          checkoutUrl: res.gatewayUrl,
          action: "redirect"
        };
      }
      if (gateway === "uddoktapay") {
        const res = await UddoktaPayService.initPayment(config, txn, appBaseUrl, channel);
        if (!res.success || !res.gatewayUrl) {
          paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: res.message });
          return {
            success: false,
            transactionId: txnId,
            gateway,
            mode: config.mode,
            action: "modal",
            message: res.message || "UddoktaPay session initialization failed."
          };
        }
        return {
          success: true,
          transactionId: txnId,
          gateway,
          mode: config.mode,
          checkoutUrl: res.gatewayUrl,
          action: "redirect"
        };
      }
      return {
        success: false,
        transactionId: txnId,
        gateway,
        mode: config.mode,
        action: "modal",
        message: `Unsupported gateway ${gateway}`
      };
    } catch (err) {
      paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: err.message });
      return {
        success: false,
        transactionId: txnId,
        gateway,
        mode: config.mode,
        action: "modal",
        message: err.message || "Unexpected server error during payment initiation."
      };
    }
  }
  /**
   * Handle bKash Callback & Server Verification
   */
  static async handleBkashCallback(queryParams) {
    const { paymentID, status, merchantInvoiceNumber } = queryParams;
    const txnId = merchantInvoiceNumber || Object.keys(queryParams).find((k) => k.startsWith("TXN-"));
    if (!txnId) {
      return { success: false, transactionId: "", message: "Missing transaction reference in bKash callback." };
    }
    const txn = paymentStore.getTransaction(txnId);
    if (!txn) {
      return { success: false, transactionId: txnId, message: "Transaction record not found." };
    }
    if (txn.status === "paid") {
      return { success: true, transactionId: txnId, message: "Payment already verified.", transaction: txn };
    }
    if (status === "cancel") {
      paymentStore.updateTransactionStatus(txnId, "cancelled", { failure_reason: "User cancelled payment at bKash." });
      return { success: false, transactionId: txnId, message: "Payment was cancelled." };
    }
    if (status === "failure") {
      paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: "bKash returned failure status." });
      return { success: false, transactionId: txnId, message: "Payment failed at bKash." };
    }
    const config = paymentStore.getGatewayConfig("bkash") || { id: "bkash", name: "bKash", is_enabled: true, mode: "test" };
    const execRes = await BkashService.executePayment(config, paymentID || txn.transaction_id);
    if (execRes.success) {
      const updateResult = paymentStore.updateTransactionStatus(txnId, "paid", {
        gateway_transaction_id: execRes.trxID || `TRX_${Date.now()}`,
        gateway_response: execRes.data,
        paid_at: (/* @__PURE__ */ new Date()).toISOString()
      });
      if (updateResult.transaction) {
        serverListingsStore.processVerifiedBid(updateResult.transaction);
      }
      return {
        success: true,
        transactionId: txnId,
        message: "Payment verified and confirmed by bKash.",
        transaction: updateResult.transaction
      };
    }
    paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: execRes.message });
    return {
      success: false,
      transactionId: txnId,
      message: execRes.message || "bKash verification failed."
    };
  }
  /**
   * Handle SSLCommerz Callback & Server-to-Server Validation
   */
  static async handleSSLCommerzCallback(bodyOrQuery) {
    const { tran_id, val_id, status, error } = bodyOrQuery;
    const txnId = tran_id;
    if (!txnId) {
      return { success: false, transactionId: "", message: "Missing tran_id in SSLCommerz payload." };
    }
    const txn = paymentStore.getTransaction(txnId);
    if (!txn) {
      return { success: false, transactionId: txnId, message: "Transaction record not found." };
    }
    if (txn.status === "paid") {
      return { success: true, transactionId: txnId, message: "Transaction already paid.", transaction: txn };
    }
    if (status === "CANCELLED") {
      paymentStore.updateTransactionStatus(txnId, "cancelled", { failure_reason: "User cancelled payment at SSLCommerz." });
      return { success: false, transactionId: txnId, message: "Payment was cancelled." };
    }
    if (status === "FAILED") {
      paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: error || "SSLCommerz payment failed." });
      return { success: false, transactionId: txnId, message: "Payment failed at SSLCommerz." };
    }
    const config = paymentStore.getGatewayConfig("sslcommerz") || { id: "sslcommerz", name: "SSLCommerz", is_enabled: true, mode: "test" };
    const valRes = await SSLCommerzService.validateTransaction(config, val_id || "SSL_DEMO_01", txn.amount, txn.transaction_id);
    if (valRes.valid) {
      const update = paymentStore.updateTransactionStatus(txnId, "paid", {
        gateway_transaction_id: val_id || `VAL_${Date.now()}`,
        gateway_response: valRes.data,
        paid_at: (/* @__PURE__ */ new Date()).toISOString()
      });
      if (update.transaction) {
        serverListingsStore.processVerifiedBid(update.transaction);
      }
      return {
        success: true,
        transactionId: txnId,
        message: "SSLCommerz payment validated successfully.",
        transaction: update.transaction
      };
    }
    paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: valRes.message || "Validation failed" });
    return {
      success: false,
      transactionId: txnId,
      message: valRes.message || "SSLCommerz validation failed."
    };
  }
  /**
   * Handle Nagad Callback & Verification
   */
  static async handleNagadCallback(queryParams) {
    const { payment_ref_id, order_id, status } = queryParams;
    const txnId = order_id;
    if (!txnId) {
      return { success: false, transactionId: "", message: "Missing order_id in Nagad callback." };
    }
    const txn = paymentStore.getTransaction(txnId);
    if (!txn) {
      return { success: false, transactionId: txnId, message: "Transaction not found." };
    }
    if (txn.status === "paid") {
      return { success: true, transactionId: txnId, message: "Already marked as paid.", transaction: txn };
    }
    const config = paymentStore.getGatewayConfig("nagad") || { id: "nagad", name: "Nagad", is_enabled: true, mode: "test" };
    const verifyRes = await NagadService.verifyPayment(config, payment_ref_id || "NAGAD_REF");
    if (verifyRes.verified) {
      const update = paymentStore.updateTransactionStatus(txnId, "paid", {
        gateway_transaction_id: payment_ref_id || `NAGAD_${Date.now()}`,
        gateway_response: verifyRes.data,
        paid_at: (/* @__PURE__ */ new Date()).toISOString()
      });
      if (update.transaction) {
        serverListingsStore.processVerifiedBid(update.transaction);
      }
      return {
        success: true,
        transactionId: txnId,
        message: "Nagad payment successfully verified.",
        transaction: update.transaction
      };
    }
    paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: verifyRes.message });
    return {
      success: false,
      transactionId: txnId,
      message: verifyRes.message || "Nagad verification failed."
    };
  }
  /**
   * Handle DBBL Rocket Callback & Verification
   */
  static async handleRocketCallback(queryParams) {
    const { order_id } = queryParams;
    const txnId = order_id;
    if (!txnId) {
      return { success: false, transactionId: "", message: "Missing order_id in Rocket callback." };
    }
    const txn = paymentStore.getTransaction(txnId);
    if (!txn) {
      return { success: false, transactionId: txnId, message: "Transaction not found." };
    }
    if (txn.status === "paid") {
      return { success: true, transactionId: txnId, message: "Already paid.", transaction: txn };
    }
    const config = paymentStore.getGatewayConfig("rocket") || { id: "rocket", name: "Rocket", is_enabled: true, mode: "test" };
    const verifyRes = await RocketService.verifyPayment(config, order_id);
    if (verifyRes.verified) {
      const update = paymentStore.updateTransactionStatus(txnId, "paid", {
        gateway_transaction_id: `ROCKET_TRX_${Date.now()}`,
        gateway_response: verifyRes.data,
        paid_at: (/* @__PURE__ */ new Date()).toISOString()
      });
      if (update.transaction) {
        serverListingsStore.processVerifiedBid(update.transaction);
      }
      return {
        success: true,
        transactionId: txnId,
        message: "Rocket transaction confirmed.",
        transaction: update.transaction
      };
    }
    paymentStore.updateTransactionStatus(txnId, "failed", { failure_reason: verifyRes.message });
    return {
      success: false,
      transactionId: txnId,
      message: verifyRes.message || "Rocket verification failed."
    };
  }
  /**
   * Handle aamarPay Callback & Server-to-Server Verification
   */
  static async handleAamarPayCallback(bodyOrQuery) {
    const { mer_txnid, tran_id, pg_txnid, pay_status, status, error_title, error_desc, reason } = bodyOrQuery;
    const txnId = mer_txnid || tran_id || Object.keys(bodyOrQuery).find((k) => k.startsWith("TXN-"));
    if (!txnId) {
      return { success: false, transactionId: "", message: "Missing transaction identifier in aamarPay callback." };
    }
    const txn = paymentStore.getTransaction(txnId);
    if (!txn) {
      return { success: false, transactionId: txnId, message: "Transaction record not found." };
    }
    if (txn.status === "paid") {
      return { success: true, transactionId: txnId, message: "Transaction already verified and paid.", transaction: txn };
    }
    const normalizedStatus = (pay_status || status || "").toString().toLowerCase();
    if (normalizedStatus.includes("cancel")) {
      paymentStore.updateTransactionStatus(txnId, "cancelled", {
        failure_reason: "User cancelled payment at aamarPay checkout."
      });
      return { success: false, transactionId: txnId, message: "Payment was cancelled." };
    }
    if (normalizedStatus.includes("fail") || normalizedStatus.includes("reject")) {
      paymentStore.updateTransactionStatus(txnId, "failed", {
        failure_reason: error_title || error_desc || reason || "aamarPay payment failed."
      });
      return { success: false, transactionId: txnId, message: "Payment failed at aamarPay." };
    }
    const config = paymentStore.getGatewayConfig("aamarpay") || {
      id: "aamarpay",
      name: "aamarPay",
      is_enabled: true,
      mode: "test"
    };
    const verifyTargetId = pg_txnid || txnId;
    const verifyRes = await AamarPayService.verifyTransaction(config, verifyTargetId, txn.amount, txn.transaction_id);
    if (verifyRes.valid) {
      const update = paymentStore.updateTransactionStatus(txnId, "paid", {
        gateway_transaction_id: pg_txnid || verifyRes.data?.pg_txnid || `AAMAR_${Date.now()}`,
        gateway_response: { ...bodyOrQuery, verification: verifyRes.data },
        paid_at: (/* @__PURE__ */ new Date()).toISOString()
      });
      if (update.transaction) {
        serverListingsStore.processVerifiedBid(update.transaction);
      }
      return {
        success: true,
        transactionId: txnId,
        message: "aamarPay payment successfully verified.",
        transaction: update.transaction
      };
    }
    paymentStore.updateTransactionStatus(txnId, "failed", {
      failure_reason: verifyRes.message || "aamarPay server verification failed."
    });
    return {
      success: false,
      transactionId: txnId,
      message: verifyRes.message || "aamarPay server verification failed."
    };
  }
  /**
   * Handle UddoktaPay Callback & Server Verification
   */
  static async handleUddoktaPayCallback(bodyOrQuery) {
    const invoice_id = bodyOrQuery.invoice_id || bodyOrQuery.invoiceId;
    const status = (bodyOrQuery.status || "").toString().toLowerCase();
    const txnIdQuery = bodyOrQuery.txn || bodyOrQuery.transaction_id;
    if (status === "cancel" || status === "cancelled") {
      if (txnIdQuery) {
        paymentStore.updateTransactionStatus(txnIdQuery, "cancelled", {
          failure_reason: "User cancelled payment at UddoktaPay checkout."
        });
      }
      return { success: false, transactionId: txnIdQuery || "", message: "Payment was cancelled at UddoktaPay." };
    }
    if (!invoice_id) {
      return { success: false, transactionId: txnIdQuery || "", message: "Missing invoice_id in UddoktaPay callback." };
    }
    let txn = txnIdQuery ? paymentStore.getTransaction(txnIdQuery) : void 0;
    const config = paymentStore.getGatewayConfig("uddoktapay") || {
      id: "uddoktapay",
      name: "UddoktaPay",
      is_enabled: true,
      mode: "test"
    };
    const verifyRes = await UddoktaPayService.verifyPayment(
      config,
      invoice_id,
      txn?.amount,
      txn?.transaction_id
    );
    if (!verifyRes.valid) {
      if (txn) {
        paymentStore.updateTransactionStatus(txn.transaction_id, "failed", {
          failure_reason: verifyRes.message || "UddoktaPay server verification failed."
        });
      }
      return {
        success: false,
        transactionId: txn?.transaction_id || "",
        message: verifyRes.message || "UddoktaPay payment verification failed."
      };
    }
    if (!txn) {
      const metaTxnId = verifyRes.data?.metadata?.transaction_id || verifyRes.data?.metadata?.order_id;
      if (metaTxnId) {
        txn = paymentStore.getTransaction(metaTxnId);
      }
    }
    if (!txn) {
      const allTxns = paymentStore.getAllTransactions();
      txn = allTxns.find((t) => t.gateway === "uddoktapay" && t.status === "pending");
    }
    if (!txn) {
      return { success: false, transactionId: "", message: "Associated transaction record not found." };
    }
    if (txn.status === "paid") {
      return { success: true, transactionId: txn.transaction_id, message: "Transaction already verified and paid.", transaction: txn };
    }
    const verifiedAmount = parseFloat(verifyRes.data?.amount || verifyRes.data?.charged_amount || "0");
    if (txn.amount > 0 && Math.abs(verifiedAmount - txn.amount) > 0.01) {
      paymentStore.updateTransactionStatus(txn.transaction_id, "failed", {
        failure_reason: `Amount mismatch: expected \u09F3${txn.amount}, received \u09F3${verifiedAmount}`
      });
      return {
        success: false,
        transactionId: txn.transaction_id,
        message: `Amount mismatch: expected \u09F3${txn.amount}, received \u09F3${verifiedAmount}`
      };
    }
    const update = paymentStore.updateTransactionStatus(txn.transaction_id, "paid", {
      gateway_transaction_id: verifyRes.data?.transaction_id || invoice_id,
      gateway_response: { ...bodyOrQuery, verification: verifyRes.data },
      paid_at: (/* @__PURE__ */ new Date()).toISOString()
    });
    if (update.transaction) {
      serverListingsStore.processVerifiedBid(update.transaction);
    }
    return {
      success: true,
      transactionId: txn.transaction_id,
      message: "UddoktaPay payment successfully verified.",
      transaction: update.transaction
    };
  }
  /**
   * Handle UddoktaPay Webhook / IPN Notification
   */
  static async handleUddoktaPayIpn(payload, headers) {
    const invoice_id = payload.invoice_id || payload.invoiceId;
    const apiKeyHeader = headers?.["rt-uddoktapay-api-key"] || headers?.["RT-UDDOKTAPAY-API-KEY"];
    const config = paymentStore.getGatewayConfig("uddoktapay") || {
      id: "uddoktapay",
      name: "UddoktaPay",
      is_enabled: true,
      mode: "test"
    };
    const configuredKey = UddoktaPayService.getApiKey(config);
    if (apiKeyHeader && configuredKey && apiKeyHeader !== configuredKey) {
      return { success: false, message: "Unauthorized IPN request: API Key mismatch." };
    }
    if (!invoice_id) {
      return { success: false, message: "Missing invoice_id in IPN payload." };
    }
    const metaTxnId = payload.metadata?.transaction_id || payload.metadata?.order_id;
    let txn = metaTxnId ? paymentStore.getTransaction(metaTxnId) : void 0;
    const verifyRes = await UddoktaPayService.verifyPayment(
      config,
      invoice_id,
      txn?.amount,
      txn?.transaction_id
    );
    if (!verifyRes.valid) {
      return { success: false, message: verifyRes.message || "IPN verification failed." };
    }
    if (!txn) {
      const verifiedMetaTxnId = verifyRes.data?.metadata?.transaction_id;
      if (verifiedMetaTxnId) {
        txn = paymentStore.getTransaction(verifiedMetaTxnId);
      }
    }
    if (!txn) {
      return { success: false, message: "Transaction not found for verified IPN." };
    }
    if (txn.status === "paid") {
      return { success: true, message: "Transaction already paid and processed.", transaction: txn };
    }
    const update = paymentStore.updateTransactionStatus(txn.transaction_id, "paid", {
      gateway_transaction_id: verifyRes.data?.transaction_id || invoice_id,
      gateway_response: { ipn_payload: payload, verification: verifyRes.data },
      paid_at: (/* @__PURE__ */ new Date()).toISOString()
    });
    if (update.transaction) {
      serverListingsStore.processVerifiedBid(update.transaction);
    }
    return {
      success: true,
      message: "UddoktaPay IPN processed successfully.",
      transaction: update.transaction
    };
  }
  /**
   * Handle Test Sandbox Simulator Actions
   */
  static async simulateSandboxCompletion(transactionId, action) {
    const txn = paymentStore.getTransaction(transactionId);
    if (!txn) {
      return { success: false, message: "Transaction not found." };
    }
    if (action === "success") {
      const update = paymentStore.updateTransactionStatus(transactionId, "paid", {
        gateway_transaction_id: `SIM_${txn.gateway.toUpperCase()}_${Date.now()}`,
        gateway_response: {
          simulated: true,
          mode: "test_sandbox",
          timestamp: (/* @__PURE__ */ new Date()).toISOString()
        },
        paid_at: (/* @__PURE__ */ new Date()).toISOString()
      });
      if (update.transaction) {
        serverListingsStore.processVerifiedBid(update.transaction);
      }
      return {
        success: true,
        message: `Sandbox payment completed and verified via ${txn.gateway.toUpperCase()}.`,
        transaction: update.transaction
      };
    }
    if (action === "cancel") {
      paymentStore.updateTransactionStatus(transactionId, "cancelled", {
        failure_reason: "User cancelled payment in sandbox simulator."
      });
      return { success: false, message: "Sandbox payment cancelled." };
    }
    paymentStore.updateTransactionStatus(transactionId, "failed", {
      failure_reason: "Simulated payment failure (e.g. insufficient funds or bad OTP)."
    });
    return { success: false, message: "Sandbox payment failed." };
  }
};

// src/server/visitorStore.ts
var import_fs3 = __toESM(require("fs"), 1);
var import_path3 = __toESM(require("path"), 1);
var DEFAULT_SETTINGS = {
  is_enabled: true,
  show_active_visitors: true,
  show_total_visitors: true,
  baseline_active: 0,
  baseline_total: 1317970
};
var VisitorStore = class {
  constructor() {
    this.activeSessions = /* @__PURE__ */ new Map();
    this.totalVisitors = 280;
    this.settings = { ...DEFAULT_SETTINGS };
    this.filePath = import_path3.default.join(process.cwd(), "data", "visitor_stats.json");
    this.loadFromDisk();
  }
  loadFromDisk() {
    try {
      if (import_fs3.default.existsSync(this.filePath)) {
        const raw = import_fs3.default.readFileSync(this.filePath, "utf-8");
        const data = JSON.parse(raw);
        if (typeof data.totalVisitors === "number") {
          this.totalVisitors = data.totalVisitors;
        }
        if (data.settings && typeof data.settings === "object") {
          this.settings = { ...DEFAULT_SETTINGS, ...data.settings };
        }
      } else {
        this.saveToDisk();
      }
    } catch (e) {
      console.warn("[VisitorStore] Failed to load visitor stats from disk, using defaults:", e);
    }
  }
  saveToDisk() {
    try {
      const dir = import_path3.default.dirname(this.filePath);
      if (!import_fs3.default.existsSync(dir)) {
        import_fs3.default.mkdirSync(dir, { recursive: true });
      }
      const data = {
        updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
        totalVisitors: this.totalVisitors,
        settings: this.settings
      };
      import_fs3.default.writeFileSync(this.filePath, JSON.stringify(data, null, 2), "utf-8");
    } catch (e) {
      console.error("[VisitorStore] Failed to save visitor stats to disk:", e);
    }
  }
  /**
   * Remove inactive sessions older than 30 seconds
   */
  pruneSessions() {
    const now = Date.now();
    const expiryWindow = 3e4;
    for (const [clientId, session] of this.activeSessions.entries()) {
      if (now - session.lastPing > expiryWindow) {
        this.activeSessions.delete(clientId);
      }
    }
  }
  /**
   * Handle client heartbeat ping.
   * Increments total count for new client sessions, updates last active timestamp, and returns real stats.
   */
  ping(clientId) {
    this.pruneSessions();
    const now = Date.now();
    const existing = this.activeSessions.get(clientId);
    if (!existing) {
      this.activeSessions.set(clientId, { lastPing: now, isCountedInTotal: true });
      this.totalVisitors += 1;
      this.saveToDisk();
    } else {
      existing.lastPing = now;
    }
    return this.getStats();
  }
  /**
   * Get current real-time stats
   */
  getStats() {
    this.pruneSessions();
    const realActiveCount = this.activeSessions.size;
    const computedActive = Math.max(1, realActiveCount + (this.settings.baseline_active || 0));
    const computedTotal = Math.max(0, this.totalVisitors + (this.settings.baseline_total || 0));
    return {
      activeVisitors: computedActive,
      totalVisitors: computedTotal,
      settings: { ...this.settings }
    };
  }
  /**
   * Update admin settings for counters
   */
  updateSettings(newSettings) {
    this.settings = {
      ...this.settings,
      ...newSettings
    };
    this.saveToDisk();
    return this.getStats();
  }
  /**
   * Reset stats (active sessions, total visitors, or both)
   */
  resetStats(type, customActive, customTotal) {
    if (type === "active" || type === "all") {
      this.activeSessions.clear();
      if (typeof customActive === "number") {
        this.settings.baseline_active = customActive;
      }
    }
    if (type === "total" || type === "all") {
      this.totalVisitors = 0;
      if (typeof customTotal === "number") {
        this.settings.baseline_total = customTotal;
      }
    }
    this.saveToDisk();
    return this.getStats();
  }
  /**
   * Directly set baseline counts
   */
  setCustomCounts(baselineActive, baselineTotal) {
    if (typeof baselineActive === "number") {
      this.settings.baseline_active = baselineActive;
    }
    if (typeof baselineTotal === "number") {
      this.settings.baseline_total = baselineTotal;
    }
    this.saveToDisk();
    return this.getStats();
  }
};
var visitorStore = new VisitorStore();

// server.ts
async function startServer() {
  const app = (0, import_express.default)();
  const PORT = process.env.PORT || 3e3;
  app.use(import_express.default.json({ limit: "10mb" }));
  app.use(import_express.default.urlencoded({ extended: true, limit: "10mb" }));
  const getAppBaseUrl = (req) => {
    if (process.env.APP_URL && process.env.APP_URL.trim() && !process.env.APP_URL.includes("MY_APP_URL")) {
      return process.env.APP_URL.trim().replace(/\/+$/, "");
    }
    const protocol = req.headers["x-forwarded-proto"] || req.protocol || "http";
    const host = req.headers["x-forwarded-host"] || req.headers.host || `localhost:${PORT}`;
    return `${protocol}://${host}`;
  };
  app.get("/api/health", (req, res) => {
    res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
  });
  app.get("/api/listings", (req, res) => {
    const { category, selectedCategory, timeFilter, search, q, page, limit } = req.query;
    const catId = category || selectedCategory;
    const tf = timeFilter;
    const searchQuery = search || q;
    const p = page ? parseInt(page, 10) : 1;
    const l = limit ? parseInt(limit, 10) : 50;
    const result = serverListingsStore.getRankedListings({
      categoryId: catId,
      timeFilter: tf,
      searchQuery,
      page: p,
      limit: l
    });
    res.json(result);
  });
  app.post("/api/listings/:id/click", (req, res) => {
    const { id } = req.params;
    const updated = serverListingsStore.incrementClicks(id);
    res.json({ success: !!updated, listing: updated });
  });
  app.post("/api/listings/:id/heart", (req, res) => {
    const { id } = req.params;
    const updated = serverListingsStore.incrementHearts(id);
    res.json({ success: !!updated, listing: updated });
  });
  app.get("/api/visitors/stats", (req, res) => {
    const stats = visitorStore.getStats();
    res.json({ success: true, ...stats });
  });
  app.post("/api/visitors/ping", (req, res) => {
    const { clientId } = req.body || {};
    const fallbackId = req.headers["x-forwarded-for"]?.split(",")[0] || req.socket.remoteAddress || "anonymous_client";
    const effectiveClientId = clientId && typeof clientId === "string" && clientId.trim().length > 0 ? clientId.trim() : fallbackId;
    const stats = visitorStore.ping(effectiveClientId);
    res.json({ success: true, ...stats });
  });
  app.get("/api/gateways/public", (req, res) => {
    const gateways = paymentStore.getPublicGateways();
    const defaultGateway = paymentStore.getDefaultGateway();
    res.json({
      success: true,
      gateways,
      defaultGateway
    });
  });
  app.post("/api/payments/init", async (req, res) => {
    try {
      const { targetListingId, name, url, facebook_url, website_url, imageUrl, categoryId, categoryName, amount, gateway, channel } = req.body;
      const finalFbUrl = facebook_url || url;
      if (!name || !finalFbUrl || !amount || !gateway) {
        return res.status(400).json({
          success: false,
          message: "Missing required parameters (name, facebook_url, amount, gateway)."
        });
      }
      const clientIp = req.headers["x-forwarded-for"]?.split(",")[0] || req.socket.remoteAddress;
      const appBaseUrl = getAppBaseUrl(req);
      const result = await PaymentService.initPayment(
        {
          targetListingId,
          name,
          url,
          facebook_url: finalFbUrl,
          website_url,
          imageUrl,
          categoryId: categoryId || "cat_biz",
          categoryName,
          amount: Number(amount),
          gateway,
          channel,
          customerIp: clientIp,
          originUrl: appBaseUrl
        },
        appBaseUrl
      );
      return res.json(result);
    } catch (err) {
      console.error("Payment init error:", err);
      return res.status(500).json({
        success: false,
        message: err.message || "Internal server error initializing payment."
      });
    }
  });
  app.get("/api/payments/status/:transactionId", (req, res) => {
    const { transactionId } = req.params;
    const txn = paymentStore.getTransaction(transactionId);
    if (!txn) {
      return res.status(404).json({ success: false, message: "Transaction not found." });
    }
    return res.json({
      success: true,
      transaction: {
        id: txn.id,
        transaction_id: txn.transaction_id,
        listing_id: txn.listing_id,
        listing_name: txn.listing_name,
        listing_url: txn.listing_url,
        facebook_url: txn.facebook_url,
        website_url: txn.website_url,
        listing_image: txn.listing_image,
        category_id: txn.category_id,
        amount: txn.amount,
        currency: txn.currency,
        gateway: txn.gateway,
        mode: txn.mode,
        status: txn.status,
        created_at: txn.created_at,
        updated_at: txn.updated_at,
        paid_at: txn.paid_at,
        failure_reason: txn.failure_reason
      }
    });
  });
  app.all("/api/payments/bkash/callback", async (req, res) => {
    try {
      const payload = { ...req.query, ...req.body };
      const result = await PaymentService.handleBkashCallback(payload);
      const txnId = result.transactionId || payload.merchantInvoiceNumber;
      if (req.accepts("html") && !req.xhr) {
        return res.redirect(`/?payment_status=${result.success ? "success" : "failed"}&txn=${txnId}`);
      }
      return res.json(result);
    } catch (err) {
      console.error("bKash callback error:", err);
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.all("/api/payments/sslcommerz/callback", async (req, res) => {
    try {
      const payload = { ...req.query, ...req.body };
      const result = await PaymentService.handleSSLCommerzCallback(payload);
      const txnId = result.transactionId || payload.tran_id;
      if (req.accepts("html") && !req.xhr) {
        return res.redirect(`/?payment_status=${result.success ? "success" : "failed"}&txn=${txnId}`);
      }
      return res.json(result);
    } catch (err) {
      console.error("SSLCommerz callback error:", err);
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.post("/api/payments/sslcommerz/ipn", async (req, res) => {
    try {
      const result = await PaymentService.handleSSLCommerzCallback(req.body);
      return res.status(200).send(result.success ? "IPN_SUCCESS" : "IPN_FAILED");
    } catch {
      return res.status(500).send("IPN_ERROR");
    }
  });
  app.all("/api/payments/nagad/callback", async (req, res) => {
    try {
      const payload = { ...req.query, ...req.body };
      const result = await PaymentService.handleNagadCallback(payload);
      const txnId = result.transactionId || payload.order_id;
      if (req.accepts("html") && !req.xhr) {
        return res.redirect(`/?payment_status=${result.success ? "success" : "failed"}&txn=${txnId}`);
      }
      return res.json(result);
    } catch (err) {
      console.error("Nagad callback error:", err);
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.all("/api/payments/rocket/callback", async (req, res) => {
    try {
      const payload = { ...req.query, ...req.body };
      const result = await PaymentService.handleRocketCallback(payload);
      const txnId = result.transactionId || payload.order_id;
      if (req.accepts("html") && !req.xhr) {
        return res.redirect(`/?payment_status=${result.success ? "success" : "failed"}&txn=${txnId}`);
      }
      return res.json(result);
    } catch (err) {
      console.error("Rocket callback error:", err);
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.all("/api/payments/aamarpay/callback", async (req, res) => {
    try {
      const payload = { ...req.query, ...req.body };
      const result = await PaymentService.handleAamarPayCallback(payload);
      const txnId = result.transactionId || payload.mer_txnid || payload.tran_id;
      if (req.accepts("html") && !req.xhr) {
        return res.redirect(`/?payment_status=${result.success ? "success" : "failed"}&txn=${txnId}`);
      }
      return res.json(result);
    } catch (err) {
      console.error("aamarPay callback error:", err);
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.post("/api/payments/aamarpay/ipn", async (req, res) => {
    try {
      const result = await PaymentService.handleAamarPayCallback(req.body);
      return res.status(200).send(result.success ? "IPN_SUCCESS" : "IPN_FAILED");
    } catch {
      return res.status(500).send("IPN_ERROR");
    }
  });
  app.all("/api/payments/uddoktapay/callback", async (req, res) => {
    try {
      const payload = { ...req.query, ...req.body };
      const result = await PaymentService.handleUddoktaPayCallback(payload);
      const txnId = result.transactionId || payload.txn || payload.invoice_id;
      if (req.accepts("html") && !req.xhr) {
        return res.redirect(`/?payment_status=${result.success ? "success" : "failed"}&txn=${txnId}`);
      }
      return res.json(result);
    } catch (err) {
      console.error("UddoktaPay callback error:", err);
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.post("/api/payments/uddoktapay/ipn", async (req, res) => {
    try {
      const result = await PaymentService.handleUddoktaPayIpn(req.body, req.headers);
      return res.status(result.success ? 200 : 400).json(result);
    } catch (err) {
      console.error("UddoktaPay IPN error:", err);
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.post("/api/payments/sandbox/action", async (req, res) => {
    try {
      const { transactionId, action } = req.body;
      if (!transactionId || !action) {
        return res.status(400).json({ success: false, message: "Missing transactionId or action." });
      }
      const result = await PaymentService.simulateSandboxCompletion(transactionId, action);
      return res.json(result);
    } catch (err) {
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.get("/api/admin/gateways", (req, res) => {
    const authHeader = req.headers.authorization;
    const configs = paymentStore.getAllGatewayConfigs(true);
    const defaultGateway = paymentStore.getDefaultGateway();
    return res.json({
      success: true,
      gateways: configs,
      defaultGateway
    });
  });
  app.post("/api/admin/gateways", (req, res) => {
    try {
      const { gateways, defaultGateway } = req.body;
      if (Array.isArray(gateways)) {
        paymentStore.saveAllGatewayConfigs(gateways);
      }
      if (defaultGateway) {
        paymentStore.setDefaultGateway(defaultGateway);
      }
      const updated = paymentStore.getAllGatewayConfigs(true);
      return res.json({
        success: true,
        message: "Payment gateway settings updated securely.",
        gateways: updated,
        defaultGateway: paymentStore.getDefaultGateway()
      });
    } catch (err) {
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.post("/api/admin/payments/test-connection", async (req, res) => {
    try {
      const { gatewayId, config } = req.body;
      if (!gatewayId) {
        return res.status(400).json({ success: false, message: "Gateway ID is required." });
      }
      const existingConfig = paymentStore.getGatewayConfig(gatewayId);
      const targetConfig = config ? { ...existingConfig, ...config } : existingConfig;
      if (!targetConfig) {
        return res.status(404).json({ success: false, message: `Gateway ${gatewayId} not found.` });
      }
      if (gatewayId === "uddoktapay") {
        const testResult = await UddoktaPayService.testConnection(targetConfig);
        return res.json(testResult);
      }
      if (gatewayId === "sslcommerz") {
        return res.json({
          success: true,
          message: `SSLCommerz configuration reachable (${targetConfig.mode} mode, store: ${targetConfig.store_id || "topbd_store"})`
        });
      }
      if (gatewayId === "aamarpay") {
        return res.json({
          success: true,
          message: `aamarPay configuration reachable (${targetConfig.mode} mode, store: ${targetConfig.store_id || "aamarpaytest"})`
        });
      }
      return res.json({
        success: true,
        message: `${targetConfig.name || gatewayId} connection verified successfully.`
      });
    } catch (err) {
      return res.status(500).json({ success: false, message: err.message || "Connection test failed" });
    }
  });
  app.get("/api/admin/transactions", (req, res) => {
    const txns = paymentStore.getAllTransactions();
    return res.json({
      success: true,
      total: txns.length,
      transactions: txns
    });
  });
  app.delete("/api/admin/transactions/:id", (req, res) => {
    const { id } = req.params;
    const deleted = paymentStore.deleteTransaction(id);
    return res.json({ success: deleted, message: deleted ? "Transaction deleted" : "Transaction not found" });
  });
  app.post("/api/admin/transactions/reset", (req, res) => {
    const txns = paymentStore.getAllTransactions();
    txns.forEach((t) => paymentStore.deleteTransaction(t.transaction_id));
    return res.json({ success: true, message: "All transactions cleared successfully" });
  });
  app.post("/api/admin/visitors/settings", (req, res) => {
    try {
      const updated = visitorStore.updateSettings(req.body);
      return res.json({ success: true, ...updated, message: "Visitor counter settings updated" });
    } catch (err) {
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.post("/api/admin/visitors/reset", (req, res) => {
    try {
      const { type, customActive, customTotal } = req.body || {};
      const updated = visitorStore.resetStats(type || "all", customActive, customTotal);
      return res.json({ success: true, ...updated, message: "Visitor statistics reset successfully" });
    } catch (err) {
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.post("/api/admin/listings/save", (req, res) => {
    try {
      const listingData = req.body;
      const saved = serverListingsStore.saveListing(listingData);
      return res.json({ success: true, listing: saved });
    } catch (err) {
      return res.status(500).json({ success: false, message: err.message });
    }
  });
  app.delete("/api/admin/listings/:id", (req, res) => {
    const { id } = req.params;
    const deleted = serverListingsStore.deleteListing(id);
    return res.json({ success: deleted });
  });
  if (process.env.NODE_ENV === "production") {
    const candidateDirs = [
      import_path4.default.join(process.cwd(), "production_dist"),
      import_path4.default.join(process.cwd(), "dist"),
      import_path4.default.join(__dirname, "production_dist"),
      import_path4.default.join(__dirname, "dist")
    ];
    let staticFolder = null;
    for (const cand of candidateDirs) {
      if (!cand) continue;
      const indexPath = import_path4.default.join(cand, "index.html");
      if (import_fs4.default.existsSync(indexPath)) {
        staticFolder = cand;
        break;
      }
    }
    if (staticFolder) {
      console.log(`[TOPBD Server] Serving compiled production frontend from: ${staticFolder}`);
      app.use(import_express.default.static(staticFolder));
      app.get("*", (req, res, next) => {
        if (req.path.startsWith("/api")) return next();
        res.sendFile(import_path4.default.join(staticFolder, "index.html"));
      });
    } else {
      console.error("[TOPBD Server] ERROR: No valid compiled production folder found!");
      app.get("*", (req, res, next) => {
        if (req.path.startsWith("/api")) return next();
        res.status(500).send("Production frontend build not found.");
      });
    }
  } else {
    console.log("[TOPBD Server] Mounting Vite dev middleware...");
    const vite = await (0, import_vite.createServer)({
      server: { middlewareMode: true },
      appType: "spa"
    });
    app.use(vite.middlewares);
  }
  app.listen(PORT, "0.0.0.0", () => {
    console.log(`TOPBD Server running on http://0.0.0.0:${PORT}`);
  });
}
startServer().catch((err) => {
  console.error("Failed to start server:", err);
  process.exit(1);
});
//# sourceMappingURL=server.cjs.map
