Documentation

1Introduction

In order to create a payment over the platform you have the choice between the payment page integration where the customer is redirected to our payment page, the iframe integration where the payment form is placed in an iframe using our JavaScript integration or the lightbox integration in order to achieve a seamless and PCI DSS compliant integration in your checkout.

The intention of the iframe integration is to enable the collection and validation of the payment information before the actual order is confirmed by the customer. E.g. the iframe can be embedded before actually the order is completed in the merchant application. This allows the following process (simplified) in the merchant application:

  1. The customer enters the shipping and billing information.

  2. The user selects the payment method. The application embeds the iframe with the form for collecting additional payment information. The form depends on the payment method and eventually even on the connector.

  3. The application can trigger a validation of the entered information.

  4. The user can confirm the order and the application reports the final state of the transaction to us. After this the iframe is submitted with JavaScript. The iframe may be hidden after the validation of the data. The submit will trigger a break out of the frame.

The benefit of using this integration compared to the payment page is that the integration is seamless and the customer never notice that the merchant website is left. Also the payment information can be entered before the order has to be completed. This implies that the order number can be provided after the payment information has been collected. However the integration is more complicate than the payment page integration.

Seamless Iframe Integration
Figure 1. The image shows an example of a seamless iframe integration

2Iframe Integration Details

Before you start with the integration of the iframe you should:

  1. Create an account and sign up.

  2. Create an application user under Account > Users > Application User.

  3. Learn how to authenticate and connect to our web service.

Note
Please have a look at our github repository where we offer ready to download SDK in different languages that facilitate your integration efforts drastically.

We offer you also an API Client that allows you to test the requests sent to the API and see the responses.

3System Interactions

iframe
Figure 2. Sequence Diagram of the IFrame Integration

3.1Process

Below we are going to describe the integration process in details. In order to better understand this have a look at the system integrations diagram above.

  1. Create a transaction object with the Transaction Service. To create a transaction object you can provide all the information you have in this state. The more information you provide the better we can pre validate the data and may be we can rule out some payment methods which will not work with the data. Most of the provided data can be changed before the transaction is actually confirmed.

  2. Once the transaction object is created the possible payment methods can be fetched by using fetch possible payment methods on the Transaction Service by providing the transactionId returned by the initial request and the integration mode iframe. The method returns all methods which are active for the current transaction. The method can be used to check if a particular method is active or to render all available methods. This depends on the merchant application.

  3. To embed the iframe into the website a JavaScript URL has to be fetched. For this the service method build JavaScript URL can be used. The URL returned by the service method points to the JavaScript file which needs to be embedded. It is enough to embed it only once and not for each payment method. The script can be embedded by using the <script> tag.

  4. Once the JavaScript is loaded use window.IframeCheckoutHandler(paymentMethodConfigurationId) to create a new iframe checkout handler where the paymentMethodConfigurationId is the ID of the payment method configuration as returned by fetch possible payment methods. This handler can be used to load the iframe. To load the iframe call create(containerId) where containerId is the id of the HTML element into which the iframe should be embedded. It is recommend to register a validationCallback before the iframe is created, which is called whenever the validation is triggered on the form loaded inside the iframe. The validationCallback is set on the handler with the method setValidationCallback(validationCallback). The callback should be used to display the error messages provided as an argument.

  5. It is possible to register additional and optional callbacks on the handler, before the iframe is created. The initializeCallback can be used to register a handler which is invoked after the iframe is initialized. The heightChangeCallback is invoked every time the height of the iframe changes. The callback argument is the height in pixel. The callbacks are set with the method setInitializeCallback(initializeCallback) or setHeightChangeCallback(heightChangeCallback) respectively.

  6. Add a button which triggers the validation of the form inside the iframe. To trigger the validation call the validate() method on the iframe checkout handler. You may want to hide the iframe, when the validation completed without any errors. Don’t remove the iframe. The data stored inside the frame are used later.

  7. Once the form is validated and the transaction should be completed create an order inside your application and confirm the transaction with the confirm method on the Transaction Service. You may want to use an Ajax call because otherwise the iframe gets reloaded and all the data is lost. You can also update the transaction before you confirm it. E.g. you may apply some additional charges based on the selected payment method or you may change the delivery costs or delivery address.

  8. Now the submit() method on the iframe checkout handler should be called. This results in the form inside the iframe being submitted and breaking out of the iframe i.e. leaving the merchant website. The customer is eventually asked to enter additional information. This depends on the payment method.

  9. When the transaction is authorized or failed on our side the customer will be redirected to the successUrl or failedUrl that was defined when creating the transaction object.

  10. Listen to the notification on the defined webhook URL to mark the order in the merchant system as authorized or failed. This notification listener is important because the customer may close the window before returning back to merchant application. The state of the transaction can be fetched at any time over the API.

3.2Replaced Primary Action

Some payment methods require a more complex process in multiple steps. By default, a button is included in the payment form to navigate through these steps. It is however possible, to hide these buttons and use the primary submit button in your application to trigger the events.

To handle the replaced primary actions, register a callback on the iframe handler with setReplacePrimaryActionCallback(callback). This callback is invoked with the new label as a parameter the primary button should be updated with whenever the primary action is replaced. Additionally, the button must be changed to trigger the trigger() action.

When the customer has successfully navigated through all necessary steps, the callback registered with setResetPrimaryActionCallback(callback) is invoked, which should result in the primary button being reset to default behaviour i.e. having the initial label and triggering the validate() and submit() actions.

To hide the buttons in the payment form, set the configuration accordingly before creating the iframe checkout handler:

window.IframeCheckoutHandler.configure('replacePrimaryAction', true);

3.3Technical Details

The described steps above should now be explained a bit more in details including the API operations including example requests.

3.3.1Client-side Setup

The payment acceptance via iFrame provides a seamless way to collect payment information from you client. This method is not only seamlessly integrated it also fulfills all PCI DSS requirements for merchants to keep you out of scope as good as possible and still achieves a fully integrated checkout flow.

See below an example client-side setup where the iframe will be placed using the java script resources that is provided by the platform. How the java { JavaScript URL } can be resolved, where the paymentMethodConfigurationId can be collected, etc. is showed in examples further below.

<ul id="payment-errors"></ul>
<div id="payment-form"></div>
<button id="pay-button">Pay</button>

<script src="jquery.js" type="text/javascript"></script>
<script src="{ JavaScript URL }" type="text/javascript"></script>
<script type="text/javascript">
// Set here the id of the payment method configuration the customer chose.
var paymentMethodConfigurationId = 1;

// Set here the id of the HTML element the payment iframe should be appended to.
var containerId = 'payment-form';

var handler = window.IframeCheckoutHandler(paymentMethodConfigurationId);

handler.setValidationCallback(
	function(validationResult){
		// Reset payment errors
		$('#payment-errors').html('');

		if (validationResult.success) {
			// Create the order within the shop and eventually update the transaction.
			$.ajax('http://your-shop-backend.com/create-order', {
				success: function(){
					handler.submit();
				}
			});
		} else {
			// Display errors to customer
			$.each(validationResult.errors, function(index, errorMessage){
				$('#payment-errors').append('<li>' + errorMessage + '</li>');
			});
		}
	});

//Set the optional initialize callback
handler.setInitializeCallback(function(){
		//Execute initialize code
	});

//Set the optional height change callback
handler.setHeightChangeCallback(function(height){
		//Execute code
	});

handler.create(containerId)


$('#pay-button').on('click', function(){
	handler.validate();
});
</script>

3.3.2Create a Transaction Object

In order to create a a transaction object you have to use the Transaction Create Operation. Here you provide the customer details that you have including the line items and prices. This will create a pending transaction in your space.

Note
It is recommended that you provide all information you have from the buyer. The more information we have the more accurate the selection of possible payment methods will be.

Request

{
   "billingAddress":{
      "city":"Winterthur",
      "commercialRegisterNumber":"",
      "country":"CH",
      "dateOfBirth":"",
      "emailAddress":"[email protected]",
      "familyName":"Test",
      "gender":"",
      "givenName":"Sam",
      "mobilePhoneNumber":"",
      "organizationName":"Wallee AG",
      "phoneNumber":"",
      "postcode":"8400",
      "salesTaxNumber":"",
      "salutation":"",
      "socialSecurityNumber":"",
      "state":"",
      "street":"General-Guisan-Strasse 47"
   },
   "currency":"EUR",
   "language":"de-CH",
   "lineItems":[
      {
         "amountIncludingTax":"11.87",
         "name":"Barbell Pull Up Bar",
         "quantity":"1",
         "shippingRequired":"true",
         "sku":"barbell-pullup",
         "type":"PRODUCT",
         "uniqueId":"barbell-pullup"
      },
      {
         "amountIncludingTax":"559",
         "name":"Rowing Machine",
         "quantity":"1",
         "shippingRequired":"true",
         "sku":"rowing-machine",
         "type":"PRODUCT",
         "uniqueId":"rowing-machine"
      },
      {
         "amountIncludingTax":"17.98",
         "name":"Super Whey Protein",
         "quantity":"4",
         "shippingRequired":"true",
         "sku":"super-whey",
         "taxes":[
            {
               "rate":"10",
               "title":"VAT"
            },
            {
               "rate":"3.5",
               "title":"Supplement Fee"
            }
         ],
         "type":"PRODUCT",
         "uniqueId":"super-whey"
      },
      {
         "amountIncludingTax":"12.5",
         "name":"Special Chär Test",
         "quantity":"1",
         "shippingRequired":"false",
         "sku":"special-chär-test",
         "type":"SHIPPING",
         "uniqueId":"special-chär-test"
      },
      {
         "amountIncludingTax":"12.5",
         "name":"Standard Shipping",
         "quantity":"1",
         "shippingRequired":"false",
         "sku":"standard-shipping",
         "type":"SHIPPING",
         "uniqueId":"standard-shipping"
      },
      {
         "amountIncludingTax":"-10",
         "name":"Spring Discount",
         "quantity":"1",
         "shippingRequired":"false",
         "sku":"spring-discount",
         "type":"DISCOUNT",
         "uniqueId":"spring-discount"
      }
   ],
   "merchantReference":"DEV-2630",
   "shippingAddress":{
      "city":"Winterthur",
      "commercialRegisterNumber":"",
      "country":"CH",
      "dateOfBirth":"",
      "emailAddress":"[email protected]",
      "familyName":"Test",
      "gender":"",
      "givenName":"Sam",
      "mobilePhoneNumber":"",
      "organizationName":"Wallee AG",
      "phoneNumber":"",
      "postcode":"8400",
      "salesTaxNumber":"",
      "salutation":"",
      "socialSecurityNumber":"",
      "state":"",
      "street":"General-Guisan-Strasse 47"
   }
}

Response

The response that you will receive contains the id (in the example below 109472) that will now be used to perform further operations with this transactions.

{
	"acceptHeader": null,
	"allowedPaymentMethodBrands": [],
	"allowedPaymentMethodConfigurations": [],
	"authorizationAmount": 603.85,
	"authorizationTimeoutOn": "2017-12-07T08:44:09.119Z",
	"authorizedOn": null,
	"autoConfirmationEnabled": true,
	"billingAddress": {
		"city": "Winterthur",
		"commercialRegisterNumber": "",
		"country": "CH",
		"dateOfBirth": null,
		"dependentLocality": null,
		"emailAddress": "[email protected]",
		"familyName": "Test",
		"gender": null,
		"givenName": "Sam",
		"legalOrganizationForm": null,
		"mobilePhoneNumber": "",
		"organizationName": null,
		"phoneNumber": "",
		"postcode": "8400",
		"postalState": null,
		"salesTaxNumber": "",
		"salutation": "",
		"socialSecurityNumber": "",
		"sortingCode": null,
		"street": "General-Guisan-Strasse 47"
	},
	"chargeRetryEnabled": true,
	"completedOn": null,
	"completionTimeoutOn": null,
	"confirmedBy": 0,
	"confirmedOn": null,
	"createdBy": 0,
	"createdOn": "2017-12-07T08:14:09.119Z",
	"currency": "EUR",
	"customerEmailAddress": null,
	"customerId": null,
	"customersPresence": "VIRTUAL_PRESENT",
	"endOfLife": "2017-12-21T08:14:09.119Z",
	"failedOn": null,
	"failedUrl": null,
	"failureReason": null,
	"group": {
		"beginDate": "2017-12-07T08:14:09.124Z",
		"customerId": null,
		"endDate": "2017-12-07T08:14:09.124Z",
		"id": 109478,
		"linkedSpaceId": 396,
		"plannedPurgeDate": "2017-12-07T08:19:09.124Z",
		"state": "PENDING",
		"version": 1
	},
	"id": 109472,
	"internetProtocolAddress": null,
	"internetProtocolAddressCountry": null,
	"invoiceMerchantReference": null,
	"language": "de-CH",
	"lineItems": [
		{
			"aggregatedTaxRate": 0,
			"amountExcludingTax": 11.87,
			"amountIncludingTax": 11.87,
			"attributes": {},
			"name": "Barbell Pull Up Bar",
			"quantity": 1,
			"shippingRequired": true,
			"sku": "barbell-pullup",
			"taxAmount": 0,
			"taxAmountPerUnit": 0,
			"taxes": [],
			"type": "PRODUCT",
			"uniqueId": "barbell-pullup",
			"unitPriceExcludingTax": 11.87,
			"unitPriceIncludingTax": 11.87
		},
		{
			"aggregatedTaxRate": 0,
			"amountExcludingTax": 559,
			"amountIncludingTax": 559,
			"attributes": {},
			"name": "Rowing Machine",
			"quantity": 1,
			"shippingRequired": true,
			"sku": "rowing-machine",
			"taxAmount": 0,
			"taxAmountPerUnit": 0,
			"taxes": [],
			"type": "PRODUCT",
			"uniqueId": "rowing-machine",
			"unitPriceExcludingTax": 559,
			"unitPriceIncludingTax": 559
		},
		{
			"aggregatedTaxRate": 13.5,
			"amountExcludingTax": 15.84,
			"amountIncludingTax": 17.98,
			"attributes": {},
			"name": "Super Whey Protein",
			"quantity": 4,
			"shippingRequired": true,
			"sku": "super-whey",
			"taxAmount": 2.14,
			"taxAmountPerUnit": 0.54,
			"taxes": [
				{
					"rate": 10,
					"title": "VAT"
				},
				{
					"rate": 3.5,
					"title": "Supplement Fee"
				}
			],
			"type": "PRODUCT",
			"uniqueId": "super-whey",
			"unitPriceExcludingTax": 3.96,
			"unitPriceIncludingTax": 4.5
		},
		{
			"aggregatedTaxRate": 0,
			"amountExcludingTax": 12.5,
			"amountIncludingTax": 12.5,
			"attributes": {},
			"name": "Special Chär Test",
			"quantity": 1,
			"shippingRequired": false,
			"sku": "special-chär-test",
			"taxAmount": 0,
			"taxAmountPerUnit": 0,
			"taxes": [],
			"type": "SHIPPING",
			"uniqueId": "special-chär-test",
			"unitPriceExcludingTax": 12.5,
			"unitPriceIncludingTax": 12.5
		},
		{
			"aggregatedTaxRate": 0,
			"amountExcludingTax": 12.5,
			"amountIncludingTax": 12.5,
			"attributes": {},
			"name": "Standard Shipping",
			"quantity": 1,
			"shippingRequired": false,
			"sku": "standard-shipping",
			"taxAmount": 0,
			"taxAmountPerUnit": 0,
			"taxes": [],
			"type": "SHIPPING",
			"uniqueId": "standard-shipping",
			"unitPriceExcludingTax": 12.5,
			"unitPriceIncludingTax": 12.5
		},
		{
			"aggregatedTaxRate": 0,
			"amountExcludingTax": -10,
			"amountIncludingTax": -10,
			"attributes": {},
			"name": "Spring Discount",
			"quantity": 1,
			"shippingRequired": false,
			"sku": "spring-discount",
			"taxAmount": 0,
			"taxAmountPerUnit": 0,
			"taxes": [],
			"type": "DISCOUNT",
			"uniqueId": "spring-discount",
			"unitPriceExcludingTax": -10,
			"unitPriceIncludingTax": -10
		}
	],
	"linkedSpaceId": 396,
	"merchantReference": null,
	"metaData": {},
	"paymentConnectorConfiguration": null,
	"plannedPurgeDate": "2017-12-21T08:14:09.119Z",
	"processingOn": null,
	"refundedAmount": 0,
	"shippingAddress": {
		"city": "Winterthur",
		"commercialRegisterNumber": "",
		"country": "CH",
		"dateOfBirth": null,
		"dependentLocality": null,
		"emailAddress": "[email protected]",
		"familyName": "Test",
		"gender": null,
		"givenName": "Sam",
		"legalOrganizationForm": null,
		"mobilePhoneNumber": "",
		"organizationName": null,
		"phoneNumber": "",
		"postcode": "8400",
		"postalState": null,
		"salesTaxNumber": "",
		"salutation": "",
		"socialSecurityNumber": "",
		"sortingCode": null,
		"street": "General-Guisan-Strasse 47"
	},
	"shippingMethod": null,
	"spaceViewId": null,
	"state": "PENDING",
	"successUrl": null,
	"timeZone": "Z",
	"token": null,
	"userAgentHeader": null,
	"userFailureMessage": null,
	"userInterfaceType": null,
	"version": 1
}

3.3.3Build JavaScript URL

In order to get the URL to the JavaScript URL you can use the buildJavaScriptUrl operation to get a URL pointing to the java script that should be included in your checkout to create the iframe. Insert the JavaScript on your page where the iframe should be displayed as shown in the client-site example above.

3.3.4Fetch Possible Payment Methods

In order to integrate the iframe seamlessly in your checkout you will have to fetch the possible payment methods and render the options in the checkout.

This will return the payment method id which should then be set in the JavaScript using the paymentMethodConfigurationId.

Response

The response returns the possible payment methods for the given transactionId.

[
	{
		"dataCollectionType": "ONSITE",
		"description": {
			"availableLanguages": [
				"en-US"
			],
			"displayName": "",
			"items": [
				{
					"language": "en-US",
					"languageCode": "en",
					"translation": ""
				}
			]
		},
		"id": 510,
		"imageResourcePath": null,
		"linkedSpaceId": 396,
		"name": "Credit / Debit Card",
		"oneClickPaymentMode": "ALLOW",
		"paymentMethod": 1457546097597,
		"plannedPurgeDate": null,
		"resolvedDescription": {
			"de-DE": "Bezahlen Sie bequem per Kredit- oder Debitkarte.",
			"en-US": "Pay conveniently with your credit or debit card."
		},
		"resolvedImageUrl": "https://hub.emetec.net/s/396/resource/icon/payment/method/credit-debit-card.svg",
		"resolvedTitle": {
			"de-DE": "Kredit- / Debitkarte",
			"en-US": "Credit / Debit Card"
		},
		"sortOrder": 1,
		"spaceId": 396,
		"state": "ACTIVE",
		"title": {
			"availableLanguages": [
				"en-US"
			],
			"displayName": "",
			"items": [
				{
					"language": "en-US",
					"languageCode": "en",
					"translation": ""
				}
			]
		},
		"version": 2
	}
]

3.3.5Update Transactions

Transaction properties can be updated as long as they are not in the confirmed state. In order to do this use the update operation on the Transaction service.

Note
Have a look at the Object Versioning / Locking Section to describe how you have to handle the version property to prevent optimistic locking.

Request

In the example below we are going to update the line items and get rid of the discount line item that we added in the example above.

{
    "billingAddress": {
        "city": "Winterthur",
        "country": "CH",
        "emailAddress": "[email protected]",
        "familyName": "Test",
        "givenName": "Sam",
        "postcode": "8400",
        "street": "General-Guisan-Strasse 47"
    },
    "currency": "EUR",
    "id": 109472,
    "language": "de-CH",
    "lineItems": [
        {
            "amountIncludingTax": "11.87",
            "name": "Barbell Pull Up Bar",
            "quantity": "1",
            "sku": "barbell-pullup",
            "type": "PRODUCT",
            "uniqueId": "barbell-pullup"
        },
        {
            "amountIncludingTax": "559",
            "name": "Rowing Machine",
            "quantity": "1",
            "sku": "rowing-machine",
            "type": "PRODUCT",
            "uniqueId": "rowing-machine"
        },
        {
            "amountIncludingTax": "17.98",
            "name": "Super Whey Protein",
            "quantity": "4",
            "sku": "super-whey",
            "type": "PRODUCT",
            "uniqueId": "super-whey"
        },
        {
            "amountIncludingTax": "12.5",
            "name": "Special Chär Test",
            "quantity": "1",
            "sku": "special-chär-test",
            "type": "SHIPPING",
            "uniqueId": "special-chär-test"
        },
        {
            "amountIncludingTax": "12.5",
            "name": "Standard Shipping",
            "quantity": "1",
            "sku": "standard-shipping",
            "type": "SHIPPING",
            "uniqueId": "standard-shipping"
        }
    ],
    "shippingAddress": {
        "city": "Winterthur",
        "country": "CH",
        "emailAddress": "[email protected]",
        "familyName": "Test",
        "givenName": "Sam",
        "postcode": "8400",
        "street": "General-Guisan-Strasse 47"
    },
    "version": 3
}

Response

The response contains the updated transaction object.

{
    "billingAddress": {
        "city": "Winterthur",
        "country": "CH",
        "emailAddress": "[email protected]",
        "familyName": "Test",
        "givenName": "Sam",
        "postcode": "8400",
        "street": "General-Guisan-Strasse 47"
    },
    "currency": "EUR",
    "id": 109472,
    "language": "de-CH",
    "lineItems": [
        {
            "amountIncludingTax": "11.87",
            "name": "Barbell Pull Up Bar",
            "quantity": "1",
            "sku": "barbell-pullup",
            "type": "PRODUCT",
            "uniqueId": "barbell-pullup"
        },
        {
            "amountIncludingTax": "559",
            "name": "Rowing Machine",
            "quantity": "1",
            "sku": "rowing-machine",
            "type": "PRODUCT",
            "uniqueId": "rowing-machine"
        },
        {
            "amountIncludingTax": "17.98",
            "name": "Super Whey Protein",
            "quantity": "4",
            "sku": "super-whey",
            "type": "PRODUCT",
            "uniqueId": "super-whey"
        },
        {
            "amountIncludingTax": "12.5",
            "name": "Special Chär Test",
            "quantity": "1",
            "sku": "special-chär-test",
            "type": "SHIPPING",
            "uniqueId": "special-chär-test"
        },
        {
            "amountIncludingTax": "12.5",
            "name": "Standard Shipping",
            "quantity": "1",
            "sku": "standard-shipping",
            "type": "SHIPPING",
            "uniqueId": "standard-shipping"
        }
    ],
    "shippingAddress": {
        "city": "Winterthur",
        "country": "CH",
        "emailAddress": "[email protected]",
        "familyName": "Test",
        "givenName": "Sam",
        "postcode": "8400",
        "street": "General-Guisan-Strasse 47"
    },
    "version": 3
}

3.3.6Confirm Transaction

In case the auto confirm property is not set the transaction has to be confirmed. We recommend to do this step anyway.

The confirm transaction step should be done once the inputs of the customer have been validated and the pending order is created in your application (see step 7 in the process above). You can use the confirm operation to confirm the transaction and also set the merchant reference as you do now have an order number in your application.

Request

{
    "billingAddress": {
        "city": "Winterthur",
        "country": "CH",
        "emailAddress": "[email protected]",
        "familyName": "Test",
        "givenName": "Sam",
        "postcode": "8400",
        "street": "General-Guisan-Strasse 47"
    },
    "currency": "EUR",
    "id": 109472,
    "language": "de-CH",
    "lineItems": [
        {
            "amountIncludingTax": "11.87",
            "name": "Barbell Pull Up Bar",
            "quantity": "1",
            "sku": "barbell-pullup",
            "type": "PRODUCT",
            "uniqueId": "barbell-pullup"
        },
     ],
    "merchantReference": "DEV-2630",
    "shippingAddress": {
        "city": "Winterthur",
        "country": "CH",
        "emailAddress": "[email protected]",
        "familyName": "Test",
        "givenName": "Sam",
        "postcode": "8400",
        "street": "General-Guisan-Strasse 47"
    },
    "version": 5
}

3.3.7Fetch Transaction Update

In order to be updated about the transaction state you should register webhook notification on your side. The webhooks will update you about state changes of the selected entities and should trigger your application to further process the transaction results.

More Information about webhooks, webhooks listener and their configuration can be found in the Webhooks Documentation.

4Security Policy

If content security policy restrictions are applied in the shop, in order for this integration to work the following restrictions must be removed for https://hub.emetec.net:

  • URLs which can be loaded as valid sources for JavaScript.

  • Allow inline script executions.

  • URLs which can be loaded using iframe interfaces.

  • URLs which can be loaded using script interfaces.

For example the following header would allow that by setting CSP: script-src directive to allow loading https://hub.emetec.net URLs as valid sources for JavaScript, Unsafe inline script policy to allow inline script executions, CSP: frame-src directive to allow loading https://hub.emetec.net URLs using iframe interfaces, CSP: connect-src directive to allow loading https://hub.emetec.net URLs using script interfaces.

content-security-policy: script-src https://hub.emetec.net 'unsafe-inline'; frame-src https://hub.emetec.net; connect-src https://hub.emetec.net;