Skip to content

(SP: 3) [SHOP] add transactional customer communications for order creation and status updates#438

Merged
ViktorSvertoka merged 3 commits intodevelopfrom
lso/feat/shop-legal
Mar 31, 2026
Merged

(SP: 3) [SHOP] add transactional customer communications for order creation and status updates#438
ViktorSvertoka merged 3 commits intodevelopfrom
lso/feat/shop-legal

Conversation

@liudmylasovetovs
Copy link
Copy Markdown
Collaborator

@liudmylasovetovs liudmylasovetovs commented Mar 30, 2026

Description

This PR completes Phase 5 — customer communications for the Shop module.

It adds transactional customer notifications on top of the existing canonical event -> projector -> outbox -> worker -> transport architecture.

Phase 5 now covers:

  • order-created confirmation
  • shipped notification
  • canceled notification
  • returned notification
  • explicit and fail-closed guest recipient policy for notification delivery

The goal is to ensure that customer communication is not limited to on-site pages and that key order lifecycle changes generate real transactional notifications from authoritative persisted events.


Related Issue

Issue: #<issue_number>


Changes

  • Added order_created transactional confirmation through the existing notification pipeline
  • Added order_shipped, order_canceled, and order_returned templates and canonical-event mappings
  • Closed guest recipient policy for notifications: persisted shipping recipient email first, users.email fallback only for signed-in orders, guest-without-persisted-email fails closed explicitly

Database Changes (if applicable)

  • Schema migration required
  • Seed data updated
  • Breaking changes to existing queries
  • Transaction-safe migration
  • Migration tested locally on Neon

How Has This Been Tested?

  • Tested locally
  • Verified in development environment
  • Checked responsive layout (if UI-related)
  • Tested accessibility (keyboard / screen reader)

Additional local verification included:

  • focused WS10 notification tests
  • focused WS11 status-notification tests
  • projector / worker notification regressions
  • touched-file TypeScript verification for WS11 files
  • targeted lint verification for WS11 test file

Screenshots (if applicable)

N/A — backend / notification flow work.


Checklist

Before submitting

  • Code has been self-reviewed
  • No TypeScript or console errors
  • Code follows project conventions
  • Scope is limited to this feature/fix
  • No unrelated refactors included
  • English used in code, commits, and docs
  • New dependencies discussed with team
  • Database migration tested locally (if applicable)
  • GitHub Projects card moved to In Review

Reviewers

Summary by CodeRabbit

  • New Features

    • Order lifecycle notifications: customers now get emails for order_created, order_shipped, order_canceled, and order_returned with improved subject/body details and optional payment/total lines.
    • Best-effort canonical event emission for payment and shipping events to improve event reliability and deduplication.
  • Bug Fixes / Improvements

    • Better recipient resolution with guest vs signed-in fallback and clearer send errors.
    • Env toggle support for Monobank Google Pay.
  • Tests

    • Expanded integration tests covering end-to-end notification, idempotency, replay/backfill, and worker delivery.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
devlovers-net Ignored Ignored Preview Mar 31, 2026 5:30pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a1b7ecbb-2648-4c22-9a44-d7ba2291e6ad

📥 Commits

Reviewing files that changed from the base of the PR and between a89c091 and f3dda69.

📒 Files selected for processing (2)
  • frontend/lib/tests/shop/checkout-order-created-notification-phase5.test.ts
  • frontend/lib/tests/shop/status-notifications-phase5.test.ts
✅ Files skipped from review due to trivial changes (2)
  • frontend/lib/tests/shop/checkout-order-created-notification-phase5.test.ts
  • frontend/lib/tests/shop/status-notifications-phase5.test.ts

📝 Walkthrough

Walkthrough

Adds canonical event emission for order lifecycle events (created, canceled, shipped, returned), server-side env read for a Monobank Google Pay toggle, improved notification recipient resolution (guest vs signed-in), and extensive tests exercising projector/worker delivery and event backfill behavior.

Changes

Cohort / File(s) Summary
Server Env
frontend/lib/env/server-env.ts
Added SHOP_MONOBANK_GPAY_ENABLED to GENERATED_FALLBACK_KEYS to allow readServerEnv fallback for that toggle; minor whitespace/EOF changes.
Order creation (checkout)
frontend/lib/services/orders/checkout.ts
Switched Monobank Google Pay toggle to use readServerEnv; added writeOrderCreatedCanonicalEvent and ensureOrderCreatedCanonicalEvent; invoked ensures on new orders and idempotent replay paths.
Order cancellation (restock)
frontend/lib/services/orders/restock.ts
Added dedupe key & canonical order_canceled write plumbing: loadOrderCanceledNotificationState, buildOrderCanceledEventDedupeKey, ensureOrderCanceledCanonicalEvent; integrated ensures into restockOrder at multiple exit points.
Shipping admin actions
frontend/lib/services/shop/shipping/admin-actions.ts
Expanded ShippingStateRow selection to include order totals/currency; added buildOrderShippedEventDedupe and ensureOrderShippedCanonicalEvent; call ensured write on shipped transitions and replays.
Notifications: outbox worker
frontend/lib/services/shop/notifications/outbox-worker.ts
Extended recipient lookup with order_user_id; replaced nullable recipient with tagged union (resolved/missing) and updated loadNotificationRecipient + sendNotification to return/throw more specific missing-recipient codes.
Notifications: templates
frontend/lib/services/shop/notifications/templates.ts
Added template keys `order_created
Tests (projector/worker/e2e)
frontend/lib/tests/shop/checkout-order-created-notification-phase5.test.ts, frontend/lib/tests/shop/notifications-projector-phase3.test.ts, frontend/lib/tests/shop/notifications-worker-transport-phase3.test.ts, frontend/lib/tests/shop/status-notifications-phase5.test.ts
New/expanded Vitest suites covering canonical event writes, idempotent replay backfill, notification projector mapping for new event types, recipient fallback to user email, and end-to-end outbox worker delivery.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant OrderSvc as Order Service (checkout.ts)
    participant PaymentWriter as Payment Event Writer
    participant DB as Database
    participant Projector as Outbox Projector
    participant Worker as Notification Worker
    participant Email as Email Transport

    Client->>OrderSvc: createOrderWithItems(idempotencyKey)
    OrderSvc->>DB: INSERT order, items (or SELECT existing)
    OrderSvc->>PaymentWriter: ensureOrderCreatedCanonicalEvent(orderId, payload)
    PaymentWriter->>DB: INSERT paymentEvents (deduped)
    OrderSvc->>Client: { isNew: true/false }

    Projector->>DB: SELECT new payment/shipping events
    Projector->>DB: INSERT notificationOutbox rows

    Worker->>DB: CLAIM notificationOutbox
    Worker->>DB: SELECT recipient (includes order_user_id)
    Worker->>Email: sendShopNotificationEmail(to, subject, body)
    Email-->>Worker: delivery success
    Worker->>DB: UPDATE notificationOutbox (sent)
Loading
sequenceDiagram
    participant Admin
    participant ShippingSvc as Shipping Service (admin-actions.ts)
    participant ShippingWriter as Shipping Event Writer
    participant DB as Database
    participant RestockSvc as Restock Service (restock.ts)
    participant PaymentWriter as Payment Event Writer

    Admin->>ShippingSvc: applyShippingAdminAction(mark_shipped)
    ShippingSvc->>DB: UPDATE shipment status
    ShippingSvc->>ShippingWriter: ensureOrderShippedCanonicalEvent(shipmentState)
    ShippingWriter->>DB: INSERT shippingEvents (order_shipped)

    Admin->>RestockSvc: restockOrder(reason: canceled)
    RestockSvc->>DB: UPDATE order.status -> CANCELED
    RestockSvc->>PaymentWriter: ensureOrderCanceledCanonicalEvent(orderId)
    PaymentWriter->>DB: INSERT paymentEvents (order_canceled)
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

Suggested Reviewers

  • LesiaUKR
  • AM1007
  • ViktorSvertoka

Poem

🐇 I hopped through code at break of dawn,

Wrote canonical notes for orders born.
Cancels, ships, returns—each gets a cheer,
Outbox and worker deliver with no fear.
Hop, hop, events are safe and clear! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: adding transactional customer communications for order creation and status updates, which aligns with the primary changes across checkout, restock, shipping, and notification template files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lso/feat/shop-legal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
frontend/lib/tests/shop/status-notifications-phase5.test.ts (1)

586-627: Consider adding explicit timeout for consistency.

The restock replay test doesn't specify a timeout unlike the other tests in this file (which use 30_000). While this test may be faster, adding an explicit timeout maintains consistency and prevents potential CI timeouts.

🔧 Suggested change
-  });
+  }, 30_000);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/lib/tests/shop/status-notifications-phase5.test.ts` around lines 586
- 627, The test "restock replay backfills a missing order_canceled canonical
event without creating duplicates" lacks an explicit timeout; update the it(...)
invocation for that test to include a timeout (e.g., 30_000) to match other
tests and avoid CI flakiness—modify the test declaration that wraps the async
function which calls seedShippableOrder, restockOrder, and cleanupOrder to pass
the timeout as the last argument.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/lib/tests/shop/checkout-order-created-notification-phase5.test.ts`:
- Around line 117-119: cleanupOrder currently only deletes from orders which can
leave related rows in orderShipping, paymentEvents, and notificationOutbox and
lead to FK violations or test pollution; update cleanupOrder to delete related
rows first (orderShipping, paymentEvents, notificationOutbox) filtered by the
same orderId, then delete from orders (use the same db.delete(...) pattern and
eq(orders.id, orderId) style predicates used elsewhere) so all dependent data is
removed before the orders row.

---

Nitpick comments:
In `@frontend/lib/tests/shop/status-notifications-phase5.test.ts`:
- Around line 586-627: The test "restock replay backfills a missing
order_canceled canonical event without creating duplicates" lacks an explicit
timeout; update the it(...) invocation for that test to include a timeout (e.g.,
30_000) to match other tests and avoid CI flakiness—modify the test declaration
that wraps the async function which calls seedShippableOrder, restockOrder, and
cleanupOrder to pass the timeout as the last argument.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3f8c4b47-6ed5-4f63-80f5-2243ac450197

📥 Commits

Reviewing files that changed from the base of the PR and between 9d3b7cf and a89c091.

📒 Files selected for processing (10)
  • frontend/lib/env/server-env.ts
  • frontend/lib/services/orders/checkout.ts
  • frontend/lib/services/orders/restock.ts
  • frontend/lib/services/shop/notifications/outbox-worker.ts
  • frontend/lib/services/shop/notifications/templates.ts
  • frontend/lib/services/shop/shipping/admin-actions.ts
  • frontend/lib/tests/shop/checkout-order-created-notification-phase5.test.ts
  • frontend/lib/tests/shop/notifications-projector-phase3.test.ts
  • frontend/lib/tests/shop/notifications-worker-transport-phase3.test.ts
  • frontend/lib/tests/shop/status-notifications-phase5.test.ts

@ViktorSvertoka ViktorSvertoka merged commit 6f74dda into develop Mar 31, 2026
7 checks passed
@ViktorSvertoka ViktorSvertoka deleted the lso/feat/shop-legal branch March 31, 2026 04:31
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.

2 participants