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 a secret API key, which you will use to communicate with our APIs from your backend server.
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 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: "your_gateway_id"
}
}
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 and an access token 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.
Access Tokens
The access token you received when you created the Order will allow your customers to access our APIs from their browser for a limited time period. It is valid for 15 minutes and, once it has expired, further API requests will be rejected.
You can optionally re-generate an access token if you want to allow your customer more time to complete their payment.
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/v1/exact.js"></script>
</head>
You then need to initialize the library with the access token - do not use your secret API key!!
const exact = ExactJS("the_access_token");
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>
<!-- INSERT RESPONSE ITEMS HERE -->
<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. You will also need to provide your Order ID at this stage.
const components = exact.components({orderId: "the_order_id"});
components.addCard('cardElement');
Finally, you need to intercept the form submission and tell ExactJS what to do with the payment details supplied by the customer.
Option 1: Pay from the Browser
The first option is to configure your payment form so that your customers pay for the order directly from their browser. The ID of the completed payment will be returned to you. This ID can then be used with our Payments API to look up further details of the payment for example, or to capture an authorization.
For this option, you need to add an element to your form to accept the payment ID returned by ExactJS, so insert the following snippet into your form in place of the <!-- INSERT RESPONSE ITEMS HERE -->
comment in the sample form above.
<input type="hidden" name="payment_id" id="payment_id">
Now add a listener to tell ExactJS to pay for the order when the customer submits the form.
document.getElementById("myForm").addEventListener('submit', (event) => {
event.preventDefault();
exact.payOrder();
return false;
});
Once the customer clicks your form's submit button, ExactJS will gather the payment details they have entered and will use that to pay for the order. The payment can succeed or fail, so you will need to provide handlers for each of those conditions.
If the payment did not complete, we will want to handle the payment-failed
condition. For this condition, the payload will consist of an array of error strings, which we can display to the customer.
exact.on("payment-failed", (payload) => {
const errorElem = document.getElementById("errors");
Array.from(payload).forEach(err => {
const pElem = document.createElement("p");
pElem.textContent = err;
errorElem.append(pElem);
});
errorElem.classList.remove("hidden");
});
For the payment-complete
condition, we add a handler which accepts the payload, extracts the paymentId
, adds that to our form and then submits the form to our backend.
exact.on("payment-complete", (payload) => {
// add the payment id to your form
document.getElementById('payment_id').value = payload.paymentId;
// submit your form to your backend
document.forms.myForm.submit();
});
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.
Option 2: Tokenize from the Browser
The second option is to configure your payment form so that your customer's payment details will be tokenized from the browser. You will then need to pay for the Order from your backend server, using the token details returned by ExactJS.
You can also save the returned token details to initiate payments in the future, without incurring any PCI compliance requirements.
For this option, you need to add a few elements to your form to accept the token details returned by ExactJS, so insert the following snippet into your form in place of the <!-- INSERT RESPONSE ITEMS HERE -->
comment in the sample form above.
<input type="hidden" name="token" id="token">
<input type="hidden" name="token_type" id="token_type">
<input type="hidden" name="token_last4" id="token_last4">
<input type="hidden" name="token_brand" id="token_brand">
Now add a listener to tell ExactJS to tokenize their payment details when the customer submits the form.
document.getElementById("myForm").addEventListener('submit', (event) => {
event.preventDefault();
exact.tokenize();
return false;
});
Again, you would need to provide handlers for both the payment-failed
and payment-complete
conditions.
The payment-failed
handler has been shown earlier, but the payload after a successful tokenization is different from that of a payment, so here's a sample payment-complete
handler for tokenization.
exact.on("payment-complete", (payload) => {
// add the token details to your form
document.getElementById('token').value = payload.token;
document.getElementById('token_type').value = payload.token_type;
document.getElementById('token_last4').value = payload.last4;
document.getElementById('token_brand').value = payload.card_brand;
// submit your form to your backend
document.forms.myForm.submit();
});
The final step for this option is for you to pay for the order from your backend server, using the token you have just received. As before, you will use your secret API key to authenticate this API request.
POST /orders/the_order_id/pay
{
payment_method: {
token: {
token: "the_token",
token_type: "the_token_type"
}
}
}
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 or tokenization submission, the payOrder
or tokenize
methods 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("the_access_token");
const components = exact.components({orderId: "the_order_id"});
components.addCard('cardElement');
document.forms.myForm.addEventListener('submit', function(event) {
event.preventDefault();
clearErrors();
exact.payOrder();
return false;
});
exact.on("payment-complete", (payload) => {
document.getElementById('payment_id').value = payload.paymentId;
document.forms.myForm.submit();
});
exact.on("payment-failed", (payload) => showErrors(payload));
});
Updated almost 2 years ago