Skip to content
Type to search…

Payment SDK

Use the GoTab Payment SDK to manage saved cards, customize card forms, support 3D Secure verification, and mount Apple Pay.

Use the Payment SDK after you have chosen a payment flow. It owns browser-side card forms, saved-card access, 3D Secure verification, and Apple Pay. Your server still owns customer lookup, tab creation, stored-card payment submission, and final payment confirmation.

If GoTab should own customer sign-in and saved-card selection, leave this page and follow the GoTab Wallet guide.

Your checkout needs toStart here
Add, display, or delete cards from an integration customer profileManage saved cards
Collect a billing address for a location that requires oneCollect a billing address when required
Add 3D Secure verification to a stored-card payment3D Secure 2 and Payment Action
Display a standalone Apple Pay buttonApple Pay
Match card and address fields to your checkoutStyle card and address fields
Let GoTab authenticate the customer and display Wallet cardsGoTab Wallet

GoTab issues separate credentials for server API calls and browser SDK methods. If you have not received the browser credentials or need a payment feature enabled, contact GoTab API Support.

CredentialWhere to use itPurpose
Integrations API access secret and bearer tokenServer onlyMake Integrations API requests. These are not Payment SDK credentials.
clientApiAccessIdBrowserIdentifies your integration to the Payment SDK. Payment Action requires this value.
clientApiAccessSecretBrowserAuthorizes card-management and Wallet SDK methods. It is not your Integrations API access secret.

An integration customer is resolved or created through /api/integrations/customers. The Payment SDK uses identifiers from that customer and the selected card:

IdentifierSDK use
customer_profile_uuidScope card forms and saved-card methods to the integration customer.
payment_method_uuidIdentify the card selected by the customer when sending it to your server.
locationUuidIdentify the GoTab location where a new card will be used.
tabUuidIdentify the tab when mounting Apple Pay.

The integration customer’s customer_id is used by your server when creating a tab, not by the Payment SDK. See the stored-card payment flow for the complete relationship between the customer, tab, card, and payment request.

Include the SDK from the GoTab CDN after you have the browser credentials required by your chosen path:

<script src="https://sdk.gotab.io/payments-sdk.umd.js"></script>

The script exposes window.PaymentsSDK. If the object is missing, confirm that the script loaded successfully before debugging credentials or method configuration.

These methods manage an integration customer’s saved cards. They do not create or pay a tab. A basic stored-card checkout moves through three observable states:

  1. fetchPaymentMethods returns the customer’s cards, or an empty cards array prompts you to mount the add-card form.
  2. The customer selects a card and your browser sends its payment_method_uuid to your server.
  3. Your server submits the payment, retrieves the latest tab, and confirms that the tab is closed with no balance due.

Mount initAddCardForm in the browser after your server has resolved or created the integration customer:

PaymentsSDK.initAddCardForm('#add-card', {
clientApiAccessId,
clientApiAccessSecret,
customerProfileUuid,
locationUuid,
formProps: {},
onSuccess: (_status, { card }) => selectPaymentMethod(card),
onError: showError,
});

onSuccess runs after the card has been stored. Here, selectPaymentMethod represents your checkout’s selection logic; the returned card.payment_method_uuid is the identifier to send to your server when the customer confirms payment.

const { cards } = await PaymentsSDK.fetchPaymentMethods(
clientApiAccessId,
clientApiAccessSecret,
customerProfileUuid,
);

Render the returned cards in your checkout and retain the selected card’s payment_method_uuid. An empty array means the integration customer has no saved cards yet; keep the add-card form available.

const deleted = await PaymentsSDK.deletePaymentMethod(
clientApiAccessId,
clientApiAccessSecret,
customerProfileUuid,
paymentMethodUuid,
);

When deleted is true, remove the card from the checkout or fetch the list again. Fetch and delete operations are both scoped to customerProfileUuid. A listed card has this shape:

interface Card {
payment_method_uuid: string;
visual_cue: string;
payment_type: string;
expire_year: string;
expire_month: string;
name: string;
zip: string;
}

A billing address is not required by default. GoTab submits it with a stored-card payment only when both 3DS2 and the billing-address requirement are enabled for the location. Contact GoTab API Support to confirm whether that requirement applies to your integration.

The billing address belongs to the saved payment method, not to the payment request. Collect it while adding a new card, or add or modify it later with initEditMethodForm. Billing addresses are not returned in Card list results and should not be stored in browser storage.

initAddCardForm collects only card fields by default. When adding a new card, set includeBillingAddress: true to collect an address and save it with that payment method. Omit countryCode for an editable US default, or provide a two-letter code to fill and lock the country field:

PaymentsSDK.initAddCardForm('#add-card', {
clientApiAccessId,
clientApiAccessSecret,
customerProfileUuid,
locationUuid,
formProps: {},
includeBillingAddress: true,
countryCode: 'CA',
onSuccess: (_status, { card }) => selectPaymentMethod(card),
onError: showError,
});

The form provides country-specific labels and validation for US, CA, and AU, and generic address labels for other two-letter country codes. The SDK uppercases the country code and any region or postal-code fields whose country format requires it. The saved card’s zip value is populated from postalCode.

For an already saved card, use initEditMethodForm to add a billing address or modify the existing one. The card must belong to the integration customer, and its identity remains read-only:

PaymentsSDK.initEditMethodForm('#edit-method', {
clientApiAccessId,
clientApiAccessSecret,
customerProfileUuid,
paymentMethod: card,
countryCode: 'CA',
onSuccess: showPaymentMethods,
onCancel: showPaymentMethods,
onError: showError,
onLoading: setLoading,
});

This example returns to the saved-card list after save or cancel. If you opened the editor in response to BILLING_ADDRESS_REQUIRED, retry the payment once from onSuccess; canceling the editor should not retry it.

The editor unmounts before it calls onSuccess or onCancel. The initializer also returns an unmount() handle if your page needs to remove it earlier; manually unmounting does not run either callback. If no address exists, countryCode becomes the locked initial country; without it, the form starts with editable US. If an address already exists, a different configured country is ignored so it cannot overwrite the stored country.

The address contract is:

interface Address {
addressLine1: string;
addressLine2?: string;
dependentLocality?: string;
locality: string;
administrativeArea?: string;
postalCode: string;
countryCode: string;
}
type BillingAddress = Address;

GoTab enables 3D Secure 2 (3DS2) per location for eligible stored-card payments. If it is not enabled for a location, collectPaymentCustomerContext(clientApiAccessId) and Payment Action are not part of the stored-card flow. Contact GoTab API Support to confirm activation before adding this integration path.

Once 3DS2 is enabled, your checkout needs to collect browser context for every payment attempt and display Payment Action whenever the card issuer requests verification.

The browser can observe progress, but only your server decides whether checkout is complete. Keep the current payment controls disabled from submission until reconciliation reaches either a paid or safely retryable state:

flowchart TD
  confirm[Customer confirms payment] --> context[Collect fresh browser context]
  context --> submit[Server sends the payment request]
  submit -->|HTTP 200| reconcile[Fetch latest tab and payment state]
  submit -->|HTTP 202 with handoff| action[Retain handoff and mount initPaymentAction]
  submit -->|Response lost| recover[Retransmit the same request]
  recover --> submit
  action --> observed[Complete, cancel, error, pending, or return]
  observed --> reconcile
  reconcile -->|Closed with no balance| fulfill[Fulfill the order]
  reconcile -->|Payment still pending| wait
  wait[Keep payment controls disabled] --> reconcile
  reconcile -->|Safe to retry| retry[Offer another payment method]

Call collectPaymentCustomerContext(clientApiAccessId) when the customer confirms payment, then send its result to your server with the selected payment_method_uuid:

const paymentCustomerContext =
await PaymentsSDK.collectPaymentCustomerContext(clientApiAccessId);
await fetch('/checkout/pay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tabUuid,
paymentMethodUuid: selectedCard.payment_method_uuid,
paymentCustomerContext,
}),
});

The helper returns:

interface PaymentCustomerContext {
browser: {
colorDepth: number;
language: string;
screenHeight: number;
screenWidth: number;
timezoneOffsetMinutes: number;
};
deviceFingerprint?: string;
}

You can also construct the same object without calling the helper:

const deviceFingerprint = await collectYourDeviceFingerprint();
const paymentCustomerContext = {
browser: {
colorDepth: window.screen.colorDepth,
language: window.navigator.language,
screenHeight: window.screen.height,
screenWidth: window.screen.width,
timezoneOffsetMinutes: new Date().getTimezoneOffset(),
},
...(deviceFingerprint ? { deviceFingerprint } : {}),
};

GoTab does not require a particular fingerprinting library. If you supply deviceFingerprint, use a non-empty value from your own device-identification implementation that identifies the shopper’s device and is no longer than 5,000 characters. Omit it when unavailable instead of sending placeholder data or a new random value for every payment.

When you use the helper, clientApiAccessId must identify an enabled integration, and the checkout page’s origin must appear in that integration’s gotab_wallet_allowed_origins setting. This is the same origin registration used by GoTab Wallet, even if you do not otherwise use Wallet. Use the access ID issued to the integration that owns the stored card. The SDK collects the browser measurements locally. For the optional device fingerprint, production creates a temporary hidden frame at https://gotab.io/wallet/payment-action?clientApiAccessId=<clientApiAccessId>, exchanges messages only with that origin, combines the response with those measurements, and removes the frame.

The device fingerprint is best effort. If the frame cannot load or respond, or fingerprint collection fails or times out, the helper still resolves with the local browser measurements and omits deviceFingerprint.

Supplying the context yourself avoids the helper’s hidden frame. If a payment response includes a handoff, however, initPaymentAction still requires the same registered origin and a CSP that permits the GoTab frame.

Your server adds the customer email, source IP, Accept header, and User-Agent from the same request. See Add 3D Secure 2 for the complete browser-to-server example and Integrations API request.

Enabling 3DS2 does not mean every customer will be challenged. The card issuer determines the outcome for each payment:

  • Frictionless: The payment is authenticated without customer interaction and continues through the normal payment response.
  • Challenge: The API returns HTTP 202 with status: "requires_action", and your integration displays Payment Action so the customer can verify the payment.
  • Pending: The API returns HTTP 202 with status: "pending" while the existing attempt is still in progress. This can be the response to the original request or to the same request repeated after a timeout.

Whenever a successful HTTP 202 response includes a handoff, mount initPaymentAction and pass it unchanged, whether the status is requires_action or pending. Keep the handoff in the current checkout state while the payment remains incomplete.

Each Payment Action callback should ask your server to check the latest payment status. In this example, confirmPaymentOnYourServer represents an endpoint in your application that retrieves the latest GoTab tab and payment state, then returns paid, pending, or retryable to the browser.

async function reconcilePayment() {
let state;
try {
state = await confirmPaymentOnYourServer(tabUuid);
} catch {
showPaymentStatusUnavailable();
return;
}
if (state === 'paid') {
fulfillOrder();
return;
}
if (state === 'pending') {
showPaymentStillProcessing();
return;
}
showAnotherPaymentMethod();
}
if (paymentResponse.handoff) {
PaymentsSDK.initPaymentAction('#payment-action', {
clientApiAccessId,
handoff: paymentResponse.handoff,
onComplete: reconcilePayment,
onCancel: reconcilePayment,
onError: reconcilePayment,
});
}

If a timeout or dropped connection prevents the browser from receiving the payment result, repeat the exact original Integrations API payment request through your server. The API checks for the existing attempt before creating a payment and returns its current handoff or terminal state. Use the same recovery path if a reload loses the in-memory handoff; do not construct a different payment request.

The server should return paid only after confirming a closed tab with no balance due. It should return pending while the existing payment is still active, and retryable only after confirming that another payment can safely begin. If the server check fails, showPaymentStatusUnavailable should keep payment controls disabled and tell the customer that the payment cannot be confirmed yet.

The component unmounts before it calls onComplete or onCancel. The initializer also returns an unmount() handle if your page needs to remove it earlier. Closing the component or calling unmount() does not cancel the payment. If verification must continue on another page, the component prompts the customer to continue and returns them to your checkout afterward.

The issuer returns the customer to the checkout URL from which Payment Action launched. The URL fragment contains a one-time gotab_payment_resume value; do not copy it into application URLs, analytics, or logs. On that same checkout route, mount initPaymentAction again without handoff. The SDK consumes its fragment value, preserves your existing query and other fragment entries, and resumes the same payment:

PaymentsSDK.initPaymentAction('#payment-action', {
clientApiAccessId,
onComplete: reconcilePayment,
onCancel: reconcilePayment,
onError: reconcilePayment,
});

Closing the component, refreshing the page, or navigating away does not cancel an in-progress payment. A callback reports what the SDK observed; your server’s latest tab and payment state determines what the checkout can do next.

Result or eventWhat the customer seesWhat your integration does next
HTTP 200Confirmation or a brief finishing stateFetch the latest tab on your server. Complete the order only when the tab is closed with no balance due.
HTTP 202 with a handoff (requires_action or pending)Payment Action or a processing stateRetain the handoff, keep other payment controls disabled, and mount initPaymentAction with it.
Payment request times out or its response is lostA processing stateRepeat the exact original payment request through your server. Mount Payment Action if the recovered response includes a handoff; do not create a different payment.
succeeded callbackConfirmation or a brief finishing stateReconcile on your server before completing the order.
pending callbackA clear “still processing” stateRetain the handoff, keep payment controls disabled, and continue checking the existing payment. Do not submit a new payment.
refused callbackA payment-not-completed messageReconcile on your server. Offer another method only after the server confirms it is safe to retry.
Cancel, SDK error, refresh, or navigation awayCheckout or a processing stateReconcile on your server before enabling payment controls. Resume Payment Action when the customer returns from verification; if the handoff was lost, repeat the original payment request to recover it.
Handoff expires or cannot resumeA payment-status messageTreat expiry as an unavailable verification session, not proof of payment failure. Reconcile on your server before deciding whether to retry.

Before release, test each path your checkout supports:

  • A payment that completes without a challenge.
  • A challenge that succeeds and one that is refused.
  • A pending HTTP 202 response that includes a handoff.
  • A refresh before verification begins and after the customer returns.
  • A lost initial payment response. Repeat the exact request and confirm that the existing paymentId, handoff, and expiresAt are recovered.
  • A lost callback or temporary network failure during server reconciliation.
  • A double click or repeated mount while the original payment is pending.
  • A payment that remains pending longer than the customer’s browser session.

Ask GoTab API Support which test scenarios and payment methods are available for your integration environment.

Before mounting the button, create the tab with a supported guest identifier but without an integration customerId, serve checkout over HTTPS, and register the checkout domain for Apple Pay. Ask GoTab API Support to confirm that Apple Pay is enabled for the location.

Then mount the button with the tab and registered hostname:

PaymentsSDK.initApplePay('#apple-pay', {
tabUuid,
domainName: window.location.hostname,
onPaymentSuccess: async () => {
const tab = await confirmTabOnYourServer(tabUuid);
if (tab.status === 'CLOSED' && tab.balanceDue === 0) {
fulfillOrder();
} else {
showPaymentStillProcessing();
}
},
});

The button renders only in supported Apple and Safari environments. onPaymentSuccess is a progress signal: complete the order only after your server retrieves the latest tab and confirms that it is closed with no balance due. See the Apple Pay flow for the full checkout sequence.

Once the payment path works, use appearance to apply shared styling to card and billing-address fields. Use formProps for card-field options such as placeholders, card icons, and per-field CSS. CSS in formProps overrides the shared appearance for that field.

const appearance = {
field: {
color: '#16130f',
backgroundColor: '#ffffff',
borderColor: '#bcbcbc',
borderRadius: '4px',
fontFamily: 'Inter, sans-serif',
fontSize: '15px',
placeholderColor: '#777777',
focusBorderColor: '#8a6d2c',
errorColor: '#c62828',
successColor: '#15803d',
},
};

styleOverrides is a regular JavaScript object, not an SDK configuration property. Define it once and spread it into multiple formProps entries to reuse the same card-field styles:

const styleOverrides = {
css: {
boxSizing: 'border-box',
fontFamily: 'Arial, sans-serif',
'&::placeholder': { color: '#999999' },
},
successColor: '#2e8b57',
errorColor: '#dc143c',
};

Combine the shared appearance with the per-field overrides, placeholders, and callbacks when mounting the form:

PaymentsSDK.initAddCardForm('#add-card', {
clientApiAccessId: 'YOUR_CLIENT_API_ACCESS_ID',
clientApiAccessSecret: 'YOUR_CLIENT_API_ACCESS_SECRET',
customerProfileUuid: 'CUSTOMER_PROFILE_UUID',
locationUuid: 'LOCATION_UUID',
formProps: {
first_name: { placeholder: 'First name', ...styleOverrides },
last_name: { placeholder: 'Last name', ...styleOverrides },
email: { placeholder: 'Email address', ...styleOverrides },
card_number: { placeholder: '•••• •••• •••• ••••', showCardIcon: true, ...styleOverrides },
cvc: { placeholder: 'CVC', showCardIcon: true, ...styleOverrides },
card_exp: { placeholder: 'MM / YY', ...styleOverrides },
},
includeBillingAddress: true,
appearance,
onLoading: (loading) => setCardFormLoading(loading),
onSuccess: (_status, { card }) => selectPaymentMethod(card),
onError: (error) => showCardFormError(error.message),
stateCallback: (state) => updateCardFormState(state),
});

For card fields, the SDK applies formProps after appearance, so per-field options and CSS take precedence. Because styleOverrides is spread only into formProps, use appearance for styling that should also apply to billing-address fields. Your page CSS controls labels, buttons, and layout.

MethodPurpose
initAddCardForm(container, config)Add a saved card, optionally with a billing address.
initEditMethodForm(container, config)Add or modify a saved card’s billing address.
fetchPaymentMethods(id, secret, customerProfileUuid)Fetch an integration customer’s cards.
deletePaymentMethod(id, secret, customerProfileUuid, paymentMethodUuid)Delete an integration customer’s card.
collectPaymentCustomerContext(clientApiAccessId)Collect browser measurements and an optional device fingerprint for a 3DS2-enabled stored-card payment.
initPaymentAction(container, config)Continue or resume a 3D Secure payment action.
initWallet(container, config)Mount GoTab Wallet in checkout or card-management mode.
initApplePay(container, config)Mount a standalone Apple Pay checkout button.

Every initializer accepts an HTMLElement or CSS selector as container. initEditMethodForm and initPaymentAction return an { unmount() } handle.

Used by initAddCardForm.

PropertyTypeRequiredDescription
clientApiAccessIdstringYesBrowser SDK access ID.
clientApiAccessSecretstringYesBrowser SDK access credential.
customerProfileUuidstringYesIntegration customer profile that owns the saved card.
locationUuidstringYesLocation where the payment method will be used.
formPropsobjectYesCard-field placeholders, icons, validation, and per-field CSS. Use {} for defaults.
includeBillingAddressbooleanNoCollects a billing address and saves it with the new payment method. Defaults to false.
countryCodestringNoTwo-letter initial country. When supplied, the country field is locked.
appearancePaymentFormAppearanceNoShared appearance for card and billing-address fields.
onSuccess(status, data) => voidYesReceives the HTTP status and { card, errors } after the method is stored.
onError(error) => voidYesReceives initialization, validation, or submission errors.
onLoading(isLoading) => voidNoReports initialization and submission loading state.
stateCallback(state) => voidNoReports card-field focus, validity, errors, and detected card details.

stateCallback receives:

interface FieldState {
isDirty: boolean;
isFocused: boolean;
isValid: boolean;
isEmpty: boolean;
isTouched: boolean;
errorMessages: string[];
last4?: string;
bin?: string;
cardType?: string;
}
PropertyTypeRequiredDescription
clientApiAccessIdstringYesBrowser SDK access ID.
clientApiAccessSecretstringYesBrowser SDK access credential.
customerProfileUuidstringYesIntegration customer profile that owns the card.
paymentMethodCardYesExisting card identity. Card fields remain read-only.
countryCodestringNoTwo-letter fallback country when no address is stored.
appearancePaymentFormAppearanceNoShared billing-address field appearance.
onSuccess(billingAddress) => voidYesReceives the saved billing address.
onError(error) => voidYesReceives load, validation, or submission errors.
onCancel() => voidNoRuns when the editor closes without saving.
onLoading(isLoading) => voidNoReports loading and submission state.
PropertyTypeRequiredDescription
clientApiAccessIdstringYesPublic SDK access ID issued to the same integration as the API payment.
handoffstringNoPass the value unchanged whenever a successful HTTP 202 response includes it, whether the status is requires_action or pending. Omit it after an issuer return so the SDK consumes gotab_payment_resume from the URL fragment.
themeWalletThemeNoTheme applied to the verification screen. See the supported Wallet themes.
onComplete(result) => voidYesReports a succeeded, refused, or pending result. Reconcile the payment on your server before changing checkout state.
onCancel() => voidNoRuns when the customer chooses Back to checkout. It does not cancel the payment; reconcile on your server.
onError(error) => voidNoReports initialization, timeout, or invalid-response errors. Reconcile on your server before allowing another payment.
interface PaymentActionResult {
status: 'succeeded' | 'refused' | 'pending';
paymentId: string;
}

Treat PaymentActionResult as an update for your checkout UI. Check the latest tab and payment state through your server before changing what the customer can do next.

PropertyTypeRequiredDescription
tabUuidstringYesUUID of the tab created without an integration customerId.
domainNamestringYesRegistered HTTPS hostname serving your checkout.
onPaymentSuccess(data) => voidNoRuns after Apple Pay completes. Confirm the tab on your server before completing the order.

See GoTab Wallet configuration for WalletConfig.

If your page sends a blocking Content Security Policy, merge the following sources into its existing directives. These are additions, not a complete policy; include only capabilities you use.

SDK capabilityRequired CSP addition
Wallet, Apple Pay, or Payment ActionAdd the GoTab origin provided for your environment to frame-src.
Cross-origin SDK API callsAdd the configured API origin to connect-src. Same-origin /api requests need no additional source.
Card entry fieldsAdd https://js.verygoodvault.com and https://js3.verygoodvault.com to script-src and frame-src; add both plus https://vgs-collect-keeper.apps.verygood.systems to connect-src.
3DS2 context collection with collectPaymentCustomerContext(clientApiAccessId)Add https://gotab.io to frame-src.
SDK forms and Payment ActionAdd 'unsafe-inline' to style-src.

For a page using every capability, the additions are equivalent to:

script-src https://js.verygoodvault.com https://js3.verygoodvault.com;
connect-src <API origin> https://js.verygoodvault.com https://js3.verygoodvault.com https://vgs-collect-keeper.apps.verygood.systems;
frame-src https://gotab.io https://js.verygoodvault.com https://js3.verygoodvault.com;
style-src 'unsafe-inline';

Replace <API origin> with an origin, not a full path, and retain existing sources such as 'self'. This example uses GoTab’s production origin; use the GoTab origin provided for another environment.

For Metro, 3DS2 context collection requires https://gotab.io in frame-src.

  • Keep Integrations API credentials and bearer tokens on your server.
  • Use only the separately issued browser credentials in Payment SDK configuration.
  • Ensure every checkout origin that calls collectPaymentCustomerContext(clientApiAccessId) or mounts initPaymentAction is covered by the integration’s gotab_wallet_allowed_origins setting.
  • For 3DS2-enabled locations, collect payment context only when the customer confirms payment.
  • Do not add handoff values, payment-session tokens, or bearer tokens to your own URLs or logs.
  • Do not start another payment while the current payment is pending or awaiting verification.
  • Fetch the latest tab and verify its final state and balance before completing the order.