summaryrefslogtreecommitdiffhomepage
path: root/packages/console/app/src/routes
diff options
context:
space:
mode:
authorFrank <[email protected]>2025-10-16 14:59:44 -0400
committerFrank <[email protected]>2025-10-16 14:59:46 -0400
commit7ec5e49e19122f53d99836bdda0d7a98eb61c868 (patch)
tree6ee0dceb9ff90aa580491607fd65ee783985cc5a /packages/console/app/src/routes
parent1c1380d3c8163393c7133981d43c9ec5abf4da43 (diff)
downloadopencode-7ec5e49e19122f53d99836bdda0d7a98eb61c868.tar.gz
opencode-7ec5e49e19122f53d99836bdda0d7a98eb61c868.zip
zen: support stripe link
Diffstat (limited to 'packages/console/app/src/routes')
-rw-r--r--packages/console/app/src/routes/stripe/webhook.ts6
-rw-r--r--packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css6
-rw-r--r--packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx40
3 files changed, 43 insertions, 9 deletions
diff --git a/packages/console/app/src/routes/stripe/webhook.ts b/packages/console/app/src/routes/stripe/webhook.ts
index c12f47afc..f1fa73c91 100644
--- a/packages/console/app/src/routes/stripe/webhook.ts
+++ b/packages/console/app/src/routes/stripe/webhook.ts
@@ -32,7 +32,8 @@ export async function POST(input: APIEvent) {
.update(BillingTable)
.set({
paymentMethodID,
- paymentMethodLast4: paymentMethod.card!.last4,
+ paymentMethodLast4: paymentMethod.card?.last4 ?? null,
+ paymentMethodType: paymentMethod.type,
})
.where(eq(BillingTable.customerID, customerID))
})
@@ -77,7 +78,8 @@ export async function POST(input: APIEvent) {
balance: sql`${BillingTable.balance} + ${centsToMicroCents(Billing.CHARGE_AMOUNT)}`,
customerID,
paymentMethodID: paymentMethod.id,
- paymentMethodLast4: paymentMethod.card!.last4,
+ paymentMethodLast4: paymentMethod.card?.last4 ?? null,
+ paymentMethodType: paymentMethod.type,
reload: true,
reloadError: null,
timeReloadError: null,
diff --git a/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css
index 123bb1c86..b562dcd45 100644
--- a/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css
+++ b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css
@@ -70,6 +70,12 @@
font-weight: 500;
color: var(--color-text);
}
+
+ [data-slot="type"] {
+ font-size: var(--font-size-sm);
+ font-weight: 400;
+ color: var(--color-text-muted);
+ }
}
}
diff --git a/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx
index f9084bbf1..af4a47e48 100644
--- a/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx
+++ b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx
@@ -1,8 +1,8 @@
import { json, query, action, useParams, useAction, createAsync, useSubmission } from "@solidjs/router"
-import { createMemo, Show } from "solid-js"
+import { createMemo, Match, Show, Switch } from "solid-js"
import { Billing } from "@opencode-ai/console-core/billing.js"
import { withActor } from "~/context/auth.withActor"
-import { IconCreditCard } from "~/component/icon"
+import { IconCreditCard, IconStripe } from "~/component/icon"
import styles from "./billing-section.module.css"
import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js"
import { BillingTable } from "@opencode-ai/console-core/schema/billing.sql.js"
@@ -61,6 +61,7 @@ export function BillingSection() {
// Scenario 1: User has not added billing details and has no balance
// const balanceInfo = () => ({
// balance: 0,
+ // paymentMethodType: null as string | null,
// paymentMethodLast4: null as string | null,
// reload: false,
// reloadError: null as string | null,
@@ -70,6 +71,7 @@ export function BillingSection() {
// Scenario 2: User has not added billing details but has a balance
// const balanceInfo = () => ({
// balance: 1500000000, // $15.00
+ // paymentMethodType: null as string | null,
// paymentMethodLast4: null as string | null,
// reload: false,
// reloadError: null as string | null,
@@ -79,6 +81,7 @@ export function BillingSection() {
// Scenario 3: User has added billing details (reload enabled)
// const balanceInfo = () => ({
// balance: 750000000, // $7.50
+ // paymentMethodType: "card",
// paymentMethodLast4: "4242",
// reload: true,
// reloadError: null as string | null,
@@ -88,12 +91,23 @@ export function BillingSection() {
// Scenario 4: User has billing details but reload failed
// const balanceInfo = () => ({
// balance: 250000000, // $2.50
+ // paymentMethodType: "card",
// paymentMethodLast4: "4242",
// reload: true,
// reloadError: "Your card was declined." as string,
// timeReloadError: new Date(Date.now() - 3600000) as Date // 1 hour ago
// })
+ // Scenario 5: User has Link payment method
+ // const balanceInfo = () => ({
+ // balance: 500000000, // $5.00
+ // paymentMethodType: "link",
+ // paymentMethodLast4: null as string | null,
+ // reload: true,
+ // reloadError: null as string | null,
+ // timeReloadError: null as Date | null
+ // })
+
const balanceAmount = createMemo(() => {
return ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2)
})
@@ -136,13 +150,25 @@ export function BillingSection() {
<div data-slot="payment">
<div data-slot="credit-card">
<div data-slot="card-icon">
- <IconCreditCard style={{ width: "32px", height: "32px" }} />
+ <Switch fallback={<IconCreditCard style={{ width: "32px", height: "32px" }} />}>
+ <Match when={balanceInfo()?.paymentMethodType === "link"}>
+ <IconStripe style={{ width: "32px", height: "32px" }} />
+ </Match>
+ </Switch>
</div>
<div data-slot="card-details">
- <Show when={balanceInfo()?.paymentMethodLast4} fallback={<span data-slot="number">----</span>}>
- <span data-slot="secret">••••</span>
- <span data-slot="number">{balanceInfo()?.paymentMethodLast4}</span>
- </Show>
+ <Switch
+ fallback={
+ <Show when={balanceInfo()?.paymentMethodLast4} fallback={<span data-slot="number">----</span>}>
+ <span data-slot="secret">••••</span>
+ <span data-slot="number">{balanceInfo()?.paymentMethodLast4}</span>
+ </Show>
+ }
+ >
+ <Match when={balanceInfo()?.paymentMethodType === "link"}>
+ <span data-slot="type">Linked to Stripe</span>
+ </Match>
+ </Switch>
</div>
</div>
<div data-slot="button-row">