Overview
MechZie supports two payment methods: Online (Razorpay) and Cash / COD. Every monetary operation is backed by an immutable double-entry ledger — thepayments table records Razorpay state, while ledger_entries is the financial source of truth.
- Online — customer pays via Razorpay checkout (UPI, card, etc.). Platform collects the full amount and credits the mechanic’s wallet.
- Cash / COD — customer pays the mechanic in cash on-site. The mechanic confirms receipt, and the platform fee is deducted from their future earnings.
Architecture
Payment Method Selection
When creating a job, the customer chooses a payment method:
If omitted, defaults to
online.
Online Payment Flow
Two paths confirm a payment — the optimistic verify (fast UI) and the webhook (canonical truth). Both are idempotent; if they race, the first one wins.Step-by-Step (Online)
1
Create a Razorpay Order
After the mechanic marks a job as Response (200):Amount is in paisa. Display as
completed, the customer creates a payment order:₹${(amount / 100).toStringAsFixed(0)}.2
Open Razorpay Checkout
Use the Razorpay Flutter SDK:
3
Verify Payment (Optimistic)
After Razorpay returns success, verify the signature:On success, this also triggers the platform fee split into the ledger (mechanic payable 90% + platform fee 10%). The webhook is the canonical fallback if this path fails.
If verify and webhook disagree, webhook always wins. The ledger uses idempotency keys to prevent double-processing.
Cash / COD Payment Flow
For COD jobs, there’s no Razorpay involvement. The mechanic collects cash directly from the customer and confirms receipt via the API.Step-by-Step (Cash / COD)
1
Customer creates COD job
The customer selects “Pay by Cash” when creating a job:The job proceeds through the normal lifecycle (dispatching → accepted → en_route → arrived → in_progress → completed).
2
Customer pays cash to mechanic
After the mechanic marks the job as
completed, the customer’s app shows the amount to pay in cash. This is a UI-only step — no API call needed from the customer.3
Mechanic confirms cash received
Once the mechanic has collected cash, they confirm via the API:Response (200):This endpoint:
- Transitions the job to
paid - Records the COD collection in the ledger
- Deducts the platform fee from the mechanic’s wallet balance
COD Ledger Entries
When a mechanic confirms a COD payment,CommissionService.processCodCommission() creates these ledger entries:
The platform fee is deducted from the mechanic’s future earnings (wallet balance goes negative by the fee amount).
Platform Fee Split
When a payment is captured (online) or confirmed (COD),CommissionService splits the gross amount. The mechanic always receives 90%. For online payments, the gateway processing fee (PG fee) is a platform expense — it never reduces the mechanic’s payout.
The platform fee rate is dynamic — stored in
platform_config as platform_fee_bps (default: 1000 = 10%) and changeable without redeployment.
Online vs COD Fee Handling
PG Fee Tracking
The payment-gateway processing cost is recorded separately as a platform expense — not deducted from the mechanic:- On
payment.capturedwebhook — if Razorpay reportsfeeandtaxfields,PgFeeService.recordActualPgFee()posts apg_fee_actualledger entry:DR PLATFORM_FEE_REVENUE → CR PLATFORM_PG_EXPENSE - From settlement reports — if the webhook fee was 0, a corrected entry can be posted with
source: 'settlement_report'(distinct idempotency key). pg_fee_bps_estimate(default: 200 = 2%) is available for margin estimation only — never used in actual ledger calculations.
Webhook Processing
The webhook pipeline guarantees exactly-once processing viapayment_events.razorpay_event_id:
Handled events:
Endpoint:
POST /api/v1/payments/webhook
- No authentication (Razorpay sends directly)
- Verified via HMAC-SHA256 in
X-Razorpay-Signatureheader - Always returns 200 to prevent Razorpay retries
Frontend devs don’t need to implement webhook handling — it’s server-side only. But you should handle the case where
verify succeeds but the UI should wait for the job status to update to paid.Checking Payment Status
For COD jobs, there is no
payments table record — the job transitions directly from completed to paid when the mechanic confirms cash. Check the job’s status field instead.Payment Statuses
Cancellation Fees
Late cancellations incur fees. The fee amount is configurable inplatform_config.
Customer cancellation
Cancelling fromen_route, arrived, or in_progress → flat ₹50 fee (default). The customer cannot create new jobs until the fee is paid (402 PAYMENT_REQUIRED).
Mechanic cancellation
Consecutive cancellations above a threshold (default: 3 in 30 days) → ₹100 penalty deducted from their wallet automatically.Check for Unpaid Fees
create-order → Razorpay checkout → verify flow with type: "cancellation_fee".
Pricing Model
Client SDKs Required
The Razorpay Key ID will be provided via your app’s environment configuration. Do not hardcode it. COD payments require no additional SDKs.
