Loading...
Loading...
Loading...
Anders & A-Cube S.r.l. / June 1, 2024 โข 2 min read
Open Banking UI is a production-ready payment interface application that provides seamless integration with open banking APIs. Built with React 18, Vite, and Material-UI, it delivers a secure and user-friendly experience for initiating payments, managing accounts, and viewing transaction history.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Vite + React 18 Frontend โ
โ Payment Flow โข Account Linking โข History โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Redux Toolkit State Layer โ
โ Auth โข Payments โข Accounts โข Transactions โ
โโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
โ โ
โโโโโโโโผโโโโโโโโโโโ โโโโโโโโโโโผโโโโโโโโโโโโโ
โ Open Banking โ โ Security Layer โ
โ APIs โ โ - OAuth 2.0 โ
โ - AISP โ โ - PKCE Flow โ
โ - PISP โ โ - Token Refresh โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
Payment Initiation Service Provider (PISP):
Account Information Service Provider (AISP):
Payment Initiation:
import { usePayment } from '@/hooks/usePayment'
import { PaymentRequest } from '@/types/payment'
export function PaymentForm() {
const { initiatePayment, loading, error } = usePayment()
const handleSubmit = async (data: PaymentRequest) => {
const result = await initiatePayment({
creditorAccount: {
iban: data.iban,
currency: 'EUR'
},
instructedAmount: {
amount: data.amount,
currency: 'EUR'
},
debtorAccount: {
iban: userAccount.iban
},
remittanceInformationUnstructured: data.description
})
if (result.success) {
// Redirect to bank for authorization
window.location.href = result.authorizationUrl
}
}
return (
<form onSubmit={handleSubmit}>
<TextField label="Recipient IBAN" name="iban" required />
<TextField label="Amount" name="amount" type="number" required />
<TextField label="Description" name="description" />
<Button type="submit" disabled={loading}>
Initiate Payment
</Button>
{error && <Alert severity="error">{error.message}</Alert>}
</form>
)
}Strong Customer Authentication:
export function SCAHandler() {
const { completeAuthentication } = useAuth()
const location = useLocation()
useEffect(() => {
const params = new URLSearchParams(location.search)
const code = params.get('code')
const state = params.get('state')
if (code && state) {
completeAuthentication({ code, state })
}
}, [location])
return
OAuth 2.0 + PKCE Flow:
import { generateCodeVerifier, generateCodeChallenge } from '@/utils/pkce'
export const useAccountLinking = () => {
const initiateLink = async (bankId: string) => {
// Generate PKCE challenge
const codeVerifier = generateCodeVerifier()
const codeChallenge = await generateCodeChallenge(codeVerifier)
// Store verifier for callback
sessionStorage.setItem('code_verifier', codeVerifier)
// Build authorization URL
const params = new URLSearchParams({
response_type: 'code'
Infinite Scroll with Virtualization:
import { useInfiniteQuery } from '@tanstack/react-query'
import { useVirtual } from 'react-virtual'
export function TransactionList({ accountId }) {
const {
data,
fetchNextPage,
hasNextPage
} = useInfiniteQuery({
queryKey: ['transactions', accountId],
queryFn: ({ pageParam = 0 }) =>
fetchTransactions(accountId, { offset: pageParam }),
getNextPageParam: (lastPage) => lastPage.nextOffset
})
const
React Intl Integration:
import { IntlProvider, FormattedMessage, FormattedNumber } from 'react-intl'
export function App() {
const [locale, setLocale] = useState('en')
const messages = useMemo(() => require(`./locales/${locale}.json`), [locale])
return (
<IntlProvider locale={locale} messages={messages}>
<Router>
<Routes />
</Router>
</IntlProvider
# Development build
npm run build:dev # Uses .env.dev
# Sandbox build
npm run build:sandbox # Uses .env.sandbox
# Production build
npm run build:production # Uses .env.productionDeployment Script:
#!/bin/bash
# Build for environment
npm run build:${ENV}
# Upload to S3
aws --profile=${AWS_PROFILE} s3 sync --delete ./dist s3://pay-${ENV}.acubeapi.com
# Invalidate CloudFront cache
aws --profile=${AWS_PROFILE} cloudfront create-invalidation \
--distribution-id ${DISTRIBUTION_ID} \
--paths "/*"MSW Handlers:
import { rest } from 'msw'
export const handlers = [
// Mock payment initiation
rest.post('/api/payments', (req, res, ctx) => {
return res(
ctx.status(201),
ctx.json({
paymentId: '123-456-789',
status: 'pending',
authorizationUrl: 'https://bank.example.com/auth'
})
)
}),
// Mock account list
rest.get('/api/accounts', (
Open Banking UI delivers a secure, user-friendly payment interface with full PSD2 compliance and modern development practices.
License: Proprietary (A-Cube S.r.l.)