Praxis Wiki logo

Hosted Payment Fields (HPF) Usage Guide HPF Usage Guide


Overview

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.

INTEGRATION

The following sequence diagram illustrates the high-level integration flow:

image

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).

Server to server call to initiate HPF

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.

Include HPF SDK in payment page

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.

Implement Your Workflow

As you design the user journey, you’ll use events emitted by the SDK to build features such as:

  • Rendering stored cards
  • Capturing new card details
  • Tokenizing the card using the tokenizeCard method

This 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.

Direct API Call to execute transaction

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.

Example

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:

image image

MIT and Zero-Authorization Support

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.