Hosted Fields

Overview

Hosted Fields allows you to securely collect payment details using iframes that are hosted by Buckaroo.
By using Hosted Fields, sensitive payment information is never directly handled by your servers, reducing your PCI compliance burden.

Key features

  • Security: Sensitive payment data is handled securely (PCI compliant).
  • Customization: Hosted Fields can be styled to match the look and feel of your site.
  • Flexibility: Can be used with various payment methods, including credit cards, debit cards, and more.

Plugin/app activation

The Hosted Fields functionality is not available yet for all plugins and apps offered by Buckaroo. Hosted Fields will soon be launched in the Buckaroo Magento 2 plugin.

How do I activate Hosted Fields in the Buckaroo Magento 2 plugin?

  1. Go to the payment method settings in the plugin and navigate to Credit and debitcards or Cards.
  2. Enter your Buckaroo Hosted Fields Client ID which can be found in the Buckaroo Plaza under Settings β†’ Token registration.
  3. Enter your Buckaroo Hosted Fields Client Secret which can also be found in the Buckaroo Plaza under Settings β†’ Token registration.
  4. Choose the credit and debit card brands you wish to accept and for which you hold a subscription with Buckaroo.
  5. Choose whether to activate the payment method in test mode or live mode depending on whether you prefer to test first or proceed directly to live.
  6. Save your settings.

Keep an eye on our documentation for updates on the availability of Hosted Fields in other Buckaroo plugins and apps.

Custom integration

Getting started

Prerequisites

  • Access to Buckaroo's Token API
  • Token API credentials (credentials or API key) from Buckaroo.
  • Basic knowledge of HTML, CSS, and JavaScript.

Token API - Getting Token

To obtain a token from the Token API, you first need to create an application in you Buckaroo Plaza account. You will need to have the role of Administrator or Technical Administrator to access the Token Registration Page.

Step 1: Login to plaza: Buckaroo Plaza.
Step 2: Navigate to Settings > Token registration













Step 3: On top right, you will find Register new application.




Step 4: Click on "Register New Application," and a modal will appear. Enter your application name, select the desired scopes (e.g., "hostedfields - save"), and click "Save".


















Your application will be displayed as shown below. The key details to focus on are the Client ID and Client Secret, as these are required.





Server Side

To retrieve a token from the Token API, you must set up a secure environment using your application's credentials (Client id & Client secret). These credentials should be stored securely, such as in a secrets manager, to protect sensitive data.

Token API Response Structure

The Token API response returns the following JSON structure:

{
  "access_token": "jwt-token",
  "token_type": "Merchant",
  "expires_in": 900
}
  • access_token: The JWT token that can be used for authentication.
  • token_type: The type of token, e.g., "Merchant."
  • expires_in: The duration (in seconds) before the token expires. In this case, the token is valid for 15 minutes (900 seconds).

Below are examples of simple methods to retrieve a token from the Token API. These are general-purpose samples and can be adapted to fit the specific needs of your application.

.NET

HttpPost("initialize-token")]
public async Task<IActionResult> InitializeToken()
{
    var tokenUrl = new Uri("https://auth.buckaroo.io/oauth/token");

    var clientId = _configuration["Secrets:ClientId"];
    var clientSecret = _configuration["Secrets:ClientSecret"];

    using var httpClient = new HttpClient();
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("scope", "hostedfields:save"),
        new KeyValuePair<string, string>("grant_type", "client_credentials"),
    });

    var basicAuthHeader = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{clientId}:{clientSecret}"));
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", basicAuthHeader);

    var response = await httpClient.PostAsync(tokenUrl, content);
    var responseString = await response.Content.ReadAsStringAsync();

    if (response.IsSuccessStatusCode)
    {
        var responseModel = JsonSerializer.Deserialize<TokenResponse>(responseString);
        return Ok(new
        {
            AccessToken = responseModel.AccessToken,
        });
    }
    else
    {
        return BadRequest(new
        {
            Message = "Token request failed.",
            Details = responseString
        });
    }
}

Node JS

const express = require('express');
const axios = require('axios');
const qs = require('qs');

const router = express.Router();

router.post('/initialize-token', async (req, res) => {

    const tokenUrl = 'https://auth.buckaroo.io/oauth/token';
    const clientId = process.env.CLIENT_ID;
    const clientSecret = process.env.CLIENT_SECRET;

    const formData = qs.stringify({
        scope: 'hostedfields:save',
        grant_type: 'client_credentials',
    });

    const authHeader = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`;

    try {
        const response = await axios.post(tokenUrl, formData, {
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Authorization': authHeader,
            },
        });
        
        res.status(200).json({
            AccessToken: response.data.access_token,
        });
    } catch (error) {
        res.status(400).json({
            Message: 'Token request failed.',
            Details: error.response?.data || error.message,
        });
    }
});

module.exports = router;

PHP

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

Route::post('/initialize-token', function (Request $request) {
    
    $tokenUrl = 'https://auth.buckaroo.io/oauth/token';
    $clientId = env('CLIENT_ID');
    $clientSecret = env('CLIENT_SECRET');

    // Prepare the form data
    $formData = [
        'scope' => 'hostedfields:save',
        'grant_type' => 'client_credentials',
    ];

    $authHeader = base64_encode("$clientId:$clientSecret");

    try {
        $response = Http::withHeaders([
            'Authorization' => "Basic $authHeader",
            'Content-Type' => 'application/x-www-form-urlencoded',
        ])->asForm()->post($tokenUrl, $formData);

        if ($response->successful()) {
            $responseBody = $response->json();
            return response()->json([
                'AccessToken' => $responseBody['access_token'],
            ], 200);
        }

        return response()->json([
            'Message' => 'Token request failed.',
            'Details' => $response->body(),
        ], 400);
    } catch (Exception $e) {
        return response()->json([
            'Message' => 'An error occurred while requesting the token.',
            'Details' => $e->getMessage(),
        ], 500);
    }
});

Python

from flask import Flask, jsonify, request
import requests
import os
from requests.auth import HTTPBasicAuth

app = Flask(__name__)

@app.route('/initialize-token', methods=['POST'])
def initialize_token():
    token_url = "https://auth.buckaroo.io/oauth/token"
    client_id = os.getenv("CLIENT_ID")
    client_secret = os.getenv("CLIENT_SECRET")

    # Prepare the form data
    form_data = {
        "scope": "hostedfields:save",
        "grant_type": "client_credentials"
    }

    try:
        response = requests.post(
            token_url,
            data=form_data,
            auth=HTTPBasicAuth(client_id, client_secret),
            headers={"Content-Type": "application/x-www-form-urlencoded"}
        )

        if response.status_code == 200:
            response_data = response.json()
            return jsonify({
                "AccessToken": response_data.get("access_token")
            }), 200
        else:
            return jsonify({
                "Message": "Token request failed.",
                "Details": response.text
            }), response.status_code
    except Exception as e:
        return jsonify({
            "Message": "An error occurred while requesting the token.",
            "Details": str(e)
        }), 500

if __name__ == '__main__':
    app.run(debug=True)

Java

@RestController
public class TokenController {

    @Value("${secrets.client-id}")
    private String clientId;

    @Value("${secrets.client-secret}")
    private String clientSecret;

    @PostMapping("/initialize-token")
    public String initializeToken() {
        String tokenUrl = "https://auth.buckaroo.io/oauth/token";

        String basicAuth = Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes());
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Basic " + basicAuth);
        headers.add("Content-Type", "application/x-www-form-urlencoded");

        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("scope", "hostedfields:save");
        body.add("grant_type", "client_credentials");

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
        RestTemplate restTemplate = new RestTemplate();

        ResponseEntity<Map> response = restTemplate.exchange(tokenUrl, HttpMethod.POST, request, Map.class);

        return response.getBody().get("access_token").toString();
    }
}

Set up your payment page

  1. Load the Buckaroo Hosted Fields SDK

Include the Hosted Fields SDK on your webpage. For example:

<script src="https://hostedfields-externalapi.prod-pci.buckaroo.io/v1/sdk"></script>
  1. Create HTML Elements for Hosted Fields
  • Required: Add HTML Wrappers where the Hosted Fields will be embedded. For example:
<div id="cc-name-wrapper"></div>
<div id="cc-number-wrapper"></div>
<div id="cc-expiry-wrapper"></div>
<div id="cc-cvc-wrapper"></div>
  • Optional: Add error code wrappers where validation errors from the SDK will be rendered. For example:
<div>
<div id="cc-name-wrapper"></div>
<p id="cc-name-error"></p>
</div>
  • Optional: Adding labels for each field for documentation purposes only.
    The SDK will not affect labels. For example:
<div>
<label id="cc-name-label">
<div id="cc-name-wrapper"></div>
<p id="cc-name-error"></p>
</div>
  • Required: Add Pay button:
<button id="pay" type="button">Submit</button>

Initialize Hosted Fields

Steps to initialize Buckaroo Hosted Fields:

ℹ️

Merchants should implement their own internal logic to retrieve a token from the token API using their own credentials or API key. Once the token is obtained, it should be passed to the SDK for further processing.


Replace jwt-token with retrieved token from Token API.

const init = async function () {
    var sdkClient = new BuckarooHostedFieldsSdk.HFClient("jwt-token");

Language

Optional: Set the language of the SDK. Validation errors will appear in selected language.

sdkClient.setLanguage('nl');
LanguageValue
English (default)en
Dutchnl
Germande
Frenchfr
Spanishes

Expiry date format

Optional: Set the expiry date format for credit card.

sdkClient.setExpiryDateFormat('MM/YYYY');
Supported formats
MM / YY (default)
MM / YYYY

Disable card logos

Optional: Card Brand Logos will not be rendered at all. (By default, Card Logos are shown)

sdkClient.disableCardLogos();

Supported credit cards

Required: You must specify supported cards using this method. See the service name for reference.

sdkClient.setSupportedServices(['MasterCard', 'Amex']);
BrandService nameExample
VisaVisa4563 5500 0000 0005
MastercardMastercard5386 8600 0000 0000
MaestroMaestro5666 5555 4444 3333
American ExpressAMEX3718 4997 1840 005

Validation

Get the pay button HTML element.

let payButton = document.getElementById("pay");

Starting a payment session.

await sdkClient.startSession(function (event) {

Handling validation errors.

The SDK will automatically render the error codes on the provided error wrappers.

ℹ️

The sdkClient.handleValidation() function requires parameters in a specific order:

  1. event,
  2. ID of the error element for cardholder name,
  3. ID of the error element for card number,
  4. ID of the error element for expiry
  5. ID of the error element for CVC.
sdkClient.handleValidation(event, 'cc-name-error', 'cc-number-error', 'cc-expiry-error', 'cc-cvc-error');

Disabling the pay button.

ℹ️

sdkClient.formIsValid() will perform validation on each mounted field resulting in:

true - if all inputs are valid
false - if any of inputs is invalid.

 if (payButton) {
     payButton.disabled = !sdkClient.formIsValid();

Service detection

Declare string where Service type is going to be stored.

let service = null; or let service = "";

Inside event handler, initialize service by:

 service = sdkClient.getService();

Finally send Payment request based on Service.

Styling

Styling Details:

  • All CSS parameters are nullable, meaning they can be customized or left undefined.
  • Each parameter also has a default value, ensuring consistent appearance if no customization is applied.
    Card Logo Detection and Styling: The SDK includes advanced functionality for detecting card logos and applying custom styling. For example:
 let cardLogoStyling = {
    height:"80%",
    position: 'absolute',
    border: '1px solid gray',
    radius: '5px',
    opacity:'1',
    transition:'all 0.3s ease',
    right:'5px',
    backgroundColor:'inherit'
}; 
AttributeDefault ValueSupported Values
height80%All CSS units (ex. 100px; 1.2rem; 100%;)
positionabsoluteAll CSS units (ex. absolute; relative;)
border1px solid grayAll CSS units (ex. solid; 1.2rem solid;)
radius5pxAll CSS units (ex. 100px; 10%;)
opacity1All CSS units (ex. 1; 0; 0.5;)
transition all 0.3s easeAll CSS units (ex. all 0.3s ease;)
right 5pxAll CSS units (ex. 5px; 1.2rem; 10%;)
backgroundColorAll CSS units (ex. brown; red; #F3F3F3;)

❗️

Important

It's at the discretion of the merchant whether to apply cardLogoStyling, as many card issuers do not allow modification of their logos' appearance.

Custom Field Styling: Merchants can leverage the SDK to customize the styling of fields, allowing for tailored visual presentations that align with specific design requirements.

let styling = {
    fontSize:"14px",
    fontFamily: 'Consolas, Liberation Mono, Menlo, Courier, monospace',
    textAlign: 'left',
    background: 'inherit',
    color:'black',
    placeholderColor:'grey',
    cardLogoStyling: cardLogoStyling
}; 
AttributeDefault ValueSupported Values
fontSize100%All CSS units (ex. 100px; 1.2rem; 100%;)
fontFamilyArial, sans-serifAll CSS units (ex. Arial, sans-serif)
height100Only Pixels Supported
width200Only Pixels Supported
letterSpacingnormalAll CSS units (ex. normal; 1px; 1rem;)
textAlignleftAll CSS units (ex. right; left;)
textDecorationnoneAll CSS units (ex. underline;)
textTransformnoneAll CSS units (ex. capitalize; lowercase;)
backgroundinheritAll CSS units (ex. inherit; green;)
colorlight-darkAll CSS units (ex. red; #F3F3F3;)
placeholderColorgreyAll CSS units (ex. red; #F3F3F3;)

ℹ️

Note:

For consistency in design, consider applying cardLogoStyling within baseStyling where appropriate.


Our SDK supports multiple styling objects to cater to different states of the input fields:

  • baseStyling: Styling applied to the hosted field in its default state.
  • validStyling: Optional styling for when the input is valid.
  • invalidStyling: Optional styling for when the input is invalid.

This flexibility enables merchant to customize the appearance of hosted fields based on various conditions, enhancing user interaction and visual feedback.

Mounting Fields

Cardholder Name

Explanation:

chnameOptions is an object that configures the Cardholder Name field with the following properties:

  • id: The element ID to be used for the Cardholder Name input field.
  • placeHolder: A string to be displayed as a placeholder in the input field. For example, "John Doe".
  • labelSelector: The CSS selector for the label associated with this input field. This should match the label declared by the merchant for this input.
  • baseStyling: An object defining the base styling for the input field.
  • validStyling: An optional object to customize the appearance of the input field when the input is valid.
  • invalidStyling: An optional object to customize the appearance of the input field when the input is invalid.

The mountCardHolderName function mounts the input field into the specified wrapper element (#cc-name-wrapper in this example). The then method is used to focus on the field once it is mounted. Note that the focus should be applied only to the input field that the merchant wants to focus on.

let chnameOptions = {
    id: "ccname",
    placeHolder: "John Doe",
    labelSelector: "#cc-name-label",
    baseStyling: styling,
    validStyling: validStyling, //optional
    invalidStyling: invalidStyling, //optional
};

await sdkClient.mountCardHolderName("#cc-name-wrapper", chnameOptions)
    .then(function (cardHolderNameField) {
        cardHolderNameField.focus();
    });

The same approach can be applied to mount other fields like CardNumber, Expiry Date, and CVC.
Each field will have its corresponding mounting function and options object. Below you will find examples of mounting the other fields:

Card number

let ccOptions = {
    id: "cc",
    placeHolder: "555x xxxx xxxx xxxx",
    labelSelector: "#cc-number-label",
    baseStyling: styling,
    cardLogoStyling: cardLogoStyling (optional - read in Styling)
};

await sdkClient.mountCardNumber("#cc-number-wrapper", ccOptions);

Expiry date

let expiryDateOptions = {
    id: "expiry",
    placeHolder: "MM / YY",
    labelSelector: "#cc-expiry-label",
    baseStyling: styling
};

await sdkClient.mountExpiryDate("#cc-expiry-wrapper", expiryDateOptions);

CVC

let cvcOptions = {
    id: "cvc",
    placeHolder: "1234",
    labelSelector: "#cc-cvc-label",
    baseStyling: styling
};

await sdkClient.mountCvc("#cc-cvc-wrapper", cvcOptions);

Handling payment submission

payButton.addEventListener("click", async function () {
    let paymentToken = await sdkClient.submitSession();
});

The submitSession method will:

  • Return a sessionId, which serves as a payment token.
  • Close the current session.

After retrieving the sessionId, the merchant must set up the payment request to process the payment.

{
    "Currency": "EUR",
    "AmountDebit": 0.01,
    "Invoice": "MasterCard PayWithToken",
    "Description": "MasterCard PayWithToken",
    "ReturnUrl": "https://www.yourreturnurl.nl ",
    "ReturnUrlCancel": "https://www.yourreturnurl.nl/Cancel",
    "ReturnUrlError": "https://www.yourreturnurl.nl/Error",
    "ReturnUrlReject": "https://www.yourreturnurl.nl/Reject",
    "PushUrl": "https://www.yourpushurl.com ",
    "PushUrlFailure": "https://www.yourpushurlfailure.com ",
    "Services": {
        "ServiceList": [
            {        
                "Name": "Mastercard",
                "Action": "PayWithToken",
                "Parameters": [
                    {
                        "Name": "SessionId",
                        "Value": "hf_43ab37u53XIDOpJg"
                    }
                ]
            }
        ]
    }
}

This approach ensures that when the user clicks the payment button, the SDK handles the session submission, providing a sessionId that the merchant can use to complete the payment process. It is important for the merchant to properly set up and handle the payment request using this sessionId to ensure a seamless transaction experience.

To view a sample of the implementation of SDK:

@inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment env

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="theme-color" content="#ffffff">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="icon" sizes="128x128" href="https://resources.wehkamp.nl/latest/images/favicon/favicon-128.png">
    <link rel="preload" as="font" type="font/woff2" crossorigin="true"
          href="https://resources.wehkamp.nl/latest/fonts/gibson-light.woff2">
    <link rel="preload" as="font" type="font/woff2" crossorigin="true"
          href="https://resources.wehkamp.nl/latest/fonts/gibson-regular.woff2">
    <link rel="preload" as="font" type="font/woff2" crossorigin="true"
          href="https://resources.wehkamp.nl/latest/fonts/gibson-semibold.woff2">
    <link rel="preload" as="font" type="font/woff2" crossorigin="true"
          href="https://resources.wehkamp.nl/13.10.1/fonts/wehkamp-icons.woff2">

    <style type="text/css">
        @@font-face {
            font-family: Gibson;
            src: url(https://resources.wehkamp.nl/latest/fonts/gibson-regular.woff2) format("woff2"), url(https://resources.wehkamp.nl/latest/fonts/gibson-regular.woff) format("woff"), url(https://resources.wehkamp.nl/latest/fonts/gibson-regular.ttf) format("ttf");
            font-weight: 400;
            font-display: fallback
        }

        @@font-face {
            font-family: Gibson;
            src: url(https://resources.wehkamp.nl/latest/fonts/gibson-light.woff2) format("woff2"), url(https://resources.wehkamp.nl/latest/fonts/gibson-light.woff) format("woff"), url(https://resources.wehkamp.nl/latest/fonts/gibson-light.ttf) format("ttf");
            font-weight: 100;
            font-display: fallback
        }

        @@font-face {
            font-family: Gibson;
            src: url(https://resources.wehkamp.nl/latest/fonts/gibson-semibold.woff2) format("woff2"), url(https://resources.wehkamp.nl/latest/fonts/gibson-semibold.woff) format("woff"), url(https://resources.wehkamp.nl/latest/fonts/gibson-semibold.ttf) format("ttf");
            font-weight: 700;
            font-display: fallback
        }

        @@font-face {
            font-family: WehkampIcons;
            src: url(https://resources.wehkamp.nl/13.10.1/fonts/wehkamp-icons.woff2) format("woff2"), url(https://resources.wehkamp.nl/13.10.1/fonts/wehkamp-icons.woff) format("woff"), url(https://resources.wehkamp.nl/13.10.1/fonts/wehkamp-icons.ttf) format("ttf");
            font-weight: 400;
            font-display: block
        }

        html,
        body {
            font-family: Gibson, arial, geneva, sans-serif;
        }
    </style>
    <script src="https://localhost:7287/v1/sdk"></script>
    <script src="https://cdn.tailwindcss.com"></script>
</head>

<body class="overflow-hidden py-20 bg-gray-200 flex justify-center">
    <div class="w-full max-w-lg">
        <header class="mb-5">
            <svg class="m-auto show-for-tablet-landscape" id="wehkamp_1_" height="40" width="194" x="0px" y="0px" viewBox="0 0 1940 400" xml:space="preserve"><style>
                                                                                                                                                                  .st0 {
                                                                                                                                                                      fill-rule: evenodd;
                                                                                                                                                                      clip-rule: evenodd;
                                                                                                                                                                      fill: #f55
                                                                                                                                                                  }</style><path id="wehkamp_text" class="st0" d="M1857.6 128.5c45.4 0 82.4 36.5 82.4 81.5s-37 81.5-82.4 81.5c-20.9 0-40-7.8-54.6-20.5v59h-27.8V130.7h27.8V149c14.6-12.7 33.7-20.5 54.6-20.5zm-492.9 0c20.9 0 40 7.8 54.6 20.5v-18.3h27.8v119.6c0 9.6 7.6 18.9 10.6 21.6l-19 20.1c-1.4-1.3-11-10.4-16.1-24.2a82.562 82.562 0 01-57.9 23.6c-45.4 0-82.4-36.6-82.4-81.5 0-44.8 36.9-81.4 82.4-81.4zm-197-38.5v103.5l66.5-62.9h40.3l-68.8 65c4.6 4.8 13.5 14.2 22.9 24.1l1.1 1.2c.6.6 1.2 1.2 1.7 1.8l1.7 1.8c8.3 8.7 16.4 17.2 21.7 22.8 7.5 7.1 21.6 17 30 17v27.5c-23.1 0-46.9-22.4-49.6-25l-.4-.4c-12.3-12.9-39.7-41.7-49.3-51.9l-17.9 16.9v57.8h-27.8V90h27.9zm-302.8 38.5c45.4 0 82.4 36.6 82.4 81.5v13.8H812.1c6.2 23.1 27.5 40.2 52.8 40.2v27.5c-45.4 0-82.4-36.5-82.4-81.5s36.9-81.5 82.4-81.5zm811.5 0c42.6 0 77.2 34.2 77.2 76.3v84.6h-27.8v-84.6c0-26.9-22.1-48.8-49.3-48.8s-49.3 21.9-49.3 48.8v84.6h-27.8v-84.6c0-26.9-22.1-48.8-49.3-48.8-27.2 0-49.3 21.9-49.3 48.8v84.6H1473V130.6h27.8v15.5c13.4-11 30.6-17.7 49.3-17.7 26.2 0 49.3 12.9 63.3 32.7 13.7-19.7 36.9-32.6 63-32.6zM991.8 90v56.1c13.4-11 30.6-17.7 49.3-17.7 42.6 0 77.2 34.2 77.2 76.3v84.6h-27.8v-84.6c0-26.9-22.1-48.8-49.3-48.8-27.2 0-49.3 21.9-49.3 48.8v84.6h-27.8V90h27.7zm-481.5 40.6l54.3 123.8 54.3-123.8h30.6l54.3 123.8L758 130.6h30.3L718.9 289l.1.3h-30.6l.1-.3-54.4-124.1L579.8 289l.1.3h-30.6l.1-.3L480 130.6h30.3zM1857.6 156c-30.1 0-54.6 24.2-54.6 54s24.5 54 54.6 54 54.6-24.2 54.6-54-24.5-54-54.6-54zm-492.9 0c-30.1 0-54.6 24.2-54.6 54s24.5 54 54.6 54 54.6-24.2 54.6-54-24.5-54-54.6-54zm-499.8 0c-25.3 0-46.6 17.1-52.8 40.2h105.6c-6.2-23.1-27.5-40.2-52.8-40.2z"></path><path id="wehkamp_icon" class="st0" d="M200 0c110.5 0 200 89.5 200 200s-89.5 200-200 200S0 310.5 0 200 89.5 0 200 0zM78.8 131.7H49.2l67.9 156.7-.1.3h29.9l-.1-.3L200 165.6l53.2 122.8-.1.3H283l-.1-.3 67.9-156.7h-29.7L268 254.2l-53-122.5h-30l-53.1 122.5-53.1-122.5z"></path></svg>
        </header>
        <div class="shadow-md rounded overflow-hidden mb-4">
            <div class="bg-[#ff5555] px-8 pt-6 pb-8 text-white">
                <h2 class="text-2xl font-bold">Shopping Cart</h2>
                <div class="mt-3">
                    <div class="flex align-justify full-width type-label-l">
                        <div class="flex-auto pr-2">1x</div>
                        <div class="flex-auto flex-free">
                            <span class="text-capitalize-reset">
                                JACK &amp; JONES JEANS
                                INTELLIGENCE - Slim Fit Jeans
                            </span>
                            <div>
                                <span class="flex-auto text-sm text-gray-900">
                                    Blue,
                                    Size 29-34
                                </span>
                            </div>
                        </div>
                        <div class="pl-2">
                            <div class="text-align-right"></div>
                            <div class="blaze-row no-padding sub-row align-right">
                                <span class="position-relative type-price">89.99</span>
                            </div>
                        </div>
                    </div>
                </div>
                <hr class="my-3" />
                <footer class="Basket__basketFooter___nIEdr">
                    <div>
                        <div class="flex align-justify">
                            <div class="flex-1">Shipping</div>
                            <div class="flex-1 text-right">
                                <span class="position-relative type-price type-label-l">Free</span>
                            </div>
                        </div>
                        <div class="flex align-justify font-bold">
                            <div class="flex-1">Total</div>
                            <div class="flex-1 text-right">
                                <span class="position-relative type-price type-label-l-heading">89.99</span>
                            </div>
                        </div>
                    </div>
                </footer>
            </div>
            <form class="bg-white px-8 pt-6 pb-8">
                <div class="mb-4">
                    <h1 class="text-2xl font-bold">Payment</h1>
                </div>
                <div class="mb-4">
                    <label id="cc-name-label" class="block text-gray-700 text-sm font-bold mb-2">
                        Cardholder Name
                    </label>
                    <div id="cc-name-wrapper"
                         class="shadow h-12 appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
                    </div>
                    <p id="cc-name-error" class="text-red-500 text-xs italic mt-1"></p>
                </div>
                <div class="mb-4">
                    <label id="cc-number-label" class="block text-gray-700 text-sm font-bold mb-2">
                        Card Number
                    </label>
                    <div id="cc-number-wrapper"
                         class="shadow h-12 appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
                    </div>
                    <p id="cc-number-error" class="text-red-500 text-xs italic mt-1"></p>
                </div>
                <div class="mb-6">
                    <div class="grid grid-cols-2 gap-4">
                        <div class="col-span-1">
                            <label id="cc-expiry-label" class="block text-gray-700 text-sm font-bold mb-2">
                                Expiry Date
                            </label>
                            <div id="cc-expiry-wrapper"
                                 class="shadow h-12 appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
                            </div>
                            <p id="cc-expiry-error"  class="text-red-500 text-xs italic mt-1"></p>
                        </div>
                        <div class="col-span-1">
                            <label id="cc-cvc-label" class="block text-gray-700 text-sm font-bold mb-2">
                                CVC
                            </label>
                            <div id="cc-cvc-wrapper"
                                 class="shadow h-12 appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
                            </div>
                            <p id="cc-cvc-error" class="text-red-500 text-xs italic mt-1"></p>
                        </div>
                    </div>
                </div>
                <div class="flex items-center justify-between">
                    <button id="pay"
                            class="bg-[#ff5555] hover:bg-blue-700 text-white py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                            type="button">
                        Submit
                    </button>
                    <a class="inline-block align-baseline text-sm text-gray-500 hover:text-gray-800" href="#">
                        Cancel
                    </a>
                </div>
            </form>
        </div>
        <p class="text-center text-gray-500 text-xs">
            &copy; @DateTime.Now.Year checkout.wehkamp.nl
        </p>
    </div>
    <script>
        const init = async function () {

            // Initialize SDK
            var sdkClient = new BuckarooHostedFieldsSdk.HFClient("jwt-token-here");

            // Language of appearing errors.
            sdkClient.setLanguage('en');
            
            // Expiry date format.
            sdkClient.setExpiryDateFormat('MM/YYYY');

            // Required: Set Supported Cards
            sdkClient.setSupportedServices(['MasterCaVrd', 'Amex']);

            // Payment button (Submit)
            let payButton = document.getElementById("pay");
            
            let service = "";
            
            // Start Session
            await sdkClient.startSession(function (event) {
                // Event, CardHolderName, CardNumber, CardExpiry, CVC - error id's
                sdkClient.handleValidation(event, 'cc-name-error', 'cc-number-error', 'cc-expiry-error', 'cc-cvc-error');

                if (payButton) {
                    let disabled = !sdkClient.formIsValid();

                    payButton.disabled = disabled;

                    if (disabled) {
                        payButton.style.backgroundColor = "#ff5555";
                        payButton.style.cursor = "not-allowed";
                        payButton.style.opacity = "0.5";
                    } else {
                        payButton.style.backgroundColor = "";
                        payButton.style.cursor = "";
                        payButton.style.opacity = "";
                    }
                }
              
               service = sdkClient.getService();
            });

            // Styling of all fields.
            let styling = {
                fontSize:"14px",
                fontFamily: 'Consolas, Liberation Mono, Menlo, Courier, monospace',
                textAlign: 'left',
                background: 'inherit',
                color:'black',
                placeholderColor:'grey'
            };

            // Options for CardholderName.
            let chnameOptions = {
                id: "ccname",
                placeHolder: "John Doe",
                labelSelector: "#cc-name-label",
                baseStyling: styling
            };

            // Mount CardholderName and focus in it
            await sdkClient.mountCardHolderName("#cc-name-wrapper", chnameOptions)
                .then(function (cardHolderNameField) {
                    cardHolderNameField.focus();
                });

            // Options for Cardnumber. optional: CardLogoStyling
            let ccOptions = {
                id: "cc",
                placeHolder: "555x xxxx xxxx xxxx",
                labelSelector: "#cc-number-label",
                baseStyling: styling
            };

            // Mount CardNumber.
            await sdkClient.mountCardNumber("#cc-number-wrapper", ccOptions);

            // Options for CVC.
            let cvcOptions = {
                id: "cvc",
                placeHolder: "1234",
                labelSelector: "#cc-cvc-label",
                baseStyling: styling
            };

            // Mount CVC.
            await sdkClient.mountCvc("#cc-cvc-wrapper", cvcOptions);

            // Options for Expiry Date.
            let expiryDateOptions = {
                id: "expiry",
                placeHolder: "MM / YY",
                labelSelector: "#cc-expiry-label",
                baseStyling: styling
            };

            // Mount Expiry Date.
            await sdkClient.mountExpiryDate("#cc-expiry-wrapper", expiryDateOptions);

            // Button to submit payment, and close session: returns SessionId
            payButton.addEventListener("click", async function () {
                // Replace with your payment strategy
                let paymentToken = await sdkClient.submitSession();
            });
        }
        init();
    </script>

</body>

</html>