/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import i18next from 'i18next'
import { useState, useCallback } from 'react'
import { toast } from 'sonner'
import {
calculateAmount,
calculateStripeAmount,
calculateWaffoAmount,
calculateWaffoPancakeAmount,
requestPayment,
requestStripePayment,
isApiSuccess,
} from '../api'
import {
isStripePayment,
isWaffoPayment,
isWaffoPancakePayment,
submitPaymentForm,
} from '../lib'
import type { AmountRequest, AmountResponse } from '../types'
// ============================================================================
// Payment Hook
// ============================================================================
type AmountCalculator = (request: AmountRequest) => Promise
export interface PaymentAmountCalculators {
regular: AmountCalculator
stripe: AmountCalculator
waffo: AmountCalculator
waffoPancake: AmountCalculator
}
const defaultPaymentAmountCalculators: PaymentAmountCalculators = {
regular: calculateAmount,
stripe: calculateStripeAmount,
waffo: calculateWaffoAmount,
waffoPancake: calculateWaffoPancakeAmount,
}
export async function requestPaymentAmount(
topupAmount: number,
paymentType: string,
calculators: PaymentAmountCalculators = defaultPaymentAmountCalculators
): Promise {
let calculator = calculators.regular
if (isStripePayment(paymentType)) {
calculator = calculators.stripe
} else if (isWaffoPayment(paymentType)) {
calculator = calculators.waffo
} else if (isWaffoPancakePayment(paymentType)) {
calculator = calculators.waffoPancake
}
const response = await calculator({ amount: topupAmount })
if (!isApiSuccess(response) || !response.data) {
return 0
}
return Number.parseFloat(response.data)
}
export function usePayment() {
const [amount, setAmount] = useState(0)
const [calculating, setCalculating] = useState(false)
const [processing, setProcessing] = useState(false)
// Calculate payment amount
const calculatePaymentAmount = useCallback(
async (topupAmount: number, paymentType: string) => {
try {
setCalculating(true)
const calculatedAmount = await requestPaymentAmount(
topupAmount,
paymentType
)
setAmount(calculatedAmount)
return calculatedAmount
} catch {
setAmount(0)
return 0
} finally {
setCalculating(false)
}
},
[]
)
// Process payment
const processPayment = useCallback(
async (topupAmount: number, paymentType: string) => {
try {
setProcessing(true)
const isStripe = isStripePayment(paymentType)
const amount = Math.floor(topupAmount)
const response = isStripe
? await requestStripePayment({
amount,
payment_method: 'stripe',
})
: await requestPayment({
amount,
payment_method: paymentType,
})
if (!isApiSuccess(response)) {
toast.error(response.message || i18next.t('Payment request failed'))
return false
}
// Handle Stripe payment
if (isStripe && response.data?.pay_link) {
window.open(response.data.pay_link as string, '_blank')
toast.success(i18next.t('Redirecting to payment page...'))
return true
}
// Handle non-Stripe payment
if (!isStripe && response.data) {
const url = (response as unknown as { url?: string }).url
if (url) {
submitPaymentForm(url, response.data)
toast.success(i18next.t('Redirecting to payment page...'))
return true
}
}
return false
} catch {
toast.error(i18next.t('Payment request failed'))
return false
} finally {
setProcessing(false)
}
},
[]
)
return {
amount,
calculating,
processing,
calculatePaymentAmount,
processPayment,
setAmount,
}
}