This repository was archived by the owner on Sep 27, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Payments
Syed Ahmer Shah edited this page Sep 27, 2026
·
1 revision
Cash at pickup is always available — the MarketLink brief asks for pay-at-the-stall, and GleanGrid keeps it. On top of that, customers can pay online with Easypaisa, JazzCash or a debit / credit card. Each method can be switched off in Admin → Settings.
-
Checkout reserves the stock. An online checkout creates the orders (one per stall) and one
paymentsrow for the whole checkout, then holds the stock for a payment window (default 20 minutes, adjustable from 5 to 120 in Admin → Settings). Farmers aren't notified and can't accept until it's paid. - The payment page. Cards: a 3D card that tilts with the pointer and flips for the CVC. Wallets: a phone that shows the approval request. A countdown shows how long the stock is held.
-
Paid. Every order in the checkout becomes
paid, farmers get their New pre-order e-mail, and the customer gets a receipt. -
Not paid in time.
gleangrid:expire-payments(scheduled every minute) releases the stock and returns any coupon. - Refunds are automatic when the customer cancels before the cut-off or the farmer declines. Admins can refund any payment from Admin → Payments, which shows every attempt, decline, capture and refund on a timeline.
| Risk | Defence |
|---|---|
| Double tap on Pay, or two tabs | The payment row is locked FOR UPDATE and moves pending → processing exactly once; a second attempt gets payment in progress
|
| A flaky connection resends the request | Every attempt carries a one-time idempotency key under a unique index; a replay returns the existing result and charges nothing |
| A tampered amount | Amounts are always recomputed on the server from the orders — the browser never says how much to charge |
| A forged gateway callback | Callbacks are CSRF-exempt but must carry a valid HMAC-SHA256 signature (compared in constant time) and the matching amount — otherwise 403 / 422 |
| The gateway confirms after the window closed | The stock may already be back on sale: the late capture is detected (payment no longer processing), logged as late_capture and refunded automatically
|
| A refund running twice | Refunds use a conditional UPDATE … WHERE payment_status = 'paid'
|
| Card data leaking | Card numbers and CVCs are validated (Luhn, expiry, CVC length) and passed to the gateway but never stored, logged or flashed back; only the brand and last four digits are kept. Wallet numbers are masked (0300•••4567) |
| Brute-forcing the pay button | 8 attempts a minute and 40 an hour per account, 5 per checkout, 90 status polls a minute |
-
PAYMENTS_MODE=sandbox(default) runs a built-in simulator, so the whole flow can be tried without real money. Test data on the payment page:4242 4242 4242 4242approves,4000 0000 0000 0002is declined,4000 0000 0000 9995has no funds; any03XXXXXXXXXwallet number approves after a few seconds, numbers ending in0000are declined. -
PAYMENTS_MODE=livewithJAZZCASH_MERCHANT_ID,JAZZCASH_PASSWORDandJAZZCASH_INTEGRITY_SALTuses JazzCash's hosted checkout (pp_SecureHash, HMAC-SHA256, verified again on the signed callback). - Live Easypaisa and card acquiring need a merchant agreement; they plug into the same
App\Payments\PaymentGatewayinterface and stay hidden in live mode until a driver is configured.
See also: Race Conditions · Security Model · Order Lifecycle
GleanGrid · MIT licence · Live site · Report a security issue · Built in Hyderabad, Sindh