(SP: 3) [SHOP] add public legal surfaces and align checkout consent with privacy flow#419
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📝 WalkthroughWalkthroughAdds localized public legal pages and content components, enforces explicit checkout legal consent (client UI, payload validation, route handling, and order service), introduces public support/seller helpers and env var Changes
Sequence DiagramsequenceDiagram
actor User
participant Cart as CartPageClient
participant API as /api/shop/checkout
participant OrderSvc as createOrderWithItems
participant DB as Database
User->>Cart: Toggle consent checkbox
Cart->>Cart: Update legalConsentAccepted state
User->>Cart: Click "Place Order"
alt consent not accepted
Cart->>Cart: Set legalConsentUiError + checkoutError
Cart-->>User: Block submission (UI)
else consent accepted
Cart->>API: POST /api/shop/checkout (payload includes legalConsent with termsAccepted, privacyAccepted, termsVersion, privacyVersion)
API->>API: Validate via checkoutPayloadSchema
alt schema fails due to legalConsent
API->>Cart: 400 LEGAL_CONSENT_REQUIRED
else validation ok
API->>OrderSvc: createOrderWithItems(..., legalConsent)
OrderSvc->>DB: Insert order (may insert/ensure consent snapshot during idempotency replay)
DB-->>OrderSvc: Order + consent persisted
OrderSvc-->>API: Order result
API-->>Cart: 200 success (order created)
Cart-->>User: Redirect to confirmation
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fc1b33375a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/lib/services/orders/checkout.ts (1)
521-545:⚠️ Potential issue | 🟠 MajorDon't backfill explicit consent versions from env defaults.
Now that checkout requires explicit consent, silently replacing blank
termsVersion/privacyVersionwith current env values can persist acceptance for a document version the caller never actually sent. This service is the last guard before persistence, so it should fail closed here instead of normalizing.💡 Proposed fix
- const versions = getShopLegalVersions(); - const termsAccepted = args.legalConsent.termsAccepted; const privacyAccepted = args.legalConsent.privacyAccepted; @@ - const termsVersion = normalizeLegalVersion( - args.legalConsent.termsVersion, - versions.termsVersion - ); - const privacyVersion = normalizeLegalVersion( - args.legalConsent.privacyVersion, - versions.privacyVersion - ); + const termsVersion = args.legalConsent.termsVersion.trim(); + const privacyVersion = args.legalConsent.privacyVersion.trim(); + + if (!termsVersion || !privacyVersion) { + throw new InvalidPayloadError( + 'Legal consent versions are required before checkout.', + { + code: 'LEGAL_CONSENT_REQUIRED', + } + ); + }After this,
normalizeLegalVersion()and thegetShopLegalVersionsimport can be removed if nothing else uses them.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/lib/services/orders/checkout.ts` around lines 521 - 545, Currently the code silently backfills missing consent versions using getShopLegalVersions and normalizeLegalVersion; instead, update the checkout validation to require explicit args.legalConsent.termsVersion and args.legalConsent.privacyVersion when termsAccepted/privacyAccepted are true and throw an InvalidPayloadError (e.g., code: 'TERMS_VERSION_REQUIRED' / 'PRIVACY_VERSION_REQUIRED') if they are missing or blank; remove the calls to normalizeLegalVersion and getShopLegalVersions (and the imports) if nothing else depends on them after this change.
🧹 Nitpick comments (1)
frontend/lib/tests/shop/legal-cookie-privacy-alignment-phase4.test.ts (1)
23-44: Consider adding locale identification to test assertions for easier debugging.When iterating over locales, if an assertion fails, the error message won't indicate which locale caused the failure. Consider using
it.eachor adding the locale to the assertion message.💡 Suggested improvement
- it('keeps checkout consent keys present for all supported locales', () => { - for (const messages of [en, uk, pl]) { + it.each([ + ['en', en], + ['uk', uk], + ['pl', pl], + ] as const)('keeps checkout consent keys present for %s locale', (_locale, messages) => { expect( getAtPath(messages as Record<string, unknown>, [ 'shop', 'cart', 'checkout', 'consent', 'prefix', ]) ).toBeTruthy(); expect( getAtPath(messages as Record<string, unknown>, [ 'shop', 'cart', 'checkout', 'consent', 'required', ]) ).toBeTruthy(); - } });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/lib/tests/shop/legal-cookie-privacy-alignment-phase4.test.ts` around lines 23 - 44, The test 'keeps checkout consent keys present for all supported locales' iterates over locale message objects (en, uk, pl) without identifying which locale fails; update the test using it.each or include the locale variable in the expect messages so failures show the locale, e.g., iterate with a (localeName, messages) pair and call getAtPath(messages, [...]) inside expects that include localeName in the failure message; reference the test name and the variables en, uk, pl and the helper getAtPath to locate the change.
🤖 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/components/tests/footer-shop-legal-links.test.tsx`:
- Around line 3-4: The Link mock used in footer-shop-legal-links.test.tsx is
dropping props and therefore not forwarding data-testid attributes (so selectors
like "footer-legal-link-*" aren’t actually tested); update the mocked Link
component to accept and forward all anchor props (e.g., include a rest props
parameter and spread it onto the returned <a> element) so data-testid, target,
rel, etc. are preserved when the Footer renders, ensuring the existing tests
that query by data-testid exercise the real selector contract.
In `@frontend/lib/legal/public-contact.ts`:
- Around line 1-5: The PUBLIC_SUPPORT_EMAIL constant used by
getPublicSupportEmail() ('contact@devlovers.net') conflicts with the translation
key legal.contactEmail which contains "devlovers.net@gmail.com"; pick one source
of truth: either update PUBLIC_SUPPORT_EMAIL (and getPublicSupportEmail) to the
correct address used in translations, or update all translation entries
(en.json, pl.json, uk.json) to match PUBLIC_SUPPORT_EMAIL, or remove the unused
legal.contactEmail key from all message files if every component now imports
getPublicSupportEmail() directly; ensure the chosen email is consistent across
PUBLIC_SUPPORT_EMAIL, getPublicSupportEmail, and translation keys (or remove
those keys) so no components reference differing addresses.
In `@frontend/lib/services/orders/checkout.ts`:
- Around line 512-519: The new hard-fail on missing legal consent (throwing
InvalidPayloadError) makes the path racy with idempotency: ensure order and
order_legal_consents are persisted atomically or make the missing-consent state
transient/retriable. Update the checkout flow to either (a) insert or upsert the
order_legal_consents row before finalizing the order inside the same DB
transaction so creation is atomic with the order, or (b) change the validation
in assertIdempotencyCompatible to tolerate a transiently-missing
order_legal_consents by treating “no consent yet but order recently created with
same idempotency key” as retriable (retryable error) instead of a hard
rejection; locate and update the throw at the InvalidPayloadError site and the
logic in assertIdempotencyCompatible and the later consent-handling block to
implement one of these two strategies and ensure idempotent safe behavior for
repeated requests.
In `@frontend/messages/pl.json`:
- Around line 1748-1750: The Polish copy is inconsistent: checkout.ts
(orderTotalCents) includes shipping in the pre-confirmation total, but the keys
shop.cart.summary.shippingInformationalOnly and shippingPayOnDeliveryNote still
state shipping is informational/not charged; update those keys in pl.json
(including the similar block at 1778-1780) so their wording matches the
charges.title/body (or otherwise clearly state that shipping is included in the
total charged before order confirmation), ensuring all cart summary strings
consistently reflect that shipping is included.
---
Outside diff comments:
In `@frontend/lib/services/orders/checkout.ts`:
- Around line 521-545: Currently the code silently backfills missing consent
versions using getShopLegalVersions and normalizeLegalVersion; instead, update
the checkout validation to require explicit args.legalConsent.termsVersion and
args.legalConsent.privacyVersion when termsAccepted/privacyAccepted are true and
throw an InvalidPayloadError (e.g., code: 'TERMS_VERSION_REQUIRED' /
'PRIVACY_VERSION_REQUIRED') if they are missing or blank; remove the calls to
normalizeLegalVersion and getShopLegalVersions (and the imports) if nothing else
depends on them after this change.
---
Nitpick comments:
In `@frontend/lib/tests/shop/legal-cookie-privacy-alignment-phase4.test.ts`:
- Around line 23-44: The test 'keeps checkout consent keys present for all
supported locales' iterates over locale message objects (en, uk, pl) without
identifying which locale fails; update the test using it.each or include the
locale variable in the expect messages so failures show the locale, e.g.,
iterate with a (localeName, messages) pair and call getAtPath(messages, [...])
inside expects that include localeName in the failure message; reference the
test name and the variables en, uk, pl and the helper getAtPath to locate the
change.
🪄 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: b2fec2be-756e-48fd-80a9-fa5dff3f0d75
📒 Files selected for processing (30)
frontend/.env.examplefrontend/app/[locale]/delivery-policy/page.tsxfrontend/app/[locale]/payment-policy/page.tsxfrontend/app/[locale]/returns-policy/page.tsxfrontend/app/[locale]/seller-information/page.tsxfrontend/app/[locale]/shop/cart/CartPageClient.tsxfrontend/app/[locale]/shop/cart/page.tsxfrontend/app/api/shop/checkout/route.tsfrontend/components/legal/DeliveryPolicyContent.tsxfrontend/components/legal/LegalPageShell.tsxfrontend/components/legal/PaymentPolicyContent.tsxfrontend/components/legal/PrivacyPolicyContent.tsxfrontend/components/legal/ReturnsPolicyContent.tsxfrontend/components/legal/SellerInformationContent.tsxfrontend/components/legal/TermsOfServiceContent.tsxfrontend/components/shared/Footer.tsxfrontend/components/tests/footer-shop-legal-links.test.tsxfrontend/lib/legal/public-contact.tsfrontend/lib/legal/public-seller-information.tsfrontend/lib/services/orders/checkout.tsfrontend/lib/tests/shop/checkout-legal-consent-contract.test.tsfrontend/lib/tests/shop/checkout-legal-consent-phase4.test.tsfrontend/lib/tests/shop/checkout-route-stripe-disabled-recovery.test.tsfrontend/lib/tests/shop/legal-cookie-privacy-alignment-phase4.test.tsfrontend/lib/tests/shop/public-legal-pages-phase4.test.tsfrontend/lib/tests/shop/public-seller-information-phase4.test.tsfrontend/lib/validation/shop.tsfrontend/messages/en.jsonfrontend/messages/pl.jsonfrontend/messages/uk.json
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (6)
frontend/messages/en.json (1)
1718-1720: Add a production guard for seller placeholders.Publishing
"To be added"on legal seller fields in production can create compliance exposure. Consider a release-time check that blocks deploys when this placeholder exists in public legal copy.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/messages/en.json` around lines 1718 - 1720, Add a release-time (CI/prepublish) validation that fails the build if the seller placeholder exists: scan frontend/messages/en.json for the placeholders.toBeAdded key or the literal value "To be added" and exit non‑zero when found; integrate this check into the existing CI pipeline or npm prepublish script (e.g., validate-localization or prepublish step) so deploys are blocked if that placeholder is present in production-bound builds.frontend/lib/tests/shop/checkout-shipping-phase3.test.ts (1)
158-158: Consider extractingTEST_LEGAL_CONSENTto a shared test utilities module.The same constant is duplicated across
order-items-variants.test.ts,checkout-shipping-phase3.test.ts, andcheckout-legal-consent-phase4.test.ts. Extracting it to a shared test helper would reduce duplication and ensure consistency when version strings need updating.Also applies to: 225-225, 272-272, 315-315, 357-357, 438-438, 460-460, 495-495
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/lib/tests/shop/checkout-shipping-phase3.test.ts` at line 158, Multiple tests duplicate the TEST_LEGAL_CONSENT constant; extract it into a shared test utilities module and import it from tests to remove duplication. Create or reuse a test helper (e.g., test/utils or tests/helpers) that exports TEST_LEGAL_CONSENT, update references in files using the constant (tests mentioning TEST_LEGAL_CONSENT such as in order-items-variants.test, checkout-shipping-phase3.test, checkout-legal-consent-phase4.test) to import from that helper, and ensure any tests still pass by keeping the exported value identical to the current constant.frontend/messages/pl.json (2)
325-328: Use policy-style labels in the legal footer.
"Płatność","Dostawa"and"Zwroty"read like actions/categories, while these links open policy pages. Matching the destination titles will make the footer clearer and more consistent with"Polityka Prywatności"/"Warunki Świadczenia Usług".Suggested copy tweak
- "payment": "Płatność", - "delivery": "Dostawa", - "returns": "Zwroty", + "payment": "Polityka płatności", + "delivery": "Polityka dostawy", + "returns": "Polityka zwrotów",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/messages/pl.json` around lines 325 - 328, Replace the action-style values for the JSON keys "payment", "delivery", and "returns" with policy-style labels that match the destination pages (e.g., change "Płatność" → "Polityka płatności", "Dostawa" → "Polityka dostawy", "Zwroty" → "Polityka zwrotów") so the footer reads consistently with "Polityka Prywatności" and "Warunki Świadczenia Usług"; update the values for the keys "payment", "delivery", and "returns" in the messages JSON accordingly.
485-492: Prefer a neutral checkbox sentence here.
"Zapoznałem(-am) się..."reads awkwardly in a legal consent checkbox and forces a gendered form. A neutral construction will read cleaner for all customers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/messages/pl.json` around lines 485 - 492, The consent checkbox text under the "consent" object uses a gendered phrase in "prefix" ("Zapoznałem(-am) się..."); change it to a neutral construction and ensure the pieces still concatenate correctly with "termsLink", "and", "privacyLink", and "suffix". Update "consent.prefix" to a neutral sentence such as a short neutral verb form (e.g., "Potwierdzam zapoznanie się i akceptuję") and, if needed, make a matching neutral adjustment to "consent.required" so the validation message uses non-gendered language as well.frontend/messages/uk.json (2)
1743-1745: Replaceредирект/авторитетнимwith customer-facing wording.This sentence still sounds internal on a public policy page. Something like “повернення на сторінку магазину” and “остаточним підтвердженням” would read much more naturally.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/messages/uk.json` around lines 1743 - 1745, Update the "body" string under the "confirmation" object (keys "confirmation" -> "title"/"body") to use customer-facing phrasing: replace the word "редирект" with "повернення на сторінку магазину" and replace "авторитетним підтвердженням" with "остаточним підтвердженням" so the sentence reads naturally for customers.
1764-1766: Use one carrier naming convention across the Ukrainian shop copy.This block adds another
Nova Poshtavariant, while the same file also usesНова пошта/Нової поштиin Line 412 and Line 950. Picking one form here will keep the policy page consistent with the rest of the checkout flow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/messages/uk.json` around lines 1764 - 1766, The strings for keys "warehouse", "locker", and "courier" use the Latin "Nova Poshta" variant; update these three values to the Cyrillic naming used elsewhere ("Нова пошта" / appropriate inflection) so they match the existing entries (e.g., change "Самовивіз із відділення Nova Poshta" to the same wording pattern used in lines that currently use "Нова пошта"). Ensure the inflection matches the phrase (nominative/genitive) consistent with other entries in the file.
🤖 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/messages/en.json`:
- Around line 1746-1748: Update the inconsistent shipping statements in
frontend/messages/en.json by harmonizing the "body" text for the affected
message objects (the entries with "title": "What is charged online" and the
other similar entry around the 1776–1778 block) so they consistently state
whether the delivery cost shown in checkout is an estimated/indicative charge or
the actual amount charged, and clarify when shipping is paid (e.g., "delivery
cost shown is an estimate included in your checkout total" OR "delivery is paid
to the carrier on delivery; the checkout shows an estimated delivery cost for
reference"). Modify the "body" values to use one clear version across all
related keys and ensure any legal copy elsewhere (the entries around lines
~403–404) matches that same stance to remove the contradiction.
In `@frontend/messages/pl.json`:
- Around line 1713-1714: Update the Polish meta description string for the
seller page (the "metaDescription" entry in frontend/messages/pl.json) to
include the missing noun after "kontaktowe" so the snippet reads grammatically
correct; replace the fragment "Publiczne dane sprzedawcy i kontaktowe dla sklepu
DevLovers." with a version that explicitly names the contact info (for example,
"Publiczne dane sprzedawcy i dane kontaktowe sklepu DevLovers." or "Publiczne
dane sprzedawcy i informacje kontaktowe sklepu DevLovers.") so the browser
preview is not broken.
In `@frontend/messages/uk.json`:
- Around line 1747-1749: The "charges" translation entry contradicts earlier
policy text that states shipping is "Лише інформаційно" and paid to the carrier
on delivery; decide which billing model is correct and make the copy consistent:
either change the "charges" key (title/body) to state that shipping is
informational and paid on delivery, or update the earlier policy paragraphs (the
ones referenced at lines 403-404 and 1777-1779) to reflect that shipping is
added to the order total and may be charged online—ensure the chosen model is
used consistently across the "charges" entry and the other policy paragraphs so
the public legal copy mirrors the actual checkout behavior.
---
Nitpick comments:
In `@frontend/lib/tests/shop/checkout-shipping-phase3.test.ts`:
- Line 158: Multiple tests duplicate the TEST_LEGAL_CONSENT constant; extract it
into a shared test utilities module and import it from tests to remove
duplication. Create or reuse a test helper (e.g., test/utils or tests/helpers)
that exports TEST_LEGAL_CONSENT, update references in files using the constant
(tests mentioning TEST_LEGAL_CONSENT such as in order-items-variants.test,
checkout-shipping-phase3.test, checkout-legal-consent-phase4.test) to import
from that helper, and ensure any tests still pass by keeping the exported value
identical to the current constant.
In `@frontend/messages/en.json`:
- Around line 1718-1720: Add a release-time (CI/prepublish) validation that
fails the build if the seller placeholder exists: scan frontend/messages/en.json
for the placeholders.toBeAdded key or the literal value "To be added" and exit
non‑zero when found; integrate this check into the existing CI pipeline or npm
prepublish script (e.g., validate-localization or prepublish step) so deploys
are blocked if that placeholder is present in production-bound builds.
In `@frontend/messages/pl.json`:
- Around line 325-328: Replace the action-style values for the JSON keys
"payment", "delivery", and "returns" with policy-style labels that match the
destination pages (e.g., change "Płatność" → "Polityka płatności", "Dostawa" →
"Polityka dostawy", "Zwroty" → "Polityka zwrotów") so the footer reads
consistently with "Polityka Prywatności" and "Warunki Świadczenia Usług"; update
the values for the keys "payment", "delivery", and "returns" in the messages
JSON accordingly.
- Around line 485-492: The consent checkbox text under the "consent" object uses
a gendered phrase in "prefix" ("Zapoznałem(-am) się..."); change it to a neutral
construction and ensure the pieces still concatenate correctly with "termsLink",
"and", "privacyLink", and "suffix". Update "consent.prefix" to a neutral
sentence such as a short neutral verb form (e.g., "Potwierdzam zapoznanie się i
akceptuję") and, if needed, make a matching neutral adjustment to
"consent.required" so the validation message uses non-gendered language as well.
In `@frontend/messages/uk.json`:
- Around line 1743-1745: Update the "body" string under the "confirmation"
object (keys "confirmation" -> "title"/"body") to use customer-facing phrasing:
replace the word "редирект" with "повернення на сторінку магазину" and replace
"авторитетним підтвердженням" with "остаточним підтвердженням" so the sentence
reads naturally for customers.
- Around line 1764-1766: The strings for keys "warehouse", "locker", and
"courier" use the Latin "Nova Poshta" variant; update these three values to the
Cyrillic naming used elsewhere ("Нова пошта" / appropriate inflection) so they
match the existing entries (e.g., change "Самовивіз із відділення Nova Poshta"
to the same wording pattern used in lines that currently use "Нова пошта").
Ensure the inflection matches the phrase (nominative/genitive) consistent with
other entries in the file.
🪄 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: afa31371-415d-44d1-9385-4f8a8b659f03
📒 Files selected for processing (9)
frontend/components/tests/footer-shop-legal-links.test.tsxfrontend/lib/services/orders/checkout.tsfrontend/lib/tests/shop/checkout-legal-consent-phase4.test.tsfrontend/lib/tests/shop/checkout-shipping-phase3.test.tsfrontend/lib/tests/shop/legal-cookie-privacy-alignment-phase4.test.tsfrontend/lib/tests/shop/order-items-variants.test.tsfrontend/messages/en.jsonfrontend/messages/pl.jsonfrontend/messages/uk.json
✅ Files skipped from review due to trivial changes (1)
- frontend/lib/tests/shop/legal-cookie-privacy-alignment-phase4.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/components/tests/footer-shop-legal-links.test.tsx
Description
This PR completes the Phase 4 legal/compliance work for Shop across Workstream 8 and Workstream 9.
It adds public legal surfaces required for shop users, exposes them through shop-scoped footer links, and aligns the checkout consent flow with the privacy/cookie wording so consent is no longer recorded implicitly.
The goal of this PR is to close the remaining launch-blocking legal UX gaps without changing payment, shipping, or order lifecycle behavior outside the exact scope of these workstreams.
Note:
Related Issue
Issue: #<issue_number>
Changes
checkout_implicitconsent without explicit user action.data-testidselectors to footer legal links for stable test automation.Database Changes (if applicable)
How Has This Been Tested?
Additional notes:
Screenshots (if applicable)
Checklist
Before submitting
Reviewers
Summary by CodeRabbit
New Features
Chores