Drop-in & wallets
Understand gateway-selected layouts, wallet setup, and styling in Payment SDK 0.7.0.
Gateway-selected layouts
Call hw.mount() once for the whole checkout. The gateway session decides its layout; there
are no individual wallet or drop-in controllers in 0.7.0.
| Layout | Parts added in order | requiresSubmit |
|---|---|---|
card_and_wallets | Apple Pay, Google Pay, card | true |
card_only | Card | true |
drop_in, form with its own button | Apple Pay when offered, then drop-in form | false |
drop_in, form that uses your Pay button | Drop-in form | true |
The layout and available methods are configured for your HighWire account. When the session includes
a method list, a method absent from that list gets no slot. When the list is omitted,
card_and_wallets offers wallet slots; browser and device availability determine whether they show.
Show your Pay button only when checkout.requiresSubmit is true; a drop-in form with its own
payment button takes the payment there. Handle every method through onProcessing, onResult,
and onError; see Integration.
The CDN build includes the wallet loader. Wallet components run on your page; the card field and drop-in run inside HighWire frames. Availability also depends on browser, device, and account configuration. An unavailable wallet can stay hidden without reporting an error.
Drop-in forms that use your Pay button
Some account configurations use a drop-in form without a payment button of its own. The form takes cards only: the shopper enters the card in it, and your page supplies the email, the billing details and the Pay button:
- Enable your Pay button while
onAvailabilityChangereportstrue. Callcheckout.submit({ customerEmail, billingAddress })with the full billing address: at least thename, a two-lettercountryand thepostalCode. The SDK checks the name and country; the form validates the rest of the address for that country and rejects one without a postal code.customerPhoneis not sent. - HighWire checks the email before the card is charged. A payment takes up to 3 different emails, so
the shopper can correct one. Without
customerEmail,submit()uses the email of the payment's last declined attempt; before any decline it rejectsHW_INCOMPLETE. onProcessing('dropIn')fires whensubmit()sends a new submission, while HighWire checks the email.submit()resolvessucceeded,pendingordeclined. After a decline withretryable: truethe shopper can try another card;retryable: falseends the checkout.- Refused card details, billing address or email, a fourth email on the same payment, and a
submission HighWire did not take reject
HW_INCOMPLETEwith recoverycorrect. - When HighWire denies the email,
submit()rejectsHW_DECLINEDwith recoveryrecheck. A changed email or save box that HighWire refuses while the payment is in progress rejectsHW_TOKENIZE_IN_PROGRESSwith recoveryrecheck. Both keep the payment, because an earlier attempt may still capture: a latersubmit()only reads its status, for up to 3 minutes, and the checkout takes no other payment. Check it on your server before starting another checkout. - Errors carry
recovery. After an unknown outcome, anothersubmit()only checks the original payment, andcheckout.recheck()checks it without submitting. - With
savePaymentMethodForFutureUse: true, the form shows an unticked save box under the card fields. Only the shopper's tick saves the card, under the emailsubmit()sends; asucceededresult then carriescustomerIdandpaymentMethodId. It never carriesmaskedCard. - The form shows the payment provider's buyer terms under the card fields and runs card verification itself. Closing its verification dialog keeps the attempt and offers to finish verification. A verification step can also take the shopper's tab to another page and back to a HighWire return page; your page then loses its state, so settle the order from your server's view of the payment.
The Plain HTML and React / Next.js guides show complete controls, billing fields included, that work for this form and every other layout.
Collect the billing address in the form
Mount with billingAddressForm: 'full' to have the shopper enter the billing address in the
drop-in form instead of on your page. submit() then takes the email alone:
const checkout = await hw.mount({
container: '#payment',
amount: 2999,
currency: 'usd',
billingAddressForm: 'full',
// …your callbacks, unchanged
});
// From your Pay button, enabled while onAvailabilityChange reports true:
await checkout.submit({ customerEmail: 'shopper@example.com' });submit()takes nobillingAddress. Passing one, even{}, rejectsHW_UNSUPPORTEDwith recoverycorrectbefore anything is sent.- When the form's fields are incomplete,
submit()rejectsHW_INCOMPLETEwith recoverycorrectand sends nothing. The form may not mark the missing field, so ask the shopper to check both the card and the billing fields. - The address stays inside the form. Neither the SDK nor the payment status returns it, and it cannot be prefilled.
- The form grows taller as the shopper adds address lines. Do not cap the container's height.
- Only a drop-in form that uses your Pay button supports
'full', and the checkout's form is known only oncemount()has created the payment. When a'full'mount rejectsHW_UNSUPPORTED, mount again with'none'and show your own billing fields: the same payment is reused. To switch modes on a mounted checkout,unmount()it and mount again. - When HighWire's service cannot show the billing fields, the failure arrives after
mount()resolved:onErrorreceivesHW_UNSUPPORTEDwith recoveryunavailable, andonReadynever fires.
Apple Pay setup
- Ask HighWire to register your checkout domain for Apple Pay and provide the required domain-association file.
- Serve the file unchanged at
https://your-domain/.well-known/apple-developer-merchantid-domain-association, as plain text, with no extension or redirect. - Request that URL and verify it returns the file, rather than your site's HTML fallback.
- Confirm domain verification with HighWire before enabling Apple Pay in production.
Apple Pay renders over HTTPS in Safari. Elsewhere, its slot stays hidden or data-hw-empty
without calling onError. A checkout inside a third-party iframe also needs Safari 17 or later,
allow="payment" on that iframe, and both domains registered.
Google Pay needs no domain-association file. Its sheet shows the merchant name from the session.
Styling and accessibility hooks
| Hook | Meaning |
|---|---|
data-hw-layout on the container | Current layout name. |
data-hw-slot="applePay", "googlePay", "card", "dropIn" | Direct child slots appended after any existing children. Reorder with flex/grid order. |
hidden on a wallet slot | Wallet unavailable or retired after expiry. Do not override it with a display rule on the slot. |
data-hw-empty | Slot currently draws nothing. Collapse spacing, rather than hiding the slot. |
inert, aria-disabled="true" on wallet/drop-in slots | Payment in progress or a card-confirm answer still unknown. A drop-in form that uses your Pay button is also locked until it can take a submission, and while a submission runs or its outcome is unknown, except while the form shows a verification step. |
data-hw-processing on the container | Method being confirmed, from onProcessing until result or error. An error with recovery wait leaves it in place. |
#payment { display: flex; flex-direction: column; position: relative; }
#payment > [data-hw-slot] { margin-bottom: 12px; }
#payment > [data-hw-empty] { margin-bottom: 0; }
#payment > [data-hw-slot="card"] { order: -1; }
#payment[data-hw-processing]::after {
content: '';
position: absolute;
inset: 0;
background: rgb(255 255 255 / .7);
}Never set display: none on an empty slot: it can no longer measure itself and may never recover.
Use an accessible status message alongside a visual processing overlay.
Drop-in theming and overlay behavior depend on your account configuration. Confirm supported behavior with HighWire and test the configured checkout in your page layout. HighWire's card 3-D Secure dialog opens above the page.
unmount() removes slots and data-hw-processing, and restores the container's original
data-hw-layout. A checkout that ends removes its slots automatically.
Session lifetime
Payment sessions expire. When the SDK reports HW_SESSION_EXPIRED, stop using that checkout and
reconcile its payment on your server before creating a replacement. The payment may have completed
even when the page did not receive a final answer.
Use the shared result-handling rules for every method. Browser results guide the UI; fulfillment requires server verification.
Content Security Policy
Allow the SDK script, gateway requests, and HighWire frames. Wallet and device-screening components also run on the merchant page. Obtain the required account-specific sandbox and production allowlists from HighWire before enforcing CSP.
| Directive | Starting allowance |
|---|---|
script-src | 'self' and https://cdn.highwirepayments.com/sdk/, plus the wallet and screening script origins supplied by HighWire. |
frame-src | Your configured HighWire field origin, plus wallet, screening, and verification origins from the account allowlist. |
connect-src | 'self' and your HighWire gateway origin, plus the wallet and screening endpoints from the account allowlist. |
These starting allowances are not a complete policy. Ask HighWire for the origins that apply to
your enabled methods and environment. Do not substitute broad hosting-domain wildcards.
External merchant integration files work with 'self'; inline scripts need a nonce or hash.
Wallet components can use inline style attributes; if you restrict styles, test the required
style-src or style-src-attr allowances with HighWire in report-only mode.
3-D Secure requires frame-src and, when set, form-action to allow the HighWire gateway
and verification pages. Issuer origins vary by card: a fixed list can block challenges. Test
the necessary allowances on your checkout route; some integrations need https: for those
two directives to support issuer handoffs.
Run Content-Security-Policy-Report-Only through card, wallet, device screening, and challenge
flows, then confirm observed origins with HighWire before enforcement. Also check default-src,
img-src, and other restricted directives.
Keep the CDN allowance scoped to /sdk/ or the exact file. SRI verifies the script tag you
provide; it does not secure every script allowed by your policy.
Blocked wallet scripts leave those wallets unavailable while card or drop-in payment can continue.