Revup Unified SDK 1.0
Reference release: 1.0.29 — the last patch of the 1.0 line. Status: maintained. New integrations should start on 1.3. Existing 1.0 integrations keep working; when you are ready to move, follow Migrating 1.0 → 1.1.
Everything on this page describes 1.0.x only. Options, events and behaviours introduced later are documented on their own version page — nothing here silently inherits from a newer release, and nothing here is superseded by a newer page.
Contents
- What the SDK does
- Quick start
- Loading the SDK script
- The container
- RevupConfig — constructor
- RevupOptions — init()
- form — card form options
- apms — alternative payment methods
- userInfo — payer email
- Languages
- Event contract
- Cleanup and re-initialization
- Environments, URLs and CSP
- Known limits of the 1.0 line
1. What the SDK does
RevUp is a single hosted script that renders a complete payment block inside one element of your checkout page. It orchestrates two things:
- A credit / debit card form, hosted in a sandboxed iframe on RevUp's domain, so card data never touches your page.
- Alternative payment methods (APMs) — Apple Pay, Google Pay, PayPal, Stripe Link and Amazon Pay — rendered as buttons above the card form, limited to the methods enabled for your merchant account.
The script exposes one global constructor, Revup, and reports everything that happens through one native DOM event, onRevupMessage.
In 1.0 the layout is a single stack: APM buttons first, then the card form (or a "pay with card" entry point when the form starts collapsed). There are no payment zones and no accordion — those arrive in 1.1.
2. Quick start
<div id="revup-container"></div>
<script
type="text/javascript"
charset="utf-8"
src="https://test.hosted.revuppayments.com/payments-widget/sdk/1/0/loader.js"
crossorigin="anonymous"
integrity="sha512-…">
</script>
window.addEventListener('onRevupSDKLoaded', async () => {
const revup = new Revup({
merchantDomain: window.location.host,
apiKey: 'YOUR_PUBLIC_API_KEY',
orderId: 'ORDER_ID_FROM_YOUR_BACKEND',
});
const stopListening = revup.onRevupMessage((event) => {
console.log(event.type, event);
});
await revup.init({
containerId: 'revup-container',
language: 'en-US',
form: { collapsed: false, showZipCode: true },
apms: { enabled: ['apple_pay', 'google_pay', 'paypal'] },
});
});
Order of operations that must be respected:
- Create the order on your backend and get its
orderId. - Load the script and wait for
onRevupSDKLoaded. - Make sure the container element exists in the DOM.
new Revup(config)→ attach youronRevupMessagelistener →await revup.init(options).
init() is asynchronous. It resolves once the layout is built, the APM modules have been initialized and the card form iframe has been requested.
3. Loading the SDK script
The SDK is delivered by a small loader script. The loader is what you reference in your page; it resolves and fetches the actual bundle at runtime.
The loader URL
https://<host>/payments-widget/sdk/<major>/<minor>/loader.js
| Environment | Loader URL (1.0 line) |
|---|---|
| Sandbox | https://test.hosted.revuppayments.com/payments-widget/sdk/1/0/loader.js |
| Live | https://hosted.revuppayments.com/payments-widget/sdk/1/0/loader.js |
The two path segments before loader.js are the SDK's major and minor version. Patch releases inside a minor (1.0.28 → 1.0.29) do not change your script URL: the loader resolves the current patch on every page load. You only edit your <script> tag when RevUp announces a new minor or major.
Subresource integrity
Use integrity in production. The SRI hash is issued per environment and per minor line, and RevUp provides it to you together with your API keys — request the current value from your RevUp contact rather than copying it from an example. Always pair integrity with crossorigin="anonymous".
The integrity value is not published in this documentation. You may request it from RevUp by escalating a support ticket.
<script
type="text/javascript"
charset="utf-8"
src="https://hosted.revuppayments.com/payments-widget/sdk/1/0/loader.js"
crossorigin="anonymous"
integrity="sha512-…">
</script>
Dynamic loading (React, Vue, …)
const script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.src = REVUP_SDK_URL;
script.crossOrigin = 'anonymous';
if (INTEGRITY_HASH) script.integrity = INTEGRITY_HASH;
document.head.appendChild(script);
The ready signal
The loader fires onRevupSDKLoaded on window once Revup is available. Never construct Revup before that event:
window.addEventListener('onRevupSDKLoaded', () => {
// window.Revup is now defined
});
4. The container
<div id="revup-container"></div>
The element must already be in the DOM when you call init(). Any id works — you pass it as containerId. If the element is missing, init() throws:
Container with id: <containerId> not found, please check if the container is present in the DOM
The SDK renders its layout inside this element and manages its contents. Do not place your own children inside it.
5. RevupConfig — constructor
const revup = new Revup({
merchantDomain: window.location.host,
apiKey: 'YOUR_PUBLIC_API_KEY',
orderId: 'ORDER_ID',
// version: '2',
});
| Property | Type | Required | Description |
|---|---|---|---|
merchantDomain | string | Yes | Your merchant domain, e.g. window.location.host or your production domain. It must match a domain registered for your merchant account. |
apiKey | string | Yes | The public API key for the environment you are targeting (sandbox or live). |
orderId | string | Yes | Identifier of the order this checkout session pays for. In 1.0 this is the only place orderId can be supplied. |
version | string | No | API version path segment. Defaults to the SDK's built-in version (currently "2"). Unrelated to the SDK version in the loader URL. |
The constructor performs no network calls; it only records the configuration. All work happens in init().
6. RevupOptions — init()
await revup.init({
containerId: 'revup-container',
language: 'en-US',
form: { /* … */ },
apms: { /* … */ },
userInfo: { /* … */ },
});
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
containerId | string | Yes | — | id of the element that will host the payment UI. |
language | string | No | browser language, then 'en-US' | BCP 47 language code for form labels, APM button labels and placeholders. See Languages. |
form | object | No | see below | Card form options. See form. |
apms | object | No | {} | Alternative payment method options. See apms. |
userInfo | object | No | {} | Payer information collected by the card form. See userInfo. |
1.0 does not accept
orderIdorappearanceininit().orderIdis a constructor-only field, and visual styling is configured throughform.styles/form.buttonStyles. The top-levelappearanceobject arrives in 1.1.
Public methods
| Method | Returns | Description |
|---|---|---|
revup.init(options) | Promise<void> | Builds the layout, initializes APMs, loads the card form. Throws if containerId is not in the DOM. |
revup.getApmsAvailable() | Promise<string[]> | Payment methods enabled for your merchant account in the current session. Safe to call before init(). Resolves to [] when nothing is available. |
revup.onRevupMessage(handler) | () => void | Subscribes to SDK events and returns a cleanup function. See Event contract. |
Revup.getAvailableLanguages() | string[] | Static. All language codes the SDK supports. Callable before init(). |
getApmsAvailable() adds an extra round-trip and is not required for initialization — if you use it, call it in parallel with loading the SDK.
7. form — card form options
form: {
collapsed: false,
showZipCode: true,
cardBrands: ['visa', 'mastercard', 'american_express'],
buttonStyles: {
backgroundColor: '#306de5',
textColor: '#FFFFFF',
height: 44,
borderRadius: 16,
},
}
| Property | Type | Default | Description |
|---|---|---|---|
collapsed | boolean | true | When true, the card form is hidden behind a card entry point — but only if at least one APM button actually renders. If no APM button renders, the form is always shown expanded regardless of this value. |
showZipCode | boolean | false | Shows the ZIP / postal-code field. |
cardBrands | CardBrand[] | ['visa', 'mastercard'] | Brand marks shown in the card-number field before a brand is detected, and the set used to recognise the brand as the payer types. An empty array is ignored and the default is kept. |
buttonStyles | object | see below | Colours and geometry of the card form's submit button. |
styles | object | — | Additional style overrides forwarded to the hosted form. |
theme | string | 'bootstrap' | Base theme of the hosted form. |
textDirection | string | 'ltr' | Accepted and stored, but has no visual effect in 1.0 — the hosted form does not read it. Right-to-left mirroring is only implemented from 1.2. |
selfHostedDomain | boolean | true | When true, 3-D Secure device-fingerprint (DDC) requests run inside the hosted card form iframe. Set to false when the SDK is embedded directly on your primary checkout page and your gateway requires a first-party browser context for those requests. Nested-iframe setups are not supported with false. |
userInfo | object | {} | Same shape as top-level userInfo; the top-level form is preferred. |
buttonStyles
| Key | Type | Default | Notes |
|---|---|---|---|
backgroundColor | string | '#306de5' | Any CSS colour. |
textColor | string | '#FFFFFF' | Any CSS colour. |
height | number (px) | 44 | Clamped to 44–55. Non-numeric values fall back to the default. |
borderRadius | number (px) | 16 | Clamped to 0–60. Non-numeric values fall back to the default. |
Option validation
Every key inside form is type-checked at runtime. A key with the wrong type — or a key the SDK does not recognise — is rejected, the default is used instead, and a message is written to the browser console:
<key> is not a valid option, default value was setted instead.
This never throws and never blocks the checkout.
Supported card brands
visa, mastercard, american_express, unionpay, jcb, discover, diners_club, maestro, visa_electron, vpay, rupay, elo, elo_new, hipercard, verve, troy, mir, cartes_bancaires, girocard, dankort, bancontact, interac, bc_card, dinacard, cabal, napas, mada, knet, benefit.
8. apms — alternative payment methods
In 1.0 the APM configuration is flat: one list of enabled methods plus two side-tables keyed by method name.
apms: {
enabled: ['apple_pay', 'google_pay', 'paypal', 'stripe_link', 'amazon_pay'],
buttonTypes: {
apple_pay: 'book',
google_pay: 'buy',
paypal: 'checkout',
},
capabilityDetection: ['apple_pay', 'google_pay'],
}
| Property | Type | Description |
|---|---|---|
enabled | string[] | The methods to render, in render order. Each name is additionally filtered against the methods your merchant account actually has enabled; a name that is not enabled is silently skipped. |
buttonTypes | Record<string, string> | Vendor button style variant, per method. |
capabilityDetection | string[] | Methods that must pass a device/browser support check before their button is rendered. Methods not listed skip the check. |
Available method names
apple_pay, google_pay, paypal, stripe_link, amazon_pay.
credit_card is not an APM name — the card flow is configured through form. Query what your account has enabled with revup.getApmsAvailable().
Button style variants (buttonTypes)
| Method | Accepted values |
|---|---|
apple_pay | add-money, book, buy, check-out, continue, contribute, donate, order, pay, plain, reload, rent, set-up, subscribe, support, tip, top-up |
google_pay | book, buy, checkout, donate, order, pay, plain, subscribe |
paypal | paypal, checkout, buynow, pay, installment |
stripe_link, amazon_pay | not applicable — the value is ignored |
An unrecognised value falls back to plain and dispatches an apm.unavailable event carrying the explanation; the button still renders.
Capability detection
| Method | Check performed |
|---|---|
apple_pay | ApplePaySession.canMakePayments(), plus canMakePaymentsWithActiveCard() when a merchant identifier is available |
google_pay | isReadyToPay() |
| all others | none — the flag is ignored |
If a listed method fails its check, the SDK dispatches apm.unavailable for that method and does not render an unusable button.
APM initialization is fault-isolated: a method that fails to initialize removes itself and never prevents the other methods or the card form from rendering.
9. userInfo — payer email
userInfo: {
email: { required: true, value: 'buyer@example.com' },
}
| Property | Type | Default | Description |
|---|---|---|---|
email.required | boolean | false | When true, the hosted card form shows an email field above the card inputs and requires a valid address before the payment can be submitted. |
email.value | string | '' | Pre-fills the email field when it is shown. |
When the payer supplies an email, the payment request includes it (trimmed). When email.required is false or omitted, no email field is shown.
10. Languages
language accepts a BCP 47 code. When omitted, the SDK detects the browser language and falls back to en-US.
19 supported languages: en-US, ar-SA, cs-CZ, de-DE, es-ES, fr-FR, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-PT, ro-RO, ru-RU, th-TH, tr-TR, uk-UA, zh-CN, zh-TW.
Read them at runtime with Revup.getAvailableLanguages() — a static method, callable before init().
language: 'ar-SA'translates the strings only. Right-to-left layout mirroring does not exist in 1.0.
11. Event contract
Everything the SDK reports reaches your page as a single native CustomEvent named onRevupMessage, dispatched on document.
Listening
// Helper — unwraps event.detail for you and returns a cleanup function
const stop = revup.onRevupMessage((event) => {
console.log(event.type, event);
});
// Or the native listener — read the SDK event from event.detail
document.addEventListener('onRevupMessage', (e) => {
const event = e.detail;
});
Guarantees
- Events are dispatched on
documentwithbubbles: false. A listener onwindow, or on your container element, will never fire. Listen ondocument. context,dataanderrorare omitted when empty — always use optional chaining (event.context?.orderId).- Payloads are sanitized before dispatch: card numbers, CVVs, API keys and other PCI/PII patterns are masked. Raw card data never appears in an event.
- Critical misconfiguration (missing container, invalid required fields) throws — it is not dispatched as an event.
- These events are advisory. Always confirm the final payment outcome server-side using
transactionId/orderId.
Envelope
Every event has the same shape:
{
id: string; // UUID v4, unique per event
type: SdkEventType; // what happened
source: 'form' | 'apm';
timestamp: string; // ISO 8601
context?: {
orderId?: string;
transactionId?: string;
apm?: string; // APM events only
transactionStatus?: string;
};
data?: unknown; // event-specific, see below
error?: {
name: string; // machine-readable, see Error names
message: string; // human-readable, sanitized
statusCode?: number; // HTTP status, when the failure came from an HTTP response
};
}
There is no error.code — the machine-readable field is error.name.
Event types
type | source: 'form' | source: 'apm' | Meaning |
|---|---|---|---|
initialized | ✅ | — | The card form iframe is loaded and ready. |
validation.changed | ✅ | — | Card form validity changed. |
payment.submitted | ✅ | ✅ | A payment attempt started. Not an outcome. |
payment.action_required | ✅ | ✅ | Further action is needed (3-D Secure challenge on the card path). |
payment.success | ✅ | ✅ | Terminal — approved. |
payment.failed | ✅ | ✅ | Terminal — declined or errored. |
payment.cancelled | ✅ (gateway-reported) | ✅ | Terminal — the payer backed out. |
apm.unavailable | — | ✅ | An APM cannot be used and its button will not render. |
Only payment.success, payment.failed and payment.cancelled end a payment attempt.
source: 'form' — the card form
initialized
Fires once, when the form iframe has loaded and reported its size.
| Field | Value |
|---|---|
context.orderId | the order id |
data.message | 'initialized' |
Use it to remove your loading placeholder. It is not a signal that the payment can be submitted — use validation.changed for that.
validation.changed
Fires whenever the form's validity or submission state changes.
| Field | Value |
|---|---|
context.orderId | the order id |
data.isValid | boolean — all fields pass validation |
data.isCalling | boolean — a submission is in flight |
This is the signal to enable or disable your own payment button.
1.0 does not report which individual fields are invalid.
data.invalidFieldsarrives in 1.2.
payment.submitted
Fires when the payer submits the form, before the gateway responds.
| Field | Value |
|---|---|
context.orderId | the order id |
data.isValid | true |
data.isCalling | true |
Treat it strictly as a "processing" state.
Terminal card events — one shared payload
payment.action_required, payment.success, payment.failed (gateway decline) and payment.cancelled are all produced from the same gateway response, so they share one payload shape. The event type is derived from the gateway's transactionStatus:
Gateway transactionStatus | Event dispatched |
|---|---|
success | payment.success |
failed | payment.failed (error.name: 'PaymentFailed') |
cancelled, cancelled_by_user | payment.cancelled |
waiting_user_interaction, in_process | payment.action_required |
| anything else | payment.submitted |
Payload:
| Field | Value |
|---|---|
context.orderId | the order id |
context.transactionId | the gateway transaction id, when one exists |
data.action | present only when the gateway returned a challenge — see below |
data.transactionResult.status | the raw status the form received |
data.transactionResult.response | the full gateway response body |
transactionStatus and transactionId are deliberately not repeated at the top of data: they are represented by the event type and by context.transactionId. The gateway's own copy is still readable at data.transactionResult.response.transactionStatus.
payment.action_required — the 3-D Secure challenge
When the gateway asks for a challenge, data.action describes where to send the payer. The same object is reachable at data.transactionResult.response.action.
| Field | Value |
|---|---|
data.action.url | URL to redirect to or POST to |
data.action.method | 'GET' or 'POST' |
data.action.parameters | array of { key, value } pairs to submit |
document.addEventListener('onRevupMessage', (e) => {
const { type, source, data } = e.detail;
if (type !== 'payment.action_required' || source !== 'form') return;
const { url, method, parameters } = data.action;
if (method === 'POST') {
const form = document.createElement('form');
form.method = 'POST';
form.action = url;
for (const { key, value } of parameters) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = value;
form.appendChild(input);
}
document.body.appendChild(form);
form.submit();
} else {
window.location.href = url;
}
});
payment.failed — two different causes
| Cause | error.name | Where the detail is |
|---|---|---|
The gateway declined (transactionStatus: 'failed') | PaymentFailed | data.transactionResult.response |
| The form could not submit at all (network error, configuration/encryption failure) | FormSubmitError | error.message |
Read the decline reason from
data, not fromerror.message. On aPaymentFailedeventerror.messageis the generic string"Payment failed". The gateway's actual reason is in the response body atdata.transactionResult.response.message. On aFormSubmitErrorevent there is no gateway response, anderror.messageis the real description.
error.statusCode is never set on form events. When the payment call itself failed with an HTTP error, the status is at data.transactionResult.status.
Do not rethrow — the form re-enables itself and the payer can retry.
source: 'apm' — alternative payment methods
Every APM event carries context.apm with the method identifier, except apm.unavailable raised by the SDK's own APM bootstrap.
| Event | When | Payload |
|---|---|---|
payment.submitted | The payer opened the wallet sheet / popup | context: { orderId, apm }, data.message |
payment.success | The gateway approved | context: { transactionId, orderId, apm }, data.message |
payment.failed | The gateway declined, or the vendor flow errored | context: { transactionId?, orderId, apm }, error: { name, message, statusCode? } |
payment.cancelled | The payer dismissed the wallet sheet or popup | context: { orderId, apm }, data.message ('User cancelled' for Apple Pay and Google Pay) |
apm.unavailable | The method cannot render | context: { apm, orderId? }, error: { name, message, statusCode? } |
apm.unavailable is dispatched for, among others: the vendor SDK failing to load, an unsupported device when capability detection is enabled, no active card in the wallet, a missing vendor configuration, an invalid buttonTypes value, or the merchant-level availability lookup failing.
payment.cancelled is a normal outcome, not a failure — restore your checkout state and let the payer choose again.
Error names
error.name | Origin |
|---|---|
apms_initialization_error | The SDK's APM bootstrap: configuration validation, or the availability lookup |
apple_pay_error | Apple Pay |
google_pay_error | Google Pay |
paypal_error | PayPal |
stripe_link_error | Stripe Link (and Amazon Pay, which is served through the same module in 1.0) |
PaymentFailed | Card form — gateway decline |
FormSubmitError | Card form — submission could not be sent |
A complete handler
const stop = revup.onRevupMessage((event) => {
const { type, source, context, data, error } = event;
switch (type) {
case 'initialized':
hideLoadingPlaceholder();
break;
case 'validation.changed':
myPayButton.disabled = !data.isValid || data.isCalling;
break;
case 'payment.submitted':
showSpinner();
break;
case 'payment.action_required':
if (source === 'form' && data?.action) redirectToChallenge(data.action);
break;
case 'payment.success':
// Advisory — confirm server-side before fulfilling
confirmOnServer(context.orderId, context.transactionId);
break;
case 'payment.failed':
showError(error.message);
break;
case 'payment.cancelled':
restoreCheckout();
break;
case 'apm.unavailable':
hideMethod(context?.apm);
break;
}
});
12. Cleanup and re-initialization
1.0 has no destroy() method. To tear the SDK down:
// 1. Remove your event listener
stopListening();
// 2. Clear the container
const container = document.getElementById('revup-container');
if (container) container.innerHTML = '';
// 3. For a new order, build a fresh instance
revup = new Revup({ ...config, orderId: newOrderId });
await revup.init({ containerId: 'revup-container', form: { /* … */ } });
Because orderId is a constructor field in 1.0, changing the order always requires a new Revup instance. Do not reuse an instance across orders.
A proper
destroy()— which also removes the injectedpreconnecthints and appearance state thatinnerHTML = ''leaves behind — arrives in 1.1, together with the ability to switch order without recreating the instance.
13. Environments, URLs and CSP
| Environment | Loader host |
|---|---|
| Sandbox | https://test.hosted.revuppayments.com |
| Live | https://hosted.revuppayments.com |
Use the API key that matches the environment; a sandbox key will not work against live.
Your Content Security Policy must allow:
script-src— the loader host above, andhttps://js.stripe.comwhen Stripe-served methods (Stripe Link, Amazon Pay) are enabled.frame-src— the RevUp form origin, and the wallet/vendor origins for the APMs you enable.connect-src— the RevUp API origin.
The exact origins for your account are provided with your API keys. The SDK's API base URL and form URL are fixed at build time; your integration only supplies merchantDomain, apiKey and orderId.
14. Known limits of the 1.0 line
These are the reasons to plan a move to a newer minor. Each links to where it is solved.
| Limit in 1.0 | Resolved in |
|---|---|
orderId can only be set on the constructor; a new order needs a new instance | 1.1 |
No destroy() — teardown leaves injected page state behind | 1.1 |
Styling limited to form.styles / form.buttonStyles | 1.1 — full Appearance API |
| One flat APM stack; no secondary / overflow zones | 1.1 |
| Only 5 APMs; no iDEAL, Pix or SEPA Direct Debit | 1.1 |
| No session API — every change is a full teardown | 1.1 |
validation.changed does not say which fields are invalid | 1.2 |
textDirection: 'rtl' has no visual effect | 1.2 |
Patch history of the 1.0 line
The loader always serves the newest patch of the minor, so a merchant pinned to /sdk/1/0/ is on 1.0.29. Patches in this line were bug fixes, performance work and security updates; the public API described on this page is the 1.0.29 surface and did not change in a breaking way within the line.