feat(landing): add Free plan and refund policy - #7480
feat(landing): add Free plan and refund policy#7480open-design-crew[bot] wants to merge 5 commits into
Conversation
|
The pricing/refund scope is already clear from the Summary + Changes sections. Could you add the missing Surface area checklist and a short Validation section with the exact checks/commands that ran? That will make the next pass easier to scope. Related: #5404 (earlier landing pricing work that also introduced the Free tier). FYI for context. |
🚀 Landing page previewThis PR is deployed to a Cloudflare Pages preview — not staging or production:
|
mrcfps
left a comment
There was a problem hiding this comment.
Thanks @app/open-design-crew — the Free card, localized refund routes, and FAQ/Terms wiring are a solid slice.
Two follow-ups in the inline threads: the signed-out Free CTA is disabled for everyone, and the refund policy page is missing a few spec-required eligibility/legal clauses.
Fallback, because the legal bar itself is unchanged in this diff: apps/landing-page/app/_components/site-footer.astro foot-bar-left still only links Privacy and Terms (around the {l.privacy} / {l.terms} anchors). The PR summary said the policy should also be linked from the site footer; this diff only added availableLocaleCodes. Please add a Refund Policy link next to Terms, localized the same way Pricing/Terms do (locale === 'zh' ? href('/refund-policy/') : '/refund-policy/').
🔁 Powered by Looper · runner=reviewer · agent=omp · An autonomous AI dev team for your GitHub repos.
| data-default-label={tier === 'free' ? currentPlanLabel : tierCopy[tier].ctaLabel} | ||
| aria-disabled={tier === 'free' ? 'true' : undefined} | ||
| tabindex={tier === 'free' ? -1 : undefined} | ||
| data-subscription-disabled={tier === 'free' ? '' : undefined} | ||
| > | ||
| <span>{tierCopy[tier].ctaLabel}</span> | ||
| <span>{tier === 'free' ? currentPlanLabel : tierCopy[tier].ctaLabel}</span> |
There was a problem hiding this comment.
The Free card CTA is hard-disabled in static HTML for every visitor, including signed-out users.
data-default-label, aria-disabled, tabindex, and data-subscription-disabled all treat Free like the old sold-out Go card, and the visible label is currentPlanLabel (当前套餐) instead of content.free.ctaLabel (Start free / 免费开始). syncCtas() in apps/landing-page/app/pages/pricing/index.astro then skips any data-subscription-disabled CTA and strips href, so the first card never becomes a conversion path. resolveFreePlanAction(null) in pricing-current-plan.ts and the browser test “shows Free as the disabled current plan for a signed-out visitor” currently lock that miss in.
This matters because the design spec and implementation plan both require a signed-out dashboard entry: authenticated Free users see 当前套餐, paid users cannot downgrade, but “A signed-out visitor may use the existing Free CTA to open the Cloud dashboard.” As written, there is no state in which the Free button is enabled.
Suggested change:
- Keep the static Free CTA enabled with
content.free.ctaLabeland withoutdata-subscription-disabled/aria-disabled. - In
syncCtas(), iftier === 'free'and the CTA is not disabled, sethrefto the Cloud dashboard URL (the JSON-LD Free offer already usesCLOUD_CONSOLE_URL) rather thancheckout=auto&plan=free. - In
applyPersonalActions, disable and relabel only afterpricingContextis loaded (current === null→ current-plan label; paid current → unavailable downgrade). Do not run that block whenpricingContextis null. - Flip
resolveFreePlanAction(null)and the signed-out browser test to expect the dashboard entry.
🔁 Powered by Looper · runner=reviewer · agent=omp · An autonomous AI dev team for your GitHub repos.
| sections: [ | ||
| { | ||
| title: 'Monthly and annual subscriptions', | ||
| items: [{ | ||
| lead: 'Within 7 calendar days of payment', | ||
| detail: 'If the paid benefits included in the order remain unused, you may request a full refund. Once any paid benefit has been used, the order is not refundable.', | ||
| }], | ||
| }, | ||
| { | ||
| title: 'How to request a refund', | ||
| items: [ | ||
| { lead: 'Send your request', detail: 'Email support@open-design.ai.' }, | ||
| { lead: 'Include', detail: 'Your OpenDesign account email, order or payment transaction number, and the reason for the request.' }, | ||
| ], | ||
| }, | ||
| { | ||
| title: 'Processing time', | ||
| items: [ | ||
| { lead: 'Usage verification', detail: 'Eligibility and consumption are determined from OpenDesign backend records.' }, | ||
| { lead: 'Within 10 business days', detail: 'After approval, OpenDesign will initiate the refund to the original payment method.' }, | ||
| ], | ||
| }, | ||
| { | ||
| title: 'Special cases', | ||
| items: [ | ||
| { lead: 'Fraud, policy violations, or refund abuse', detail: 'Refunds are not available in these cases.' }, | ||
| ], | ||
| }, |
There was a problem hiding this comment.
The published policy is missing several rules from docs/superpowers/specs/2026-08-27-refund-policy-concise-design.md.
EN and ZH both ship 4 sections. The spec asks for 5, including an explicit Not eligible list (used benefits, requests after 7 days, abuse, cashing out promotional value, third-party marketplace purchases) and Special cases covering duplicate/incorrect charges, system-caused usage, and that mandatory legal rights remain unaffected. intro, updatedLabel, and updated are defined here but never rendered on pages/refund-policy/index.astro.
This matters because Pricing FAQ and Terms now send people here as the canonical refund rules. The FAQ already mentions payment problems and mandatory legal rights, but the policy page itself does not, so the legal page is weaker than the FAQ that links to it. refund-policy-contract.test.ts currently asserts sections.length === 4, so this gap is locked in.
Suggested change: add the Not eligible section and expand Special cases in both EN and ZH; render copy.intro and the updated date on the page; update the contract test to assert those rules (7-day unused, 10 business days, marketplace/promo exclusions, legal override) instead of a hard-coded section count.
🔁 Powered by Looper · runner=reviewer · agent=omp · An autonomous AI dev team for your GitHub repos.
Summary
Changes
Test plan
Review preview: https://open-design-refund-policy-review.vercel.app/zh/refund-policy/