{"record":{"id":"1a6052445d1d6d39","repo":"Significant-Gravitas/AutoGPT","slug":"success-url-and-cancel-url-must-match-the-platform","errorCode":null,"errorMessage":"success_url and cancel_url must match the platform frontend origin","messagePattern":"success_url and cancel_url must match the platform frontend origin","errorType":"http","errorClass":"HTTPException","httpStatus":422,"severity":"error","filePath":"autogpt_platform/backend/backend/api/features/v1.py","lineNumber":1325,"sourceCode":"    # actionable error instead of the misleading \"must match the platform\n    # frontend origin\" 422 that _validate_checkout_redirect_url would otherwise\n    # produce when `allowed` is empty.\n    if not (settings.config.frontend_base_url or settings.config.platform_base_url):\n        logger.error(\n            \"update_subscription_tier: neither frontend_base_url nor \"\n            \"platform_base_url is configured; cannot validate checkout redirect URLs\"\n        )\n        raise HTTPException(\n            status_code=503,\n            detail=(\n                \"Payment redirect URLs cannot be validated: \"\n                \"frontend_base_url or platform_base_url must be set on the server.\"\n            ),\n        )\n    if not _validate_checkout_redirect_url(\n        request.success_url\n    ) or not _validate_checkout_redirect_url(request.cancel_url):\n        raise HTTPException(\n            status_code=422,\n            detail=\"success_url and cancel_url must match the platform frontend origin\",\n        )\n    try:\n        url = await create_subscription_checkout(\n            user_id=user_id,\n            tier=tier,\n            success_url=request.success_url,\n            cancel_url=request.cancel_url,\n            billing_cycle=request.billing_cycle,\n            datafast_visitor_id=x_datafast_visitor_id,\n            datafast_session_id=x_datafast_session_id,\n        )\n    except ValueError as e:\n        raise HTTPException(status_code=422, detail=str(e))\n    except stripe.StripeError as e:\n        logger.exception(\n            \"Stripe error creating checkout session for user %s: %s\", user_id, e","sourceCodeStart":1307,"sourceCodeEnd":1343,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/v1.py#L1307-L1343","documentation":"Raised (422) by update_subscription_tier when _validate_checkout_redirect_url rejects request.success_url or request.cancel_url. The validator only accepts URLs whose origin matches the configured frontend/platform base origin, which prevents an attacker from supplying a success_url pointing at a phishing page that mimics a completed payment.","triggerScenarios":"Calling the subscription checkout endpoint with a success_url or cancel_url whose scheme/host differs from the configured frontend_base_url/platform_base_url origin — e.g. success_url=https://evil.example/done, or a correct host on http:// while the config origin is https://, or a URL with a typo'd subdomain.","commonSituations":"Frontend deployed on a new domain while the backend still has the old base URL configured; local frontend on http://localhost:3000 but backend configured with the production origin (or vice versa); clients constructing redirect URLs from window.location while behind a proxy that rewrites the host.","solutions":["Send success_url and cancel_url whose scheme+host exactly match the server-configured frontend_base_url (only path/query may differ).","If the mismatch is legitimate (new domain, env change), update frontend_base_url/platform_base_url on the backend to match where the frontend actually runs.","Check for scheme mismatch (http vs https) and stray ports — origin comparison includes both."],"exampleFix":"// before\nconst res = await api.upgradeTier({\n  success_url: `https://old-domain.example/success`,\n  cancel_url: `https://old-domain.example/cancel`,\n});\n\n// after — build from the same origin the app is served from\nconst res = await api.upgradeTier({\n  success_url: `${window.location.origin}/success`,\n  cancel_url: `${window.location.origin}/cancel`,\n});","handlingStrategy":"validation","validationCode":"function validRedirect(url: string, base: string): boolean {\n  try { return new URL(url).origin === new URL(base).origin; }\n  catch { return false; }\n}\nconst base = appConfig.frontendBaseUrl;\nif (!validRedirect(successUrl, base) || !validRedirect(cancelUrl, base)) {\n  throw new Error('Redirect URLs must share the platform origin');\n}","typeGuard":"const isSameOrigin = (u: string, base: string) => {\n  try { return new URL(u).origin === new URL(base).origin; } catch { return false; }\n};","tryCatchPattern":"catch (e) { if (e.response?.status === 422 && /frontend origin/.test(e.response.data.detail)) { rebuildRedirectsFromLocationOrigin(); } else throw e; }","preventionTips":["Always construct success/cancel URLs from window.location.origin (or the server-advertised base URL), never from hardcoded domains.","Keep the frontend's configured origin and the backend's frontend_base_url in sync via shared config.","Remember origin comparison is exact: scheme, host, and port must all match."],"tags":["payments","stripe","redirect","security","http-422","validation"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}