This commit is contained in:
Frank
2025-09-16 17:49:37 -04:00
parent ef10097329
commit 8c71107a93
3 changed files with 39 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
import { Billing } from "@opencode/cloud-core/billing.js" 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 { For } from "solid-js"
import { withActor } from "~/context/auth.withActor" import { withActor } from "~/context/auth.withActor"
import { formatDateUTC, formatDateForTable } from "./common" import { formatDateUTC, formatDateForTable } from "./common"
@@ -12,9 +12,15 @@ const getPaymentsInfo = query(async (workspaceID: string) => {
}, workspaceID) }, workspaceID)
}, "payment.list") }, "payment.list")
const downloadReceipt = action(async (workspaceID: string, paymentID: string) => {
"use server"
return withActor(() => Billing.generateReceiptUrl({ paymentID }), workspaceID)
}, "receipt.download")
export function PaymentSection() { export function PaymentSection() {
const params = useParams() const params = useParams()
const payments = createAsync(() => getPaymentsInfo(params.id)) const payments = createAsync(() => getPaymentsInfo(params.id))
const downloadReceiptAction = useAction(downloadReceipt)
return ( return (
payments() && payments() &&
@@ -31,6 +37,7 @@ export function PaymentSection() {
<th>Date</th> <th>Date</th>
<th>Payment ID</th> <th>Payment ID</th>
<th>Amount</th> <th>Amount</th>
<th>Receipt</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -44,6 +51,20 @@ export function PaymentSection() {
</td> </td>
<td data-slot="payment-id">{payment.id}</td> <td data-slot="payment-id">{payment.id}</td>
<td data-slot="payment-amount">${((payment.amount ?? 0) / 100000000).toFixed(2)}</td> <td data-slot="payment-amount">${((payment.amount ?? 0) / 100000000).toFixed(2)}</td>
<td data-slot="payment-receipt">
<button
onClick={async () => {
const receiptUrl = await downloadReceiptAction(params.id, payment.paymentID!)
if (receiptUrl) {
window.open(receiptUrl, "_blank")
}
}}
data-slot="receipt-button"
style="cursor: pointer;"
>
download
</button>
</td>
</tr> </tr>
) )
}} }}

View File

@@ -538,7 +538,6 @@ export async function handler(
async function reload() { async function reload() {
if (!apiKey) return if (!apiKey) return
// acquire reload lock
const lock = await Database.use((tx) => const lock = await Database.use((tx) =>
tx tx
.update(BillingTable) .update(BillingTable)

View File

@@ -224,4 +224,21 @@ export namespace Billing {
return session.url 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
},
)
} }