NAV

For any question, we are one click away

Contact us

Web SDK Payment

About

This is a library that generates a payment form directly on your page using separate frames for each field.

Pros:

Cons:

The library helps in collecting card data, validating and verifying it, making a payment, and automatically redirecting the buyer to the returnUrl specified in the settings (finish page).

How to use

Connect the script

Test environment:

<script src="https://dev.bpcbt.com/payment/modules/multiframe/main.js"></script>

Production environment:

<script src="https://dev.bpcbt.com/payment/modules/multiframe/main.js"></script>

Preparation

First, you must create an HTML form for accepting payment. The form must contain the following blocks: #pan, #expiry, #cvc, and the button #pay. It is not necessary to use exactly these field names. You will be able to customize the names you need during initialization.

Here's an example of an HTML form that doesn't enable stored credentials:

...
<div class="card-body">
    <div class="col-12">
        <label for="pan" class="form-label">Card number</label>
        <!-- Container for card number field -->
        <div id="pan" class="form-control"></div>
    </div>
    <div class="col-6 col-expiry">
        <label for="expiry" class="form-label">Expiry</label>
        <!-- Container for expiry card field -->
        <div id="expiry" class="form-control"></div>
    </div>
    <div class="col-6 col-cvc">
        <label for="cvc" class="form-label">CVC / CVV</label>
        <!-- Container for CVC/CVV field -->
        <div id="cvc" class="form-control"></div>
    </div>
</div>
<!-- Pay button -->
<button class="btn btn-primary btn-lg" type="submit" id="pay">
    <!-- Payment loader -->
    <span class="spinner-border spinner-border-sm visually-hidden" id="pay-spinner"></span>
    <span>Pay</span>
</button>
<!-- Container for errors -->
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>
...

If you use stored credentials, you need to enable an additional HTML block: #select-binding.

Here's an example of an HTML form with stored credential fields:

...
<div class="card-body">
    <div class="col-12" id="select-binding-container" style="display: none">
        <!-- Select for bindings -->
        <select class="form-select" id="select-binding" aria-label="Default select example">
            <option selected value="new_card">Pay with a new card</option>
        </select>
    </div>
    <div class="col-12">
        <label for="pan" class="form-label">Card number</label>
        <!-- Container for card number field -->
        <div id="pan" class="form-control"></div>
    </div>
    <div class="col-6 col-expiry">
        <label for="expiry" class="form-label">Expiry</label>
        <!-- Container for expiry card field -->
        <div id="expiry" class="form-control"></div>
    </div>
    <div class="col-6 col-cvc">
        <label for="cvc" class="form-label">CVC / CVV</label>
        <!-- Container for cvc/cvv field -->
        <div id="cvc" class="form-control"></div>
    </div>
    <label class="col-12" id="save-card-container">
        <!-- Save card checkbox -->
        <input class="form-check-input" type="checkbox" value="" id="save-card" />
        Save card
    </label>
</div>
<!-- Pay button -->
<button class="btn btn-primary btn-lg" type="submit" id="pay">
    <!-- Payment loader -->
    <span class="spinner-border spinner-border-sm visually-hidden" id="pay-spinner"></span>
    <span>Pay</span>
</button>
<!-- Container for errors -->
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>
...

You can add any additional fields to the form, such as the cardholder name, email, phone, etc. However, don't forget to pass them into the doPayment() method later.

Web SDK initialization

Description payment form

You need to execute the constructor function new window.PaymentForm()`.

window.PaymentForm() can take the following properties:

PaymentForm initialization properties

mdOrder string required
Order ID

Description of fields on the form

apiContext string optional
Context (a part of the payment gateway URL after the domain) for API queries.
By default, the apiContext is automatically taken from the link used to connect the script modules/multiframe/main.js.

language string optional
Language used for localization of errors and placeholder names.
Default value is en.

autofocus boolean optional
Automatically shift focus as fields are filled out.
Default value is true

showPanIcon boolean optional
Show payment system icon.
Default value is true

panIconStyle CSSStyleDeclaration optional
Custom styles for payment system icon

Custom styles for payment system icon

containerClassName string optional
Optional class name for container.
Default value is field-container

onFormValidate (result: boolean) => void optional
Callback for handling form validation change.
For example:
onFormValidate: (isValid) => {
    alert(isValid ? 'Сongratulations!' : 'Oops! We regret.'); 
}

Web SDK initialization example

 const webSdkPaymentForm = new window.PaymentForm({
      // Order number (order registration happens before initialization of the form)
      mdOrder: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
      // Name of the class that will be set for containers with frames
      containerClassName: 'field-container',
      onFormValidate: (isValid) => {
        // Handling form validation
      },
      // Context for API queries
      apiContext: '/payment',
      // Language - is used for localization of errors and placeholder names.
      // The language must be supported in Merchant settings
      language: 'en',
      // Automatically shift focus as fields are filled out
      autoFocus: true,
      // Show payment system icon
      showPanIcon: true,
      // Custom styles for payment system icon
      panIconStyle: {
        height: '16px',
        top: 'calc(50% - 8px)',
        right: '8px',
      },
      fields: {
        pan: {
          container: document.querySelector('#pan'),
          onFocus: (containerElement) => {
            // Action when field gets focus
            // (containerElement contains link to field container element)
          },
          onBlur: (containerElement) => {
            // Action when field gets focus off it
            // (containerElement contains link to field container element)
          },
          onValidate: (isValid, containerElement) => {
            // Action when field is valid
            // (isValid is true if field is valid, otherwise is false)
            // (containerElement contains link to field container element)
          },
        },
        expiry: {
          container: document.querySelector('#expiry'),
          // ...
        },
        cvc: {
          container: document.querySelector('#cvc'),
          // ...
        },
      },
      // Style for input fields
      styles: {
        // Base state
        base: {
          color: 'black',
        },
        // Focused state
        focus: {
          color: 'blue',
        },
        // Disabled state
        disabled: {
          color: 'gray',
        },
        // Has valid value
        valid: {
          color: 'green',
        },
        // Has invalid value
        invalid: {
          color: 'red',
        },
        // Style for placeholder
        placeholder: {
          // Base style
          base: {
            color: 'gray',
          },
          // Style when focused
          focus: {
            color: 'transparent',
          },
        },
      },
    });

Stylization

You can define styles for the iframe container using class names:

className is set on initialization in the containerClassName parameter.

Checkout without stored credentials

Step 1. Execute initialization method

After defining Web SDK parameters, you must call init(). This function returns a callback where you can, for example, hide the loader or do something else. init() returns a promise.

For example:

webSdkFormWithoutBindings
    .init()
    .then(() => {
        // Script initialized successfully
        document.querySelector('.payment-form-loader').classList.remove('payment-form-loader--active');
    })
    .then((error) => {
      // Errors occurred during the initialization of the script. Further execution is not possible.
    }); ;

Step 2. Pay button click handling. Execute payment method.

You can call the payment in any way convenient for you. To do this, call the function doPayment(). You do not need to send card data. The Web SDK will handle it. doPayment() returns a promise.

The method takes the following parameters:

phone String optional
Customer's phone number

email String optional
Customer's email

cardholderName String optional
Name of the cardholder.

jsonParams Object optional
Additional fields. For example, you can send additional information about the order or any other information that is useful to you. For example, jsonParams: { "t-shirt-color": "black", "size": "M" }

Call example:

webSdkFormWithoutBindings.doPayment({
    // Additional params
    email: 'foo@bar.com',
    phone: '4420123456789',
    cardholderName: 'JOHN DOE',
    jsonParams: { foo: 'bar' },
})
    .then((result) => {
        console.log('result', result);
    })
    .catch((e) => {
        // Error processing. For example, we show a block with an error
        errorEl.innerHTML = e.message;
        errorEl.classList.remove('visually-hidden');
    })
    .finally(() => {
        // Execute in any case. For example, make the "Pay" button active again.
        payButton.disabled = false;
        spinnerEl.classList.add('visually-hidden');
    });

Demo without stored credentials

Web SDK Payment requires an mdOrder (pre-registered order in the gateway).
You can register an order through the Merchant Portal or through the API.

Alternatively, you can try using xxxxx-xxxxx-xxxxx-xxxxx if you only want to test the payment form.

The entire demo code

<div class="container_demo">
    <div class="about">
        <form name="formRunTest">
            <label for="mdOrder"> Order ID (mdOrder) <br><span class="label__desc">(Should come from the backend. This input only for demo)</span></label>
            <div class="run-test">
                <input id="mdOrder" type="text" placeholder="Paste the mdOrder registered for sandbox"/>
                <button class="btn-mini" id="load" type="submit">Load</button>
            </div>
        </form>
    </div>
    <div class="payment-form">
        <div class="payment-form-loader payment-form-loader--active">
            Web SDK Payment requires mdOrder (pre-registered order in the gateway).<br>
            You can register an order through Merchant Portal or through API. <br><br>
            Or try use <code>xxxxx-xxxxx-xxxxx-xxxxx</code> if you to want check only payment form.
        </div>
        <div class="card-body">
            <div class="col-12">
                <label for="pan" class="form-label">Card number</label>
                <div id="pan" class="form-control"></div>
            </div>
            <div class="col-6 col-expiry">
                <label for="expiry" class="form-label">Expiry</label>
                <div id="expiry" class="form-control"></div>
            </div>
            <div class="col-6 col-cvc">
                <label for="cvc" class="form-label">CVC / CVV</label>
                <div id="cvc" class="form-control"></div>
            </div>
        </div>
        <button class="btn btn-primary btn-lg" type="submit" id="pay">
            <span
            class="spinner-border spinner-border-sm me-2 visually-hidden"
            role="status"
            aria-hidden="true"
            id="pay-spinner">
            </span>
            <span>Pay</span>
        </button>
        <div class="error my-2 text-center text-danger visually-hidden" id="error"></div>
    </div>
</div>

<script>
document.addEventListener('DOMContentLoaded', () => {

    document.formRunTest.addEventListener('submit', function(e) {
        e.preventDefault();
        const mrOrderInput = document.getElementById('mdOrder');
        mrOrderInput.classList.remove("invalid");
        if (!/(\w+-){3,10}\w+/g.test(mrOrderInput.value)) {
            mrOrderInput.classList.add("invalid");
            return;
        }

        // Initialization of Web SDK. A required mdOrder (order ID) is needed.
        initPayment(mrOrderInput.value);
    });

    let webSdkFormWithoutBindings;

    function initPayment(mdOrder) {
        webSdkFormWithoutBindings = new window.PaymentForm({
            mdOrder: mdOrder,
            onFormValidate: () => {},
            language: 'en',
            containerClassName: 'field-container',
            autoFocus: true,
            showPanIcon: true,
            panIconStyle: {
                height: '16px',
                top: 'calc(50% - 8px)',
                right: '8px',
            },
            fields: {
                pan: {
                    container: document.querySelector('#pan'),
                },
                expiry: {
                    container: document.querySelector('#expiry'),
                },
                cvc: {
                    container: document.querySelector('#cvc'),
                },
            },
            styles: {
                base: {
                    padding: '0px 16px',
                    color: 'black',
                    fontSize: '18px',
                },
                invalid: {
                    color: 'red',
                },
                placeholder: {
                    base: {
                        color: 'gray',
                    },
                    focus: {
                        color: 'transparent',
                    },
                },
            },
        });

        // Action after initialization
        webSdkFormWithoutBindings
            .init()
            .then(() => {
                document.querySelector('.payment-form-loader').classList.remove('payment-form-loader--active');
            });
    }

    // Pay handler
    document.querySelector('#pay').addEventListener('click', () => {
        const payButton = document.querySelector('#pay');

        // Make the "Pay" button inactive to avoid double payments
        payButton.disabled = true;

        // Show the loader to the user
        const spinnerEl = document.querySelector('#pay-spinner');
        spinnerEl.classList.remove('visually-hidden');

        // Hide the error container
        const errorEl = document.querySelector('#error');
        errorEl.classList.add('visually-hidden');

        // Start payment
        webSdkFormWithoutBindings.doPayment({
            // Additional parameters
            email: 'foo@bar.com',
            phone: '4420123456789',
            cardholderName: 'Ludwig van Beethoven',
            jsonParams: { size: 'L' },
        })
            .then((result) => {
                console.log('result', result);
            })
            .catch((e) => {
                // Execute on error
                errorEl.innerHTML = e.message;
                errorEl.classList.remove('visually-hidden');
            })
            .finally(() => {
                // Execute in any case. For example, make the "Pay" button active again.
                payButton.disabled = false;
                spinnerEl.classList.add('visually-hidden');
            });
    });
});

</script>

Checkout with stored credentials

Step 1. Execute initialization method

After defining Web SDK parameters, you must call init(). This function returns a callback where you can, for example, hide the loader or do something else. init() returns a promise.

For example:

// Initialization
webSdkFormWithBindings
  .init()
  .then(({ orderSession }) => {
    // The `orderSession` object contains all the information about the order, including information about the bindings.
    console.info("orderSession", orderSession);

    // Show select binding element
    document.querySelector("#select-binding-container").style.display = orderSession.bindings.length ? "" : "none";

    // Fill the select with stored credentials
    orderSession.bindings.forEach((binding) => {
      document
        .querySelector("#select-binding")
        .options.add(new Option(binding.pan, binding.id));
    });

    // Handle select stored credential or a new card
    document
      .querySelector("#select-binding")
      .addEventListener("change", function () {
        const bindingId = this.value;
        if (bindingId !== "new_card") {
          webSdkFormWithBindings.selectBinding(bindingId);

          // Hide the 'Save card' checkbox
          document.querySelector("#save-card-container").style.display = "none";
        } else {
          // Selecting stored credentials with null means switching to a new card
          webSdkFormWithBindings.selectBinding(null);

          // Show the 'Save card' checkbox
          document.querySelector("#save-card-container").style.display = "";
        }
      });

    // When the form is ready, we can hide the loader
    document.querySelector("#pay-form-loader").classList.add("visually-hidden");
  })
  .catch((error) => {
    // Errors occurred during the initialization of the script. Further execution is not possible.
  });

Step 2. Pay button click handling. Execute payment method.

You can call payment in any way convenient for you. To do this, call the function doPayment(). You do not need to send card data. WebSDK will do it. doPayment() returns promise.

The method takes the following parameters:

phone String optional
Customer's phone number

saveCard boolean optional
Saving card option. For example, document.querySelector('#save-card').checked

email String optional
Customer's email

cardholderName String optional
Name of the card holder.

jsonParams Object optional
Additional fields. For example, you can send additional information about the order or any other information that is useful to you. For example, jsonParams: { "t-shirt-color": "black", "size": "M" }

Apply stored credentials

To pay with a stored credential, you need to pass the selected bindingId to the form before calling doPayment:
webSdkFormWithBindings.selectBinding('bindingId here');

If you changed your mind and want to pay with a new card don't forget to remove bindingId from the form:
webSdkFormWithBindings.selectBinding(null);

Call example:

webSdkFormWithBindings.doPayment({
    // Additional parameters
    email: 'foo@bar.com',
    phone: '4420123456789',
    saveCard: document.querySelector('#save-card').checked,
    cardholderName: 'JOHN DOE',
    jsonParams: { foo: 'bar' },
})
    .then((result) => {
        console.log('result', result);
    })
    .catch((e) => {
        // Error processing. For example, we show a block with an error
        errorEl.innerHTML = e.message;
        errorEl.classList.remove('visually-hidden');
    })
    .finally(() => {
        // Execute in any case. For example, make the "Pay" button active again.
        payButton.disabled = false;
        spinnerEl.classList.add('visually-hidden');
    });

Demo with stored credentials

Web SDK Payment requires mdOrder (pre-registered order in the gateway).
You can register an order through Merchant Portal or through API.

Or try use xxxxx-xxxxx-xxxxx-xxxxx if you to want check only payment form.

The entire demo code

<div class="container_demo">
    <div class="about">
        <form name="formRunTest">
            <label for="mdOrder"> Order ID (mdOrder) <br><span class="label__desc">(This input is only for demo purposes. The actual order ID should come from the backend.)</span></label>
            <div class="run-test">
                <input id="mdOrder" type="text" placeholder="Paste the mdOrder registered for sandbox"/>
                <button class="btn-mini" id="load" type="submit">Load</button>
            </div>
        </form>
    </div>
    <div class="payment-form">
        <div class="payment-form-loader payment-form-loader--active">
            Web SDK Payment requires mdOrder (pre-registered order in the gateway).<br>
            You can register an order through Merchant Portal or through API. <br><br>
            Or try use <code>xxxxx-xxxxx-xxxxx-xxxxx</code> if you to want check only payment form.
        </div>
        <div id="pay-form-loader" class="spinner-container visually-hidden">
            <div class="spinner-border" role="status"></div>
        </div>
        <div class="card-body">
            <div class="col-12" id="select-binding-container" style="display: none">
              <select class="form-select" id="select-binding" aria-label="Default select example">
                <option selected value="new_card">Pay with a new card</option>
              </select>
            </div>
            <div class="col-12">
                <label for="pan" class="form-label">Card number</label>
                <div id="pan" class="form-control"></div>
            </div>
            <div class="col-6 col-expiry">
                <label for="expiry" class="form-label">Expiry</label>
                <div id="expiry" class="form-control"></div>
            </div>
            <div class="col-6 col-cvc">
                <label for="cvc" class="form-label">CVC / CVV</label>
                <div id="cvc" class="form-control"></div>
            </div>
            <label class="col-12" id="save-card-container">
              <input class="form-check-input" type="checkbox" value="" id="save-card" />
              Save card
            </label>
        </div>
        <button class="btn btn-primary btn-lg" type="submit" id="pay">
            <span
            class="spinner-border spinner-border-sm me-2 visually-hidden"
            role="status"
            aria-hidden="true"
            id="pay-spinner">
            </span>
            <span>Pay</span>
        </button>
        <div class="error my-2 visually-hidden" id="error"></div>
    </div>
</div>

<script>
    document.addEventListener('DOMContentLoaded', () => {

    // Handler initialization for expamle input
    function handleSubmit(e){
        e.preventDefault();
        const mrOrderInput = document.getElementById('mdOrder');
        mrOrderInput.classList.remove("invalid");
        if (!/(\w+-){3,10}\w+/g.test(mrOrderInput.value)) {
            mrOrderInput.classList.add("invalid");
            return;
        }
        // Removing example placeholder
        document.querySelector('.payment-form-loader').classList.remove('payment-form-loader--active');
        // Adding payment form loader
        document.querySelector('#pay-form-loader').classList.remove('visually-hidden');

        // Initialization of Web SDK. A required mdOrder (order ID) is needed.
        initPayment(mrOrderInput.value);
    }

    // Register event for example input
    document.formRunTest.addEventListener('submit', handleSubmit);

    let webSdkFormWithBindings;

    function initPayment(mdOrder) {
        webSdkFormWithBindings = new window.PaymentForm({
            // Order number (order registration happens before initialization of the form)
            mdOrder: mdOrder,
            // Handling form validation
            onFormValidate:(isValid) => {
                // For example, you can disable "pay" and "Get token" buttons if from is not valid, like this:
                // const payButton = document.querySelector('#pay');
                // payButton.disabled = !isValid;
            },
            //  Context for API queries
            apiContext: '/payment',
            // Language - is used for localization of errors and names for placeholders.
            // The language should be supported in the merchant's settings
            language: 'en',
            // Class name for container elements containing iFrames
            containerClassName: 'field-container',
            // Automatic switching of focus when fields are filled
            autoFocus: true,
            // Show payment system icon
            showPanIcon: true,
            // Additional styles for the payment system icon
            panIconStyle: {
                height: '16px',
                top: 'calc(50% - 8px)',
                right: '8px',
            },
            // Field settings
            fields: {
                // Container element in which the iFrame with the field will be placed
                pan: {
                    container: document.querySelector('#pan'),
                },
                // Card expiration date
                expiry: {
                    container: document.querySelector('#expiry'),
                },
                // CVC/CVV-code
                cvc: {
                    container: document.querySelector('#cvc'),
                },
            },
            // Additional styles to customize the appearance of input fields within iFrames
            styles: {
                base: {
                    padding: '0px 16px',
                    color: 'black',
                    fontSize: '18px',
                },
                disabled: {
                    backgroundColor: '#e9ecef',
                },
                invalid: {
                    color: 'red',
                },
                placeholder: {
                    base: {
                        color: 'gray',
                    },
                    focus: {
                        color: 'transparent',
                    },
                },
            },
        });

        // Action after initialization
        webSdkFormWithBindings
            .init()
            .then(({ orderSession }) => {
                // The `orderSession` object contains all the information about the order, including information about the stored credentials.
                console.info('orderSession', orderSession);

                // Show the select stored credential element
                document.querySelector('#select-binding-container').style.display = orderSession.bindings.length
                ? ''
                : 'none';

                // Fill the select with stored credentials
                orderSession.bindings.forEach((binding) => {
                document.querySelector('#select-binding').options.add(new Option(binding.pan, binding.id));
                });

                // Handle select stored credential or a new card
                document.querySelector('#select-binding').addEventListener('change', function () {
                const bindingId = this.value;
                if (bindingId !== 'new_card') {
                    // Set binding id
                    webSdkFormWithBindings.selectBinding(bindingId);

                    // Hide the 'Save card' checkbox
                    document.querySelector('#save-card-container').style.display = 'none';
                } else {
                    // Selecting stored credentials with null means switching to a new card
                    webSdkFormWithBindings.selectBinding(null);

                    // Show the 'Save card' checkbox
                    document.querySelector('#save-card-container').style.display = '';
                }
                });

                // When the form is ready, we can hide the loader
                document.querySelector('#pay-form-loader').classList.add('visually-hidden');

                // Remove the event for the example input
                document.formRunTest.removeEventListener('submit', handleSubmit);
            })
            .catch((error) => {
                // Execute on error
                const errorEl = document.querySelector('#error');
                errorEl.innerHTML = e.message;
                errorEl.classList.remove('visually-hidden');
            });
    }

    // Payment handler
    document.querySelector('#pay').addEventListener('click', () => {
        // Make the "Pay" button inactive to avoid double payments
        const payButton = document.querySelector('#pay');
        payButton.disabled = true;

        // Show the loader, for the user
        const spinnerEl = document.querySelector('#pay-spinner');
        spinnerEl.classList.remove('visually-hidden');

        // Hide the error container
        const errorEl = document.querySelector('#error');
        errorEl.classList.add('visually-hidden');

        // Start payment
        webSdkFormWithBindings.doPayment({
            // Additional parameters
            email: 'foo@bar.com',
            phone: '4420123456789',
            cardholderName: 'JOHN DOE',
            saveCard: document.querySelector('#save-card').checked,
            jsonParams: { foo: 'bar' },
        })
            .then((result) => {
                // Here you can do something with payment result
                console.log('result', result);
            })
            .catch((e) => {
                // Execute on error
                errorEl.innerHTML = e.message;
                errorEl.classList.remove('visually-hidden');
            })
            .finally(() => {
                // Execute in any case. For example, make the "Pay" button active again.
                payButton.disabled = false;
                spinnerEl.classList.add('visually-hidden');
            });
    });   
})

</script>