Skip to content

Payments, Credits & Reconciliation — Technical Reference

Part of the Aashray Technical Reference. Pairs with Business Logic — Payments, Credits & Reconciliation, which describes these rules in plain terms with no code detail. Related technical detail for the booking types that get charged here lives in Booking Lifecycle, Stays: Rooms & Flats, Food, Travel, Adhyayan & Utsav, Status & Cron, and the Admin Panel Map.

This file is 100% derived from reading the backend, admin, and app source on main — no operational/staff-knowledge content lives here (that's in the business-logic companion). It covers exactly how charges, the Razorpay order → checkout → webhook → settlement flow, store credit, the transactions ledger, the 24-hour pending-payment window, and the admin settlement / reconciliation / voucher / credit reporting surface are implemented: every route, the data model, admin mechanics, edge cases, and known defects.

The hub of everything here is CardDb (cardno). Every Transactions row belongsTo a CardDb and is linked by bookingid to a RoomBooking / FlatBooking (declared associations) or — resolved dynamically by category — to a ShibirBookingDb, TravelDb, UtsavBooking, or FoodDb. Store credit is a JSON blob on card_db.credits. There is no separate payment/refund table — the transactions row is the ledger entry, and its status column carries the entire lifecycle.

Verification status: audited & anchored per docs/DOCS-METHODOLOGY.md on 2026-07-07 against aashray-backend main. Every rule carries a [src: …] anchor. All 10 discrepancies were confirmed against code this pass; three stale code-location references (flat-price function, adhyayan formula, utsav line) were corrected.


A. Charges — where real prices come from

What it is. The single source of truth for the amount a member owes. The member app assembles a booking and calls a validate endpoint; the response's per-line charge values and totalCharge are exactly what the UI renders. The app has a legacy constants/prices.js file but it is dead code — the live numbers are these backend constants and per-record amounts.

Backend rules & logic. Price sources (config/constants.js + per-record columns):

Booking type Charge formula Source
Room roomCharge(roomtype) * nights → NAC ₹700/night, AC ₹1100/night NAC_ROOM_PRICE=700, AC_ROOM_PRICE=1100 [src: config/constants.js:25-26]; roomCharge() [src: helpers/roomBooking.helper.js:478-480]
Flat roomCharge('nac') * nights (= ₹700/night); ₹0 if the booker is the flat owner checkFlatAvailabilityForMumukshus flat branch [src: helpers/roomBooking.helper.js:755]
Adhyayan (shibir) min(available_seats, people) * shibir.amount (only when shibir status == open; waitlisted seats charge ₹0) [src: helpers/adhyayanBooking.helper.js:403-405]
Utsav utsav_package.amount (only when utsav status == open; waitlisted = ₹0) [src: helpers/utsavBooking.helper.js:357-359]
Food (breakfast/lunch/dinner) BREAKFAST_PRICE=60, LUNCH_PRICE=120, DINNER_PRICE=120 per plate [src: config/constants.js:22-24; helpers/foodBooking.helper.js:52-60,271-284]
Travel always ₹0 — status awaiting confirmation, charged/adjusted later by travel admin [src: helpers/travelBooking.helper.js:277]

The validate controllers (validate() in both mumukshuBooking.controller.js and guestBooking.controller.js) loop over primary_booking + addons, dispatch by booking_type, sum each detail's charge into response.totalCharge, and attach an availableCredits figure per line (see §B) for room, flat, utsav, and food — but not for adhyayan or travel. [src: helpers/roomBooking.helper.js:689,756-757; helpers/utsavBooking.helper.js:360; helpers/foodBooking.helper.js:290; helpers/adhyayanBooking.helper.js:411-416 (adhyayan returns only charge)]

Validation response shape: { roomDetails[], flatDetails[], adhyayanDetails[], utsavDetails[], foodDetails{}, travelDetails{}, totalCharge }. Each accommodation/utsav/food line carries charge and (where applicable) availableCredits; room lines add nights, status, roomType, per-person id/name.

Every route (charges).

Method Path Auth realm Purpose Key params/body
POST /api/v1/mumukshu/validate Member (validateCard) Price + credit preview for a mumukshu/self multi-booking { primary_booking, addons[] } each { booking_type, details }
POST /api/v1/guest/validate Member (validateCard) Price + credit preview for a guest multi-booking { primary_booking, addons[] }
POST /api/v1/unified/validate Member (validateCard) DeprecateddeprecatedEndpoint throws HTTP 410 [src: routes/client/unifiedBooking.routes.js:7-9,18]

Discrepancies. (1) Flat is priced at the NAC room rate (roomCharge('nac')), never the AC rate — there is no separate flat tariff constant. [src: helpers/roomBooking.helper.js:755] (2) Adhyayan validate returns no availableCredits even though a member may hold adhyayan credit; the app therefore can never pre-apply credit to an adhyayan line. [src: helpers/adhyayanBooking.helper.js:411-416] (3) Prices are hard-coded constants, not DB-configurable, except adhyayan/utsav which read a per-record amount. [src: config/constants.js:22-26]


B. Credits (store credit)

What it is. Non-cash, non-refundable store credit. 1 credit = ₹1. Held on card_db.credits as a JSON object keyed by credit type: { room, food, travel, utsav, adhyayan }. Flat credit is folded into the room bucket (getCreditType() maps flat → room).

Member-app behavior. Credits are shown on the Profile "Available Credits" card (Stay / Travel / Food / Utsav) and inline on the Review screen (original struck through, green "−₹X credit", net). They are applied automatically at checkout; the member never chooses to spend them.

Backend rules & logic (helpers/transactions.helper.js):

  • Applying credit (useCredit) — called from createPendingTransaction at booking time. For the transaction's credit type, creditsUsed = min(amount, availableCredits); the transaction is updated to amount = charge − creditsUsed, discount = creditsUsed, description = "credits used: N", and the card's credit bucket is decremented. If credit fully covers the charge (amount == 0) the transaction is set to completed immediately and the booking is confirmed (pending checkin for room/flat, confirmed otherwise) — no Razorpay needed. [src: helpers/transactions.helper.js:326-358]
  • Previewing credit (usableCredits) — the read-only version used inside validate. It mutates a cloned card.credits (a tempUser) so multi-person previews deduct sequentially without touching the real balance. [src: helpers/transactions.helper.js:386; helpers/roomBooking.helper.js:650,748]
  • Earning credit (addCredit) — only via cancelTransaction (see §D). Credit accrues to the getCreditType(bookingType) bucket; a bucket that hits 0 is deleted from the JSON. [src: helpers/transactions.helper.js:289-293,295,407-409]

Which booking types actually earn / spend credit:

Type Credit applied at checkout? Credit earned on cancel?
Room Yes (room) Yes (member or admin cancel of a paid booking)
Flat Yes (room bucket) Yes (room bucket)
Food Yes (food) Yes
Utsav Yes (utsav) No on normal cancel (see §D) — only via the admin re-credit path
Adhyayan No (validate omits it) No
Travel No (charge is ₹0) No

Refund model. There is no cash refund anywhere in the system. Cancelling a paid booking converts the paid amount to store credit (status → credited). Cancelling an unpaid booking that had credit pre-applied returns only the previously-used credit (the discount). Cancel dialogs mention nothing about refunds.

Discrepancies. (1) Utsav asymmetry — utsav credit can be spent at checkout and can be previewed, but a normal utsav cancellation grants no credit (utsav is in the "not credited, keep as completed" branch), so a utsav bucket can realistically only appear via the admin double-cancel path. [src: helpers/transactions.helper.js:166-177,196-207] (2) adjustAmount(card, booking, transaction, amount, updatedBy, t) (admin re-price helper) calls addCredit(user, …) but user is not a parameter of adjustAmount — a latent ReferenceError (dead/broken path). [src: helpers/transactions.helper.js:257-287 (addCredit(user,…) at :270)] (3) The admin credits report (fetchCredits) parses only room/food/travel/utsav keys — an adhyayan bucket, if it ever existed, would be invisible. [src: controllers/admin/accountsManagement.controller.js:775-778]


C. Razorpay flow (order → pay/payv2 → checkout → webhook → settlement)

What it is. All online money moves through Razorpay. The backend creates an order, the app opens the native Razorpay sheet, and Razorpay calls back a webhook that captures the payment and confirms bookings. Settlement/reconciliation is imported later from Razorpay Excel exports (§F).

Order creation (generateOrderId, transactions.helper.js:414). Builds { amount: rupees*100 (paise), currency: 'INR', receipt: uuidv4(), notes: { app:'aashray', env } }. Only calls the real Razorpay API when ['prod','qa'].includes(NODE_ENV) and amount > 0; otherwise it fabricates a fake order object with a uuid id (so dev/staging never hit Razorpay). [src: helpers/transactions.helper.js:414-439 (gate at :431)] Order creation happens in three places, each followed by updateRazorpayTransactions() which stamps the razorpay_order_id onto the relevant transaction rows: 1. Inline "Pay Now" at booking time — mumukshuBooking / guestBooking controllers, only if req.user.country == 'India' and amount > 0. 2. POST /razorpay/pay (v1 pending payments). 3. POST /razorpay/payv2 (v2 pending payments — the one the app uses).

Webhook capture (verifyPayment, payment.controller.js:32). [src: controllers/client/payment.controller.js:32-269; routes/client/payment.routes.js:12] Every webhook is first archived into razorpay_webhook, then: - Reads payload.payment.entity.{order_id, id, status}. - Ignores any status not in {captured, failed, authorized} (returns 200 "Invalid status" — Razorpay must get a 200 or it retries). - Opens a DB transaction with lock: { update: true } and loads transactions for that razorpay_order_id whose status ∈ {pending, cash pending, failed, authorized}. - Per transaction, by webhook status: - authorized → transaction status = authorized (money held, not captured; booking untouched). - captured → transaction status = completed; resolve the booking via getBookingType/getBooking and set booking status = pending checkin (room/flat) or confirmed (adhyayan/utsav/travel). Food bookings' status is deliberately not changed. For late-checkout fees, a transaction exists with no booking — handled gracefully (if (booking)). - failed → transaction status = failed. - Commits, then fires per-type WhatsApp status messages (room/flat/travel/utsav) and a unified confirmation email. If no matching pending transactions are found, it rolls back and returns "No pending bookings found".

Every route (Razorpay).

Method Path Auth realm Purpose Key params/body
POST /api/v1/razorpay/verifyPayment None (public webhook) Razorpay server-to-server capture/authorize/fail callback { payload.payment.entity: { order_id, id, status } }
POST /api/v1/razorpay/pay Member (validateCard) v1: create order for selected bookingids (blocks any food) { bookingids: [] }
POST /api/v1/razorpay/payv2 Member (validateCard) v2: create order for [{bookingid, category}] (food allowed only if category matches) { data: [{ bookingid, category }] }

Member-app behavior. Sheet branded "Vitraag Vigyaan Aashray", INR, theme #F1AC09, prefilled email/phone/name. On success → /paymentConfirmation; on failure/cancel → /paymentFailed. A ₹0 (fully credit-covered) or Pay-Later booking skips Razorpay entirely and goes to /bookingConfirmation.

Discrepancies. (1) verifyPayment performs no Razorpay signature verification and has no auth middleware — anyone who can POST the expected JSON shape can flip transactions to completed and confirm bookings. Significant security gap. [src: controllers/client/payment.controller.js:32-269; routes/client/payment.routes.js:12] (2) STATUS_PAYMENT_CAPTURED is a defined transaction enum value but the webhook maps captured → completed, so captured is only ever a webhook status string, never a stored transaction status. [src: payment.controller.js:110; config/constants.js:54] (3) /pay (v1) hard-rejects any order containing a food line, while /payv2 permits food when the category matches — inconsistent handling between the two surfaces. [src: payment.controller.js:294-305 (v1); payment.controller.js:361-375 (v2)]


D. The transactions lifecycle — every status & transition

Data model (transactions.model.js, table transactions). [src: models/transactions.model.js:44-78]

Field Type Notes
id INT PK auto
cardno STRING FK → card_db.cardno belongsTo CardDb
bookingid STRING links to Room/Flat (declared assoc) or Shibir/Travel/Utsav/Food (by category)
category STRING one of room, flat, adhyayan, utsav, travel, food, breakfast, lunch, dinner — mapped to a booking type via bookingTypeMap
amount DECIMAL net payable after credit deduction
discount DECIMAL (default 0) credits used
razorpay_order_id STRING nullable stamped when an order is created
description STRING nullable e.g. "credits used: N", "credits added: N"
status ENUM full lifecycle (below)
updatedBy STRING (default 'USER') RAZORPAY_CALLBACK for webhook, admin username, or user

The status set (config/constants.js) [src: config/constants.js:32,36,45,46,51,53,54,55,56,57,58]:

  • pending (STATUS_PAYMENT_PENDING) — online payment awaited (India users). Subject to the 24h auto-cancel.
  • cash pending (STATUS_CASH_PENDING) — cash owed; created for admin bookings or non-India users. Never expires.
  • authorized — Razorpay authorized but not yet captured.
  • completed (STATUS_PAYMENT_COMPLETED) — paid online (captured) or fully covered by credit.
  • cash completed (STATUS_CASH_COMPLETED) — cash collected/marked by admin.
  • failed (STATUS_PAYMENT_FAILED) — payment attempt failed (retryable via pending payments).
  • cancelled (STATUS_CANCELLED) — user-cancelled, no credit issued.
  • admin cancelled (STATUS_ADMIN_CANCELLED) — admin- or cron-cancelled, no credit.
  • credited (STATUS_CREDITED) — cancellation converted the amount to store credit.
  • captured (STATUS_PAYMENT_CAPTURED) — enum value present but unused as a stored status (see §C discrepancy).

Transitions (cancelTransaction, transactions.helper.js:112). [src: helpers/transactions.helper.js:112-255] On cancel, totalAmount = amount + discount (gross); credit awarded = totalAmount if the txn was completed/cash completed, else discount (previously-applied credit). Then: - User cancel of travel or utsav → early return, no credit, transaction left completed. [src: helpers/transactions.helper.js:166-177] - Adhyayan / utsav / migrated bookings (ifMigrated: bookingid < 36 chars or description contains "came from datachef migration") → no credit; if it was completed/cash completed it stays completed (kept for reports), else moves to cancelled/admin cancelled. [src: helpers/transactions.helper.js:196-207; helpers/booking.helper.js:87-97] - Everything else (room/flat/food) with credit > 0addCredit(...), status → credited, description = "credits added: N". [src: helpers/transactions.helper.js:208-212] - Re-cancelling an already-cancelled txn is only allowed for admin, which force-credits the full amount + discountcredited. Re-cancelling admin cancelled/credited throws 400. - Finally the row is normalized: discount = 0, amount = totalAmount, new status.

Bus-assignment cleanup: cancelling any transaction also removes the booking from any TravelBusPassengers group and nulls a coordinator_bookingid if it pointed at it.

Discrepancies. The model file itself carries a // TODO: remove cash completed and payment completed status comment — the team considers part of this enum legacy. [src: models/transactions.model.js:61]


E. Pending payments & the 24-hour auto-cancel cron

What it is. Outstanding dues surface in the app's "Pending Payments" list (fed by GET /profile/transactions filtered to pending,cash pending,failed). Non-cash pending items expire 24 hours after creation; the cron enforces this by cancelling the booking + transaction.

The cron (cron.js). MAX_APP_PAYMENT_DURATION = 24*60 minutes. The main job runs every 30 minutes (*/30 * * * *): [src: cron.js:38,43] 1. getPendingTransactions(now − 24h) → transactions with status ∈ {pending, failed}, card.country = 'INDIA', and createdAt <= cutoff. Note: cash pending is excluded by design, and non-India users are excluded (their transactions are cash pending anyway). [src: helpers/transactions.helper.js:441-463 (country filter :451)] 2. For each non-food transaction, resolve and collect the booking. 3. cancelBookings sets each booking status = admin cancelled; for adhyayan it promotes the next waitlisted seat (and creates attendance) + resets attendance; for utsav it cancels utsav food and opens the seat; for travel it promotes the next waiting booking. 4. cancelTransactions(systemUser, txns, t, admin=true) runs the §D admin-cancel logic (so a paid pending item that somehow expired would credit). 5. cancelMeals cancels the food bookings. 6. Commits, then sends cancellation emails, "open booking" emails, and per-type WhatsApp.

Other crons in the same file (not payment-related): meal-count WhatsApp at 21:00/22:00/23:00 IST, and a WiFi low-code alert every 30 min. A getUnpaidPastBookings path (cancel bookings whose check-in is already past) exists but is commented out.

Member-app behavior. Live color-coded countdown per item (green > 3h, orange ≤ 3h, red "Expired"); expired items are non-selectable ("Payment expired"); cash pending items show no timer. Pay-Later bookings display a 24h warning modal before confirming.

Discrepancies. (1) Country casing mismatchcreatePendingTransaction marks a txn cash pending when card.country != 'India' (mixed case), but the cron's getPendingTransactions filters country = 'INDIA' (upper). If any card stores the country as 'India' (not 'INDIA'), that member's pending transactions would be skipped by the cron and never auto-cancelled. Worth auditing the stored value. [src: helpers/transactions.helper.js:38 vs :451] (2) Self food transactions are created pending yet online payment for food is blocked (§C) — such items can only be cleared by cash or the 24h auto-cancel.


F. Admin — settlements, reconciliation, vouchers & credit reports

All routes below sit under /api/v1/admin/accounts, gated by auth + authorizeRoles(superAdmin, accountsAdmin, accountsAdminPra). [src: app.js:188; routes/admin/accountsManagement.routes.js:26-39; config/constants.js:82,96,97] The admin UI pages live in admin/account/* (settlementReport, settlementRecon, settlementBreakdown, voucherCreation, creditsReport, creditTransaction, debitTransaction, pendingTransaction).

Data model. - razorpay_settlement (RazorpaySettlement): id (settlement id, PK), amount, status, fees, tax, utr, cerated_at [sic — misspelled column]. Populated by uploading Razorpay's settlement report Excel; dedup by id. [src: models/razorpay_settlement.model.js:32] - razorpay_settlement_recon (RazorpaySettlementRecon): composite PK (payment_id, order_id), plus amount, fees, tax, credit_amount, payment_notes, settlement_id, settled_at, settlement_utr. Populated by uploading Razorpay's per-payment recon Excel (upsert keyed on order+payment). Ties each payment to a settlement. [src: models/razorpay_settlement_recon.model.js]

How the reports work. - Settlement report upload parses the sheet, formats/validates dates (DD/MM/YYYY HH:mm:ss), skips rows missing a date, and bulkCreates only new settlement ids. - Recon upload upserts one recon row per (order_id, entity_id), mapping fee (exclusive tax), tax, credit, order_notes, settlement_id, settled_at, settlement_utr (and a settled_by field written but not defined on the model). - fetchAllSettlements returns settlements with fees/tax overwritten by the sum from recon grouped by settlement_id. - fetchTransactionsBySettlementId joins app transactions to recon (pre-aggregated per order to avoid fan-out) and UNIONs "Satshrut Transactions" — captured razorpay_webhook payments whose order isn't in the local transactions table (i.e. cross-app payments to the same Razorpay account), reading the amount from the webhook JSON. - fetchTransactionsByPaymentId drills a single order into per-line detail (booking/room/flat/shibir/utsav/travel/food joins, bookedBy + bookedFor cards), again UNIONing the Satshrut webhook-only fallback (matching a card by the last 10 digits of the payment contact). - fetchCompletedTransactions (voucher creation) returns completed/cash completed/credited transactions with optional startDate/endDate, category (food expands to all meal categories), adhyayanId, utsavId filters — used to generate receipts/vouchers. - Credits reportfetchCredits lists every card with a non-empty credits JSON, parsed into roomCredits/foodCredits/travelCredits/utsavCredits; fetchCreditTransactions?cardno=&category= rebuilds a running-balance ledger for one card from credited (+) rows and completed+"credits used:" (−) rows. - Credit / Debit / Pending reportsfetchAllCreditTransactions (status credited, dated by updatedAt); fetchAllDebitTransactions (excludes credited, includes completed with "credits used: %" — i.e. credit consumption, plus a credits_remaining extracted from the card JSON); fetchPendingTransactions (cash pending + pending).

Every route (admin accounts).

Method Path Auth realm Purpose Key params/body
GET /api/v1/admin/accounts/fetchcompleted Accounts admin Completed/credited txns for voucher creation startDate, endDate, category, adhyayanId, utsavId
GET /api/v1/admin/accounts/fetchpending Accounts admin Pending + cash-pending txns startDate, endDate
GET /api/v1/admin/accounts/fetchcredits Accounts admin Credited transactions (by updatedAt) startDate, endDate
GET /api/v1/admin/accounts/fetchdebits Accounts admin Credit-consumption transactions startDate, endDate
POST /api/v1/admin/accounts/setrep Accounts admin Upload Razorpay settlement report Excel multipart file
POST /api/v1/admin/accounts/updateset Accounts admin Upload Razorpay recon Excel (upsert) multipart file
GET /api/v1/admin/accounts/fetchset Accounts admin List settlements (fees/tax summed from recon) startDate, endDate
GET /api/v1/admin/accounts/fetchTransactions/:settlementId Accounts admin Orders under one settlement (+Satshrut fallback) :settlementId
GET /api/v1/admin/accounts/fetchTransactions/payment/:razorpay_order_id Accounts admin Per-line detail for one order (+Satshrut fallback) :razorpay_order_id
GET /api/v1/admin/accounts/credits Accounts admin All cards holding store credit
GET /api/v1/admin/accounts/fetchcreditstransactions Accounts admin Running credit ledger for one card cardno, category

Admin operations. Accounts admins do not create transactions here — they collect cash (marking cash pending → cash completed elsewhere), reconcile Razorpay settlements against local transactions, generate vouchers/receipts, and audit member credit balances. (Booking creation/cancellation — which is what mints and burns credit — lives in the booking/admin-booking controllers, see Booking Lifecycle.)

Discrepancies. (1) cerated_at is a genuine misspelled column, relied on across queries. [src: models/razorpay_settlement.model.js:32] (2) settled_by is written on recon upsert but absent from the RazorpaySettlementRecon model definition. [src: controllers/admin/accountsManagement.controller.js:439 (written); models/razorpay_settlement_recon.model.js (absent)] (3) Reconciliation depends on Razorpay Excel exports being manually uploaded — there is no automated settlement ingestion, so fetchAllSettlements fees/tax are only as fresh as the last upload.


Data & relationships (summary)

  • CardDb (cardno) 1—N Transactions (cardno); Transactions belongsTo CardDb.
  • Transactions (bookingid) belongsTo RoomBooking / FlatBooking (declared); resolved dynamically to ShibirBookingDb / TravelDb / UtsavBooking / FoodDb via categorybookingTypeMap.
  • card_db.credits — JSON store-credit wallet keyed { room, food, travel, utsav, adhyayan }; flat folds into room.
  • razorpay_webhook — append-only archive of every webhook payload (source of truth for cross-app "Satshrut" reconciliation).
  • razorpay_settlement 1—N razorpay_settlement_recon (via settlement_id); recon N—1 to an order (order_id) which maps to transactions.razorpay_order_id.

Edge cases & branches

  • ₹0 net (credit ≥ charge) → transaction completed + booking confirmed at creation; no Razorpay, no order.
  • Late-checkout fee → a transaction with no corresponding booking; webhook capture handles if (booking) and simply marks the txn completed.
  • Non-India user → transaction created cash pending (never expires); online order never generated (country == 'India' gate); blocked from Razorpay in-app.
  • Waitlisted adhyayan/utsavcharge = 0, no transaction cost until promoted.
  • Migrated bookings (ifMigrated) → cancellation never credits; completed stays completed.
  • Webhook race → row-level lock: { update: true } on the matching transactions; duplicate/late webhooks that find nothing pending simply return 200 with "No pending bookings".
  • Fake orders in dev/qa-non-prod → uuid id, no real Razorpay call.

Discrepancies

Each item below is a summary of a discrepancy detailed and [src:]-anchored in its section above (§A–§F); all were re-confirmed against main on 2026-07-07.

  1. /razorpay/verifyPayment has no signature verification and no auth — a forgeable public endpoint that can confirm bookings and complete transactions.
  2. Country casing'India' (booking) vs 'INDIA' (cron filter) can strand a member's pending transactions from auto-cancel.
  3. Utsav credit asymmetry — spendable/previewable but not earned on normal cancel.
  4. Adhyayan credit never applied in validate (and never earned).
  5. Flat priced at NAC room rate, no dedicated flat tariff.
  6. adjustAmount references an undefined user → broken admin re-price path.
  7. STATUS_PAYMENT_CAPTURED enum value unused as a stored transaction status; model TODO flags cash completed/completed as legacy.
  8. /pay vs /payv2 disagree on food handling.
  9. Settlement schema: misspelled cerated_at; settled_by written but not modeled.
  10. Self food is created pending but cannot be paid online → cleared only by cash or 24h auto-cancel.

How this connects to other domains (technical)

  • Booking Lifecycle — every booking type funnels into createPendingTransaction, which both applies credit and decides pending vs cash pending; booking confirmation is a side effect of payment capture or full-credit coverage here.
  • Status & Cron — the 24h auto-cancel job is the enforcement arm of the pending status; booking status (pending checkin, confirmed, admin cancelled) is driven by transaction transitions in this doc.
  • Admin Panel Map — the /admin/accounts surface (settlements, recon, vouchers, credit/debit/pending reports) is the operational read/reconcile layer over the transactions, razorpay_settlement, and razorpay_settlement_recon tables described above.
  • Profile / Account — the member's transaction history and Available Credits card read the same transactions ledger and card_db.credits wallet.
  • Cross-app ("Satshrut") payments — payments to the shared Razorpay account that never created a local transactions row are recovered for reconciliation purely from razorpay_webhook JSON.