feat: require compliance confirmation for paid features
Gate payment, redemption, subscription, and invitation reward flows behind an audited compliance acknowledgement.
This commit is contained in:
@@ -30,6 +30,7 @@ interface AffiliateRewardsCardProps {
|
||||
user: UserWalletData | null
|
||||
affiliateLink: string
|
||||
onTransfer: () => void
|
||||
complianceConfirmed?: boolean
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
@@ -37,6 +38,7 @@ export function AffiliateRewardsCard({
|
||||
user,
|
||||
affiliateLink,
|
||||
onTransfer,
|
||||
complianceConfirmed = true,
|
||||
loading,
|
||||
}: AffiliateRewardsCardProps) {
|
||||
const { t } = useTranslation()
|
||||
@@ -110,6 +112,7 @@ export function AffiliateRewardsCard({
|
||||
{hasRewards && (
|
||||
<Button
|
||||
onClick={onTransfer}
|
||||
disabled={!complianceConfirmed}
|
||||
className='h-9 shrink-0 px-3'
|
||||
size='sm'
|
||||
>
|
||||
@@ -117,6 +120,13 @@ export function AffiliateRewardsCard({
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{!complianceConfirmed ? (
|
||||
<p className='text-muted-foreground text-xs lg:col-span-3'>
|
||||
{t(
|
||||
'Referral reward transfer is disabled until the administrator confirms compliance terms.'
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -135,6 +135,7 @@ export function RechargeFormCard({
|
||||
const hasWaffoPaymentMethods =
|
||||
Array.isArray(waffoPayMethods) && waffoPayMethods.length > 0
|
||||
const minTopup = getMinTopupAmount(topupInfo)
|
||||
const redemptionEnabled = topupInfo?.enable_redemption !== false
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -445,49 +446,59 @@ export function RechargeFormCard({
|
||||
)}
|
||||
|
||||
{/* Redemption Code Section */}
|
||||
<div className='space-y-2.5 border-t pt-4 sm:space-y-3 sm:pt-6'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Gift className='text-muted-foreground h-4 w-4' />
|
||||
<Label
|
||||
htmlFor='redemption-code'
|
||||
className='text-muted-foreground text-xs font-medium tracking-wider uppercase'
|
||||
>
|
||||
{t('Have a Code?')}
|
||||
</Label>
|
||||
</div>
|
||||
<div className='grid grid-cols-[minmax(0,1fr)_auto] gap-2'>
|
||||
<Input
|
||||
id='redemption-code'
|
||||
value={redemptionCode}
|
||||
onChange={(e) => onRedemptionCodeChange(e.target.value)}
|
||||
placeholder={t('Enter your redemption code')}
|
||||
className='h-9 min-w-0'
|
||||
/>
|
||||
<Button
|
||||
onClick={onRedeem}
|
||||
disabled={redeeming}
|
||||
variant='outline'
|
||||
className='h-9 px-4'
|
||||
>
|
||||
{redeeming && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
|
||||
{t('Redeem')}
|
||||
</Button>
|
||||
</div>
|
||||
{topupLink && (
|
||||
<p className='text-muted-foreground text-xs'>
|
||||
{t('Need a redemption code?')}{' '}
|
||||
<a
|
||||
href={topupLink}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='inline-flex items-center gap-1 underline-offset-4 hover:underline'
|
||||
{redemptionEnabled ? (
|
||||
<div className='space-y-2.5 border-t pt-4 sm:space-y-3 sm:pt-6'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Gift className='text-muted-foreground h-4 w-4' />
|
||||
<Label
|
||||
htmlFor='redemption-code'
|
||||
className='text-muted-foreground text-xs font-medium tracking-wider uppercase'
|
||||
>
|
||||
{t('Get one here')}
|
||||
<ExternalLink className='h-3 w-3' />
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{t('Have a Code?')}
|
||||
</Label>
|
||||
</div>
|
||||
<div className='grid grid-cols-[minmax(0,1fr)_auto] gap-2'>
|
||||
<Input
|
||||
id='redemption-code'
|
||||
value={redemptionCode}
|
||||
onChange={(e) => onRedemptionCodeChange(e.target.value)}
|
||||
placeholder={t('Enter your redemption code')}
|
||||
className='h-9 min-w-0'
|
||||
/>
|
||||
<Button
|
||||
onClick={onRedeem}
|
||||
disabled={redeeming}
|
||||
variant='outline'
|
||||
className='h-9 px-4'
|
||||
>
|
||||
{redeeming && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
|
||||
{t('Redeem')}
|
||||
</Button>
|
||||
</div>
|
||||
{topupLink && (
|
||||
<p className='text-muted-foreground text-xs'>
|
||||
{t('Need a redemption code?')}{' '}
|
||||
<a
|
||||
href={topupLink}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='inline-flex items-center gap-1 underline-offset-4 hover:underline'
|
||||
>
|
||||
{t('Get one here')}
|
||||
<ExternalLink className='h-3 w-3' />
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Alert className='border-t'>
|
||||
<AlertDescription>
|
||||
{t(
|
||||
'Redemption codes are disabled until the administrator confirms compliance terms.'
|
||||
)}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</TitledCard>
|
||||
)
|
||||
}
|
||||
|
||||
+3
@@ -319,6 +319,9 @@ export function Wallet(props: WalletProps) {
|
||||
user={user}
|
||||
affiliateLink={affiliateLink}
|
||||
onTransfer={() => setTransferDialogOpen(true)}
|
||||
complianceConfirmed={
|
||||
topupInfo?.payment_compliance_confirmed !== false
|
||||
}
|
||||
loading={affiliateLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+6
@@ -145,6 +145,12 @@ export interface TopupInfo {
|
||||
enable_waffo_pancake_topup?: boolean
|
||||
/** Minimum topup amount for Waffo Pancake */
|
||||
waffo_pancake_min_topup?: number
|
||||
/** Whether redemption code usage is enabled */
|
||||
enable_redemption?: boolean
|
||||
/** Whether compliance confirmation has been completed */
|
||||
payment_compliance_confirmed?: boolean
|
||||
/** Current compliance terms version */
|
||||
payment_compliance_terms_version?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user