Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 23 additions & 33 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
<!-- We require pull request titles to follow the Conventional Commits specification ( https://www.conventionalcommits.org/en/v1.0.0/#summary ). Please make sure your title follow these conventions -->
<!-- PR title must follow Conventional Commits: https://www.conventionalcommits.org/en/v1.0.0/#summary -->

## What does this PR do?
<!-- AI agents: no promotional footers, don't advertise yourself or any tool/service (e.g. "Generated by …"). -->

<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
## What & why

Fixes #(issue)
<!-- What changed and why — problem first, then solution. Ground it in the diff, not commit titles;
keep it short enough to grasp without opening files. -->

<!-- Please provide a screenshots or a loom video for visual changes to speed up reviews
Loom Video: https://www.loom.com/
-->
## Linear ticket

<!-- Paste the full Linear ticket URL (e.g. https://linear.app/formbricks/issue/ENG-1234/…) so it
auto-links. No ticket? Say why. Closing a GitHub issue? Add "Fixes #123". -->

## How this was tested

<!-- REQUIRED. What you actually ran to verify this, with real results: automated tests + outcome
(e.g. `pnpm test` ✅), tests added/updated, manual checks and what you saw. Couldn't test it? Say why. -->

-

<!-- REQUIRED for any UI change: attach before/after screenshots or a recording. No visual proof → sent back. -->

## QA / Test Plan

<!--
This section is the source of truth for release QA — QA checklists are generated from it. Describe the
PR's ACTUAL behavior, not the commit titles. Keep steps click-by-click and grounded in real behavior.
Omit a subsection only when it genuinely does not apply. Flag any inferred specifics (thresholds, rate
limits, timeouts) for human verification.
Source of truth for release QA. Audience: a manual tester with no repo/CLI access, using only the
running app (UI) or live API (HTTP). Write each step as an observable action → expected result. No
automated verification here (`pnpm test`, `grep`, `pnpm build`, code references) — that belongs in
"How this was tested". Describe the ACTUAL behavior click-by-click; omit a subsection only when it
truly doesn't apply. Flag inferred specifics (thresholds, limits, timeouts) for human verification.
-->

**How to test** <!-- happy path first, then edge cases (boundaries, negative input, permissions) -->
Expand All @@ -35,25 +47,3 @@ limits, timeouts) for human verification.
**Migrations / env / cutover** <!-- new/changed env vars (diff .env.example), DB migrations, cutover steps; write "none" if none -->

- none

## Checklist

<!-- We're starting to get more and more contributions. Please help us making this efficient for all of us and go through this checklist. Please tick off what you did -->

### Required

- [ ] Filled out the "QA / Test Plan" section in this PR (behavior, edge cases, preconditions, risks, migrations/env)
- [ ] Read [How we Code at Formbricks](<[https://github.com/formbricks/formbricks/blob/main/CONTRIBUTING.md](https://formbricks.com/docs/contributing/how-we-code)>)
- [ ] Self-reviewed my own code
- [ ] Commented on my code in hard-to-understand bits
- [ ] Ran `pnpm build`
- [ ] Checked for warnings, there are none
- [ ] Removed all `console.logs`
- [ ] Merged the latest changes from main onto my branch with `git pull origin main`
- [ ] My changes don't cause any responsiveness issues
- [ ] First PR at Formbricks? [Please sign the CLA!](https://cla-assistant.io/formbricks/formbricks) Without it we wont be able to merge it 🙏

### Appreciated

- [ ] If a UI change was made: Added a screen recording or screenshots to this PR
- [ ] Updated the Formbricks Docs if changes were necessary
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Heuristic:

Commits follow a lightweight Conventional Commit format (`fix:`, `chore:`, `feat:`) and usually append the PR number, e.g. `fix: update OpenAPI schema (#6617)`. Keep commits scoped and lint-clean. Pull requests should outline the problem, summarize the solution, and link to issues or product specs. Attach screenshots or gifs for UI-facing work, list any migrations or env changes, and paste the output of relevant commands (`pnpm test`, `pnpm lint`, `pnpm db:migrate:dev`) so reviewers can verify readiness.

Every PR must use `.github/pull_request_template.md` — including its `## QA / Test Plan` section, which is the source of truth for release QA (follow the guidance inline in the template; keep its subsection headings). Agents fill it on PR open and re-update it in the same turn on every PR change (new commits, scope or review fixes) so it never drifts from the diff — treat a stale QA section as a bug.
Every PR must use `.github/pull_request_template.md` and follow its inline guidance — the template is the source of truth for PR structure, including the `## QA / Test Plan` section used to generate release QA. Fill every section from the actual diff on PR open, and re-update it in the same turn on every change (new commits, scope or review fixes) so it never drifts — treat a stale section as a bug.

## Next.js Documentation

Expand Down
1 change: 1 addition & 0 deletions apps/web/i18n.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2374,6 +2374,7 @@ checksums:
workspace/settings/billing/payment_error_contact_support: 3145080022ff17d06e41afe78363a6cc
workspace/settings/billing/payment_error_failed_invoice: 5344b45e2acaf0287b69b043d37a964d
workspace/settings/billing/payment_error_requires_action: 934ac70b26f53819314add46305b6d87
workspace/settings/billing/payment_method_required: 34c8095c7fe1f9c64aadf2206abf9c80
workspace/settings/billing/pending_change_removed: c80cc7f1f83f28db186e897fb18282a3
workspace/settings/billing/pending_plan_badge: 1283929a2810dcf6110765f387dc118e
workspace/settings/billing/pending_plan_change_description: 6923bada769d33cadcad557521362c1f
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Bitte kontaktiere <supportLink>hola@formbricks.com</supportLink> für Unterstützung.",
"payment_error_failed_invoice": "Die Zahlung konnte nicht abgeschlossen werden.",
"payment_error_requires_action": "Die Zahlung erfordert eine zusätzliche Authentifizierung.",
"payment_method_required": "Füge eine Zahlungsmethode hinzu, um zu einem kostenpflichtigen Tarif zu wechseln.",
"pending_change_removed": "Geplante Planänderung entfernt.",
"pending_plan_badge": "Geplant",
"pending_plan_change_description": "Dein Plan wechselt am {date} zu {plan}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Please contact <supportLink>hola@formbricks.com</supportLink> for support.",
"payment_error_failed_invoice": "Payment could not be completed.",
"payment_error_requires_action": "Payment requires additional authentication.",
"payment_method_required": "Add a payment method to switch to a paid plan.",
"pending_change_removed": "Scheduled plan change removed.",
"pending_plan_badge": "Scheduled",
"pending_plan_change_description": "Your plan will switch to {plan} on {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Por favor, contacta con <supportLink>hola@formbricks.com</supportLink> para recibir asistencia.",
"payment_error_failed_invoice": "No se pudo completar el pago.",
"payment_error_requires_action": "El pago requiere autenticación adicional.",
"payment_method_required": "Añade un método de pago para cambiar a un plan de pago.",
"pending_change_removed": "Cambio de plan programado eliminado.",
"pending_plan_badge": "Programado",
"pending_plan_change_description": "Tu plan cambiará a {plan} el {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Merci de contacter <supportLink>hola@formbricks.com</supportLink> pour obtenir de l'aide.",
"payment_error_failed_invoice": "Le paiement n'a pas pu être effectué.",
"payment_error_requires_action": "Le paiement nécessite une authentification supplémentaire.",
"payment_method_required": "Ajoute un moyen de paiement pour passer à un forfait payant.",
"pending_change_removed": "Changement de formule programmé supprimé.",
"pending_plan_badge": "Programmé",
"pending_plan_change_description": "Votre forfait passera à {plan} le {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/hu-HU.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Kérem, vegye fel a kapcsolatot a következő címmel támogatásért: <supportLink>hola@formbricks.com</supportLink>.",
"payment_error_failed_invoice": "A fizetés nem volt teljesíthető.",
"payment_error_requires_action": "A fizetés további hitelesítést igényel.",
"payment_method_required": "Adjon hozzá egy fizetési módot a fizetős csomagra való váltáshoz.",
"pending_change_removed": "Az ütemezett csomagváltoztatás eltávolítva.",
"pending_plan_badge": "Ütemezett",
"pending_plan_change_description": "A csomagja {plan} csomagra fog váltani ekkor: {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "サポートについては<supportLink>hola@formbricks.com</supportLink>までお問い合わせください。",
"payment_error_failed_invoice": "支払いを完了できませんでした。",
"payment_error_requires_action": "支払いには追加の認証が必要です。",
"payment_method_required": "有料プランに切り替えるには、お支払い方法を追加してください。",
"pending_change_removed": "予定されていたプラン変更を取り消しました。",
"pending_plan_badge": "変更予定",
"pending_plan_change_description": "プランは {date} に {plan} に切り替わります。",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/nl-NL.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Neem contact op met <supportLink>hola@formbricks.com</supportLink> voor ondersteuning.",
"payment_error_failed_invoice": "De betaling kon niet worden voltooid.",
"payment_error_requires_action": "De betaling vereist aanvullende verificatie.",
"payment_method_required": "Voeg een betaalmethode toe om over te schakelen naar een betaald abonnement.",
"pending_change_removed": "Geplande abonnementswijziging verwijderd.",
"pending_plan_badge": "Gepland",
"pending_plan_change_description": "Je abonnement wordt op {date} omgezet naar {plan}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Entre em contato com <supportLink>hola@formbricks.com</supportLink> para obter suporte.",
"payment_error_failed_invoice": "Não foi possível concluir o pagamento.",
"payment_error_requires_action": "O pagamento requer autenticação adicional.",
"payment_method_required": "Adicione um método de pagamento para mudar para um plano pago.",
"pending_change_removed": "Mudança de plano agendada removida.",
"pending_plan_badge": "Agendado",
"pending_plan_change_description": "Seu plano mudará para {plan} em {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/pt-PT.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Por favor, contacta <supportLink>hola@formbricks.com</supportLink> para obter suporte.",
"payment_error_failed_invoice": "O pagamento não pôde ser concluído.",
"payment_error_requires_action": "O pagamento requer autenticação adicional.",
"payment_method_required": "Adiciona um método de pagamento para mudares para um plano pago.",
"pending_change_removed": "Alteração de plano agendada removida.",
"pending_plan_badge": "Agendado",
"pending_plan_change_description": "O teu plano mudará para {plan} em {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/ro-RO.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Te rugăm să contactezi <supportLink>hola@formbricks.com</supportLink> pentru asistență.",
"payment_error_failed_invoice": "Plata nu a putut fi finalizată.",
"payment_error_requires_action": "Plata necesită autentificare suplimentară.",
"payment_method_required": "Adaugă o metodă de plată pentru a trece la un plan plătit.",
"pending_change_removed": "Schimbarea de plan programată a fost anulată.",
"pending_plan_badge": "Programat",
"pending_plan_change_description": "Abonamentul tău va trece la {plan} pe {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/ru-RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Пожалуйста, свяжитесь с нами <supportLink>hola@formbricks.com</supportLink> для получения помощи.",
"payment_error_failed_invoice": "Не удалось завершить платёж.",
"payment_error_requires_action": "Платёж требует дополнительной аутентификации.",
"payment_method_required": "Добавь способ оплаты, чтобы перейти на платный тариф.",
"pending_change_removed": "Запланированное изменение тарифа отменено.",
"pending_plan_badge": "Запланирован",
"pending_plan_change_description": "Твой тариф сменится на {plan} на {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/sv-SE.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Vänligen kontakta <supportLink>hola@formbricks.com</supportLink> för support.",
"payment_error_failed_invoice": "Betalningen kunde inte genomföras.",
"payment_error_requires_action": "Betalningen kräver ytterligare autentisering.",
"payment_method_required": "Lägg till en betalningsmetod för att byta till en betald plan.",
"pending_change_removed": "Schemalagd abonnemangsändring har tagits bort.",
"pending_plan_badge": "Schemalagd",
"pending_plan_change_description": "Din plan kommer att byta till {plan} den {date}.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/tr-TR.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "Destek için lütfen <supportLink>hola@formbricks.com</supportLink> ile iletişime geç.",
"payment_error_failed_invoice": "Ödeme tamamlanamadı.",
"payment_error_requires_action": "Ödeme için ek doğrulama gerekiyor.",
"payment_method_required": "Ücretli bir plana geçmek için bir ödeme yöntemi ekle.",
"pending_change_removed": "Planlanmış plan değişikliği kaldırıldı.",
"pending_plan_badge": "Planlandı",
"pending_plan_change_description": "Planınız {date} tarihinde {plan} olarak değişecek.",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/zh-Hans-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "请联系 <supportLink>hola@formbricks.com</supportLink> 获取支持。",
"payment_error_failed_invoice": "支付无法完成。",
"payment_error_requires_action": "支付需要额外验证。",
"payment_method_required": "添加付款方式以切换到付费套餐。",
"pending_change_removed": "已取消预定的方案变更。",
"pending_plan_badge": "已预定",
"pending_plan_change_description": "你的套餐将在 {date} 切换为 {plan}。",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/zh-Hant-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@
"payment_error_contact_support": "如需協助,請聯絡 <supportLink>hola@formbricks.com</supportLink>。",
"payment_error_failed_invoice": "付款無法完成。",
"payment_error_requires_action": "付款需要額外的身份驗證。",
"payment_method_required": "新增付款方式以切換至付費方案。",
"pending_change_removed": "已取消預定的方案變更。",
"pending_plan_badge": "已排程",
"pending_plan_change_description": "您的方案將於 {date} 切換至 {plan}。",
Expand Down
7 changes: 7 additions & 0 deletions apps/web/modules/ee/billing/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,13 @@ export const changeBillingPlanAction = authenticatedActionClient.inputSchema(ZCh
throw new ResourceNotFoundError("OrganizationBilling", organizationId);
}

// Mirror the client rule server-side: a paid plan change requires a collected payment method.
// The client routes no-card paid changes through the add-card checkout; this rejects any direct
// call that bypasses it (e.g. a trialing no-card org trying to launder into active paid Pro).
if (parsedInput.targetPlan !== "hobby" && organization.billing.stripe?.hasPaymentMethod !== true) {
throw new OperationNotAllowedError("payment_method_required");
}

const result = await switchOrganizationToCloudPlan({
organizationId,
customerId: organization.billing.stripeCustomerId,
Expand Down
4 changes: 4 additions & 0 deletions apps/web/modules/ee/billing/components/pricing-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ const getActionErrorMessage = (serverError: string, t: (key: string) => string)
return t("workspace.settings.billing.yearly_checkout_unavailable");
}

if (serverError === "payment_method_required") {
return t("workspace.settings.billing.payment_method_required");
}

return t("common.something_went_wrong_please_try_again");
};

Expand Down
Loading
Loading