/*
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 {
PAYMENT_TYPES,
DEFAULT_PRESET_MULTIPLIERS,
DEFAULT_PAYMENT_TYPE,
DEFAULT_MIN_TOPUP,
} from '../constants'
import type { PaymentMethod, PresetAmount, TopupInfo } from '../types'
// ============================================================================
// Payment Processing Functions
// ============================================================================
/**
* Check if browser is Safari
*/
function isSafariBrowser(): boolean {
return (
navigator.userAgent.includes('Safari') &&
!navigator.userAgent.includes('Chrome')
)
}
/**
* Submit payment form (for non-Stripe payments)
*/
export function submitPaymentForm(
url: string,
params: Record
): void {
const form = document.createElement('form')
form.action = url
form.method = 'POST'
// Don't open in new tab for Safari
if (!isSafariBrowser()) {
form.target = '_blank'
}
// Add form parameters
Object.entries(params).forEach(([key, value]) => {
const input = document.createElement('input')
input.type = 'hidden'
input.name = key
input.value = String(value)
form.appendChild(input)
})
document.body.appendChild(form)
form.submit()
document.body.removeChild(form)
}
/**
* Check if payment method is Stripe
*/
export function isStripePayment(paymentType: string): boolean {
return paymentType === PAYMENT_TYPES.STRIPE
}
/**
* Check if payment method is Waffo
*/
export function isWaffoPayment(paymentType: string): boolean {
return paymentType === PAYMENT_TYPES.WAFFO
}
/**
* Check if payment method is Waffo Pancake
*
* Pancake is a metered-style payment that goes through a dedicated checkout
* URL flow rather than the generic epay form submission, so it must be
* special-cased in payment dispatch logic.
*/
export function isWaffoPancakePayment(paymentType: string): boolean {
return paymentType === PAYMENT_TYPES.WAFFO_PANCAKE
}
export interface PaymentProcessors {
regular: (topupAmount: number, paymentType: string) => Promise
waffo: (topupAmount: number, payMethodIndex: number) => Promise
waffoPancake: (topupAmount: number) => Promise
}
export async function dispatchSelectedPayment(
paymentMethod: PaymentMethod,
topupAmount: number,
waffoMethodIndex: number | null,
processors: PaymentProcessors
): Promise {
if (isWaffoPayment(paymentMethod.type)) {
if (waffoMethodIndex === null) {
return false
}
return processors.waffo(topupAmount, waffoMethodIndex)
}
if (isWaffoPancakePayment(paymentMethod.type)) {
return processors.waffoPancake(topupAmount)
}
return processors.regular(topupAmount, paymentMethod.type)
}
/**
* Get default payment type from topup info
*/
export function getDefaultPaymentType(topupInfo: TopupInfo | null): string {
if (!topupInfo) {
return DEFAULT_PAYMENT_TYPE
}
// Return first available payment method or default
if (topupInfo.pay_methods?.length > 0) {
return topupInfo.pay_methods[0].type
}
if (topupInfo.enable_stripe_topup) {
return PAYMENT_TYPES.STRIPE
}
if (topupInfo.enable_waffo_topup) {
return PAYMENT_TYPES.WAFFO
}
if (topupInfo.enable_waffo_pancake_topup) {
return PAYMENT_TYPES.WAFFO_PANCAKE
}
return DEFAULT_PAYMENT_TYPE
}
/**
* Get minimum topup amount from topup info
*/
export function getMinTopupAmount(topupInfo: TopupInfo | null): number {
if (!topupInfo) {
return DEFAULT_MIN_TOPUP
}
if (topupInfo.enable_online_topup) {
return topupInfo.min_topup
}
if (topupInfo.enable_stripe_topup) {
return topupInfo.stripe_min_topup
}
if (topupInfo.enable_waffo_topup) {
return topupInfo.waffo_min_topup || DEFAULT_MIN_TOPUP
}
if (topupInfo.enable_waffo_pancake_topup) {
return topupInfo.waffo_pancake_min_topup || DEFAULT_MIN_TOPUP
}
return DEFAULT_MIN_TOPUP
}
/**
* Generate preset amounts based on minimum topup
*/
export function generatePresetAmounts(minAmount: number): PresetAmount[] {
return DEFAULT_PRESET_MULTIPLIERS.map((multiplier) => ({
value: minAmount * multiplier,
}))
}
/**
* Merge custom preset amounts with discounts
*/
export function mergePresetAmounts(
amountOptions: number[],
discounts: Record
): PresetAmount[] {
if (!amountOptions || amountOptions.length === 0) {
return []
}
return amountOptions.map((amount) => ({
value: amount,
discount: discounts[amount] || 1.0,
}))
}