Embedded Balance component
Show a connected account's balances and recent activity in your product with the Embedded Balance component.
The Embedded Balance component is a set of pre-built elements. Mount only the elements you need, and arrange them in your own page. The component includes the following:
- Layouts adapt across desktop and mobile. You still control page composition and branding.
- Multi-language support to localize the experience.
- One shared theme applies to every element.
- Airwallex updates formatting and layout in the hosted elements.
To see the component, go to the Airwallex demo site.
The Embedded Balance component doesn't support Strong Customer Authentication (SCA). If a regulation such as the second Payment Services Directive (PSD2) applies, build SCA in your own product around the component, for example with two-factor authentication.
Before you begin
Ensure you have the following:
- A platform account and a connected account to display the component.
- A Client ID and API key. For more information about access tokens, see Obtain an access tokenAPI.
- A sandbox environment for testing before production.
Initialize the SDK
Install the package
Install Airwallex.js:
1npm install @airwallex/[email protected]
Set up the server for authentication
Obtain an access tokenAPI with your Client ID and API key. Then generate a Proof Key for Code Exchange (PKCE) code_verifier and code_challenge with the S256 method in RFC 7636 Section 4. code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier))).
-
Generate a
code_verifier. The length must be an integer from 43 to 128.JavaScript1const dec2hex = (dec) => ('0' + dec.toString(16)).slice(-2);23const generateCodeVerifier = () => {4 const length = Math.floor(Math.random() * (128 - 43 + 1)) + 43;5 const array = new Uint32Array(Math.ceil(length / 2));6 window.crypto.getRandomValues(array);7 return Array.from(array, dec2hex).join('');8};910const codeVerifier = generateCodeVerifier(); -
Install
js-base64, then create thecode_challengefrom the verifier.JavaScript1import { Base64 } from 'js-base64';23const generateCodeChallenge = async (verifier) => {4 const hashed = await window.crypto.subtle.digest(5 'SHA-256',6 new TextEncoder().encode(verifier),7 );8 return Base64.fromUint8Array(new Uint8Array(hashed), true);9};1011const codeChallenge = await generateCodeChallenge(codeVerifier); -
On your server, call Authorize a connected accountAPI. Pass the connected account's open ID in the
x-on-behalf-ofheader. The open ID is the connected account ID, in the formatacct_xxxxxx. For the Embedded Balance component,scopemust include bothr:awx_action:financial_transactions_viewandr:awx_action:balances_view. On the scope name mapping, those are the previous names forfinancial_transaction:readandbalance:read.Shell1curl -X POST https://api.sandbox.airwallex.com/api/v1/authentication/authorize \2 -H 'Content-Type: application/json' \3 -H 'Authorization: Bearer {{ACCESS_TOKEN}}' \4 -H 'x-on-behalf-of: {{CONNECTED_ACCOUNT_OPEN_ID}}' \5 -d '{6 "code_challenge": "{{CODE_CHALLENGE}}",7 "scope": [8 "r:awx_action:financial_transactions_view",9 "r:awx_action:balances_view"10 ]11 }'The response includes an
authorization_code. Return it to your client asauthCode.
Call init
Call init with the authorization code. Use 'sandbox' while you test, and set env to 'prod' when you go live.
1import { init } from '@airwallex/components-sdk';23const sdk = await init({4 env: 'sandbox', // Set to 'prod' when you go live5 authCode,6 codeVerifier,7 clientId,8 locale: 'en', // 'zh' for Chinese9});
Add balance elements to your page
This example mounts the overview. Each other element needs its own container and its own mount call.
-
Add an empty container for the overview.
HTML1<div id="balance-overview-container"></div> -
Create
balanceOverviewand mount it. The overview renders in#balance-overview-container. Mount each element once.JavaScript1const { createElement } = sdk;23const overview = await createElement('balanceOverview', {4 currencies: ['USD', 'EUR', 'GBP'],5});6overview.mount('balance-overview-container'); -
Create any other element you want to display, then call
mountwith that element's container id.JavaScript1const list = await createElement('balanceList', {2 currencies: ['USD', 'EUR', 'GBP'],3});4const detail = await createElement('balanceDetail', { currency: 'USD' });5const info = await createElement('balanceInfo', { currency: 'USD' });6const chart = await createElement('balanceHistoryChart', { currency: 'USD' });7const scheduledtxn = await createElement('balanceScheduledTransactions', { currency: 'USD' });8const activities = await createElement('balanceActivities', { currency: 'USD' });9const activityDetails = await createElement('balanceActivityDetails', { transactionId: 'txn_123' });
The following elements are available:
| Element | Description | Options |
|---|---|---|
balanceOverview | Total balance with a multi-currency overview. | currencies?: string[] |
balanceList | List of balances across currencies. | currencies?: string[] |
balanceDetail | Single-currency balance detail. | currency: string |
balanceInfo | Pending, available, and account balances for a currency. | currency: string |
balanceHistoryChart | 30-day or 90-day balance history chart. | currency: string |
balanceActivities | Balance events that change the balance amount. | currency?: string |
balanceActivityDetails | Detail view for one activity or transaction. | transactionId: string |
balanceScheduledTransactions | Transactions scheduled for future settlement. | currency: string |
Optional: Customize the theme
Every balance element accepts the same theme options through appearance (mode and color variables) and theme.typography (for example, fontFamily).
-
Define one
themeOptionsobject.JavaScript1const themeOptions = {2 appearance: {3 mode: 'light',4 variables: {5 colorBrand: '#4c4a51',6 colorText: '#14171A',7 colorBackground: '#ffa3a3',8 colorError: '#D91807',9 colorSuccess: '#008044',10 },11 radius: { s: '2px', m: '4px', l: '6px' },12 shadows: {13 s: '0 2px 12px rgb(20 23 26 / 6%)',14 m: '0 4px 24px rgb(20 23 26 / 12%)',15 l: '0 4px 32px rgb(20 23 26 / 16%)',16 },17 space: { s: '16px', m: '24px', l: '32px' },18 },19 theme: { typography: { fontFamily: '"SF Mono", Menlo, Monaco, monospace' } },20}; -
Pass
themeOptionsinto eachcreateElementcall.JavaScript1const overview = await createElement('balanceOverview', {2 currencies: ['USD', 'EUR', 'GBP'],3 ...themeOptions,4});
Handle element events
Listen with element.on(eventCode, handler). Each element emits the following events:
| Event | Payload | Elements | When it fires |
|---|---|---|---|
ready | None | All | The balance element is ready. |
error | { code?: string; message: string } | All | An error occurs in the element. |
balanceSelected | { currency: string } | balanceOverview, balanceList | The user selects a balance row. |
activitySelected | { transactionId: string } | balanceActivities | The user selects an activity row. |
Use balanceSelected to choose the currency for a single-currency element. Use activitySelected to pass transactionId into balanceActivityDetails.
-
Add a listener for the events you handle.
JavaScript1overview.on('ready', () => {2 // The element has rendered.3});4overview.on('error', ({ message }) => console.error(message));56overview.on('balanceSelected', ({ currency }) => {7 console.log('Selected currency:', currency);8});910activities.on('activitySelected', ({ transactionId }) => {11 console.log('Selected transaction:', transactionId);12});
Refresh an expired session
The SDK session can expire while an element is mounted. The element then emits error with code: 'TOKEN_EXPIRED'.
-
Fetch a new authorization code from your server, using the same authorize and PKCE flow as the Initialize the SDK section of this document.
-
Call
init()again with that value asauthCode. You don't need to remount the element.JavaScript1element.on('error', async (err) => {2 if (err.code === 'TOKEN_EXPIRED') {3 // Fetch a new authorization code with the authorize call and PKCE flow above.4 await init({5 env: 'sandbox',6 authCode,7 codeVerifier,8 clientId,9 locale: 'en',10 });11 }12});
Test your integration
To verify the component in the sandbox environment, complete these steps:
- Call
initwithenvset to'sandbox'and an authorization code for a connected account that has a balance. - Mount
balanceOverviewand confirm the balances render in the container. - Select a balance row and an activity row. Confirm your handlers receive
currencyandtransactionId. - After the element emits
TOKEN_EXPIRED, callinit()with a new authorization code and confirm the element renders again without a remount.
Next steps
Now that the component is mounted:
- Compare layout, currencies, and activity with the Airwallex demo site.
- For
initoptions this page doesn't set, such aslocaleandenv, see InitializationJS.