## Why Stripe checkout used Stripe-only tables. This extracts a shared payment CORE. New checkout writes `payment_order`. Old Stripe tables stay so in-progress Sessions can still settle. This PR is [1/2]. [#2368](https://github.com/moeru-ai/airi/pull/2368) is [2/2]. That PR archives leftover Stripe tables after in-progress Sessions finish or expire. ## Changes - Add `payment_order` and `provider_account`. - Copy `stripe_checkout_session` into `payment_order`. - Copy `stripe_customer` into `provider_account`. - Keep `stripe_*` tables and `user_flux.stripe_customer_id`. - New checkout writes `payment_order` and stores `metadata.payment_order_id`. - Webhook resolves new Sessions by `metadata.payment_order_id`. - Webhook resolves older Sessions by `provider_order_id`, then by a leftover `stripe_checkout_session` row. - That leftover-row lookup covers Sessions opened before this deploy, and rows written while 0023 runs. [#2368](https://github.com/moeru-ai/airi/pull/2368) deletes it after those Sessions finish or expire. ## Test plan - [x] `pnpm exec vitest run server/apps/api/src/services/domain/payment/tests/payment.test.ts server/apps/api/src/routes/stripe` - [x] `pnpm -F @proj-airi/api-server typecheck` - [x] `git diff --check` ## Visual changes No user-visible changes. ## Open: settle after account deletion Account deletion stamps `payment_order.deletedAt`. A Checkout Session that is still open can still pay after that stamp. This PR keeps `main` behavior. `settle` loads the row by id, including a soft-deleted row, then marks it `paid` and credits Flux. Follow-up policy: if `deletedAt` is set, skip. Do not update the archive row. Do not credit Flux. Do not insert a live `provider_account`. Stripe remains the payment record. Skip matches the soft-delete rule: a deleted row is gone, not write it again. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: RainbowBird <git@luoling.moe>
15 lines
679 B
TypeScript
15 lines
679 B
TypeScript
import { bigint, pgTable, text, timestamp } from 'drizzle-orm/pg-core'
|
|
|
|
// NOTICE: bare userId is intentional — no FK to user.id. better-auth hard-deletes
|
|
// the user row; a cascade would wipe these soft-delete archive rows.
|
|
// See `server/apps/api/docs/ai-context/account-deletion.md`.
|
|
export const userFlux = pgTable('user_flux', {
|
|
userId: text('user_id').primaryKey(),
|
|
flux: bigint('flux', { mode: 'number' }).notNull().default(0),
|
|
// NOTICE:
|
|
// Unused at runtime. Keep the column so drizzle-kit does not emit DROP.
|
|
stripeCustomerId: text('stripe_customer_id'),
|
|
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
|
deletedAt: timestamp('deleted_at'),
|
|
})
|