fix: restore original URL on Stripe cancel and add return_to on success#158
fix: restore original URL on Stripe cancel and add return_to on success#158
Conversation
- Accept optional returnUrl in CheckoutInput (GraphQL schema + Zod validation) - Pass returnUrl from frontend (window.location.href) via useStripeCheckout hook - Use returnUrl as cancel_url in Stripe session (fallback to /payment/cancel) - Encode returnUrl as return_to query param on success_url so the success page can render a 'Return to page' button - Update PaymentSuccessPage to read return_to param and conditionally render the return button - Clarify FRONTEND_URL in .env.example must be a base URL with no path suffix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WalkthroughAdds an optional return URL through the Stripe checkout flow (frontend hook/types → GraphQL input → backend service → Stripe session), validates same-origin return URLs, and surfaces a conditional "Return to page" link on the payment success page. Also updates resume documentation and an env example. Changes
Sequence DiagramsequenceDiagram
participant User as User
participant FE as Frontend\n(useStripeCheckout)
participant GQL as GraphQL\n(Resolver)
participant SVC as Backend\n(stripe service)
participant STR as Stripe API
participant Success as Success Page
User->>FE: Click "Checkout"
FE->>FE: Capture window.location.href as returnUrl
FE->>GQL: CREATE_CHECKOUT_SESSION(productKey,email,returnUrl)
GQL->>GQL: Validate input (including returnUrl origin)
GQL->>SVC: createCheckoutSession(..., returnUrl)
SVC->>SVC: isSameOrigin(returnUrl)? → compute success_url & cancel_url
SVC->>STR: stripe.checkout.sessions.create(success_url, cancel_url, ...)
STR-->>SVC: sessionId & checkout URL
SVC-->>GQL: session info
GQL-->>FE: session info
FE->>STR: Redirect user to Stripe checkout
User->>STR: Complete payment
STR->>Success: Redirect to success_url (includes return_to if provided)
Success->>Success: Read/sanitize return_to
Success->>User: Show "Return to page" button (if valid)
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly Related PRs
Poem
🚥 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 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.
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)
_docs/studying/refs/resume.md (1)
2-95:⚠️ Potential issue | 🟠 MajorSplit this resume rewrite out of the Stripe PR.
This whole document change is off-mission for a PR that’s supposed to fix Stripe return URLs. Bundling unrelated resume edits into a payments change makes review, rollback, and cherry-picks unnecessarily messy. Keep the checkout surgery clean; don’t bench-press docs drift in the same set.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@_docs/studying/refs/resume.md` around lines 2 - 95, The PR includes unrelated resume edits (resume.md) alongside the Stripe return-URL fix; remove the resume changes from this checkout-focused PR by reverting or resetting modifications to resume.md (e.g., undo the commit or checkout the file from the base branch) so the branch only contains the Stripe/checkout changes, then create a new branch (e.g., docs/resume-update) with the resume edits and open a separate PR titled "docs: update resume" for that work; target symbols: resume.md (the markdown file containing the SUMMARY, TECHNICAL SKILLS, PROFESSIONAL EXPERIENCE, PROJECTS, EDUCATION, SOFT SKILLS sections), current Stripe/checkout branch (your checkout/stripe-return-url fix branch), and the new docs branch (docs/resume-update).
🧹 Nitpick comments (1)
backend/.env.example (1)
37-38: Good docs, but comments don't stop bugs—code does.This comment is solid documentation, and making the value explicit instead of a placeholder is a step up. But here's the problem: you're asking developers to READ and FOLLOW instructions perfectly. That's like asking Goggins to skip a workout—theoretically possible, but why rely on it?
Right now, if someone fat-fingers a trailing slash into their
.envfile (sayFRONTEND_URL=http://localhost:3000/), your code instripe.tswill happily concatenate and produce malformed URLs likehttp://localhost:3000//payment/cancel. That's a production bug waiting to happen becauseconfig.tsreadsFRONTEND_URLas-is with zero defensive handling.Elite-level code doesn't hope developers read comments—it enforces requirements.
💪 Proposed defensive fix in config.ts
Add trimming logic where
frontendUrlis assigned inbackend/src/config/config.ts:- frontendUrl: process.env.FRONTEND_URL || 'http://localhost:3000', + frontendUrl: (process.env.FRONTEND_URL || 'http://localhost:3000').replace(/\/+$/, ''),This strips trailing slashes automatically, making your code bulletproof regardless of what someone throws in their
.envfile. No more hope and pray—just guaranteed correctness.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/.env.example` around lines 37 - 38, The FRONTEND_URL env value is used raw and can contain a trailing slash, causing double-slash URLs (e.g., in stripe.ts); update the config loading so the FRONTEND_URL (or exported frontendUrl) is normalized by trimming whitespace and removing any trailing slashes when assigning it in config.ts (e.g., sanitize process.env.FRONTEND_URL into frontendUrl) so all downstream concatenation produces correct paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@_docs/studying/refs/resume.md`:
- Line 56: Update the user-facing project labels to use the correct product name
"GitHub" instead of the lowercase "github"; specifically change occurrences like
"## luisfaria.dev Portfolio (github / website)" and the other instances noted
(the similar labels at the other occurrences) so they read "GitHub" (e.g., "##
luisfaria.dev Portfolio (GitHub / website)").
In `@backend/src/services/stripe.ts`:
- Around line 112-115: The cancelUrl/successUrl construction uses unvalidated
returnUrl allowing open-redirects; update the code around cancelUrl and
successUrl to defensively validate returnUrl before using it: attempt to parse
returnUrl with the URL constructor, ensure its origin matches config.frontendUrl
origin (or only allow relative paths), and if validation fails fall back to the
existing `${config.frontendUrl}/payment/...` defaults; apply the same
validatedReturnUrl (or encoded relative path) when building successUrl so both
`cancelUrl` and `successUrl` never directly redirect to arbitrary external
hosts.
In `@backend/src/validation/schemas/checkout.schema.ts`:
- Line 13: The returnUrl schema currently uses z.string().url() which allows
external origins; update the returnUrl validator to ensure the URL's origin
matches your frontend origin (config.frontendUrl) by parsing the value (new
URL(value).origin) and refining/transforming it — e.g., replace returnUrl:
z.string().url().optional() with a .refine(...) (or a z.preprocess + refine)
that checks new URL(value).origin === new URL(config.frontendUrl).origin and
fails validation otherwise; reference the returnUrl field and config.frontendUrl
when implementing the check.
In `@frontend/src/app/payment/success/page.tsx`:
- Around line 88-92: The rendered Link using the unvalidated returnTo value
(from return_to query) can cause an open-redirect; validate and normalize
returnTo before rendering. In page.tsx ensure the code that sets/uses returnTo
only allows relative paths or absolute URLs that match your app origin: parse
the incoming return_to, reject or replace any value with a safe fallback if it
has a different hostname or an unsafe scheme (e.g., not http/https), and prefer
pathname-only values; then pass the sanitized variable to the Link component
(where returnTo is currently used) so only safe internal redirects are rendered.
---
Outside diff comments:
In `@_docs/studying/refs/resume.md`:
- Around line 2-95: The PR includes unrelated resume edits (resume.md) alongside
the Stripe return-URL fix; remove the resume changes from this checkout-focused
PR by reverting or resetting modifications to resume.md (e.g., undo the commit
or checkout the file from the base branch) so the branch only contains the
Stripe/checkout changes, then create a new branch (e.g., docs/resume-update)
with the resume edits and open a separate PR titled "docs: update resume" for
that work; target symbols: resume.md (the markdown file containing the SUMMARY,
TECHNICAL SKILLS, PROFESSIONAL EXPERIENCE, PROJECTS, EDUCATION, SOFT SKILLS
sections), current Stripe/checkout branch (your checkout/stripe-return-url fix
branch), and the new docs branch (docs/resume-update).
---
Nitpick comments:
In `@backend/.env.example`:
- Around line 37-38: The FRONTEND_URL env value is used raw and can contain a
trailing slash, causing double-slash URLs (e.g., in stripe.ts); update the
config loading so the FRONTEND_URL (or exported frontendUrl) is normalized by
trimming whitespace and removing any trailing slashes when assigning it in
config.ts (e.g., sanitize process.env.FRONTEND_URL into frontendUrl) so all
downstream concatenation produces correct paths.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 733c5243-40b3-4180-ab2e-92b2cf6b2015
⛔ Files ignored due to path filters (1)
_docs/.DS_Storeis excluded by!**/.DS_Store
📒 Files selected for processing (9)
_docs/studying/refs/resume.mdbackend/.env.examplebackend/src/resolvers/stripe/mutations.tsbackend/src/schemas/types/stripeTypes.tsbackend/src/services/stripe.tsbackend/src/validation/schemas/checkout.schema.tsfrontend/src/app/payment/success/page.tsxfrontend/src/lib/graphql/types/stripe.types.tsfrontend/src/lib/hooks/useStripeCheckout.ts
- checkout.schema.ts: add origin refine to returnUrl Zod validator so only URLs matching config.frontendUrl origin are accepted - stripe.ts: add isSameOrigin() defense-in-depth check before using returnUrl as cancel_url/success_url; falls back to /payment/* defaults - payment/success/page.tsx: add sanitizeReturnUrl() to validate return_to query param against FRONTEND_URL origin before rendering as link - resume.md: fix github → GitHub capitalization in project labels Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
backend/src/services/stripe.ts (1)
81-90: Extract the origin rule before future-you forks reality.
isSameOrigin()here duplicatesisAllowedReturnUrlOrigin()inbackend/src/validation/schemas/checkout.schema.ts. Two copies of the same security rule is how drift sneaks in and punches you later. A tiny shared helper would keep the schema check and the service fallback locked to one policy.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/src/services/stripe.ts` around lines 81 - 90, Replace the duplicated origin-check logic by extracting a single shared helper (e.g., export a new function like isAllowedReturnUrlOrigin or isSameOriginHelper) and use it from both places; remove the duplicate implementation in the Stripe service (function isSameOrigin) and import/call the shared helper instead, and update the schema code (which currently defines isAllowedReturnUrlOrigin) to import the same helper so both the checkout schema and the Stripe service use the identical origin-check function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@_docs/studying/refs/resume.md`:
- Around line 73-74: Update the blurb string in the resume entry (the line
starting "7+ projects implemented and mapped on this Open Source repo...") to
use the correct, tightened stack casing: replace "Open Source" with
"open-source", "Typescript" with "TypeScript", and "Javascript" with
"JavaScript" so the line reads with consistent, professional terminology.
- Around line 58-60: The phrase "httpOnly JWT auth" is inaccurate and has wrong
casing; replace it with the canonical, clearer wording "JWT auth via HttpOnly
cookies" (use "HttpOnly" casing) in the sentence describing the resume project
so the authentication method is precise and professionally phrased.
---
Nitpick comments:
In `@backend/src/services/stripe.ts`:
- Around line 81-90: Replace the duplicated origin-check logic by extracting a
single shared helper (e.g., export a new function like isAllowedReturnUrlOrigin
or isSameOriginHelper) and use it from both places; remove the duplicate
implementation in the Stripe service (function isSameOrigin) and import/call the
shared helper instead, and update the schema code (which currently defines
isAllowedReturnUrlOrigin) to import the same helper so both the checkout schema
and the Stripe service use the identical origin-check function.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 13a871ff-9627-4535-9513-b99a1b99fefe
📒 Files selected for processing (4)
_docs/studying/refs/resume.mdbackend/src/services/stripe.tsbackend/src/validation/schemas/checkout.schema.tsfrontend/src/app/payment/success/page.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/app/payment/success/page.tsx
| Full-stack portfolio with AI chatbot, NASA APOD integration, Stripe payments, httpOnly JWT auth, | ||
| and a CI/CD pipeline (GitHub Actions → GHCR → DigitalOcean). Includes Sentry observability, | ||
| Redis rate limiting (Lua), SSR SEO with JSON-LD structured data, and 192+ automated tests. |
There was a problem hiding this comment.
Tighten the auth phrasing here.
httpOnly JWT auth is doing burpees without form. HttpOnly should keep its canonical casing, and the current wording is a bit technically fuzzy. JWT auth via HttpOnly cookies reads sharper and is more accurate on a resume.
✏️ Tiny cleanup
-Full-stack portfolio with AI chatbot, NASA APOD integration, Stripe payments, httpOnly JWT auth,
+Full-stack portfolio with AI chatbot, NASA APOD integration, Stripe payments, JWT auth via HttpOnly cookies,
and a CI/CD pipeline (GitHub Actions → GHCR → DigitalOcean). Includes Sentry observability,
Redis rate limiting (Lua), SSR SEO with JSON-LD structured data, and 192+ automated tests.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@_docs/studying/refs/resume.md` around lines 58 - 60, The phrase "httpOnly JWT
auth" is inaccurate and has wrong casing; replace it with the canonical, clearer
wording "JWT auth via HttpOnly cookies" (use "HttpOnly" casing) in the sentence
describing the resume project so the authentication method is precise and
professionally phrased.
| ## Masters’ Degree in SWE & AI (GitHub) | ||
| 7+ projects implemented and mapped on this Open Source repo, documenting my Master's degree SWE/AI. Notes, projects, insights & experiments in AI/ML, software dev & beyond. Apps in Python, C#, Typescript, Javascript. Term 2 of 8 to go. |
There was a problem hiding this comment.
Polish the stack names in this blurb.
This line is close, but Open Source, Typescript, and Javascript make the resume look less dialed-in than it is. Small copy, real signal. Tighten it to open-source, TypeScript, and JavaScript.
🧹 Suggested wording fix
-7+ projects implemented and mapped on this Open Source repo, documenting my Master's degree SWE/AI. Notes, projects, insights & experiments in AI/ML, software dev & beyond. Apps in Python, C#, Typescript, Javascript. Term 2 of 8 to go.
+7+ projects implemented and mapped in this open-source repo, documenting my Master's degree SWE/AI. Notes, projects, insights & experiments in AI/ML, software dev & beyond. Apps in Python, C#, TypeScript, and JavaScript. Term 2 of 8 to go.🧰 Tools
🪛 LanguageTool
[uncategorized] ~74-~74: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...projects implemented and mapped on this Open Source repo, documenting my Master's degree SW...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@_docs/studying/refs/resume.md` around lines 73 - 74, Update the blurb string
in the resume entry (the line starting "7+ projects implemented and mapped on
this Open Source repo...") to use the correct, tightened stack casing: replace
"Open Source" with "open-source", "Typescript" with "TypeScript", and
"Javascript" with "JavaScript" so the line reads with consistent, professional
terminology.
Summary by CodeRabbit
Documentation
New Features
Chores