ExactJS Payment Forms

Create custom, secure payment forms using ExactJS components

The ExactJS library allows you to create your own payment forms hosted on your servers and drop in UI components for entering PCI data. Since the PCI-related UI components continue to be served from our site, and the data entered into them is never submitted to your servers, you do not have to worry about data security and the burden of maintaining PCI compliance.

ExactJS gives you the freedom to design and manage payment forms without having to redirect the customer away from your checkout page and without PCI data passing through or being stored in your network.

Getting Started

If you have not already done so, please read the API Access Set-up guide and ensure you have generated both a secret API key and a shareable API key.

You will use the secret key to communicate with our APIs from your backend server, and you will use your shareable key with the ExactJS library from your customer's browser.

Creating the Order

Before rendering your payment form for the customer, you need to send the details of the order to our servers. From your server, send a POST request to our Orders API with the details of your customer's order.

You will need to use your secret API key to authenticate this request.

POST /orders
{
  amount: 1123,
  capture: true,
  terminal: {
    gateway_id: "AD0161-01"
  }
}

The above example is the bare minimum required to create an order. Please see the Orders API reference for the various other options available when creating an Order.

This will return an order ID which you will need to include on your payment form.

You can also use the ID with the Orders API to update the order as many times as you need until the customer has paid. Once the order has been paid, further updates will be rejected.

Building Your Form

When building your payment form, you first need to add our ExactJS library.

For the sandbox environment, you should load the file from the api.exactpaysandbox.com domain.
For production, load it from the api.exactpay.com domain.

<head>
  <script src="https://api.exactpaysandbox.com/js/exact.js"></script>
</head>

You then need to initialise the library with both the ID of the order you created through our API and your shareable API key - do not use your secret API key!!

const exact = ExactJS("your_shareable_api_key", "the_order_id");

Create your payment form, including an element to which we will attach our credit card UI. Here's a simple example:

<form id="myForm" action="your_form_url" method="post">
  <div>
    <label for="email-address"">Email address</label>
    <input type="email" id="email-address" name="email-address" autocomplete="email">
  </div>

  <div id="cardElement">
    <!-- our credit card UI component will be attached here -->
  </div>

  <div>
    <label for="address">Address</label>
    <input type="text" id="address" name="address" autocomplete="street-address">
  </div>

  <div>
    <label for="apartment">Apartment, suite, etc.</label>
    <input type="text" id="apartment" name="apartment">
  </div>

  <div>
    <label for="city">City</label>
    <input type="text" id="city" name="city">
  </div>

  <div>
    <label for="province">Province</label>
    <input type="text" id="province" name="province">
  </div>

  <div>
    <label for="postal-code">Postal code</label>
    <input type="text" id="postal-code" name="postal-code" autocomplete="postal-code">
  </div>

  <!--
    we'll return the ID of the customer's payment which you can then
    submit to your server for future reference
  -->
  <input type="hidden" name="payment_id" id="payment_id">

  <div>
    <input type="submit" name="commit" value="Pay Now" data-disable-with="Pay Now">
  </div>
</form>

Tell ExactJS to add the credit card UI to your form.

const components = exact.components();
components.addCard('cardElement');

Finally, you need to intercept the form submission and tell ExactJS to pay for the order using the credit card details supplied by the customer.

document.getElementById("myForm").addEventListener('submit', (event) => {
  event.preventDefault();

  const form = event.target.closest("form");
  exact.submitPayment()
    .then(payment_id => {
        // add the payment id to your form
      document.getElementById('payment_id').value = payment_id
        // submit your form to your backend
      form.submit()
    })
    .catch(err => console.error(err));
});

You should save the payment ID on your server as it will allow you to use the Payments API to get more details about the payment, or to perform follow-up transactions such as captures, refunds or voids.

Displaying Errors

If something goes wrong during the payment process, whether it's due to the customer entering invalid information, or something going wrong with the payment submission, the submitPayment method will fail with an array of error strings. You are responsible for displaying these errors to your customer, placed and styled in whichever way suits your design.

Here's a simple example, which assumes your payment form has a <div id="errors"> element somewhere on the page.

const errorElem = document.getElementById("errors");

const showErrors = (errs) => {
  Array.from(errs).forEach(err => {
    const pElem = document.createElement("p");
    pElem.textContent = err;
    errorElem.append(pElem);
  });
  errorElem.classList.remove("hidden");
};

const clearErrors = () => {
  errorElem.classList.add("hidden");
  Array.from(errorElem.children).forEach(elem => {
    if(elem.tagName === "P") {
      errorElem.removeChild(elem);
    }
  });
};

document.addEventListener('DOMContentLoaded', function() {
  const exact = ExactJS("your_shareable_key", "the_order_id");
  const components = exact.components();
  components.addCard('cardElement');

  document.forms.myForm.addEventListener('submit', function(event) {
    event.preventDefault();
    clearErrors();

    const form = event.target.closest("form");
    exact.submitPayment()
      .then(payment_id => {
        // add the payment ID to the form and submit to your server
        document.getElementById('payment_id').value = payment_id;
        form.submit();
      })
      .catch(err => showErrors(err));
  });
});