HPF Usage Guide
Praxis Hosted Payment Fields (hereafter HPF) is a new method for implementing credit card payment flows with Praxis.
It enables you as a merchant to fully customize the credit card form while ensuring that sensitive payment details remain secure and are never handled directly by your website or platform. As a result, PCI-DSS certification is not required on your end.
HPF is used in conjunction with the Praxis Direct API to process transactions using any credit card payment solution that supports server-to-server integration. It also retains the advantages of Praxis’s smart routing and cascading features, which help increase approval ratios for transactions.
The following sequence diagram illustrates the high-level integration flow:
HPF utilizes encryption and secure server-side transmission for sensitive data. Therefore, HTTPS is the only allowed protocol for the host page (the page where HPF is integrated).
The first step is to initiate an HPF session. The API documentation for this step is available here: open-hpf-session.
This call initializes the HPF session, and a successful response will return a session_token
to be used when initializing the SDK in the next step.
{danger.fa-exclamation-triangle} IMPORTANT: Domain where HPF is hosted must be whitelisted in advance for your API account. The domain is a critical part of the HPF initialization process.
The second step involves using the received session_token
to request the HPF session when initializing the HPF SDK.
The SDK enables full customization of the payment fields’ appearance to match your website’s design. You can provide your own CSS to override default settings configured in the Praxis Backoffice.
While your website controls everything outside the payment fields, the HPF SDK provides the embedded interface and events needed to manage the payment form. This includes rendering card templates, handling validation errors, and more.
As you design the user journey, you’ll use events emitted by the SDK to build features such as:
tokenizeCard
methodThis process results in an hpf_auth_token
.
At the end of this workflow, your server must call the next step to execute the transaction.
{danger.fa-exclamation-triangle} IMPORTANT: The Direct API call must be made server-to-server. It should never be initiated from the user's browser.
Refer to the HPF SDK reference documentation for full implementation details: javascript_sdk.
The final step is to use the hpf_auth_token
in a Direct API call to process the transaction. See details here: Direct API card.
To better understand how to build your own payment page using HPF, refer to the sample code provided.
{danger.fa-exclamation-triangle} IMPORTANT: To make the example work, you must define and replace the following parameters:
YOUR_PAYMENT_FORM_SUBMISSION_URL
PAYLOAD_SRC
SESSION_TOKEN
Code Sample:
<html>
<head>
<style>
.hpf-view {
font-size: 16px;
line-height: 1;
}
.hpf-view .hpf-form-container {
width: 100%;
max-width: 550px;
margin: 50px auto;
background-color: #fff;
padding: 30px;
box-shadow: 0 8px 8px 0px rgb(0 0 0 / 5%);
border-radius: 8px;
position: relative;
min-height: 350px;
}
.hpf-view .hpf-form-container .hpf-loader-container {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255,255,255,0.7);
}
.hpf-view .hpf-form-container .hpf-loader-container .hpf-loader-icon {
display: inline-block;
width: 40px;
height: 40px;
}
.hpf-view .hpf-form-container .hpf-loader-container .hpf-loader-icon:after {
content: " ";
display: block;
width: 35px;
height: 35px;
margin: 8px;
border-radius: 50%;
border: 6px solid #fff;
border-color: #f0ecec transparent #f0ecec transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.hpf-view .hpf-form-container .hpf-title {
text-align: center;
padding-bottom: 15px;
margin: 0;
font-size: 24px;
font-weight: 700;
}
.hpf-view .hpf-form .input-item .hpf-input-error {
width: 100%;
font-size: 11px;
transition: 0.15s all ease;
font-style: italic;
color: red;
height: 15px;
opacity: 0;
}
.hpf-view .hpf-form .input-item.has-error .hpf-input-error {
opacity: 1;
}
.hpf-view .hpf-form .btn-container {
display: flex;
justify-content: center;
align-items: center;
padding: 15px 10px;
}
.hpf-view .hpf-form .btn {
background-color: rgb(251 146 60);
height: 50px;
width: 100%;
max-width: 240px;
border-radius: 10px;
border: none;
font-size: 16px;
font-weight: 700;
cursor: pointer;
color: white;
transition: 0.5s;
outline: none;
opacity: 1;
}
.hpf-view .hpf-form .btn:hover {
background-color: rgb(249 115 22);
}
.hpf-view .hpf-form .hpf-fields-container {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.hpf-view .hpf-form .hpf-fields-container .input-item iframe {
height:55px;
width: 100%;
}
.hpf-view .hpf-form .hpf-fields-container .input-item {
position: relative;
min-height: 72px;
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-list-dropdown {
position: absolute;
-webkit-user-select: none;
user-select: none;
width: 30px;
height: 30px;
padding: 5px;
top: 22px;
right: 2%;
max-height: 100%;
display: none;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 1;
transition: 0.3s;
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-list-dropdown.active {
rotate: 180deg;
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-list-dropdown:hover {
background-color: rgba(238, 238, 238, 0.31);
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-list-dropdown .svg-dropdown {
width: 15px;
height: 15px;
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-templates-list {
width: 100%;
padding: 10px 0;
position: absolute;
top: calc(100% - 10px);
left: 0;
border-radius: 4px;
box-shadow: 0 2px 3px rgb(0 0 0 / 50%);
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
max-height: 160px;
overflow: auto;
z-index: 1;
background-color: #f8f8f8;
display: none;
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-templates-list.show {
display: block;
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-templates-list .hpf-card-list-item {
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-templates-list .hpf-card-list-item:hover{
background-color: #f0ecec;
}
.hpf-view .hpf-form .hpf-fields-container .input-item.card_number .card-templates-list .hpf-card-list-item.active {
font-weight: 600;
background-color: #f0ecec;
}
</style>
<script src="PAYLOAD_SRC?t=${new Date().getTime()}"></script>
</head>
<body>
<div class="hpf-view">
<div id="hpf-container-id" class="hpf-form-container">
<div id="hpf-loader-id" class="hpf-loader-container">
<span class="hpf-loader-icon"></span>
</div>
<h4 class="hpf-title">Pay with Credit Card</h4>
<form id="hpf-form-id" class="hpf-form" method="post">
<div class="hpf-fields-container">
<div class="input-item card_number">
<div class="card-input-container">
<div id="hpf-card-number"></div>
<div class="hpf-input-error">Invalid value</div>
</div>
<span id="hpf-card-dropdown" class="card-list-dropdown">
<svg class="svg-dropdown" x="0px" y="0px" width="451.847px" height="451.847px" viewBox="0 0 451.847 451.847"><g><path d="M225.923,354.706c-8.098,0-16.195-3.092-22.369-9.263L9.27,151.157c-12.359-12.359-12.359-32.397,0-44.751 c12.354-12.354,32.388-12.354,44.748,0l171.905,171.915l171.906-171.909c12.359-12.354,32.391-12.354,44.744,0 c12.365,12.354,12.365,32.392,0,44.751L248.292,345.449C242.115,351.621,234.018,354.706,225.923,354.706z"></path></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></svg>
</span>
<div id="hpf-card-list" class="card-templates-list"></div>
</div>
<div class="input-item card_holder">
<div id="hpf-card-holder"></div>
<div class="hpf-input-error">Invalid value</div>
</div>
<div class="input-item exp">
<div id="hpf-exp"></div>
<div class="hpf-input-error">Invalid value</div>
</div>
<div class="input-item cvv">
<div id="hpf-cvv"></div>
<div class="hpf-input-error">Invalid value</div>
</div>
</div>
<div class="btn-container">
<button type="submit" class="btn">Deposit</button>
</div>
</form>
</div>
</div>
<script>
const transaction_type = 'payment'; // define here your transaction type
let PraxisGateInstance;
const praxisValidationObj = {
card_number: false,
card_holder: false,
exp: false,
cvv: false
}
const praxisGateInterval = setInterval(async function () {
if (typeof window.PraxisGate !== 'undefined') {
clearInterval(praxisGateInterval)
PraxisGateInstance = new PraxisGate({
sessionToken: 'SESSION_TOKEN', // token from open_session request
containers: {
card_number: '#hpf-card-number',
card_holder: '#hpf-card-holder',
exp: '#hpf-exp',
cvv: transaction_type === 'payment' ? '#hpf-cvv' : null // cvv container should not be provided if it's withdraw
},
onLoad: async function () { // trigger when all fields loaded, before init validation
const list = await PraxisGateInstance.getCustomerCards(); // list of available templates also can be received by Agent API get_customer_cards request
renderPraxisCardListTemplates(list);
document.getElementById('hpf-card-dropdown').style.display = 'flex';
document.getElementById('hpf-loader-id').style.display = 'none';
},
onValid: async function () { // trigger when all fields are valid
Object.keys(praxisValidationObj)?.forEach((key) => {
praxisHideHpfError(key)
})
},
onValidationSuccess: function(fieldName) { // triggers on field change
praxisValidationObj[fieldName] = true;
praxisHideHpfError(fieldName);
},
onValidationError: function(fieldName) { // triggers on field change
praxisValidationObj[fieldName] = false;
},
labels: {
display: [window.PG_INPUT_SHOW_LABELS, window.PG_INPUT_SHOW_PLACEHOLDERS],
label_card_number: 'Credit Card',
label_card_holder: 'Card Holder',
label_exp: 'Expires',
label_cvv: 'CSC/CVV',
label_no_cards_saved: 'No Cards Saved',
label_new_card: 'New Card',
placeholder_card_number: '0000 0000 0000 0000',
placeholder_card_holder: 'Card Holder',
placeholder_exp: 'MM/YY',
placeholder_cvv: 'CVV'
}
});
}
}, 150);
// dropdown click handler
document.getElementById('hpf-card-dropdown')?.addEventListener('click', () => {
const cardListDropdown = document.getElementById('hpf-card-dropdown');
if(cardListDropdown) {
const container = document.getElementById('hpf-card-list');
if(container) {
if(container.classList.contains('show')) {
cardListDropdown.classList.remove('active');
container.classList.remove('show');
} else {
cardListDropdown.classList.add('active');
container.classList.add('show');
}
} else {
console.error('container for cardListDropdown not found')
}
} else {
console.error('cardListDropdown not found')
}
})
// render card list template
const renderPraxisCardListTemplates = (templates = []) => {
const container = document.getElementById('hpf-card-list');
if(container) {
const newItem = document.createElement('div');
newItem.setAttribute('class', 'hpf-card-list-item new-card-option');
newItem.innerText = '+ New card';
container.appendChild(newItem);
newItem.addEventListener('click', () => selectPraxisCardListTemplate(newItem, []));
if(templates?.length > 0) {
templates?.forEach((templateItem) => {
const item = document.createElement('div');
item.setAttribute('class', `hpf-card-list-item ${templateItem?.is_default ? 'active' : ''}`);
item.innerText = templateItem?.card_number;
container.appendChild(item);
item.addEventListener('click', () => selectPraxisCardListTemplate(item, templates));
})
}
} else {
console.error('container for render list not found')
}
}
// select template from list
const selectPraxisCardListTemplate = (item, templates = []) => {
if(item?.classList.contains('active')) return;
const currentCard = templates?.find((card) => card['card_number'] === item.innerText);
const currentCardToken = currentCard?.card_token ?? null;
PraxisGateInstance.pickCustomerCard(currentCardToken); // HPF SDK event to trigger event change
item.classList.add('active');
document.getElementById('hpf-card-dropdown')?.classList.remove('active');
item.parentElement.classList.remove('show');
Array.from(item.parentElement.children).forEach((node) => {
if(node !== item && node.classList.contains('active')) {
node.classList.remove('active');
}
})
Object.keys(praxisValidationObj)?.forEach((key) => {
praxisHideHpfError(key)
})
}
// function to show field error
const praxisShowHpfError = (field = '') => {
if(!field) return;
const container = document.querySelector(`.hpf-fields-container .input-item.${field}`);
if(container && !container?.classList.contains('has-error')) {
container.classList.add('has-error');
}
}
// function to hide field error
const praxisHideHpfError = (field = '') => {
if(!field) return;
const container = document.querySelector(`.hpf-fields-container .input-item.${field}`);
if(container && container?.classList.contains('has-error')) {
container.classList.remove('has-error');
}
}
// submit form
document.getElementById('hpf-form-id')?.addEventListener('submit', async (event) => {
event.preventDefault();
// in case of success, return hpf_auth_token equal to session_token from open_session request, also can be triggered in onValid HPF SDK event
const token = await PraxisGateInstance.tokenizeCard();
const canSubmit = token === 'SESSION_TOKEN';
const url = 'YOUR_PAYMENT_FORM_SUBMISSION_URL';
if(canSubmit) {
console.log('submit')
// do card Direct API Call for HPF
} else {
document.getElementById('hpf-loader-id').style.display = 'none';
if(!token && !Object.values(praxisValidationObj).some(val => val === false)) console.error('PraxisGateInstance.tokenizeCard() failed');
Object.keys(praxisValidationObj)?.forEach((key) => {
if(!praxisValidationObj[key]) {
praxisShowHpfError(key)
} else {
praxisHideHpfError(key)
}
})
}
})
</script>
</body>
</html>
The hpf_auth_token
should be passed inside the card_data
object.
As long as no actual card details are collected or transmitted via API, the PCI DSS requirement does not apply — even though it normally would for Direct API credit card transactions.
Samples of rendered payment form:
HPF can also be used for MIT (Merchant Initiated Transactions) and Zero-Authorization transactions!
To enable this, simply extend the Direct API request by adding the MIT
object into the payload. See Direct API card for full reference.
For subsequent MIT transactions, you do not need to display the HPF form, as all necessary data will be derived from the reference CIT (Customer Initiated Transaction) or Zero-Authorization transaction. However, HPF is mandatory for the initial transaction if you're not PCI DSS certified.
{danger.fa-exclamation-triangle} IMPORTANT: If you don't keep a record of transaction history, you must determine which transaction to use as a reference for future MITs.
The /get_customer_cards API endpoint helps by returning a list of successful Zero-Authorization and CIT transactions in its response.