Web SDK integration
A Web SDK integration has two parts: your server creates the payment and stores the order-to-paymentId mapping; the browser loads the SDK and creates Checkout with that paymentId. Keep the secret, request signing, customer mapping, and final payment verification on the server.
Before you begin, follow Setup to obtain your API credentials and add your server's public outbound IP addresses to the allowlist for the target environment. Generate sign for each request according to Request signing. Before going live, follow Sandbox testing to verify all scenarios used by your integration in the sandbox.
The Web SDK uses Onerway's Google Pay integration, so no Google Pay website registration is needed; that applies only to Direct API integrations.
Integration flow
- Call Create SDK transaction from your server and store the returned
paymentId. - Load the CDN script that matches the transaction environment.
- Create Checkout with
paymentId, subscribe to events, and then mount the Payment Element. - Call
confirmPayment()for cards, each local payment method, and other custom payment buttons. For SDK-owned Apple Pay and Google Pay buttons, listen only forpayment_result. - Use client events to update the page. Confirm the final status on your server through Query payments or a payment webhook.
Create a payment on your server
For an ordinary Web SDK v4 payment, submit productType=ALL, subProductType=DIRECT, and txnType=SALE to Create SDK transaction. The standard one-time payment example omits billingInformation, shippingInformation, paymentMode, and osType.
Both billingInformation and shippingInformation are optional when you create the transaction. You can provide either object when the information is available, or add it through Update SDK order before payment confirmation when your business flow requires it. If you submit an object, its nested required fields and conditions still apply. When updating the order, reuse the merchantTxnId of the original transaction creation on your server, and wait for a successful update response before the customer confirms payment.
For the current Web SDK, txnOrderMsg contains only returnUrl, products, appId, customerPlatform, periodValue, and notifyUrl. See the API Reference for whether each field is required and its conditions. Do not collect or send browser, device, or cardholder IP fields from your server; the Web SDK collects that context.
Omit paymentMode for ordinary Web SDK payments; you do not need to distinguish desktop and mobile browsers. If you send a value other than WEB, you must also send osType.
Pass only the paymentId from the create-payment response to the browser. You may use transactionId for server-side order correlation, but it cannot replace paymentId when initializing the SDK. The response also still returns redirectUrl; the current Web SDK does not consume it during initialization.
Load the SDK
The CDN and environment must match:
| Environment | CDN | environment |
|---|---|---|
| Sandbox | https://sandbox-checkout-sdk.onerway.com/v4/latest/onerway.js | sandbox |
| Production | https://checkout-sdk.onerway.com/v4/latest/onerway.js | production, the default |
v4/latest is the officially recommended long-term Production URL. Although environment defaults to production, pass it explicitly to prevent environment mix-ups when copying configuration.
<script src="https://checkout-sdk.onerway.com/v4/latest/onerway.js"></script>
<div id="onerway_checkout"></div>
<button id="pay_button" type="button">Pay now</button>
Initialize, subscribe, and mount
const checkout = await Onerway.createCheckout(paymentId, {
environment: 'production',
locale: 'en'
})
const paymentElement = checkout.createPaymentElement()
paymentElement.on('ready', (event) => {
console.log(event.availablePaymentMethods)
})
paymentElement.on('loaderror', (event) => {
console.error(event.error.code, event.error.message)
})
checkout.on('payment_result', handlePaymentResult)
paymentElement.mount('#onerway_checkout')
Subscribe before calling mount():
ready.availablePaymentMethodsis the displayable set determined by the order, server configuration, and browser wallet capabilities. Do not treat example values as a fixed enum. The SDK hides wallet buttons when a wallet is unavailable.loaderrormeans that Checkout initialization failed. Branch onevent.error.code; useevent.error.messagefor display only. The only public codes arecheckout_load_failed(the first load of payment methods failed; let the customer reinitialize) andno_available_payment_methods(no payment method remains after filtering; check the server-side configuration andpaymentMethod).- Validation failures, payment API errors, wallet cancellation, and 3DS or redirect errors are not
loaderrorevents. Handle them through theconfirmPayment()result orpayment_result.
Configure the Payment Element
Every child field of config is optional, but each merchant should decide which fields to provide based on its payment-method, form, and branding requirements. Do not assume that the default presentation fits every integration. The SDK reads the configuration when paymentElement.mount() runs. Recreate and remount the Payment Element after changing it.
| Field | Type | Default | Purpose |
|---|---|---|---|
paymentMethod | String[] | Omitted | Display allowlist that retains only methods also returned by the server |
showBillingAddress | Boolean | true | Whether to display and validate the billing-address form |
displayCardholdername | Boolean | true | Whether to display and validate the cardholder-name input |
walletButtons | Object | See below | Supported appearance settings for the official Apple Pay and Google Pay buttons |
checkoutTheme | String | light | SDK theme preset; light is currently the only public theme |
variables | Object | {} | Theme-variable overrides and the recommended customization layer |
styles | Object | {} | CSS-selector overrides with the highest priority |
customCssURL | String | SDK default CSS | Replaces the SDK base stylesheet; variables and styles still apply |
For example, a merchant can display only the methods accepted by its checkout, hide the SDK billing-address form, and apply branding to SDK-controlled content:
const checkout = await Onerway.createCheckout(paymentId, {
environment: 'production',
locale: 'en',
config: {
paymentMethod: ['CARD', 'DOKU_VA', 'GooglePay'],
showBillingAddress: false,
displayCardholdername: true,
checkoutTheme: 'light',
variables: {
colorPrimary: '#2563eb',
colorText: '#1a202c',
borderRadius: '8px'
},
walletButtons: {
googlePay: { type: 'pay', color: 'black', height: '44px' },
applePay: { type: 'pay', color: 'black', height: '44px' }
}
}
})
Restrict payment methods
paymentMethod only filters the browser presentation. It does not enable a payment method for the merchant:
| Value | Behavior |
|---|---|
| Omitted | Display every server-returned method that is available on the current device |
['CARD', 'FPX', 'GooglePay'] | Display CARD, FPX, and GooglePay only when the server also returns them |
[] | Display no methods and emit loaderror with event.error.code set to no_available_payment_methods |
Each value must exactly match the payment-method identifier returned by the server. Array order does not control display order; the server payment-method configuration and the SDK wallet region still determine the actual order. Use ready.availablePaymentMethods as the final indication that a method loaded successfully.
Configure form visibility
showBillingAddress: falsehides the billing address and stops its client-side validation. It does not change server-side field requirements for Create transaction or Update order. If your business requires billing information, collect it and update the order before payment confirmation.displayCardholdername: falsehides the cardholder name and stops its client-side validation.
Configure wallet buttons
| Field | Default | Supported values or rule |
|---|---|---|
googlePay.type | pay | book, buy, checkout, donate, order, pay, plain, subscribe |
googlePay.color | black | black, white |
applePay.type | pay | add-money, book, buy, check-out, continue, contribute, donate, order, plain, reload, rent, subscribe, support, tip, top-up, pay |
applePay.color | black | black, white, white-outline |
googlePay.width / applePay.width | 100% | CSS size string |
googlePay.height / applePay.height | 44px | CSS size string |
googlePay.radius / applePay.radius | 8px | CSS size string; the Apple / Google platforms may constrain the final appearance |
These options customize only the supported appearance of official SDK wallet buttons. They cannot make a wallet available on an unsupported device, and variables, styles, or custom CSS cannot force an appearance that the wallet platform does not allow. Setup for wallets, such as Apple Pay domain verification, is covered in Payment methods.
Configure themes and styles
checkoutThemecurrently supports onlylight.variablesis the recommended branding layer. Common supported variables arecontainerBackground,cardBackground,inputBackground,inputBrandBackground,aggregateHeaderBackground,dialogBackground,colorText,colorPrimary,colorDanger,fontFamily,fontSizeBase, andborderRadius.fontSizeBaseaccepts12px–24px;borderRadiusaccepts0px–24px.stylesmaps CSS selectors to CSS property objects, such as{ '.onerway-checkout__input': { color: '#1a202c' } }, for local adjustments that variables cannot express.customCssURLreplaces the SDK base stylesheet;variablesandstylescan still override it.
Themes and styles affect SDK-controlled content only. The legacy showPayButton and payButtonText options are no longer supported and are ignored by the SDK.
Locale
When locale is omitted, the SDK uses the browser language. If the explicit value or browser language is not supported by the current payment method, the SDK falls back to English (en).
Non-wallet payment methods support the following locale values (alphabetical):
| Code | Language | Code | Language |
|---|---|---|---|
ar | Arabic | de | German |
en | English | es | Spanish |
fi | Finnish | fr | French |
it | Italian | ja | Japanese |
ko | Korean | nl | Dutch |
no | Norwegian | pl | Polish |
pt | Portuguese | ru | Russian |
sv | Swedish | th | Thai |
zh-cn | Simplified Chinese | zh-tw | Traditional Chinese |
Wallets use their own locale enums and casing. The two wallets support the same locales except for these differences:
| Support | Locales |
|---|---|
| Both wallets | ar, ca, cs, da, de, el, en, es, fi, fr, hr, id, it, ja, ko, ms, nl, no, pl, pt, ru, sk, sv, th, tr, uk, zh |
| Google Pay only | bg, et, sl, sr |
| Apple Pay only | he, hi, hu, ro, vi, zh-TW |
Wallets also fall back to English (en) when a locale is unsupported. For example, Simplified Chinese is zh-cn for non-wallet methods and zh for wallets; Traditional Chinese is zh-tw for non-wallet methods and zh-TW for wallets. Do not invent conversions for values that are not listed.
Confirm the payment
Cards, local payment method flows, and custom buttons
These payment methods require your button to call confirmPayment():
document.querySelector('#pay_button').addEventListener('click', async () => {
const result = await checkout.confirmPayment()
handleConfirmResult(result)
})
Do not create another Checkout or another order when retrying. Keep the same paymentId, Checkout, and Payment Element while the payment remains retryable.
SDK-owned Apple Pay and Google Pay buttons
SDK-owned wallet buttons cannot call confirmPayment(). After the customer clicks an official button rendered by the SDK, receive the client result only through payment_result:
checkout.on('payment_result', (result) => {
if (result.reason?.type === 'canceled') {
// The customer closed the wallet. Restore the UI; do not map this to a payment failure.
return
}
renderClientResult(result)
})
Wallet visibility also depends on server configuration, browser, device, and wallet capabilities.
Google Pay in embedded app WebViews
Google Pay availability is capability-detected: when the requirements are not met, ready.availablePaymentMethods does not include GooglePay and the SDK does not render its button. This is expected behavior, not a failure.
- Android WebView officially supports Google Pay, but the host app must cooperate: Android WebView 137+, Google Play services 25.18.30+, the
androidx.webkit:webkit:1.14.0dependency, the Chromium payment intent actions declared in the manifest, the Payment Request API enabled, and the app integration published to Google. A custom User-Agent must appendGOOGLE_PAY_SUPPORTED. See the official Google WebView guide. - Embedded WebViews on iOS do not support Google Pay.
- When the host app does not meet these requirements, break the payment flow out to the system browser and make sure the customer is guided back to the app after payment.
paymentStatus and nextAction
The SDK returns nextAction only when paymentStatus === 'R':
nextAction.type | SDK behavior | Merchant action |
|---|---|---|
PresentToShopper | The SDK presents a QR code, local payment page, or another handoff UI on the current page. There is no final result yet. | Keep the current Checkout and wait for payment_result. Do not call confirmPayment() again. |
RedirectShopper | The SDK is about to redirect to an external payment page. There is no final result yet. 3DS uses this flow. | Wait for Onerway to return the customer to the request's returnUrl, then call Query payments on the server using the stored paymentId. |
R means that the payment flow must continue; it does not mean success or failure. nextAction is absent when paymentStatus !== 'R'.
See the paymentStatus response field for the full set of values and their definitions. Use client statuses only to update the page; do not fulfill, credit, or account for an order from them alone:
paymentStatus | Merchant action |
|---|---|
I, U, P, A | Not a final state. Keep the order pending; do not fulfill. |
R | The flow continues in an SDK handoff or redirect. Read nextAction.type and follow the table above. |
O | The payment can continue or be retried. Keep the same paymentId, Checkout, and Payment Element; do not create another order. |
S, N | A client-received result. Verify on the server before completing the order or updating its display. |
Client exceptions that do not produce a payment status are described by reason:
reason.type | Meaning |
|---|---|
validation_error | Local form validation failed; no payment status was produced |
sdk_error | An SDK local state, configuration, or invocation problem |
api_error | The payment API or a backend business call failed; reason.code is the backend's original respCode — see Response codes |
canceled | The customer canceled the current interaction; reason.code is presenter_closed (closed the SDK-presented QR code or local payment dialog), cvv_closed (closed the second card verification code dialog after Google Pay authorization), or wallet_canceled (canceled the Apple Pay or Google Pay authorization sheet) |
A cancellation may carry no paymentStatus, only reason.type === 'canceled'. Canceling the client flow is not a final payment failure; restore the page so the customer can retry.
Verify the final payment result
The confirmPayment() result, payment_result, and returnUrl can drive client navigation only; the final result is determined by webhooks: the payment result webhook for ordinary payments, the saved payment method result webhook when the customer opts in to saving a card, the subscription payment webhook for subscriptions, and the authorization, capture, and void webhook for pre-authorizations. Signature verification, acknowledgement and retries, deduplication, status interpretation, and query fallback follow the shared rules in Webhooks.
Additional considerations for the Web SDK integration: store the merchant-order-to-paymentId mapping on your server. After a redirect returns to returnUrl, do not trust a payment status in the URL — restore the page to a “confirming” state and, if no webhook has arrived, fall back to Query payments with the paymentId from your server and return the business result to the client. Do not store unredacted rawResult, request or response payloads, or payment data in browser logs, persistent storage, or analytics.
Saved cards and subscriptions
Let the customer choose whether to save a card
Keep subProductType at DIRECT and provide a stable merchantCustId. The SDK presents the save-card choice to the customer; it is not selected by default. Reuse the same merchantCustId for later payments, and the SDK displays the customer's saved cards and completes saved-card selection and payment internally; your client does not need a tokenId for that flow.
Additional considerations for the Web SDK integration: subProductType=TOKEN belongs to the legacy Web SDK save-card flow and is not used for the current Web SDK's customer-controlled save-card flow. The requirements for merchantCustId, the saved payment method result webhook, and listing and deleting saved tokens are covered in Saved payment methods.
Create the initial payment for a fixed plan
Use subProductType=SUBSCRIBE for the initial subscription payment. Keep the allowed plan mapping on your server and send a stable, readable subscription.productName; the billing model is determined by subscription.selfExecute. For a managed card subscription, you can also send subscription.bindCard as true to save the customer card when the subscription succeeds, in which case you receive two separate notifications.
Choosing between managed and self-managed subscriptions, contract credentials, lifecycle notifications, renewals and plan changes, and handling the two notifications of a subscription that also saves the card are covered in Subscription payments.
Pre-authorization
Send txnType as AUTH and keep subProductType at DIRECT to perform a pre-authorization: the order amount is held on the customer's card without an immediate charge, and the SDK integration flow is the same as an ordinary payment. After the pre-authorization succeeds, store the transactionId and paymentId from the response; your server later captures or voids through Capture or void authorization.
Scope, the lifecycle from authorization to capture or void, notifications, boundaries, and status handling are covered in Pre-authorization and capture.
Profit sharing
Profit sharing applies to the platform model: the platform merchant creates the payment with the receiving sub-merchant's merchantNo, and the payment is eligible for profit sharing only when you submit the paymentMethodOptions with profitShare=true in its share object; the SDK integration flow is the same as an ordinary payment. When you also set profitShareRate, Onerway allocates funds automatically after a successful SALE or CAPTURE; without it, your server initiates profit sharing through the API. To receive automatic profit share and reversal notifications, also set profitShareNotifyUrl. Submit paymentMethodOptions as a JSON string as required by the API.
Choosing between automatic and API-initiated profit sharing, result notifications, queries, and reversals are covered in Profit sharing.
Checkout integration
Create a checkout payment on your server, redirect the customer to the Onerway-hosted page to pay, and confirm the result through webhooks.
Direct API integration
Submit card data or tokens directly from your server through the create direct transaction API, handle 3DS redirects yourself, and rely on webhooks for the final results of payments, tokenization, subscriptions, and pre-authorizations.