Convert currencies
Use this component to let customers convert balance between the currencies in their Nium multicurrency wallet. Nium hosts the component and renders it in an iFrame, so there is no redirect.
This component only moves funds between currencies already in a customer's wallet. It doesn't send money to a beneficiary, create wallets, or onboard customers. To send outbound transfers, use Send payouts.
The component:
- Guides users through three steps, from amount and currency input to quote confirmation and a completion summary. It validates the balance at each step.
- Shows a live foreign exchange (FX) rate for the selected currency pair, including the net rate and markup. It holds that quote, by its
quoteId, so the transfer runs at the rate the user confirmed. - Lists the customer's wallet currencies in the source and destination dropdowns, with the available balance for each.
Live preview
This is the component running against the Nium sandbox with test data. Choose the currencies and an amount to try a conversion.
Before you begin
- Complete the Quick start to set up PKCE authorization. Use the scope
['mcy-wallet-move-funds']when your server requests the authorization code. Your client must be provisioned with this scope. - Have the
customerHashIdwhose wallet balance you convert. The component resolves the wallet and its currencies from it. You don't pass a wallet ID. - Make sure the customer has an active multicurrency wallet with at least two currencies.
Quick example
After you call init(), create the conversionForm element, mount it, and clean up when you're done.
- JavaScript
- React
<div id="conversion-container"></div>
const { createElement } = await import('@nium/nium-sdk')
const element = await createElement('conversionForm', {
customerHashId: '<customer-hash-id>', // required
})
element.on('submit', (e) => showConfirmation(e.result.transfer))
element.on('error', (e) => showErrorToast(e.error.message))
element.on('cancel', () => router.push('/wallet'))
element.mount('#conversion-container')
// When you're done, for example on a route change
element.destroy()
import { useEffect } from 'react'
export function ConversionForm({ customerHashId }: { customerHashId: string }) {
useEffect(() => {
let element: { destroy(): void } | undefined
let cancelled = false
;(async () => {
const { createElement } = await import('@nium/nium-sdk')
element = await createElement('conversionForm', { customerHashId })
if (cancelled) return element.destroy()
element.on('submit', (e) => showConfirmation(e.result.transfer))
element.on('error', (e) => showErrorToast(e.error.message))
element.mount('#conversion-container')
})()
return () => {
cancelled = true
element?.destroy()
}
}, [customerHashId])
return <div id="conversion-container" />
}
Options
createElement('conversionForm', options) accepts these options (type NiumConversionFormProperties).
| Option | Type | Description |
|---|---|---|
customerHashId | string | Required. Customer whose wallet balance is converted. |
theme | NiumTheme | Custom theme overrides: font family, colors, border radius. See Theming. |
Events
Listen to form events with element.on(eventName, handler). All events go from the iFrame to your page. element.on() returns a function that removes that listener. Call it when you remove the component to prevent memory leaks.
ready: The iFrame is fully loaded. Remove loading states or show the container.resize: The iFrame content height changed. Usee.heightto resize the container. Payload:{ height: number }.submit: The transfer runs when the user confirms, and the iFrame then shows a summary.submitfires when the user closes that summary. Payload:{ result: { transfer: object } }.error: The token exchange failed, the session expired (AUTH_EXPIRED), or the iFrame failed to load. Quote, balance-validation, and transfer failures appear inside the iFrame only. Always handle this event. Payload:{ error: { message: string, code: string } }.cancel: The user closed the flow before converting. Navigate away or reset your UI state.
Submit payload
The conversion is final once the user confirms it. By the time submit fires, the iFrame has already called Nium's FX and wallet services, so your client has nothing further to call.
e.result.transfer has the settled figures:
| Field | Type |
|---|---|
sourceAmount | number |
sourceCurrencyCode | string |
destinationAmount | number |
destinationCurrencyCode | string |
exchangeRate | number |
netExchangeRate | number |
markupAmount | number |
markupRate | number |
scalingFactor | number |
systemReferenceNumber | string |
Use systemReferenceNumber as the reconciliation key. Store it with the settled amounts for your audit trail. Nium invalidates the wallet balance and transaction history caches after a successful transfer, so any balance view you re-fetch shows the new figures.
Clean up
Call element.destroy() on route change, modal close, or component unmount. It removes the iFrame, detaches all listeners, and releases the SDK's internal state.
Call element.mount(selector) once per instance. To mount again, create a new element.
How the component works
Between mount and a settled transfer, the iFrame makes four service calls:
- Token exchange. It exchanges the PKCE authorization code for an access token through
/api/niam-service/oauth2/token. - Wallet resolution. With the access token, it loads the customer's wallet and per-currency balances through
GET /api/wallet-service/api/v1/client/{clientId}/customer/{customerId}/wallet. - FX quote. When the user selects a source and destination currency, it requests a live exchange rate from the Nium FX service. The rate includes markup and net rate. The component holds the returned quote for confirmation.
- Transfer. On confirmation, it posts to
/api/fx-service/api/v1/client/{clientId}/customer/{customerId}/wallet/{walletHashId}/transferand shows the settled summary.
The component checks the entered amount against the source-currency balance before the user can confirm a quote. It shows a loading state during the token exchange and wallet lookup.
Theming
Pass a NiumTheme object in the theme option to control fonts, colors, input styles, and border radius. The Theme customization reference documents all 15 theme properties, a gating matrix, and four ready-to-use example themes.
Troubleshooting
Common issues
- No currencies in the dropdown. The
customerHashIdis incorrect, or the customer's wallet has fewer than two currencies. Verify thecustomerHashIdand confirm the customer has an active multicurrency wallet with at least two currencies. - Rate not loading. The source and destination currencies are identical. The component requests a rate only when the two currencies differ.
- Insufficient balance error. The conversion amount exceeds the available source-currency balance. Validate the amount against the wallet balance in your own UI before the user submits.
- Component not rendering. The container element is missing at mount time, or an unhandled error occurred. Check the browser console for
errorevents. Make sure the container exists before you callmount(). - Incorrect role or scope error. The client isn't provisioned with the correct scope. Verify that your client has the
mcy-wallet-move-fundsscope. This is the confirmed role for this component.
For additional support, contact your Nium integration team.
Next steps
- Send payouts: send money to a beneficiary, which this component doesn't do.
- Wallet & Accounts: show balances, and let customers open currency conversion from Move Funds.
- Theme customization: match the component to your brand.