Checkout on credit add (#3555)

This commit is contained in:
Christian Byrne
2025-04-22 13:46:05 +08:00
committed by GitHub
parent bf4ad38e9b
commit e7fe2046ba
3 changed files with 89 additions and 3 deletions

View File

@@ -21,7 +21,11 @@
/>
<div class="text-3xl font-bold">{{ creditBalance }}</div>
</div>
<Button :label="$t('credits.purchaseCredits')" />
<Button
:label="$t('credits.purchaseCredits')"
:loading
@click="handlePurchaseCreditsClick"
/>
</div>
</div>
@@ -91,9 +95,17 @@ import TabPanel from 'primevue/tabpanel'
import Tag from 'primevue/tag'
import { ref } from 'vue'
// Mock data - in a real implementation, this would come from a store or API
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
import { usdToMicros } from '@/utils/formatUtil'
// TODO: Mock data - in a real implementation, this would come from a store or API
const creditBalance = ref(0.05)
// TODO: Either: (1) Get checkout URL that allows setting price on Stripe side, (2) Add number selection on credits panel
const selectedCurrencyAmount = usdToMicros(10)
const selectedCurrency = 'usd' // For now, only USD is supported on comfy-api backend
interface CreditHistoryItemData {
title: string
timestamp: string
@@ -101,6 +113,22 @@ interface CreditHistoryItemData {
isPositive: boolean
}
const { initiateCreditPurchase, loading } = useFirebaseAuthStore()
const handlePurchaseCreditsClick = async () => {
const response = await initiateCreditPurchase({
amount_micros: selectedCurrencyAmount,
currency: selectedCurrency
})
if (!response) return
const { checkout_url } = response
if (checkout_url !== undefined) {
// Go to Stripe checkout page
window.open(checkout_url, '_blank')
}
}
const creditHistory = ref<CreditHistoryItemData[]>([
{
title: 'Kling Text-to-Video v1-6',

View File

@@ -16,6 +16,16 @@ import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { useFirebaseAuth } from 'vuefire'
import { operations } from '@/types/comfyRegistryTypes'
type CreditPurchaseResponse =
operations['InitiateCreditPurchase']['responses']['201']['content']['application/json']
type CreditPurchasePayload =
operations['InitiateCreditPurchase']['requestBody']['content']['application/json']
// TODO: Switch to prod api based on environment (requires prod api to be ready)
const API_BASE_URL = 'https://stagingapi.comfy.org'
export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
// State
const loading = ref(false)
@@ -100,6 +110,39 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
return null
}
const addCredits = async (
requestBodyContent: CreditPurchasePayload
): Promise<CreditPurchaseResponse | null> => {
const token = await getIdToken()
if (!token) {
error.value = 'Cannot add credits: User not authenticated'
return null
}
const response = await fetch(`${API_BASE_URL}/customers/credit`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify(requestBodyContent)
})
if (!response.ok) {
const errorData = await response.json()
error.value = `Failed to initiate credit purchase: ${errorData.message}`
return null
}
// TODO: start polling /listBalance until balance is updated or n retries fail or report no change
return response.json()
}
const initiateCreditPurchase = async (
requestBodyContent: CreditPurchasePayload
): Promise<CreditPurchaseResponse | null> =>
executeAuthAction((_) => addCredits(requestBodyContent))
return {
// State
loading,
@@ -118,6 +161,7 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
logout,
getIdToken,
loginWithGoogle,
loginWithGithub
loginWithGithub,
initiateCreditPurchase
}
})

View File

@@ -415,3 +415,17 @@ export function compareVersions(
return 0
}
/**
* Converts a USD amount to microdollars (1/1,000,000 of a dollar).
* This conversion is commonly used in financial systems to avoid floating-point precision issues
* by representing monetary values as integers.
*
* @param usd - The amount in US dollars to convert
* @returns The amount in microdollars (multiplied by 1,000,000)
* @example
* usdToMicros(1.23) // returns 1230000
*/
export function usdToMicros(usd: number): number {
return Math.round(usd * 1_000_000)
}