Skip to content

worker: atomic quote claim, pipeline-wide revenue, image validation on update, crawler grid order - #32

Merged
Aswinmcw merged 1 commit into
mainfrom
fix/worker-money-idempotency
Sep 3, 2026
Merged

worker: atomic quote claim, pipeline-wide revenue, image validation on update, crawler grid order#32
Aswinmcw merged 1 commit into
mainfrom
fix/worker-money-idempotency

Conversation

@Aswinmcw

@Aswinmcw Aswinmcw commented Sep 3, 2026

Copy link
Copy Markdown
Member

Summary

Server-side correctness fixes from the review. No API shape changes.

  • Duplicate orders from payment_link.paid. handleQuotePaid did SELECT quote → batch(INSERT order, INSERT item, UPDATE quote). Two deliveries of the same event (Razorpay retries; x-razorpay-event-id is not guaranteed to be present) could both pass the quote.order_id check and both create an order, receipt, invoice and customer/owner emails for a single payment. The quote is now claimed first with UPDATE quotes … WHERE id = ? AND order_id IS NULL; a delivery that changes 0 rows stops before writing anything. If the order write then fails, the claim is released so the retry converts the quote instead of leaving it marked paid with a dangling order_id.
  • Revenue undercount. stats() summed status IN ('paid','shipped'), so marking an order "In production" or "Ready" removed it from the dashboard's revenue and order count until it shipped, and "Delivered" removed it again. Now counts paid, in_production, ready, shipped, delivered.
  • Image validation on update. createProduct checks image/images against assets/images.json; updateProduct accepted any string (external URL, ../, a filename that does not exist) and stored it verbatim, to be rendered as <img src> on the storefront. The update path now applies the same manifest check and stores canonical assets/images/<file> paths.
  • Crawler grid order. The homepage HTML rendered products ORDER BY sort, name while /api/products orders pinned → buyable → quote-only with a different SQL order, so the indexed grid and the visible grid differed and the page visibly reshuffled when JS ran. Both now use the same ordering.

Test plan

  • npm test green (orders 204, admin 428)
  • New tests: two concurrent deliveries with no event id → exactly one order and the quote points at it; a D1 failure on the order batch throws (so Razorpay retries), leaves the quote unclaimed, and the retry converts; stats with in_production/ready/delivered/refunded rows; update rejects https://…, //…, ../… and unknown files, accepts and canonicalises manifest files
  • After deploy: dashboard revenue figure should rise (orders currently mid-pipeline are counted again)

Made with Cursor

@Aswinmcw
Aswinmcw requested review from a team and Aswin-coder as code owners September 3, 2026 09:21

@Aswincloud-Bot Aswincloud-Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-approved: @Aswinmcw is a member of @Aswincloud/admins.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 3, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
3d-printing 502ba86 Commit Preview URL

Branch Preview URL
Sep 03 2026, 09:48 AM

@Aswinmcw
Aswinmcw added this pull request to the merge queue Sep 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 3, 2026
…venue,

validate images on product update, match the crawler grid to the API order

- handleQuotePaid read the quote, then wrote an order and marked the quote in
  one batch. Two deliveries of the same payment_link.paid — Razorpay retries,
  and the event-id header is not guaranteed — could both read order_id = NULL
  and both create an order, receipt, invoice and pair of emails. The quote is
  now claimed first with UPDATE … WHERE order_id IS NULL; only the delivery
  that changes a row goes on to write the order. A failed order write
  releases the claim so the retry can convert.
- /api/admin/stats counted only 'paid' and 'shipped', so revenue dropped the
  moment an order went to "in production" and came back when it shipped. Every
  stage from paid to delivered counts; cancelled/refunded/pending do not.
- updateProduct accepted any string for image/images — an external URL, a
  traversal, a typo — where createProduct checks the manifest. Same check now.
- The homepage's server-rendered grid used ORDER BY sort, name; the API
  orders pinned → buyable → quote-only. A crawler indexed one order and the
  visitor watched it reshuffle when main.js ran. Same order on both.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Aswinmcw
Aswinmcw force-pushed the fix/worker-money-idempotency branch from 02d8a4a to 502ba86 Compare September 3, 2026 09:47
@Aswinmcw
Aswinmcw enabled auto-merge September 3, 2026 09:47
@Aswinmcw
Aswinmcw added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit 84a0358 Sep 3, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants