Phase8: Auto bill credit card
All checks were successful
Build and Push / build (push) Successful in 1m45s

This commit is contained in:
2026-05-28 23:27:32 +02:00
parent 1741574eb2
commit a6ed74b1be
12 changed files with 288 additions and 69 deletions

View File

@@ -105,6 +105,14 @@ const MIGRATION_SQL = `
ON tenant_requests(setup_invoice_id)
WHERE setup_invoice_id IS NOT NULL;
-- Phase 9b: optional initial channel-user ids per channel package
-- collected during onboarding. JSONB so the shape can vary by
-- channel (today it's a string[] per channel id, matching
-- PiecedTenantSpec.channelUsers). Default '{}' so reads on legacy
-- rows return an empty object rather than null.
ALTER TABLE tenant_requests
ADD COLUMN IF NOT EXISTS channel_users JSONB NOT NULL DEFAULT '{}'::jsonb;
-- Feature 6: free-form customer note attached to the request.
-- Currently surfaced only by resume requests (where the customer
-- explains why they want reactivation), but the column is generic
@@ -896,8 +904,8 @@ export async function createTenantRequest(
(zitadel_org_id, zitadel_user_id, company_name, instance_name,
contact_name, contact_email, agent_name, soul_md, agents_md,
packages, billing_address, billing_notes, encrypted_secrets,
is_personal)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
is_personal, channel_users)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15::jsonb)
RETURNING *`,
[
params.zitadelOrgId,
@@ -914,6 +922,7 @@ export async function createTenantRequest(
params.billingNotes,
params.encryptedSecrets ?? null,
params.isPersonal ?? false,
JSON.stringify(params.channelUsers ?? {}),
]
);
return mapRow(result.rows[0]);
@@ -1449,6 +1458,7 @@ function mapRow(row: any): TenantRequest {
adminNotes: row.admin_notes,
tenantName: row.tenant_name,
setupInvoiceId: row.setup_invoice_id ?? null,
channelUsers: (row.channel_users ?? {}) as Record<string, string[]>,
encryptedSecrets: row.encrypted_secrets ?? null,
isPersonal: row.is_personal ?? false,
dismissedAt:
@@ -4235,6 +4245,7 @@ export async function createTenantRequestPendingPayment(params: {
billingNotes?: string;
encryptedSecrets?: Buffer | null;
isPersonal: boolean;
channelUsers?: Record<string, string[]>;
}): Promise<TenantRequest> {
await ensureSchema();
const result = await getPool().query(
@@ -4244,10 +4255,11 @@ export async function createTenantRequestPendingPayment(params: {
agent_name, soul_md, agents_md, packages,
billing_address, billing_notes,
encrypted_secrets, is_personal,
channel_users,
status, request_type
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::jsonb, $12,
$13, $14, 'pending_payment', 'provision'
$13, $14, $15::jsonb, 'pending_payment', 'provision'
)
RETURNING *`,
[
@@ -4265,6 +4277,7 @@ export async function createTenantRequestPendingPayment(params: {
params.billingNotes ?? null,
params.encryptedSecrets ?? null,
params.isPersonal,
JSON.stringify(params.channelUsers ?? {}),
]
);
return mapRow(result.rows[0]);