[Feature Request] Waffo Pancake gateway — full integration with subscription support + admin catalog binding flow (#4935)

This commit is contained in:
Hill-waffo
2026-05-22 11:00:58 +08:00
committed by GitHub
parent 8e5e89bb5b
commit 19f1821fc8
45 changed files with 2432 additions and 1086 deletions
@@ -42,6 +42,7 @@ import {
paySubscriptionStripe,
paySubscriptionCreem,
paySubscriptionEpay,
paySubscriptionWaffoPancake,
} from '../../api'
import { formatDuration, formatResetPeriod } from '../../lib'
import type { PlanRecord } from '../../types'
@@ -57,6 +58,7 @@ interface Props {
plan: PlanRecord | null
enableStripe?: boolean
enableCreem?: boolean
enableWaffoPancake?: boolean
enableOnlineTopUp?: boolean
epayMethods?: PaymentMethod[]
purchaseLimit?: number
@@ -81,9 +83,11 @@ export function SubscriptionPurchaseDialog(props: Props) {
const hasStripe = props.enableStripe && !!plan.stripe_price_id
const hasCreem = props.enableCreem && !!plan.creem_product_id
const hasWaffoPancake =
props.enableWaffoPancake && !!plan.waffo_pancake_product_id
const hasEpay =
props.enableOnlineTopUp && (props.epayMethods || []).length > 0
const hasAnyPayment = hasStripe || hasCreem || hasEpay
const hasAnyPayment = hasStripe || hasCreem || hasWaffoPancake || hasEpay
const selectedEpayMethodLabel =
(props.epayMethods || []).find((m) => m.type === selectedEpayMethod)
?.name ||
@@ -139,6 +143,29 @@ export function SubscriptionPurchaseDialog(props: Props) {
}
}
// In-tab redirect (not window.open) — user-gesture context is lost
// across the await, so a popup would be blocked. Same as the wallet hook.
const handlePayWaffoPancake = async () => {
setPaying(true)
try {
const res = await paySubscriptionWaffoPancake({ plan_id: plan.id })
if (res.message === 'success' && res.data?.checkout_url) {
toast.success(t('Redirecting to payment page...'))
window.location.href = res.data.checkout_url
} else {
toast.error(
res.message && res.message !== 'success'
? res.message
: t('Payment request failed')
)
}
} catch {
toast.error(t('Payment request failed'))
} finally {
setPaying(false)
}
}
const isSafari =
typeof navigator !== 'undefined' &&
/^((?!chrome|android).)*safari/i.test(navigator.userAgent)
@@ -262,7 +289,7 @@ export function SubscriptionPurchaseDialog(props: Props) {
<p className='text-muted-foreground text-xs'>
{t('Select payment method')}
</p>
{(hasStripe || hasCreem) && (
{(hasStripe || hasCreem || hasWaffoPancake) && (
<div className='grid grid-cols-2 gap-2 sm:flex'>
{hasStripe && (
<Button
@@ -284,6 +311,16 @@ export function SubscriptionPurchaseDialog(props: Props) {
Creem
</Button>
)}
{hasWaffoPancake && (
<Button
variant='outline'
className='flex-1'
onClick={handlePayWaffoPancake}
disabled={paying || limitReached}
>
Waffo Pancake
</Button>
)}
</div>
)}
{hasEpay && (