// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.31.1 // source: billing.sql package billingdb import ( "context" "github.com/jackc/pgx/v5/pgtype" ) const applySubscriptionSnapshot = `-- name: ApplySubscriptionSnapshot :one WITH state AS ( INSERT INTO org_billing_states ( org_id, provider, plan, subscription_status, stripe_subscription_id, stripe_subscription_item_id, current_period_start, current_period_end, cancel_at_period_end, trial_end, canceled_at, last_webhook_event_id, past_due_at, locked_at, lock_reason, grace_until ) VALUES ( $1::bigint, 'stripe', $2::org_plan, $3::billing_subscription_status, $4::text, $5::text, $6::timestamptz, $7::timestamptz, $8::boolean, $9::timestamptz, $10::timestamptz, $11::text, CASE WHEN $3::billing_subscription_status = 'past_due' THEN now() ELSE NULL END, NULL, NULL, NULL ) ON CONFLICT (org_id) DO UPDATE SET plan = EXCLUDED.plan, subscription_status = EXCLUDED.subscription_status, stripe_subscription_id = EXCLUDED.stripe_subscription_id, stripe_subscription_item_id = EXCLUDED.stripe_subscription_item_id, current_period_start = EXCLUDED.current_period_start, current_period_end = EXCLUDED.current_period_end, cancel_at_period_end = EXCLUDED.cancel_at_period_end, trial_end = EXCLUDED.trial_end, canceled_at = EXCLUDED.canceled_at, last_webhook_event_id = EXCLUDED.last_webhook_event_id, past_due_at = CASE WHEN EXCLUDED.subscription_status = 'past_due' THEN COALESCE(org_billing_states.past_due_at, now()) ELSE NULL END, locked_at = NULL, lock_reason = NULL, grace_until = NULL, updated_at = now() RETURNING org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ), org_update AS ( UPDATE orgs SET plan = $2::org_plan, updated_at = now() WHERE id = $1::bigint RETURNING id ) SELECT org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM state ` type ApplySubscriptionSnapshotParams struct { OrgID int64 Plan OrgPlan SubscriptionStatus BillingSubscriptionStatus StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz CanceledAt pgtype.Timestamptz LastWebhookEventID string } type ApplySubscriptionSnapshotRow struct { OrgID int64 Provider BillingProvider StripeCustomerID pgtype.Text StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text Plan OrgPlan SubscriptionStatus BillingSubscriptionStatus BillableSeats int32 SeatSnapshotAt pgtype.Timestamptz CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz PastDueAt pgtype.Timestamptz CanceledAt pgtype.Timestamptz LockedAt pgtype.Timestamptz LockReason NullBillingLockReason GraceUntil pgtype.Timestamptz LastWebhookEventID string CreatedAt pgtype.Timestamptz UpdatedAt pgtype.Timestamptz } func (q *Queries) ApplySubscriptionSnapshot(ctx context.Context, db DBTX, arg ApplySubscriptionSnapshotParams) (ApplySubscriptionSnapshotRow, error) { row := db.QueryRow(ctx, applySubscriptionSnapshot, arg.OrgID, arg.Plan, arg.SubscriptionStatus, arg.StripeSubscriptionID, arg.StripeSubscriptionItemID, arg.CurrentPeriodStart, arg.CurrentPeriodEnd, arg.CancelAtPeriodEnd, arg.TrialEnd, arg.CanceledAt, arg.LastWebhookEventID, ) var i ApplySubscriptionSnapshotRow err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const applyUserSubscriptionSnapshot = `-- name: ApplyUserSubscriptionSnapshot :one WITH state AS ( INSERT INTO user_billing_states ( user_id, provider, plan, subscription_status, stripe_subscription_id, stripe_subscription_item_id, current_period_start, current_period_end, cancel_at_period_end, trial_end, canceled_at, last_webhook_event_id, past_due_at, locked_at, lock_reason, grace_until ) VALUES ( $1::bigint, 'stripe', $2::user_plan, $3::billing_subscription_status, $4::text, $5::text, $6::timestamptz, $7::timestamptz, $8::boolean, $9::timestamptz, $10::timestamptz, $11::text, CASE WHEN $3::billing_subscription_status = 'past_due' THEN now() ELSE NULL END, NULL, NULL, NULL ) ON CONFLICT (user_id) DO UPDATE SET plan = EXCLUDED.plan, subscription_status = EXCLUDED.subscription_status, stripe_subscription_id = EXCLUDED.stripe_subscription_id, stripe_subscription_item_id = EXCLUDED.stripe_subscription_item_id, current_period_start = EXCLUDED.current_period_start, current_period_end = EXCLUDED.current_period_end, cancel_at_period_end = EXCLUDED.cancel_at_period_end, trial_end = EXCLUDED.trial_end, canceled_at = EXCLUDED.canceled_at, last_webhook_event_id = EXCLUDED.last_webhook_event_id, past_due_at = CASE WHEN EXCLUDED.subscription_status = 'past_due' THEN COALESCE(user_billing_states.past_due_at, now()) ELSE NULL END, locked_at = NULL, lock_reason = NULL, grace_until = NULL, updated_at = now() RETURNING user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ), user_update AS ( UPDATE users SET plan = $2::user_plan, updated_at = now() WHERE id = $1::bigint RETURNING id ) SELECT user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM state ` type ApplyUserSubscriptionSnapshotParams struct { UserID int64 Plan UserPlan SubscriptionStatus BillingSubscriptionStatus StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz CanceledAt pgtype.Timestamptz LastWebhookEventID string } type ApplyUserSubscriptionSnapshotRow struct { UserID int64 Provider BillingProvider StripeCustomerID pgtype.Text StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text Plan UserPlan SubscriptionStatus BillingSubscriptionStatus CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz PastDueAt pgtype.Timestamptz CanceledAt pgtype.Timestamptz LockedAt pgtype.Timestamptz LockReason NullBillingLockReason GraceUntil pgtype.Timestamptz LastWebhookEventID string CreatedAt pgtype.Timestamptz UpdatedAt pgtype.Timestamptz } // Mirrors ApplySubscriptionSnapshot for orgs minus the seat columns // and with `user_plan` as the plan enum. The same CTE pattern keeps // users.plan and user_billing_states.plan atomic. func (q *Queries) ApplyUserSubscriptionSnapshot(ctx context.Context, db DBTX, arg ApplyUserSubscriptionSnapshotParams) (ApplyUserSubscriptionSnapshotRow, error) { row := db.QueryRow(ctx, applyUserSubscriptionSnapshot, arg.UserID, arg.Plan, arg.SubscriptionStatus, arg.StripeSubscriptionID, arg.StripeSubscriptionItemID, arg.CurrentPeriodStart, arg.CurrentPeriodEnd, arg.CancelAtPeriodEnd, arg.TrialEnd, arg.CanceledAt, arg.LastWebhookEventID, ) var i ApplyUserSubscriptionSnapshotRow err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const clearBillingLock = `-- name: ClearBillingLock :one WITH state AS ( UPDATE org_billing_states SET plan = CASE WHEN subscription_status = 'canceled' THEN 'free' ELSE plan END, subscription_status = CASE WHEN subscription_status = 'canceled' THEN 'none' ELSE subscription_status END, locked_at = NULL, lock_reason = NULL, grace_until = NULL, updated_at = now() WHERE org_id = $1 RETURNING org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ), org_update AS ( UPDATE orgs SET plan = state.plan, updated_at = now() FROM state WHERE orgs.id = state.org_id RETURNING orgs.id ) SELECT org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM state ` type ClearBillingLockRow struct { OrgID int64 Provider BillingProvider StripeCustomerID pgtype.Text StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text Plan OrgPlan SubscriptionStatus BillingSubscriptionStatus BillableSeats int32 SeatSnapshotAt pgtype.Timestamptz CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz PastDueAt pgtype.Timestamptz CanceledAt pgtype.Timestamptz LockedAt pgtype.Timestamptz LockReason NullBillingLockReason GraceUntil pgtype.Timestamptz LastWebhookEventID string CreatedAt pgtype.Timestamptz UpdatedAt pgtype.Timestamptz } func (q *Queries) ClearBillingLock(ctx context.Context, db DBTX, orgID int64) (ClearBillingLockRow, error) { row := db.QueryRow(ctx, clearBillingLock, orgID) var i ClearBillingLockRow err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const clearUserBillingLock = `-- name: ClearUserBillingLock :one WITH state AS ( UPDATE user_billing_states SET plan = CASE WHEN subscription_status = 'canceled' THEN 'free' ELSE plan END, subscription_status = CASE WHEN subscription_status = 'canceled' THEN 'none' ELSE subscription_status END, locked_at = NULL, lock_reason = NULL, grace_until = NULL, updated_at = now() WHERE user_id = $1 RETURNING user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ), user_update AS ( UPDATE users SET plan = state.plan, updated_at = now() FROM state WHERE users.id = state.user_id RETURNING users.id ) SELECT user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM state ` type ClearUserBillingLockRow struct { UserID int64 Provider BillingProvider StripeCustomerID pgtype.Text StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text Plan UserPlan SubscriptionStatus BillingSubscriptionStatus CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz PastDueAt pgtype.Timestamptz CanceledAt pgtype.Timestamptz LockedAt pgtype.Timestamptz LockReason NullBillingLockReason GraceUntil pgtype.Timestamptz LastWebhookEventID string CreatedAt pgtype.Timestamptz UpdatedAt pgtype.Timestamptz } func (q *Queries) ClearUserBillingLock(ctx context.Context, db DBTX, userID int64) (ClearUserBillingLockRow, error) { row := db.QueryRow(ctx, clearUserBillingLock, userID) var i ClearUserBillingLockRow err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const countBillableOrgMembers = `-- name: CountBillableOrgMembers :one SELECT count(*)::integer FROM org_members WHERE org_id = $1 ` func (q *Queries) CountBillableOrgMembers(ctx context.Context, db DBTX, orgID int64) (int32, error) { row := db.QueryRow(ctx, countBillableOrgMembers, orgID) var column_1 int32 err := row.Scan(&column_1) return column_1, err } const countPendingOrgInvitations = `-- name: CountPendingOrgInvitations :one SELECT count(*)::integer FROM org_invitations WHERE org_id = $1 AND accepted_at IS NULL AND declined_at IS NULL AND canceled_at IS NULL AND expires_at > now() ` func (q *Queries) CountPendingOrgInvitations(ctx context.Context, db DBTX, orgID int64) (int32, error) { row := db.QueryRow(ctx, countPendingOrgInvitations, orgID) var column_1 int32 err := row.Scan(&column_1) return column_1, err } const createSeatSnapshot = `-- name: CreateSeatSnapshot :one WITH snapshot AS ( INSERT INTO billing_seat_snapshots ( org_id, provider, stripe_subscription_id, active_members, billable_seats, source ) VALUES ( $1::bigint, 'stripe', $2::text, $3::integer, $4::integer, $5::text ) RETURNING id, org_id, provider, stripe_subscription_id, active_members, billable_seats, source, captured_at ), state AS ( INSERT INTO org_billing_states (org_id, billable_seats, seat_snapshot_at) SELECT org_id, billable_seats, captured_at FROM snapshot ON CONFLICT (org_id) DO UPDATE SET billable_seats = EXCLUDED.billable_seats, seat_snapshot_at = EXCLUDED.seat_snapshot_at, updated_at = now() RETURNING org_id ) SELECT id, org_id, provider, stripe_subscription_id, active_members, billable_seats, source, captured_at FROM snapshot ` type CreateSeatSnapshotParams struct { OrgID int64 StripeSubscriptionID pgtype.Text ActiveMembers int32 BillableSeats int32 Source string } type CreateSeatSnapshotRow struct { ID int64 OrgID int64 Provider BillingProvider StripeSubscriptionID pgtype.Text ActiveMembers int32 BillableSeats int32 Source string CapturedAt pgtype.Timestamptz } // ─── billing_seat_snapshots ──────────────────────────────────────── func (q *Queries) CreateSeatSnapshot(ctx context.Context, db DBTX, arg CreateSeatSnapshotParams) (CreateSeatSnapshotRow, error) { row := db.QueryRow(ctx, createSeatSnapshot, arg.OrgID, arg.StripeSubscriptionID, arg.ActiveMembers, arg.BillableSeats, arg.Source, ) var i CreateSeatSnapshotRow err := row.Scan( &i.ID, &i.OrgID, &i.Provider, &i.StripeSubscriptionID, &i.ActiveMembers, &i.BillableSeats, &i.Source, &i.CapturedAt, ) return i, err } const createWebhookEventReceipt = `-- name: CreateWebhookEventReceipt :one INSERT INTO billing_webhook_events ( provider, provider_event_id, event_type, api_version, payload ) VALUES ( 'stripe', $1::text, $2::text, $3::text, $4::jsonb ) ON CONFLICT (provider, provider_event_id) DO NOTHING RETURNING id, provider, provider_event_id, event_type, api_version, payload, received_at, processed_at, process_error, processing_attempts, subject_kind, subject_id ` type CreateWebhookEventReceiptParams struct { ProviderEventID string EventType string ApiVersion string Payload []byte } // ─── billing_webhook_events ──────────────────────────────────────── func (q *Queries) CreateWebhookEventReceipt(ctx context.Context, db DBTX, arg CreateWebhookEventReceiptParams) (BillingWebhookEvent, error) { row := db.QueryRow(ctx, createWebhookEventReceipt, arg.ProviderEventID, arg.EventType, arg.ApiVersion, arg.Payload, ) var i BillingWebhookEvent err := row.Scan( &i.ID, &i.Provider, &i.ProviderEventID, &i.EventType, &i.ApiVersion, &i.Payload, &i.ReceivedAt, &i.ProcessedAt, &i.ProcessError, &i.ProcessingAttempts, &i.SubjectKind, &i.SubjectID, ) return i, err } const getOrgBillingState = `-- name: GetOrgBillingState :one SELECT org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM org_billing_states WHERE org_id = $1 ` // SPDX-License-Identifier: AGPL-3.0-or-later // ─── org_billing_states ──────────────────────────────────────────── func (q *Queries) GetOrgBillingState(ctx context.Context, db DBTX, orgID int64) (OrgBillingState, error) { row := db.QueryRow(ctx, getOrgBillingState, orgID) var i OrgBillingState err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const getOrgBillingStateByStripeCustomer = `-- name: GetOrgBillingStateByStripeCustomer :one SELECT org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM org_billing_states WHERE provider = 'stripe' AND stripe_customer_id = $1 ` func (q *Queries) GetOrgBillingStateByStripeCustomer(ctx context.Context, db DBTX, stripeCustomerID pgtype.Text) (OrgBillingState, error) { row := db.QueryRow(ctx, getOrgBillingStateByStripeCustomer, stripeCustomerID) var i OrgBillingState err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const getOrgBillingStateByStripeSubscription = `-- name: GetOrgBillingStateByStripeSubscription :one SELECT org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM org_billing_states WHERE provider = 'stripe' AND stripe_subscription_id = $1 ` func (q *Queries) GetOrgBillingStateByStripeSubscription(ctx context.Context, db DBTX, stripeSubscriptionID pgtype.Text) (OrgBillingState, error) { row := db.QueryRow(ctx, getOrgBillingStateByStripeSubscription, stripeSubscriptionID) var i OrgBillingState err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const getUserBillingState = `-- name: GetUserBillingState :one SELECT user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM user_billing_states WHERE user_id = $1 ` // ─── user_billing_states (PRO03) ────────────────────────────────── func (q *Queries) GetUserBillingState(ctx context.Context, db DBTX, userID int64) (UserBillingState, error) { row := db.QueryRow(ctx, getUserBillingState, userID) var i UserBillingState err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const getUserBillingStateByStripeCustomer = `-- name: GetUserBillingStateByStripeCustomer :one SELECT user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM user_billing_states WHERE provider = 'stripe' AND stripe_customer_id = $1 ` func (q *Queries) GetUserBillingStateByStripeCustomer(ctx context.Context, db DBTX, stripeCustomerID pgtype.Text) (UserBillingState, error) { row := db.QueryRow(ctx, getUserBillingStateByStripeCustomer, stripeCustomerID) var i UserBillingState err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const getUserBillingStateByStripeSubscription = `-- name: GetUserBillingStateByStripeSubscription :one SELECT user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM user_billing_states WHERE provider = 'stripe' AND stripe_subscription_id = $1 ` func (q *Queries) GetUserBillingStateByStripeSubscription(ctx context.Context, db DBTX, stripeSubscriptionID pgtype.Text) (UserBillingState, error) { row := db.QueryRow(ctx, getUserBillingStateByStripeSubscription, stripeSubscriptionID) var i UserBillingState err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const getWebhookEventReceipt = `-- name: GetWebhookEventReceipt :one SELECT id, provider, provider_event_id, event_type, api_version, payload, received_at, processed_at, process_error, processing_attempts, subject_kind, subject_id FROM billing_webhook_events WHERE provider = 'stripe' AND provider_event_id = $1 ` func (q *Queries) GetWebhookEventReceipt(ctx context.Context, db DBTX, providerEventID string) (BillingWebhookEvent, error) { row := db.QueryRow(ctx, getWebhookEventReceipt, providerEventID) var i BillingWebhookEvent err := row.Scan( &i.ID, &i.Provider, &i.ProviderEventID, &i.EventType, &i.ApiVersion, &i.Payload, &i.ReceivedAt, &i.ProcessedAt, &i.ProcessError, &i.ProcessingAttempts, &i.SubjectKind, &i.SubjectID, ) return i, err } const listInvoicesForOrg = `-- name: ListInvoicesForOrg :many SELECT id, org_id, provider, stripe_invoice_id, stripe_customer_id, stripe_subscription_id, status, number, currency, amount_due_cents, amount_paid_cents, amount_remaining_cents, hosted_invoice_url, invoice_pdf_url, period_start, period_end, due_at, paid_at, voided_at, created_at, updated_at, subject_kind, subject_id FROM billing_invoices WHERE subject_kind = 'org' AND subject_id = $1 ORDER BY created_at DESC, id DESC LIMIT $2 ` type ListInvoicesForOrgParams struct { SubjectID int64 Limit int32 } // PRO03: filters on the polymorphic subject columns so the index // billing_invoices_subject_created_idx services this query. The // legacy `org_id` column is kept populated by UpsertInvoice for the // transitional window; this query no longer reads it. func (q *Queries) ListInvoicesForOrg(ctx context.Context, db DBTX, arg ListInvoicesForOrgParams) ([]BillingInvoice, error) { rows, err := db.Query(ctx, listInvoicesForOrg, arg.SubjectID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []BillingInvoice{} for rows.Next() { var i BillingInvoice if err := rows.Scan( &i.ID, &i.OrgID, &i.Provider, &i.StripeInvoiceID, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.Status, &i.Number, &i.Currency, &i.AmountDueCents, &i.AmountPaidCents, &i.AmountRemainingCents, &i.HostedInvoiceUrl, &i.InvoicePdfUrl, &i.PeriodStart, &i.PeriodEnd, &i.DueAt, &i.PaidAt, &i.VoidedAt, &i.CreatedAt, &i.UpdatedAt, &i.SubjectKind, &i.SubjectID, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listInvoicesForSubject = `-- name: ListInvoicesForSubject :many SELECT id, org_id, provider, stripe_invoice_id, stripe_customer_id, stripe_subscription_id, status, number, currency, amount_due_cents, amount_paid_cents, amount_remaining_cents, hosted_invoice_url, invoice_pdf_url, period_start, period_end, due_at, paid_at, voided_at, created_at, updated_at, subject_kind, subject_id FROM billing_invoices WHERE subject_kind = $1::billing_subject_kind AND subject_id = $2::bigint ORDER BY created_at DESC, id DESC LIMIT $3::integer ` type ListInvoicesForSubjectParams struct { SubjectKind BillingSubjectKind SubjectID int64 Lim int32 } // Polymorphic invoice listing for PRO04+ callers. The org-flavored // ListInvoicesForOrg above is the same query with subject_kind // hard-coded; this surface lets a user-side caller pass kind='user' // without forking the helper. func (q *Queries) ListInvoicesForSubject(ctx context.Context, db DBTX, arg ListInvoicesForSubjectParams) ([]BillingInvoice, error) { rows, err := db.Query(ctx, listInvoicesForSubject, arg.SubjectKind, arg.SubjectID, arg.Lim) if err != nil { return nil, err } defer rows.Close() items := []BillingInvoice{} for rows.Next() { var i BillingInvoice if err := rows.Scan( &i.ID, &i.OrgID, &i.Provider, &i.StripeInvoiceID, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.Status, &i.Number, &i.Currency, &i.AmountDueCents, &i.AmountPaidCents, &i.AmountRemainingCents, &i.HostedInvoiceUrl, &i.InvoicePdfUrl, &i.PeriodStart, &i.PeriodEnd, &i.DueAt, &i.PaidAt, &i.VoidedAt, &i.CreatedAt, &i.UpdatedAt, &i.SubjectKind, &i.SubjectID, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listSeatSnapshotsForOrg = `-- name: ListSeatSnapshotsForOrg :many SELECT id, org_id, provider, stripe_subscription_id, active_members, billable_seats, source, captured_at FROM billing_seat_snapshots WHERE org_id = $1 ORDER BY captured_at DESC, id DESC LIMIT $2 ` type ListSeatSnapshotsForOrgParams struct { OrgID int64 Limit int32 } func (q *Queries) ListSeatSnapshotsForOrg(ctx context.Context, db DBTX, arg ListSeatSnapshotsForOrgParams) ([]BillingSeatSnapshot, error) { rows, err := db.Query(ctx, listSeatSnapshotsForOrg, arg.OrgID, arg.Limit) if err != nil { return nil, err } defer rows.Close() items := []BillingSeatSnapshot{} for rows.Next() { var i BillingSeatSnapshot if err := rows.Scan( &i.ID, &i.OrgID, &i.Provider, &i.StripeSubscriptionID, &i.ActiveMembers, &i.BillableSeats, &i.Source, &i.CapturedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const markCanceled = `-- name: MarkCanceled :one WITH state AS ( UPDATE org_billing_states SET plan = 'free', subscription_status = 'canceled', canceled_at = COALESCE(canceled_at, now()), locked_at = now(), lock_reason = 'canceled', grace_until = NULL, cancel_at_period_end = false, last_webhook_event_id = $1::text, updated_at = now() WHERE org_id = $2::bigint RETURNING org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ), org_update AS ( UPDATE orgs SET plan = 'free', updated_at = now() WHERE id = $2::bigint RETURNING id ) SELECT org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM state ` type MarkCanceledParams struct { LastWebhookEventID string OrgID int64 } type MarkCanceledRow struct { OrgID int64 Provider BillingProvider StripeCustomerID pgtype.Text StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text Plan OrgPlan SubscriptionStatus BillingSubscriptionStatus BillableSeats int32 SeatSnapshotAt pgtype.Timestamptz CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz PastDueAt pgtype.Timestamptz CanceledAt pgtype.Timestamptz LockedAt pgtype.Timestamptz LockReason NullBillingLockReason GraceUntil pgtype.Timestamptz LastWebhookEventID string CreatedAt pgtype.Timestamptz UpdatedAt pgtype.Timestamptz } func (q *Queries) MarkCanceled(ctx context.Context, db DBTX, arg MarkCanceledParams) (MarkCanceledRow, error) { row := db.QueryRow(ctx, markCanceled, arg.LastWebhookEventID, arg.OrgID) var i MarkCanceledRow err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const markPastDue = `-- name: MarkPastDue :one UPDATE org_billing_states SET subscription_status = 'past_due', past_due_at = COALESCE(past_due_at, now()), locked_at = now(), lock_reason = 'past_due', grace_until = $1::timestamptz, last_webhook_event_id = $2::text, updated_at = now() WHERE org_id = $3::bigint RETURNING org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ` type MarkPastDueParams struct { GraceUntil pgtype.Timestamptz LastWebhookEventID string OrgID int64 } func (q *Queries) MarkPastDue(ctx context.Context, db DBTX, arg MarkPastDueParams) (OrgBillingState, error) { row := db.QueryRow(ctx, markPastDue, arg.GraceUntil, arg.LastWebhookEventID, arg.OrgID) var i OrgBillingState err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const markPaymentSucceeded = `-- name: MarkPaymentSucceeded :one WITH state AS ( UPDATE org_billing_states SET plan = CASE WHEN subscription_status IN ('past_due', 'unpaid', 'incomplete') THEN 'team' ELSE plan END, subscription_status = CASE WHEN subscription_status IN ('past_due', 'unpaid', 'incomplete') THEN 'active' ELSE subscription_status END, past_due_at = CASE WHEN subscription_status IN ('past_due', 'unpaid', 'incomplete') THEN NULL ELSE past_due_at END, locked_at = NULL, lock_reason = NULL, grace_until = NULL, last_webhook_event_id = $1::text, updated_at = now() WHERE org_id = $2::bigint RETURNING org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ), org_update AS ( UPDATE orgs SET plan = state.plan, updated_at = now() FROM state WHERE orgs.id = state.org_id RETURNING orgs.id ) SELECT org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM state ` type MarkPaymentSucceededParams struct { LastWebhookEventID string OrgID int64 } type MarkPaymentSucceededRow struct { OrgID int64 Provider BillingProvider StripeCustomerID pgtype.Text StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text Plan OrgPlan SubscriptionStatus BillingSubscriptionStatus BillableSeats int32 SeatSnapshotAt pgtype.Timestamptz CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz PastDueAt pgtype.Timestamptz CanceledAt pgtype.Timestamptz LockedAt pgtype.Timestamptz LockReason NullBillingLockReason GraceUntil pgtype.Timestamptz LastWebhookEventID string CreatedAt pgtype.Timestamptz UpdatedAt pgtype.Timestamptz } func (q *Queries) MarkPaymentSucceeded(ctx context.Context, db DBTX, arg MarkPaymentSucceededParams) (MarkPaymentSucceededRow, error) { row := db.QueryRow(ctx, markPaymentSucceeded, arg.LastWebhookEventID, arg.OrgID) var i MarkPaymentSucceededRow err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const markUserCanceled = `-- name: MarkUserCanceled :one WITH state AS ( UPDATE user_billing_states SET plan = 'free', subscription_status = 'canceled', canceled_at = COALESCE(canceled_at, now()), locked_at = now(), lock_reason = 'canceled', grace_until = NULL, cancel_at_period_end = false, last_webhook_event_id = $1::text, updated_at = now() WHERE user_id = $2::bigint RETURNING user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ), user_update AS ( UPDATE users SET plan = 'free', updated_at = now() WHERE id = $2::bigint RETURNING id ) SELECT user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM state ` type MarkUserCanceledParams struct { LastWebhookEventID string UserID int64 } type MarkUserCanceledRow struct { UserID int64 Provider BillingProvider StripeCustomerID pgtype.Text StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text Plan UserPlan SubscriptionStatus BillingSubscriptionStatus CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz PastDueAt pgtype.Timestamptz CanceledAt pgtype.Timestamptz LockedAt pgtype.Timestamptz LockReason NullBillingLockReason GraceUntil pgtype.Timestamptz LastWebhookEventID string CreatedAt pgtype.Timestamptz UpdatedAt pgtype.Timestamptz } func (q *Queries) MarkUserCanceled(ctx context.Context, db DBTX, arg MarkUserCanceledParams) (MarkUserCanceledRow, error) { row := db.QueryRow(ctx, markUserCanceled, arg.LastWebhookEventID, arg.UserID) var i MarkUserCanceledRow err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const markUserPastDue = `-- name: MarkUserPastDue :one UPDATE user_billing_states SET subscription_status = 'past_due', past_due_at = COALESCE(past_due_at, now()), locked_at = now(), lock_reason = 'past_due', grace_until = $1::timestamptz, last_webhook_event_id = $2::text, updated_at = now() WHERE user_id = $3::bigint RETURNING user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ` type MarkUserPastDueParams struct { GraceUntil pgtype.Timestamptz LastWebhookEventID string UserID int64 } func (q *Queries) MarkUserPastDue(ctx context.Context, db DBTX, arg MarkUserPastDueParams) (UserBillingState, error) { row := db.QueryRow(ctx, markUserPastDue, arg.GraceUntil, arg.LastWebhookEventID, arg.UserID) var i UserBillingState err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const markUserPaymentSucceeded = `-- name: MarkUserPaymentSucceeded :one WITH state AS ( UPDATE user_billing_states SET plan = CASE WHEN subscription_status IN ('past_due', 'unpaid', 'incomplete') THEN 'pro' ELSE plan END, subscription_status = CASE WHEN subscription_status IN ('past_due', 'unpaid', 'incomplete') THEN 'active' ELSE subscription_status END, past_due_at = CASE WHEN subscription_status IN ('past_due', 'unpaid', 'incomplete') THEN NULL ELSE past_due_at END, locked_at = NULL, lock_reason = NULL, grace_until = NULL, last_webhook_event_id = $1::text, updated_at = now() WHERE user_id = $2::bigint RETURNING user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ), user_update AS ( UPDATE users SET plan = state.plan, updated_at = now() FROM state WHERE users.id = state.user_id RETURNING users.id ) SELECT user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at FROM state ` type MarkUserPaymentSucceededParams struct { LastWebhookEventID string UserID int64 } type MarkUserPaymentSucceededRow struct { UserID int64 Provider BillingProvider StripeCustomerID pgtype.Text StripeSubscriptionID pgtype.Text StripeSubscriptionItemID pgtype.Text Plan UserPlan SubscriptionStatus BillingSubscriptionStatus CurrentPeriodStart pgtype.Timestamptz CurrentPeriodEnd pgtype.Timestamptz CancelAtPeriodEnd bool TrialEnd pgtype.Timestamptz PastDueAt pgtype.Timestamptz CanceledAt pgtype.Timestamptz LockedAt pgtype.Timestamptz LockReason NullBillingLockReason GraceUntil pgtype.Timestamptz LastWebhookEventID string CreatedAt pgtype.Timestamptz UpdatedAt pgtype.Timestamptz } func (q *Queries) MarkUserPaymentSucceeded(ctx context.Context, db DBTX, arg MarkUserPaymentSucceededParams) (MarkUserPaymentSucceededRow, error) { row := db.QueryRow(ctx, markUserPaymentSucceeded, arg.LastWebhookEventID, arg.UserID) var i MarkUserPaymentSucceededRow err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const markWebhookEventFailed = `-- name: MarkWebhookEventFailed :one UPDATE billing_webhook_events SET process_error = $2, processing_attempts = processing_attempts + 1 WHERE provider = 'stripe' AND provider_event_id = $1 RETURNING id, provider, provider_event_id, event_type, api_version, payload, received_at, processed_at, process_error, processing_attempts, subject_kind, subject_id ` type MarkWebhookEventFailedParams struct { ProviderEventID string ProcessError string } func (q *Queries) MarkWebhookEventFailed(ctx context.Context, db DBTX, arg MarkWebhookEventFailedParams) (BillingWebhookEvent, error) { row := db.QueryRow(ctx, markWebhookEventFailed, arg.ProviderEventID, arg.ProcessError) var i BillingWebhookEvent err := row.Scan( &i.ID, &i.Provider, &i.ProviderEventID, &i.EventType, &i.ApiVersion, &i.Payload, &i.ReceivedAt, &i.ProcessedAt, &i.ProcessError, &i.ProcessingAttempts, &i.SubjectKind, &i.SubjectID, ) return i, err } const markWebhookEventProcessed = `-- name: MarkWebhookEventProcessed :one UPDATE billing_webhook_events SET processed_at = now(), process_error = '', processing_attempts = processing_attempts + 1 WHERE provider = 'stripe' AND provider_event_id = $1 RETURNING id, provider, provider_event_id, event_type, api_version, payload, received_at, processed_at, process_error, processing_attempts, subject_kind, subject_id ` func (q *Queries) MarkWebhookEventProcessed(ctx context.Context, db DBTX, providerEventID string) (BillingWebhookEvent, error) { row := db.QueryRow(ctx, markWebhookEventProcessed, providerEventID) var i BillingWebhookEvent err := row.Scan( &i.ID, &i.Provider, &i.ProviderEventID, &i.EventType, &i.ApiVersion, &i.Payload, &i.ReceivedAt, &i.ProcessedAt, &i.ProcessError, &i.ProcessingAttempts, &i.SubjectKind, &i.SubjectID, ) return i, err } const setStripeCustomer = `-- name: SetStripeCustomer :one INSERT INTO org_billing_states (org_id, provider, stripe_customer_id) VALUES ($1, 'stripe', $2) ON CONFLICT (org_id) DO UPDATE SET stripe_customer_id = EXCLUDED.stripe_customer_id, provider = 'stripe', updated_at = now() RETURNING org_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, billable_seats, seat_snapshot_at, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ` type SetStripeCustomerParams struct { OrgID int64 StripeCustomerID pgtype.Text } func (q *Queries) SetStripeCustomer(ctx context.Context, db DBTX, arg SetStripeCustomerParams) (OrgBillingState, error) { row := db.QueryRow(ctx, setStripeCustomer, arg.OrgID, arg.StripeCustomerID) var i OrgBillingState err := row.Scan( &i.OrgID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.BillableSeats, &i.SeatSnapshotAt, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const setUserStripeCustomer = `-- name: SetUserStripeCustomer :one INSERT INTO user_billing_states (user_id, provider, stripe_customer_id) VALUES ($1, 'stripe', $2) ON CONFLICT (user_id) DO UPDATE SET stripe_customer_id = EXCLUDED.stripe_customer_id, provider = 'stripe', updated_at = now() RETURNING user_id, provider, stripe_customer_id, stripe_subscription_id, stripe_subscription_item_id, plan, subscription_status, current_period_start, current_period_end, cancel_at_period_end, trial_end, past_due_at, canceled_at, locked_at, lock_reason, grace_until, last_webhook_event_id, created_at, updated_at ` type SetUserStripeCustomerParams struct { UserID int64 StripeCustomerID pgtype.Text } func (q *Queries) SetUserStripeCustomer(ctx context.Context, db DBTX, arg SetUserStripeCustomerParams) (UserBillingState, error) { row := db.QueryRow(ctx, setUserStripeCustomer, arg.UserID, arg.StripeCustomerID) var i UserBillingState err := row.Scan( &i.UserID, &i.Provider, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.StripeSubscriptionItemID, &i.Plan, &i.SubscriptionStatus, &i.CurrentPeriodStart, &i.CurrentPeriodEnd, &i.CancelAtPeriodEnd, &i.TrialEnd, &i.PastDueAt, &i.CanceledAt, &i.LockedAt, &i.LockReason, &i.GraceUntil, &i.LastWebhookEventID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const upsertInvoice = `-- name: UpsertInvoice :one INSERT INTO billing_invoices ( org_id, subject_kind, subject_id, provider, stripe_invoice_id, stripe_customer_id, stripe_subscription_id, status, number, currency, amount_due_cents, amount_paid_cents, amount_remaining_cents, hosted_invoice_url, invoice_pdf_url, period_start, period_end, due_at, paid_at, voided_at ) VALUES ( $1::bigint, 'org'::billing_subject_kind, $1::bigint, 'stripe', $2::text, $3::text, $4::text, $5::billing_invoice_status, $6::text, $7::text, $8::bigint, $9::bigint, $10::bigint, $11::text, $12::text, $13::timestamptz, $14::timestamptz, $15::timestamptz, $16::timestamptz, $17::timestamptz ) ON CONFLICT (provider, stripe_invoice_id) DO UPDATE SET org_id = EXCLUDED.org_id, stripe_customer_id = EXCLUDED.stripe_customer_id, stripe_subscription_id = EXCLUDED.stripe_subscription_id, status = EXCLUDED.status, number = EXCLUDED.number, currency = EXCLUDED.currency, amount_due_cents = EXCLUDED.amount_due_cents, amount_paid_cents = EXCLUDED.amount_paid_cents, amount_remaining_cents = EXCLUDED.amount_remaining_cents, hosted_invoice_url = EXCLUDED.hosted_invoice_url, invoice_pdf_url = EXCLUDED.invoice_pdf_url, period_start = EXCLUDED.period_start, period_end = EXCLUDED.period_end, due_at = EXCLUDED.due_at, paid_at = EXCLUDED.paid_at, voided_at = EXCLUDED.voided_at, updated_at = now() RETURNING id, org_id, provider, stripe_invoice_id, stripe_customer_id, stripe_subscription_id, status, number, currency, amount_due_cents, amount_paid_cents, amount_remaining_cents, hosted_invoice_url, invoice_pdf_url, period_start, period_end, due_at, paid_at, voided_at, created_at, updated_at, subject_kind, subject_id ` type UpsertInvoiceParams struct { OrgID int64 StripeInvoiceID string StripeCustomerID string StripeSubscriptionID pgtype.Text Status BillingInvoiceStatus Number string Currency string AmountDueCents int64 AmountPaidCents int64 AmountRemainingCents int64 HostedInvoiceUrl string InvoicePdfUrl string PeriodStart pgtype.Timestamptz PeriodEnd pgtype.Timestamptz DueAt pgtype.Timestamptz PaidAt pgtype.Timestamptz VoidedAt pgtype.Timestamptz } // ─── billing_invoices ────────────────────────────────────────────── // PRO03: writes both legacy `org_id` and polymorphic // `(subject_kind, subject_id)`. Callers continue to bind org_id only; // the subject columns are derived. After PRO04 migrates all callers // to the polymorphic shape, a follow-up migration drops `org_id` and // this query loses the legacy column from its INSERT list. func (q *Queries) UpsertInvoice(ctx context.Context, db DBTX, arg UpsertInvoiceParams) (BillingInvoice, error) { row := db.QueryRow(ctx, upsertInvoice, arg.OrgID, arg.StripeInvoiceID, arg.StripeCustomerID, arg.StripeSubscriptionID, arg.Status, arg.Number, arg.Currency, arg.AmountDueCents, arg.AmountPaidCents, arg.AmountRemainingCents, arg.HostedInvoiceUrl, arg.InvoicePdfUrl, arg.PeriodStart, arg.PeriodEnd, arg.DueAt, arg.PaidAt, arg.VoidedAt, ) var i BillingInvoice err := row.Scan( &i.ID, &i.OrgID, &i.Provider, &i.StripeInvoiceID, &i.StripeCustomerID, &i.StripeSubscriptionID, &i.Status, &i.Number, &i.Currency, &i.AmountDueCents, &i.AmountPaidCents, &i.AmountRemainingCents, &i.HostedInvoiceUrl, &i.InvoicePdfUrl, &i.PeriodStart, &i.PeriodEnd, &i.DueAt, &i.PaidAt, &i.VoidedAt, &i.CreatedAt, &i.UpdatedAt, &i.SubjectKind, &i.SubjectID, ) return i, err }