![Seamless Iframe Integration](/assets/payment/images/iframe-seamless-integration.png)
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:
The customer enters the shipping and billing information.
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.
The application can trigger a validation of the entered information.
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.
Before you start with the integration of the iframe you should:
Create an account and sign up.
Create an application user under Account > Users > Application User.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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);
The described steps above should now be explained a bit more in details including the API operations including example requests.
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>
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":"",
"organisationName":"customweb GmbH",
"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":"",
"organisationName":"customweb GmbH",
"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
}
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.
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://checkout.postfinance.ch/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
}
]
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
}
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
}
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.
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://checkout.postfinance.ch:
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://checkout.postfinance.ch URLs as valid sources for JavaScript, Unsafe inline script policy to allow inline script executions, CSP: frame-src directive to allow loading https://checkout.postfinance.ch URLs using iframe interfaces, CSP: connect-src directive to allow loading https://checkout.postfinance.ch URLs using script interfaces.
content-security-policy: script-src https://checkout.postfinance.ch 'unsafe-inline'; frame-src https://checkout.postfinance.ch; connect-src https://checkout.postfinance.ch;