11import { action , useParams , useAction , useSubmission , json , query , createAsync } from "@solidjs/router"
22import { createStore } from "solid-js/store"
33import { createMemo , For , Show } from "solid-js"
4+ import { Modal } from "~/component/modal"
45import { Billing } from "@opencode-ai/console-core/billing.js"
56import { Database , eq , and , isNull } from "@opencode-ai/console-core/drizzle/index.js"
67import { BillingTable , LiteTable } from "@opencode-ai/console-core/schema/billing.sql.js"
@@ -14,6 +15,8 @@ import { useI18n } from "~/context/i18n"
1415import { useLanguage } from "~/context/language"
1516import { formError } from "~/lib/form-error"
1617
18+ import { IconAlipay , IconUpi } from "~/component/icon"
19+
1720const queryLiteSubscription = query ( async ( workspaceID : string ) => {
1821 "use server"
1922 return withActor ( async ( ) => {
@@ -78,22 +81,25 @@ function formatResetTime(seconds: number, i18n: ReturnType<typeof useI18n>) {
7881 return `${ minutes } ${ minutes === 1 ? i18n . t ( "workspace.lite.time.minute" ) : i18n . t ( "workspace.lite.time.minutes" ) } `
7982}
8083
81- const createLiteCheckoutUrl = action ( async ( workspaceID : string , successUrl : string , cancelUrl : string ) => {
82- "use server"
83- return json (
84- await withActor (
85- ( ) =>
86- Billing . generateLiteCheckoutUrl ( { successUrl, cancelUrl } )
87- . then ( ( data ) => ( { error : undefined , data } ) )
88- . catch ( ( e ) => ( {
89- error : e . message as string ,
90- data : undefined ,
91- } ) ) ,
92- workspaceID ,
93- ) ,
94- { revalidate : [ queryBillingInfo . key , queryLiteSubscription . key ] } ,
95- )
96- } , "liteCheckoutUrl" )
84+ const createLiteCheckoutUrl = action (
85+ async ( workspaceID : string , successUrl : string , cancelUrl : string , method ?: "alipay" | "upi" ) => {
86+ "use server"
87+ return json (
88+ await withActor (
89+ ( ) =>
90+ Billing . generateLiteCheckoutUrl ( { successUrl, cancelUrl, method } )
91+ . then ( ( data ) => ( { error : undefined , data } ) )
92+ . catch ( ( e ) => ( {
93+ error : e . message as string ,
94+ data : undefined ,
95+ } ) ) ,
96+ workspaceID ,
97+ ) ,
98+ { revalidate : [ queryBillingInfo . key , queryLiteSubscription . key ] } ,
99+ )
100+ } ,
101+ "liteCheckoutUrl" ,
102+ )
97103
98104const createSessionUrl = action ( async ( workspaceID : string , returnUrl : string ) => {
99105 "use server"
@@ -147,23 +153,30 @@ export function LiteSection() {
147153 const checkoutSubmission = useSubmission ( createLiteCheckoutUrl )
148154 const useBalanceSubmission = useSubmission ( setLiteUseBalance )
149155 const [ store , setStore ] = createStore ( {
150- redirecting : false ,
156+ loading : undefined as undefined | "session" | "checkout" | "alipay" | "upi" ,
157+ showModal : false ,
151158 } )
152159
160+ const busy = createMemo ( ( ) => ! ! store . loading )
161+
153162 async function onClickSession ( ) {
163+ setStore ( "loading" , "session" )
154164 const result = await sessionAction ( params . id ! , window . location . href )
155165 if ( result . data ) {
156- setStore ( "redirecting" , true )
157166 window . location . href = result . data
167+ return
158168 }
169+ setStore ( "loading" , undefined )
159170 }
160171
161- async function onClickSubscribe ( ) {
162- const result = await checkoutAction ( params . id ! , window . location . href , window . location . href )
172+ async function onClickSubscribe ( method ?: "alipay" | "upi" ) {
173+ setStore ( "loading" , method ?? "checkout" )
174+ const result = await checkoutAction ( params . id ! , window . location . href , window . location . href , method )
163175 if ( result . data ) {
164- setStore ( "redirecting" , true )
165176 window . location . href = result . data
177+ return
166178 }
179+ setStore ( "loading" , undefined )
167180 }
168181
169182 return (
@@ -179,12 +192,8 @@ export function LiteSection() {
179192 < div data-slot = "section-title" >
180193 < div data-slot = "title-row" >
181194 < p > { i18n . t ( "workspace.lite.subscription.message" ) } </ p >
182- < button
183- data-color = "primary"
184- disabled = { sessionSubmission . pending || store . redirecting }
185- onClick = { onClickSession }
186- >
187- { sessionSubmission . pending || store . redirecting
195+ < button data-color = "primary" disabled = { sessionSubmission . pending || busy ( ) } onClick = { onClickSession } >
196+ { store . loading === "session"
188197 ? i18n . t ( "workspace.lite.loading" )
189198 : i18n . t ( "workspace.lite.subscription.manage" ) }
190199 </ button >
@@ -282,16 +291,60 @@ export function LiteSection() {
282291 < li > MiniMax M2.7</ li >
283292 </ ul >
284293 < p data-slot = "promo-description" > { i18n . t ( "workspace.lite.promo.footer" ) } </ p >
285- < button
286- data-slot = "subscribe-button"
287- data-color = "primary"
288- disabled = { checkoutSubmission . pending || store . redirecting }
289- onClick = { onClickSubscribe }
290- >
291- { checkoutSubmission . pending || store . redirecting
292- ? i18n . t ( "workspace.lite.promo.subscribing" )
293- : i18n . t ( "workspace.lite.promo.subscribe" ) }
294- </ button >
294+ < div data-slot = "subscribe-actions" >
295+ < button
296+ data-slot = "subscribe-button"
297+ data-color = "primary"
298+ disabled = { checkoutSubmission . pending || busy ( ) }
299+ onClick = { ( ) => onClickSubscribe ( ) }
300+ >
301+ { store . loading === "checkout"
302+ ? i18n . t ( "workspace.lite.promo.subscribing" )
303+ : i18n . t ( "workspace.lite.promo.subscribe" ) }
304+ </ button >
305+ < button
306+ type = "button"
307+ data-slot = "other-methods"
308+ data-color = "ghost"
309+ onClick = { ( ) => setStore ( "showModal" , true ) }
310+ >
311+ < span > Other payment methods</ span >
312+ < span data-slot = "other-methods-icons" >
313+ < span > </ span >
314+ < IconAlipay style = { { width : "16px" , height : "16px" } } />
315+ < span > </ span >
316+ < IconUpi style = { { width : "auto" , height : "10px" } } />
317+ </ span >
318+ </ button >
319+ </ div >
320+ < Modal open = { store . showModal } onClose = { ( ) => setStore ( "showModal" , false ) } title = "Select payment method" >
321+ < div data-slot = "modal-actions" >
322+ < button
323+ type = "button"
324+ data-slot = "method-button"
325+ data-color = "ghost"
326+ disabled = { checkoutSubmission . pending || busy ( ) }
327+ onClick = { ( ) => onClickSubscribe ( "alipay" ) }
328+ >
329+ < Show when = { store . loading !== "alipay" } >
330+ < IconAlipay style = { { width : "24px" , height : "24px" } } />
331+ </ Show >
332+ { store . loading === "alipay" ? i18n . t ( "workspace.lite.promo.subscribing" ) : "Alipay" }
333+ </ button >
334+ < button
335+ type = "button"
336+ data-slot = "method-button"
337+ data-color = "ghost"
338+ disabled = { checkoutSubmission . pending || busy ( ) }
339+ onClick = { ( ) => onClickSubscribe ( "upi" ) }
340+ >
341+ < Show when = { store . loading !== "upi" } >
342+ < IconUpi style = { { width : "auto" , height : "16px" } } />
343+ </ Show >
344+ { store . loading === "upi" ? i18n . t ( "workspace.lite.promo.subscribing" ) : "UPI" }
345+ </ button >
346+ </ div >
347+ </ Modal >
295348 </ section >
296349 </ Show >
297350 </ >
0 commit comments