[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 2437 additions and 1091 deletions
@@ -111,6 +111,7 @@ export function SubscriptionPlansCard({
const enableStripe = !!topupInfo?.enable_stripe_topup
const enableCreem = !!topupInfo?.enable_creem_topup
const enableWaffoPancake = !!topupInfo?.enable_waffo_pancake_topup
const enableOnlineTopUp = !!topupInfo?.enable_online_topup
const epayMethods = useMemo(
() => getEpayMethods(topupInfo?.pay_methods),
@@ -629,6 +630,7 @@ export function SubscriptionPlansCard({
plan={selectedPlan}
enableStripe={enableStripe}
enableCreem={enableCreem}
enableWaffoPancake={enableWaffoPancake}
enableOnlineTopUp={enableOnlineTopUp}
epayMethods={epayMethods}
purchaseLimit={
@@ -59,11 +59,10 @@ function getErrorMessage(message: string | undefined, data: unknown): string {
}
/**
* Hook for handling Waffo Pancake payment processing
* Hook for the Waffo Pancake hosted-checkout flow.
*
* Pancake uses a hosted checkout URL flow rather than the generic epay form
* submission, so we open the returned URL in a new tab once the backend
* returns a successful response.
* Same-tab redirect (window.location.href) rather than window.open: the
* user-gesture context is lost across the await, so popups get blocked.
*/
export function useWaffoPancakePayment() {
const [processing, setProcessing] = useState(false)
@@ -85,8 +84,8 @@ export function useWaffoPancakePayment() {
toast.error(i18next.t('Invalid payment redirect URL'))
return false
}
window.open(checkoutUrl, '_blank', 'noopener,noreferrer')
toast.success(i18next.t('Redirecting to payment page...'))
window.location.href = checkoutUrl
return true
}
}
+19 -4
View File
@@ -19,6 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
import { type ReactNode } from 'react'
import { CreditCard, Landmark } from 'lucide-react'
import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si'
import i18next from 'i18next'
import { PAYMENT_TYPES, PAYMENT_ICON_COLORS } from '../constants'
// ============================================================================
@@ -121,11 +122,25 @@ export function getPaymentIcon(
/>
)
case PAYMENT_TYPES.WAFFO_PANCAKE:
// The W glyph fills only ~40% of its viewBox vertically (wide and
// short letterform); scale(2) brings its rendered height in line
// with Stripe's S and Creem's Landmark.
return (
<CreditCard
className={className}
style={{ color: PAYMENT_ICON_COLORS[PAYMENT_TYPES.WAFFO_PANCAKE] }}
/>
<span
className={`inline-flex items-center justify-center leading-none ${className}`}
style={{ transform: 'scale(2)' }}
>
<img
src='/waffo-logo-light.svg'
alt={i18next.t('Waffo')}
className='block h-full w-full object-contain dark:hidden'
/>
<img
src='/waffo-logo-dark.svg'
alt={i18next.t('Waffo')}
className='hidden h-full w-full object-contain dark:block'
/>
</span>
)
default:
return <CreditCard className={className} />
+5
View File
@@ -51,6 +51,11 @@ export type WaffoPancakePaymentResponse = ApiResponse<
session_id?: string
expires_at?: number | string
order_id?: string
// Self-service session token + expiry — surfaced by the backend so
// future flows (refund / cancel from new-api's own UI) can use them
// without re-issuing checkout. Not consumed by the current handler.
token?: string
token_expires_at?: number | string
}
| string
>