Skip to content

Commit 8d785f4

Browse files
authored
fix: mf-6783 restore transak on-ramp via cloudflare session proxy (#12391)
1 parent 945d934 commit 8d785f4

7 files changed

Lines changed: 83 additions & 20 deletions

File tree

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@
255255
"cashtags",
256256
"celo",
257257
"endregion",
258+
"frameable",
258259
"Hrefs",
259260
"linkedin",
260261
"luma",

packages/plugins/Transak/src/SiteAdaptor/BuyTokenDialog.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { InjectedDialog } from '@masknet/shared'
2-
import { DialogContent, IconButton } from '@mui/material'
3-
import { makeStyles } from '@masknet/theme'
42
import { Close as CloseIcon } from '@mui/icons-material'
5-
import { useTransakURL } from '../hooks/useTransakURL.js'
3+
import { DialogContent, IconButton, Stack, Typography } from '@mui/material'
4+
import { LoadingBase, makeStyles } from '@masknet/theme'
5+
import { useTransakWidgetURL } from '../hooks/useTransakWidgetURL.js'
66

77
const useStyles = makeStyles()((theme) => ({
88
dialogPaper: {
@@ -37,6 +37,11 @@ const useStyles = makeStyles()((theme) => ({
3737
border: 0,
3838
borderRadius: 12,
3939
},
40+
status: {
41+
height: 630,
42+
padding: theme.spacing(2),
43+
boxSizing: 'border-box',
44+
},
4045
}))
4146

4247
interface BuyTokenDialogProps extends withClasses<'root'> {
@@ -50,7 +55,11 @@ export function BuyTokenDialog(props: BuyTokenDialogProps) {
5055
const { classes } = useStyles(undefined, { props })
5156
const { code, address, open, onClose } = props
5257

53-
const transakURL = useTransakURL({
58+
const {
59+
data: widgetUrl,
60+
isLoading: loading,
61+
error,
62+
} = useTransakWidgetURL({
5463
defaultCryptoCurrency: code,
5564
walletAddress: address,
5665
})
@@ -69,10 +78,18 @@ export function BuyTokenDialog(props: BuyTokenDialogProps) {
6978
<IconButton className={classes.close} size="small" onClick={onClose}>
7079
<CloseIcon />
7180
</IconButton>
72-
{transakURL ?
81+
{loading ?
82+
<Stack className={classes.status} alignItems="center" justifyContent="center">
83+
<LoadingBase size={36} />
84+
</Stack>
85+
: error || !widgetUrl ?
86+
<Stack className={classes.status} alignItems="center" justifyContent="center">
87+
<Typography variant="body1">
88+
Transak is temporarily unavailable. Please try again later.
89+
</Typography>
90+
</Stack>
7391
// eslint-disable-next-line react/dom/no-missing-iframe-sandbox
74-
<iframe className={classes.frame} src={transakURL} />
75-
: null}
92+
: <iframe className={classes.frame} src={widgetUrl} allow="camera;microphone;payment" />}
7693
</DialogContent>
7794
</InjectedDialog>
7895
</div>

packages/plugins/Transak/src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { PluginID } from '@masknet/shared-base'
33
export const TRANSAK_API_KEY_PRODUCTION = '253be1f0-c6d8-46e7-9d80-38f33bf973e2'
44
export const TRANSAK_API_KEY_STAGING = '4fcd6904-706b-4aff-bd9d-77422813bbb7'
55

6+
// Worker that mints a Transak session widgetUrl (the bare apiKey URL is no longer frameable).
7+
export const TRANSAK_PROXY_HOST = 'https://transak-proxy.r2d2.to'
8+
69
export const PLUGIN_ID = PluginID.Transak
710
export const PLUGIN_NAME = 'Transak'
811
export const PLUGIN_DESCRIPTION = 'The Fiat On-Ramp Aggregator on Mask Network.'

packages/plugins/Transak/src/hooks/useTransakURL.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useMemo } from 'react'
2-
import { rgbToHex, useTheme } from '@mui/material'
2+
import { rgbToHex, useTheme, type Theme } from '@mui/material'
33
import { TRANSAK_API_KEY_PRODUCTION, TRANSAK_API_KEY_STAGING } from '../constants.js'
44
import { formatEthereumAddress } from '@masknet/web3-shared-evm'
55
import type { TransakConfig } from '../types.js'
@@ -23,20 +23,30 @@ const DEFAULT_PARAMETERS: TransakConfig = {
2323
excludeFiatCurrencies: 'KRW',
2424
}
2525

26+
// Query params shared by the legacy direct URL and the session proxy.
27+
export function buildTransakSearchParams(config?: Partial<TransakConfig>, theme?: Theme): URLSearchParams {
28+
const config_: TransakConfig = {
29+
...DEFAULT_PARAMETERS,
30+
referrerDomain: location.origin,
31+
themeColor: theme ? rgbToHex(theme.palette.maskColor.dark).slice(1) : undefined,
32+
exchangeScreenTitle:
33+
config?.walletAddress ? `Buy Crypto to ${formatEthereumAddress(config.walletAddress, 4)}` : undefined,
34+
...config,
35+
}
36+
const params = new URLSearchParams()
37+
Object.entries(config_).forEach(([key, value]) => {
38+
if (value === undefined || value === null) return
39+
params.append(key, String(value))
40+
})
41+
return params
42+
}
43+
2644
export function useTransakURL(config?: Partial<TransakConfig>) {
2745
const theme = useTheme()
28-
const search = useMemo(() => {
29-
const config_: TransakConfig = {
30-
...DEFAULT_PARAMETERS,
31-
themeColor: rgbToHex(theme.palette.maskColor.dark).slice(1),
32-
exchangeScreenTitle:
33-
config?.walletAddress ? `Buy Crypto to ${formatEthereumAddress(config.walletAddress, 4)}` : void 0,
34-
...config,
35-
}
36-
const params = new URLSearchParams()
37-
Object.entries(config_).forEach(([key, value = '']) => params.append(key, String(value)))
38-
return params.toString()
46+
const search = useMemo(
47+
() => buildTransakSearchParams(config, theme).toString(),
3948
// eslint-disable-next-line react-compiler/react-compiler
40-
}, [theme.palette.primary.main, JSON.stringify(config)])
49+
[theme.palette.primary.main, JSON.stringify(config)],
50+
)
4151
return `${HOST_MAP[process.env.NODE_ENV]}?${search}`
4252
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useMemo } from 'react'
2+
import { useTheme } from '@mui/material'
3+
import { useQuery } from '@tanstack/react-query'
4+
import { TRANSAK_PROXY_HOST } from '../constants.js'
5+
import type { TransakConfig } from '../types.js'
6+
import { buildTransakSearchParams } from './useTransakURL.js'
7+
8+
// Fetches a single-use, iframe-safe widgetUrl from the session proxy.
9+
export function useTransakWidgetURL(config?: Partial<TransakConfig>) {
10+
const theme = useTheme()
11+
const url = useMemo(
12+
() => `${TRANSAK_PROXY_HOST}?${buildTransakSearchParams(config, theme).toString()}`,
13+
// eslint-disable-next-line react-compiler/react-compiler
14+
[theme.palette.primary.main, JSON.stringify(config)],
15+
)
16+
return useQuery({
17+
queryKey: ['transak', 'widget-url', url],
18+
queryFn: async () => {
19+
const res = await fetch(url)
20+
if (!res.ok) throw new Error('Failed to create a Transak session')
21+
const json = (await res.json()) as { data?: { widgetUrl?: string } }
22+
const widgetUrl = json.data?.widgetUrl
23+
if (!widgetUrl) throw new Error('Transak session did not return a widget URL')
24+
return widgetUrl
25+
},
26+
// widgetUrl is single-use (one sessionId, 5 min) — fetch once per open, never refetch or reuse.
27+
staleTime: Infinity,
28+
gcTime: 0,
29+
})
30+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { PluginTransakMessages } from './messages.js'
22
export { useTransakAllowanceCoin } from './hooks/useTransakAllowanceCoin.js'
33
export { useTransakURL } from './hooks/useTransakURL.js'
4+
export { useTransakWidgetURL } from './hooks/useTransakWidgetURL.js'

packages/plugins/Transak/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface TransakConfig {
66
defaultFiatAmount?: number
77
defaultCryptoCurrency?: string
88
walletAddress?: string
9+
referrerDomain?: string
910
themeColor?: string
1011
countryCode?: string
1112
fiatCurrency?: string

0 commit comments

Comments
 (0)