You are a senior payment-integration engineer and security reviewer. I am attaching the COMPLETE source code of an EXISTING Any / Other project. First read the whole project and understand its architecture: authentication, users, the existing Deposit / Add Money / Checkout flow, the database schema, transaction tables, routes or controllers, and the hosting constraints. === RULE 1 — DO NOT REBUILD ANYTHING === This is an INTEGRATION-ONLY task. Do not redesign, modernise, clean up or rewrite the project. Do not remove, rename or change any existing page, feature, route, table, style or script that is not required for this payment integration. Keep the existing UI exactly as it is. Change the minimum number of files and lines. Before editing, list the existing files and features. After editing, confirm that each of them still exists and still works. If you cannot integrate safely without touching unrelated code, STOP and explain what is blocking you. Do not invent a replacement. === TASK === Connect the EXISTING pay / deposit button and its amount field to the Fast Pay payment gateway. The user enters an amount, presses the existing button, a payment box opens ON TOP OF THE SAME PAGE (no redirect), pays by UPI, and the page continues. The user is credited exactly once. === GATEWAY API === Base URL: https://mail.famupigateway.site/api Create order: POST https://mail.famupigateway.site/api/create-order (server only — needs the secret API key) Check status: POST https://mail.famupigateway.site/api/check-status (server only — needs the secret API key) Live status: GET https://mail.famupigateway.site/api/payment-status?token=TOKEN&wait=1 (no key — may be called from a browser or an app) Popup script: https://mail.famupigateway.site/assets/js/antopay.js Auth header: X-API-Key: Content-Type: application/json Create-order body: { "amount": 100.00, "order_id": "UNIQUE_LOCAL_ORDER_ID", "customer_name": "User Name", "customer_mobile": "9876543210", "callback_url": "https://MY-REAL-DOMAIN/payment/callback", "description": "Wallet deposit", "expiry_minutes": 5 } Success (HTTP 201) returns: data.payment_url, data.order_id, data.token, data.amount, data.expires_at With "checkout": "direct" in the body it ALSO returns: data.upi_url, data.amount_to_pay, data.payee_name, data.upi_id (can be null), data.seconds_left, data.status_url, data.upi_apps (list of { id, name, android, ios or null } — one ready-made link per UPI app) Errors return: { "error": "...", "details": "..." } with HTTP 400 / 401 / 403 / 404 / 409 / 422 / 429 / 5xx HTTP 403 with "code": "plan_embed" means in-site checkout is not part of the merchant's plan: create the order again WITHOUT "checkout" and redirect to payment_url. Check-status body: { "order_id": "UNIQUE_LOCAL_ORDER_ID" } Returns: data.payment_status ("pending" | "success" | "failed"), data.amount (what the customer paid), data.original_amount (what was requested), data.utr, data.paid_at Live status returns: data.payment_status ("pending" | "success" | "failed"), data.amount, data.utr, data.paid_at, data.seconds_left, data.next_poll_ms With wait=1 the request stays open (up to about 15 seconds) and answers the moment the payment is known. Ask again after data.next_poll_ms milliseconds while the status is "pending". Popup: AntoPay.open({ url: payment_url, onSuccess: fn(p), onFailed: fn(p), onClose: fn(p) }) — p.order_id, p.amount, p.utr After a payment the popup sends the browser to callback_url?order_id=...&token=... unless onSuccess returns false. After payment on the hosted page or in the popup the customer's browser is redirected to: callback_url?order_id=...&token=... That redirect, the popup's onSuccess and the live status are NOT proof of payment. They only tell the SCREEN what to show. Webhook: Fast Pay sends a POST with a JSON body to my webhook URL. Headers: X-Webhook-Event (e.g. payment.success), X-Webhook-Signature, X-Webhook-Timestamp Signature = lowercase hex HMAC-SHA256 of the RAW request body, using my webhook secret as the key. Body fields: event, status, order_id, amount, original_amount, currency, utr, paid_at, customer_name (as I sent it), payer_name (name on the bank receipt, can be null), timestamp Answer with HTTP 2xx within 8 seconds. === HOW THE CUSTOMER PAYS — POPUP ON TOP OF MY OWN PAGE (in-site checkout, no redirect) === Load the script once on the page: The pay button calls MY server (fetch / AJAX). My server creates the order and answers with payment_url. The page then calls AntoPay.open({ url: payment_url, ... }). The QR code appears in a box on top of my page; the customer never leaves it. In onSuccess(p): call MY server with p.order_id; the server runs check-status and credits (step 8-10); then update the page (new balance / thank-you). Never credit from the values in p. Keep callback_url as well: the popup sends the browser there after a payment (return false from onSuccess to stay on the page), and it is the fallback when the script cannot run. If the merchant's plan has no in-site checkout, the script continues on the hosted payment page by itself — nothing extra to code. === REQUIRED FLOW === 1. Find the existing pay / deposit button and amount field. Keep the UI. 2. On the server: authenticate the current user and validate the amount with the project's own rules. 3. Create a local PENDING order with a unique order id BEFORE calling the gateway. 4. Call create-order from the SERVER with that order id and amount. Use expiry_minutes = 5. 5. Store the returned token and payment_url next to the local order. 6. Build callback_url from the real deployed domain of this project — never hard-code localhost. 7. Do NOT redirect. Return payment_url to the page as JSON and open it with AntoPay.open({ url: payment_url, onSuccess, onClose }). 8. When the customer is back on the callback page — or the popup / my own screen reports success — read order_id, load the local order and call check-status from the SERVER. 9. Credit ONLY when payment_status is "success", the order belongs to the right user, and data.original_amount equals the local order amount. 10. Use a database transaction and a row lock (or a unique constraint) so the same order can NEVER be credited twice. 11. Add a server-side webhook endpoint. Verify X-Webhook-Signature with a constant-time comparison on the raw body, then call check-status and run the SAME idempotent credit function. 12. Callback, screen report and webhook can arrive in any order, or all, or only one of them. Each must be safe on its own. 13. Never trust amount, status, order id or user id that come from the browser or the app. 14. Handle pending, failed, expired, duplicate and already-paid orders with a clear message for the user. === DATABASE === Reuse the existing user, wallet and transaction tables. Do not create a second wallet system. Add only the columns or the single table that is really needed: gateway order id (unique), user id, amount, status, token, UTR, created and paid timestamps. === ONE CONFIG PLACE === Create exactly ONE obvious server-side place (a config file or .env) for: FASTPAY_BASE_URL=https://mail.famupigateway.site/api FASTPAY_API_KEY=YOUR_API_KEY FASTPAY_WEBHOOK_SECRET=YOUR_WEBHOOK_SECRET FASTPAY_EXPIRY_MINUTES=5 The real API key must never appear in frontend code, HTML, JavaScript, API responses, logs or the repository. I will paste the real values into that one place myself after upload — use clear placeholders. === NOTES FOR Any / Other === Detect the language and framework from the attached code and follow the conventions that are already there. Use the standard HTTP client, a constant-time comparison for the signature, the raw request body for verification and a database transaction for crediting. === WHAT I WANT BACK === 1. A short summary of how the existing payment / deposit flow works today. 2. The list of files you will change or add — and nothing else. 3. The complete final content of every changed or added file (no "..." placeholders). 4. Any SQL migration needed. 5. The exact file and keys where I put my API key and webhook secret. 6. The exact webhook URL I must add in the Fast Pay dashboard. 7. A test checklist: successful payment, expired payment, page refresh on the callback, the webhook arriving twice.