feat(billing) - facilitate top up in ai chat (#21645)

Today, when a trialing user hits their AI usage cap inside the Ask AI
chat, ending the trial bounces them to the Stripe billing portal (and,
for card-less users, loses their place in the conversation). This PR
makes activating a paid plan / topping up credits feel seamless from
within the chat:

Trial users with a card on file activate their subscription in place,
without leaving the app.
Trial users without a card are sent to the Stripe payment-method portal
and, on return, the trial is ended automatically and they're dropped
back into the exact Ask AI thread they came from.
Credit-exhaustion and trial banners now reflect whether a payment method
exists (Add Credit Card vs Subscribe Now / End Trial Period) and upgrade
inline via a confirmation modal instead of redirecting to Settings.


Uploading Screen Recording 2026-06-16 at 07.51.12.mov…


https://github.com/user-attachments/assets/4ea77273-da63-4b32-b6f1-5ac9e9560651



<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21645?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Etienne
2026-06-17 18:20:11 +02:00
committed by GitHub
parent 177afde866
commit d99e479be8
48 changed files with 1562 additions and 837 deletions
@@ -0,0 +1,37 @@
import { QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
@RegisteredInstanceCommand('2.15.0', 1781280240009)
export class AddHasPaymentMethodToBillingCustomerFastInstanceCommand
implements FastInstanceCommand
{
public async up(queryRunner: QueryRunner): Promise<void> {
const tableExists = await queryRunner.query(
`SELECT 1 FROM pg_tables WHERE schemaname = 'core' AND tablename = 'billingCustomer'`,
);
if (tableExists.length === 0) {
return;
}
await queryRunner.query(
`ALTER TABLE "core"."billingCustomer" ADD COLUMN IF NOT EXISTS "hasPaymentMethod" boolean`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
const tableExists = await queryRunner.query(
`SELECT 1 FROM pg_tables WHERE schemaname = 'core' AND tablename = 'billingCustomer'`,
);
if (tableExists.length === 0) {
return;
}
await queryRunner.query(
`ALTER TABLE "core"."billingCustomer" DROP COLUMN IF EXISTS "hasPaymentMethod"`,
);
}
}