Use custom layouts when the full XMoney.paymentForm() component is too prescriptive for your checkout page. You can mount the card form, Apple Pay button, and Google Pay button separately while keeping card data and wallet processing inside xMoney's secure iframe flow.
For the quickest integration, start with Website integration.
| SDK method | Use it for |
|---|---|
XMoney.paymentForm() | One embedded form that can include card, saved cards, Apple Pay, and Google Pay. |
XMoney.paymentCard() | Card-only checkout inside your own page layout. |
XMoney.applePay() | Standalone Apple Pay button. |
XMoney.googlePay() | Standalone Google Pay button. |
XMoney.getPaymentMethodCapabilities() | Device and browser support checks before rendering wallet buttons. |
All visual components require a container. savedCardPayment() is headless and is covered in Saved cards.
Create containers for each payment method that you want to mount.
<div class="checkout-grid">
<section id="wallet-section">
<div id="apple-pay"></div>
<div id="google-pay"></div>
</section>
<section id="card-section">
<div id="payment-card"></div>
<button id="pay-button" type="button">Pay now</button>
</section>
</div>Then share the order configuration and callbacks across widgets.
const baseConfig = {
publicKey,
orderPayload,
orderChecksum,
options: {
locale: 'en-US',
enableBackgroundRefresh: true,
appearance: {
theme: 'custom',
variables: {
colorPrimary: '#0d9488',
borderRadius: '10px',
},
},
},
onReady() {
console.log('xMoney widget ready')
},
onPaymentProcessing(isProcessing) {
updateCheckoutLoading(isProcessing)
},
onPaymentComplete(transaction) {
handlePaymentResult(transaction)
},
onError(error) {
showPaymentError(error?.message || 'Payment could not be started')
},
}Apple Pay and Google Pay availability depends on device, browser, account setup, and wallet readiness. Check capabilities before creating standalone wallet widgets.
const capabilities = await window.XMoney.getPaymentMethodCapabilities()
if (!capabilities.applePay.supported) {
document.getElementById('apple-pay').hidden = true
}
if (!capabilities.googlePay.supported) {
document.getElementById('google-pay').hidden = true
}The SDK returns a reason when a wallet is unavailable. Use that for diagnostics, not for shopper-facing copy.
const widgets = []
if (capabilities.applePay.supported) {
const applePay = await window.XMoney.applePay({
...baseConfig,
container: 'apple-pay',
options: {
...baseConfig.options,
appearance: {
style: 'black',
type: 'pay',
radius: 8,
},
},
})
widgets.push(applePay)
}
if (capabilities.googlePay.supported) {
const googlePay = await window.XMoney.googlePay({
...baseConfig,
container: 'google-pay',
options: {
...baseConfig.options,
appearance: {
color: 'black',
type: 'pay',
borderType: 'no_border',
radius: 8,
},
},
})
widgets.push(googlePay)
}Wallet widgets handle their own button click, wallet sheet, payment submission, 3DS behavior, and result callback.
paymentCard() renders only the card and saved-card UI. It removes paymentMethods from the config, so mount Apple Pay and Google Pay separately if you want wallet buttons.
const card = await window.XMoney.paymentCard({
...baseConfig,
container: 'payment-card',
card: {
validationMode: 'onChange',
savedCards: {
enabled: true,
optInVisible: true,
},
submitButton: {
visible: false,
type: 'pay',
},
},
})
widgets.push(card)Hide the built-in submit button when your page has its own checkout button. Call validate() before submit() if you want to control the button state or display a custom error summary.
document.getElementById('pay-button').addEventListener('click', async () => {
const { isValid, errors } = await card.validate()
if (!isValid) {
renderValidationErrors(errors)
return
}
card.submit()
})submit() starts the payment flow. Watch onPaymentProcessing to disable your button while the SDK is working.
All visual widgets support order updates. Use this when totals change.
async function updateAllWidgets(nextOrder) {
const { orderPayload, orderChecksum } = await createCheckoutIntent(nextOrder)
await Promise.all(
widgets.map((widget) =>
widget.updateOrder({
orderPayload,
orderChecksum,
})
)
)
}Card widgets also support locale and appearance updates.
card.updateLocale('ro-RO')
card.updateAppearance({
theme: 'dark',
variables: {
colorPrimary: '#38bdf8',
},
})Destroy every mounted widget before leaving the checkout page.
function destroyCheckout() {
widgets.forEach((widget) => widget.destroy())
widgets.length = 0
}This removes iframes, message listeners, overlays, and any active 3DS modal.