From 8c71107a93525183ae3d2417d7c056d997f5e204 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 16 Sep 2025 17:49:37 -0400 Subject: [PATCH] wip: zen --- .../component/workspace/payment-section.tsx | 23 ++++++++++++++++++- cloud/app/src/routes/zen/handler.ts | 1 - cloud/core/src/billing.ts | 17 ++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/cloud/app/src/component/workspace/payment-section.tsx b/cloud/app/src/component/workspace/payment-section.tsx index a346cb55..d45b01c0 100644 --- a/cloud/app/src/component/workspace/payment-section.tsx +++ b/cloud/app/src/component/workspace/payment-section.tsx @@ -1,5 +1,5 @@ import { Billing } from "@opencode/cloud-core/billing.js" -import { query, useParams, createAsync } from "@solidjs/router" +import { query, action, useParams, createAsync, useAction } from "@solidjs/router" import { For } from "solid-js" import { withActor } from "~/context/auth.withActor" import { formatDateUTC, formatDateForTable } from "./common" @@ -12,9 +12,15 @@ const getPaymentsInfo = query(async (workspaceID: string) => { }, workspaceID) }, "payment.list") +const downloadReceipt = action(async (workspaceID: string, paymentID: string) => { + "use server" + return withActor(() => Billing.generateReceiptUrl({ paymentID }), workspaceID) +}, "receipt.download") + export function PaymentSection() { const params = useParams() const payments = createAsync(() => getPaymentsInfo(params.id)) + const downloadReceiptAction = useAction(downloadReceipt) return ( payments() && @@ -31,6 +37,7 @@ export function PaymentSection() { Date Payment ID Amount + Receipt @@ -44,6 +51,20 @@ export function PaymentSection() { {payment.id} ${((payment.amount ?? 0) / 100000000).toFixed(2)} + + + ) }} diff --git a/cloud/app/src/routes/zen/handler.ts b/cloud/app/src/routes/zen/handler.ts index bfa4d077..c2c5c45a 100644 --- a/cloud/app/src/routes/zen/handler.ts +++ b/cloud/app/src/routes/zen/handler.ts @@ -538,7 +538,6 @@ export async function handler( async function reload() { if (!apiKey) return - // acquire reload lock const lock = await Database.use((tx) => tx .update(BillingTable) diff --git a/cloud/core/src/billing.ts b/cloud/core/src/billing.ts index cbf064bf..2254adc7 100644 --- a/cloud/core/src/billing.ts +++ b/cloud/core/src/billing.ts @@ -224,4 +224,21 @@ export namespace Billing { return session.url }, ) + + export const generateReceiptUrl = fn( + z.object({ + paymentID: z.string(), + }), + async (input) => { + const { paymentID } = input + + const intent = await Billing.stripe().paymentIntents.retrieve(paymentID) + if (!intent.latest_charge) throw new Error("No charge found") + + const charge = await Billing.stripe().charges.retrieve(intent.latest_charge as string) + if (!charge.receipt_url) throw new Error("No receipt URL found") + + return charge.receipt_url + }, + ) }