Payment Optimization
...
Integration Guides
Hosted Fields SDK
29 min
integration steps install the sdk add the hosted fields sdk to your checkout page if using content security policy (csp) , update your directives connect src \<https //sdk checkouttools com> frame src \<https //sdk checkouttools com> script src \<https //sdk checkouttools com> generate an auth token before using the forter hosted fields sdk, you will need to generate a temporary authentication token in your backend before rendering your checkout page, issue a request to the /client key endpoint in the tokenization api from your backend after making the call, you will receive a jwt authentication token that you need to pass your checkout page you will need to authenticate the request using your tokenization api credentials, which are different from your core api credentials note when creating the token server side, you are able to control its ttl it is recommended to use the smallest ttl value necessary for the end user to successfully complete the transaction in the frontend, in order to reduce the chance of abuse initialize the sdk call the init method on the hosted fields sdk, passing in the auth token you received in the previous step const fortercollect = await window\ checkouttools collect init( { environment 'sandbox', authtoken 'token value' } ); add hosted fields to the checkout page form insert placeholder elements into your checkout page initialize the hosted fields using the sdk instance you created in the previous step, by calling the addfields method fortercollect addfields({ 'cc holder' { type 'card holder name' }, 'cc number' { type 'card number', }, 'cc exp' { type 'card expiration date', }, 'cc cvc' { type 'card cvc', }, }); note 1 the minimum required fields are card number , card holder name and card expiration date note2 each field id must be unique and match its html container submit the form & receive a token when the customer submits the form, call the submit() method to receive a single use token const tokenresult = await fortercollect submit(); console log(result) successful response (example) { "success" true, "token" "ftr12d4e830283b647d4b3b2a3d65f02b8ab" } send the token to your backend for later processing const response = await fetch("https //example org/pay", { method "post", body json stringify({ shippingaddress,cartitems, , fortertoken tokenresult token }), }); js sdk reference init initializes the forter hosted fields sdk standard initialization init({environment, authtoken}, \[cb (err, results)]) => promise\<void> description initialize the forter hosted fields sdk parameters parameter type required description environment string no tokenization environment to use "sandbox" | "production" (default "sandbox" ) authtoken string yes authentication token retrieved from pci tokenization api example const fortercollect = await checkouttools collect init({ environment 'sandbox', authtoken 'your auth token' }); advanced initialization for more control over the sdk's behavior, you can provide additional configuration options and event handlers init({ options, eventhandlers}, \[cb (err, results)]) => promise\<void> description this method returns a promise that can be awaited and will surface any errors alternatively, you may provide an optional callback function which will be invoked asynchronously when the action completes parameters parameter type required description options object yes configuration object eventhandlers object no object containing event handler functions options parameter type required description environment string no tokenization environment to use "sandbox" | "production" (default "sandbox" ) authtoken string yes authentication token retrieved from pci tokenization api validationtrigger string no when to trigger fields validation 'on blur' | 'on change' (default 'on blur' ) eventhandlers parameter type required description onload() no triggered when fields are loaded note only triggered on first load onerror(errors {\[fieldname string] {message string, errorcode\ string} }) no triggered on form validation errors onenterpress() no triggered when enter key is pressed in any field onescapepress() no triggered when escape key is pressed in any field onsuccess() no triggered after successful form submission note consider showing a loading indicator until this event onvaliditychange(fieldid\ string, isvalid\ boolean, error? {message string, errorcode\ string}) no triggered when field validity changes oncardbrandchange(cardbrand string) no triggered when card brand is detected example let formloading = false; const fortercollect = window\ checkouttools collect init({ onload() { formloading = true; }, async onenterpress() { const tokenizeresult = await fortercollect submit(); if (tokenizeresult success) { console log('form successfully submitted', tokenizeresult); } }, onerror(errors){ console log('form submit error ', errors) } }); fields adds one or more hosted fields to the form add fields addfields(fieldsdefinition, \[cb (err, results)]) => promise\<void> description add one or more hosted fields to the form notes this method returns a promise that resolves when all hosted fields have finished loading and are ready for use (see also onload() event alternatively , this method accepts an optional callback function which is invoked asynchronously once the action is completed parameters parameter type required description fieldsdefinition yes update fields updatefields(fieldsdefinition, \[cb (err, results)]) => promise\<void> description update one or more hosted fields in the form notes this method returns a promise that resolves when all hosted fields have finished loading and are ready for use (see also onload() event alternatively , this method accepts an optional callback function which is invoked asynchronously once the action is completed parameters parameter type required description fieldsdefinition yes submit submit(cb (err, results)) => promise<{status boolean, token\ string, errors? array<{message string, errorcode\ string}>}> description once the customer finishes entering all the relevant fields in the payment form, this method will submit them securely to be stored by forter notes this method returns a promise object that can be awaited to receive the tokenized fields it will return the tokenized payload if successful, or the field errors if the form is still in an invalid state alternatively , this method accepts an optional callback function which is invoked asynchronously once the action is completed the asynchronous result of this function is a single token string, that can later be unpacked in the server side to the individual fields, or sent as is to a third party using the forter detokenization proxy a successful tokenization does not execute a transaction or validation in order to verify the payment method, you will need to invoke a purchase or authorization from your secure, server side environment reset fields reset(fieldname? string, cb? (err, results)) => promise\<void> description reset all fields or a specific field in the form if a field name is not provided, all of the fields will be reset notes this method returns a promise object that can be awaited alternatively , this method accepts an optional callback function which is invoked asynchronously once the action is completed parameters parameter type required description fieldname string no focus focus(fieldname? string) => void description focus on a specific field in the form parameters parameter type required description fieldname string no styling forter hosted fields can mostly be styled using standard css rules and techniques when customizing your field's styles, the required techniques will differ when styling the container element itself (i e , the form control), or the input element this separation is required because the wrapping container element is hosted on your page, and the input in another, secured, web app container elements simply target the container elements you’ve created in html using the same css rules you apply to your other, non hosted, form controls this applies to macro layout like width, height, and spacing, and styling like border and box shadow name on card note even if your other inputs calculate their own height, forter hosted fields require an explicit ‘height’ to be set on the container element state classes the following css rules are automatically added to the container element, which match the internal style “ form states ” mentioned above forter hosted fields focus forter hosted fields valid forter hosted fields invalid forter hosted fields empty forter hosted fields touched forter hosted fields empty forter hosted fields dirty simply reference these classes in your stylesheets as normal { card holder name forter hosted fields focus { border solid 2px blue; } } input elements in case advanced styling rules are required for the input elements themselves, additional css rules can be passed in to the javascript sdk object using the style configuration option two types of styles can be used a string containing an existing css class you may have already defined on your page used to style input elements all css rules will be copied from this existing class and applied to the hosted field input element await sdk addfields({ 'card number' { type 'card number', style ' existing form input class' }, }); a configuration object containing custom css definitions accepted keys in this object are input standard css properties that will be applied to the internal input element (see below for supported properties) pseudo classes (eg \ hover, \ focus) nest additional rules for every html state form state classes specific css classes are automatically applied to every hosted field, depending on its state every class can be styled using specific css properties supported state classes invalid field has failed validation empty field doesn't have a value touched applied forever once the field’s first ‘blur’ event has triggered, or first keystroke when the validationtrigger option is set to on change dirty applied forever once the field’s value has changed for the first time \ focus field is currently focused media queries (these apply to the iframe dimensions) define a rule in pixels and nest additional rules of the other formats inside allowed css properties (browser specific variants will also work) "appearance", "box shadow", "color", "direction", "font", "font family", "font size", "font size adjust", "font stretch", "font style", "font variant", "font variant alternates", "font variant caps", "font variant east asian", "font variant ligatures", "font variant numeric", "font weight", "letter spacing", "line height", "margin", "margin top", "margin right", "margin bottom", "margin left", "opacity", "outline", "padding", "padding top", "padding right", "padding bottom", "padding left", "text align", "text shadow", "transition", "tap highlight color", "tap highlight color" await sdk addfields({ 'card number' { type 'card number', style { 'input' { 'color' 'black', 'padding' '10px 5px', } '\ focus' { 'border color' 'blue', }, ' touched' { 'color 'green', }, ' touched error' { 'color 'orange', 'text decorations' 'underline', }, '@media screen and (max width 600px)' { 'input' { 'font size' '10px' } }, }, } }); examples full credit card details form create a common form containing fields for collecting a credit card’s number, security code, expiration date, and cardholder name first, create the html scaffolding a form with placeholders elements that will later be replaced with forter hosted fields name on card card cvc expiration date next, in a javascript tag copy and paste the following const fortercollect = await checkouttools collect init(); const commonstyles = { "\ focus" { color "orange", }, " touched" { color "green", }, " touched error" { color "red", "text decorations" "underline", }, }; fortercollect addfields({ "card holder name" { type "text", placeholder "name on card", style commonstyles, }, "card number" { type "card number", placeholder "1111 2222 3333 4444", onfocus() { // todo handle focus }, style commonstyles, }, "card cvc" { type "card cvc", onerror(errormessage) { console error("error in field card cvv", errormessage); }, style { commonstyles, display "inline box", }, }, "card expiration" { type "card expiration date", style commonstyles, }, }); finally, wire up the form’s submit event to forter’s submit method in order to securely store the form’s state document getelementbyid("#payment form") addeventlistener("submit", function (e) { e preventdefault(); // callback based method fortercollect submit(function (err, results) { if (err) { console error("error submitting payment form", err); return; } console log("payment form submitted successfully!", results); // combine the tokenized fields along with other non sensitive fields that may exist in the form, and submit them to your backend server for further processing backend submitpaymentform({ fields { ownfields, results } }); }); // or alternatively, esnext promise based method fortercollect submit() then((results) => { if (results success) { console log("form successfully submitted", tokenizeresult); } console log("payment form submitted successfully!", results); backend submitpaymentform({ fields { ownfields, results } }); }) catch((e) => { console error("error submitting payment form", e); }); }); re collecting cvc the hosted fields sdk supports the collection of the cvc only this is useful in repeat transactions, along with a multi use token in order to reduce the risk of a fraud decline const fortercollect = await checkouttools collect init(); fortercollect addfields({ "card cvc" { type "card cvc", onerror(errormessage) { console error("error in field card cvv", errormessage); }, }, }); collecting card and cvc separately in some cases, especially when complying with pci dss (which prohibits storing cvc), you may need to collect the card details and cvc in separate sessions for example, you might collect and tokenize the card number during checkout, but defer collecting the cvc until just before authorization to support this, you can initialize two separate hosted fields sdk instances one for the card details without cvc, and another exclusively for cvc collection submit (async () => { // initialize first instance for card details (without cvc) const fortercollectcard = await window\ checkouttools collect init({ environment "sandbox", authtoken "{auth token}" // replace with actual auth token }); // initialize second instance for cvc only const fortercollectcvc = await window\ checkouttools collect init({ environment "sandbox", authtoken "{auth token}" // replace with actual auth token }); // add card details fields (excluding cvc) fortercollectcard addfields({ "card holder" { type "card holder name" }, "card number" { type "card number" }, "card exp" { type "card expiration date" } }); // add cvc field separately fortercollectcvc addfields({ "card cvc" { type "card cvc" } }); document queryselector("#submit") addeventlistener("click", async () => { // tokenize both card and cvc fields const \[cardtokenresult, cvctokenresult] = await promise all(\[ fortercollectcard submit(), fortercollectcvc submit() ]); console log("cardtokenresult", cardtokenresult); console log("cvctokenresult", cvctokenresult); // send both tokens to your backend (recommended) / await fetch("/api/process payment", { method "post", headers { "content type" "application/json" }, body json stringify({ cardtoken cardtokenresult token, cvctoken cvctokenresult token }) }); / }); sending tokens via forter detokenization proxy you'll now have two tokens payment method token forter issued pci token for pan, expiration, etc cvc only token forter issued token strictly for the cvc once both tokens are collected, you can forward both to your psp through the detokenization proxy https //docs forter com/docs/detokenization proxy integration guide#for cvc only tokens for secure, pci compliant forwarding of card data in your proxy request, you must include both tokens in the header if you are using direct tokens, send the forter token and forter cvc token if using alias based tokens, send the forter token alias key forter token alias value pair and the forter cvc token alias key forter cvc token alias value pair the cvc token is short lived, in line with pci requirements always re collect it before sending to your psp