Checkout flow documentation
Learn about different checkout flow options, submit handling, and form validation behavior when integrating UI components v2.
Customized checkout flow - GeneralTo simplify the integration, you can use the <unzer-checkout>
custom web component. This tag encapsulates core functionality needed to manage the checkout process efficiently.
At a minimum, <unzer-checkout>
should be used with a checkout button as its immediate child.
If you’re using a custom button, it’s important that the button supports the disabled
property. This allows <unzer-checkout>
to dynamically control the button’s state based on the current status of the checkout form. For example:
myButton.disabled = true; // should disable the button
myButton.disabled = false; // should enable the button
If the disabled
property is not present or supported, <unzer-checkout>
will not be able to control the button’s interactivity.
<unzer-checkout id='unzer-checkout'>
<button type="submit">Pay</button>
</unzer-checkout>
Manual control
If you prefer to implement your own logic for enabling or disabling the checkout button, you can pass the manualCheckoutButtonControl
attribute to the tag.
In this mode, <unzer-checkout>
will dispatch events to indicate when the button should be enabled or disabled, allowing you to implement your own logic and criteria for managing the button state.
<unzer-payment
id="unzer-payment"
publicKey="s-pub-xyz"
locale="de-DE">
<unzer-card></unzer-card>
</unzer-payment>
<unzer-checkout id='unzer-checkout' manualCheckoutButtonControl>
<button type="submit">Pay</button>
</unzer-checkout>
Promise.all([
customElements.whenDefined("unzer-payment"),
customElements.whenDefined("unzer-checkout"),
customElements.whenDefined("unzer-card"),
])
.then(() => {
const unzerCheckout = document.getElementById("unzer-checkout");
const unzerPayment = document.getElementById("unzer-payment");
unzerPayment.addEventListener("stateChanged", (event) => {
const enableCheckout = event.detail.enableCheckout;
// Add custom logic here and apply the button state
});
})
.catch((error) => {
/* Handle loading and initialization error */
})
If you prefer not to use <unzer-checkout>
at all, events will be dispatched in a similar manner
<unzer-payment
id="unzer-payment"
publicKey="s-pub-xyz"
locale="de-DE">
<unzer-card></unzer-card>
</unzer-payment>
<div>
<button type="submit">Pay</button>
</div>
Promise.all([
customElements.whenDefined("unzer-payment"),
customElements.whenDefined("unzer-card"),
])
.then(() => {
const unzerCheckout = document.getElementById("unzer-checkout");
const unzerPayment = document.getElementById("unzer-payment");
unzerPayment.addEventListener("stateChanged", (event) => {
const enableCheckout = event.detail.enableCheckout;
// Add custom logic here and apply the button state
})
.catch((error) => {
/* Handle loading and initialization error */
})
Custom checkout flow
For more information on checkout flow customization please see Integrate UI without the unzer-checkout component
Integrate UI without the unzer-checkout component
Learn how to integrate UI components v2 without using the <unzer-checkout>
component for maximum control over the payment flow.
Overview
While the <unzer-checkout>
component provides a simplified integration experience, you can also integrate UI components v2 without it for more control over the payment flow, custom validation, and event handling.
Basic integration structure
Non-checkout code example<unzer-payment
id="unzer-payment"
publicKey="s-pub-xyz"
locale="de-DE">
<!-- ... Here you will need to add the Unzer payment type tag, so the form UI elements will be inserted -->
<!-- e.g <unzer-paylater-invoice></unzer-paylater-invoice> -->
<unzer-customer-form></unzer-customer-form>
<button type="submit" id="yourPaymentButtonId">Pay</button>
</unzer-payment>
Promise.all([
customElements.whenDefined("unzer-payment"),
customElements.whenDefined("unzer-klarna"),
])
.then(() => {
const unzerPayment = document.getElementById("unzer-payment");
const submitButton = document.getElementById("yourPaymentButtonId");
// Event listeners for payment component
unzerPayment.addEventListener("typeCreated", (event) => {
const typeId = event.detail.id;
const typeData = event.detail.data;
console.log("Payment type created:", typeId);
console.log("Payment type data:", typeData);
// Send typeId to your server for payment processing
});
// Handle submit button click
submitButton.addEventListener("click", async (event) => {
event.preventDefault();
try {
const response = await unzerPayment.submit();
console.log("Submit response:", response);
if (response.submitResponse && response.submitResponse.success) {
const typeId = response.submitResponse.data.id;
console.log("Payment type ID:", typeId);
// Process payment on server
} else {
console.error("Submit failed:", response);
console.error("Payment submission failed");
}
} catch (error) {
console.error("Submit error:", error);
console.error("An error occurred during payment submission");
}
});
})
.catch((error) => {
console.error("Initialization error:", error);
console.error("Failed to initialize payment components");
});
Payment methods requiring special handling
There are a couple of payment methods that function a bit differently and require different integration. They are: Google Pay, Apple Pay and Paypal-express
Apple Pay integration without Unzer checkout component
If you prefer to implement your own logic, you can decide not to include the <unzer-checkout>
component.
In this mode, <unzer-checkout>
will trigger a callback function, allowing you to implement your own logic and criteria for managing the button state. You can provide an onPaymentAuthorizedCallback
function in the configuration, which lets you implement custom logic to approve or reject the payment by calling approve
or reject
based on your requirements.
<unzer-payment
id="unzer-payment"
publicKey="{{PUBLIC_KEY}}"
locale="de-DE">
<unzer-apple-pay></unzer-apple-pay>
</unzer-payment>
Promise.all([
customElements.whenDefined("unzer-payment"),
customElements.whenDefined("unzer-apple-pay"),
]).then(() => {
const unzerPayment = document.getElementById('unzer-payment');
const applePayPaymentRequest = {
countryCode: 'DE',
currencyCode: 'EUR',
supportedNetworks: ['visa', 'mastercard'],
merchantCapabilities: ['supports3DS'],
total: { label: 'Unzer GmbH', amount: '12.99' },
lineItems: [
{
"label": "Subtotal",
"type": "final",
"amount": "10.00"
},
{
"label": "Free Shipping",
"amount": "0.00",
"type": "final"
},
{
"label": "Estimated Tax",
"amount": "2.99",
"type": "final"
}
],
onPaymentAuthorizedCallback: async (paymentData, approve, reject) => {
// Implement your business logic validation here
// You can perform additional checks on paymentData before proceeding
if (requirementsNotMet) {
reject();
// Implement user notification for rejection reason
return;
}
const response = await unzerPayment.submit();
// Perform any necessary response validation
// ...
// Handle the payment response and update UI accordingly
// This step is required to proceed with the payment flow
if (response.submitResponse.success) {
approve();
} else {
reject();
}
},
};
unzerPayment.setApplePayData(applePayPaymentRequest);
}).catch((error) => {
// Handle initialization errors
console.error('Failed to initialize Apple Pay:', error);
});
Google Pay integration without Unzer checkout component
If you prefer to implement your own logic, you can decide not to include the <unzer-checkout>
component.
In this mode, <unzer-checkout>
will trigger a callback function, allowing you to implement your own logic and criteria for managing the button state. You can pass the onPaymentAuthorizedCallback
callback function in the configuration that will allow you to verify or stop the payment process by calling approve
or reject
according to your custom requirements.
<unzer-payment
id="unzer-payment"
publicKey="{{PUBLIC_KEY}}"
locale="de-DE">
<unzer-google-pay></unzer-google-pay>
</unzer-payment>
Promise.all([
customElements.whenDefined("unzer-payment"),
customElements.whenDefined("unzer-google-pay"),
]).then(() => {
const unzerPayment = document.getElementById('unzer-payment');
const googlePayPaymentRequest = {
gatewayMerchantId: "yourUnzerChannelId",
merchantInfo: {
merchantId: "yourGooglePayMerchantId",
merchantName: 'yourMerchantName'
},
transactionInfo: {
countryCode: 'DE',
currencyCode: 'EUR',
totalPrice: '19.99',
},
buttonOptions: {
buttonColor: 'black',
},
onPaymentAuthorizedCallback: async (paymentData, approve, reject) => {
// Implement your business logic validation here
// You can perform additional checks on paymentData before proceeding
if (requirementsNotMet) {
// reject() can optionally take a message object
// The message will be displayed in the Google Pay overlay to the user
// reject({message: 'Custom error message'});
reject({message: 'Payment requirements not met'});
return;
}
const response = await unzerPayment.submit();
// Perform any necessary response validation
// Handle the payment response and update UI accordingly
// This step is required to proceed with the payment flow
if (response.submitResponse.success) {
approve();
} else {
// You can also reject with a specific error message
// The message will be shown in the Google Pay overlay
reject({message: 'Payment processing failed'});
}
},
};
unzerPayment.setGooglePayData(googlePayPaymentRequest);
}).catch((error) => {
// Handle initialization errors
console.error('Failed to initialize Google Pay:', error);
});
Google Pay Express integration without Unzer checkout component
Please note that setting the following parameters in the Google Pay data configuration will enable the Google Pay Express payment method. This method requires you to manually set the customer object. More information is available here.
unzerPayment.setGooglePayData({
...
emailRequired: true,
billingAddressRequired: true,
billingAddressParameters: {
format: 'FULL',
phoneNumberRequired: true
}
...
});
<unzer-payment
id="unzer-payment"
publicKey="{{PUBLIC_KEY}}"
locale="de-DE">
<unzer-google-pay></unzer-google-pay>
</unzer-payment>
Promise.all([
customElements.whenDefined("unzer-payment"),
customElements.whenDefined("unzer-google-pay"),
]).then(() => {
const unzerPayment = document.getElementById('unzer-payment');
const googlePayPaymentRequest = {
gatewayMerchantId: "yourUnzerChannelId",
merchantInfo: {
merchantId: "yourGooglePayMerchantId",
merchantName: 'yourMerchantName'
},
transactionInfo: {
countryCode: 'DE',
currencyCode: 'EUR',
totalPrice: '19.99',
},
buttonOptions: {
buttonColor: 'black',
},
emailRequired: true,
billingAddressRequired: true,
billingAddressParameters: {
format: 'FULL',
phoneNumberRequired: true
},
onPaymentAuthorizedCallback: async (paymentData, approve, reject) => {
// Implement your business logic validation here
// You can perform additional checks on paymentData before proceeding
if (requirementsNotMet) {
// reject() can optionally take a message object
// The message will be displayed in the Google Pay overlay to the user
// reject({message: 'Custom error message'});
reject({message: 'Payment requirements not met'});
return;
}
const response = await unzerPayment.submit();
// Perform any necessary response validation
// Handle the payment response and update UI accordingly
// This step is required to proceed with the payment flow
if (response.submitResponse.success) {
approve();
} else {
// You can also reject with a specific error message
// The message will be shown in the Google Pay overlay
reject({message: 'Payment processing failed'});
}
},
};
unzerPayment.setGooglePayData(googlePayPaymentRequest);
}).catch((error) => {
// Handle initialization errors
console.error('Failed to initialize Google Pay:', error);
});
PayPal Express integration without Unzer checkout component
You can attach a click
event handler on the unzer-paypal-express
component to handle submission attempts manually.
<unzer-payment
id="unzer-payment"
publicKey="{{PUBLIC_KEY}}"
locale="de-DE">
<unzer-paypal-express id="paypalPaymentButtonId"></unzer-paypal-express>
</unzer-payment>
Promise.all([
customElements.whenDefined("unzer-payment"),
customElements.whenDefined("unzer-paypal-express"),
]).then(() => {
const unzerPayment = document.getElementById('unzer-payment');
const paypalSubmitButton = document.getElementById("paypalPaymentButtonId");
paypalSubmitButton.addEventListener("click", async (event) => {
event.stopPropagation();
const response = await unzerPayment.submit();
if (response.submitResponse) {
// Handle payment response processing
console.log("submit response: ", response.submitResponse);
}
});
}).catch((error) => {
// Handle initialization errors
console.error('Failed to initialize PayPal Express:', error);
});
Key differences from checkout component
- Manual submit handling: You call
unzerPayment.submit()
directly instead of relying on the checkout component - Event-driven architecture: You listen to events emitted by the payment component
- Custom validation handling: You manage form validation state manually
- Flexible UI control: You have full control over the submit button and user interface
- Direct response handling: You handle the submit response directly without the checkout component’s abstraction
Benefits of non-checkout integration
- Full control: Complete control over the payment flow and user interface
- Custom validation: Implement your own validation logic and error handling
- Flexible styling: No constraints from the checkout component’s styling
- Event-driven: React to specific events as they occur
- Integration flexibility: Easier to integrate with existing forms and workflows
- Shop system compatibility: Better integration with shop systems where submit button handling is not under your control
Submit payment without checkout component
When not using the <unzer-checkout>
component, you need to manually handle the submit process and event handling.
Manual submit handling
Instead of using <unzer-checkout>
, you can call the submit()
method directly on the <unzer-payment>
element:
const unzerPayment = document.getElementById("unzer-payment");
const submitButton = document.getElementById("yourPaymentButtonId");
submitButton.addEventListener("click", async (event) => {
const response = await unzerPayment.submit();
console.log("response: ", response);
if (response.submitResponse) {
console.log("submit response: ", response.submitResponse);
}
});
Getting payment type ID
After successful submission, you can access the payment type ID from the response:
const response = await unzerPayment.submit();
if (response.submitResponse && response.submitResponse.success) {
const typeId = response.submitResponse.data.id;
// Use typeId for server-side payment processing
console.log("Payment type ID:", typeId);
}
Form validation behavior
Important: Unzer form validation is automatically triggered only when you call the submit()
method. It is not possible to manually trigger Unzer’s built-in validation outside of the submit process.
However, you can implement your own additional validation logic before calling submit()
:
const unzerPayment = document.getElementById("unzer-payment");
const submitButton = document.getElementById("yourPaymentButtonId");
submitButton.addEventListener("click", async (event) => {
// Your custom validation logic
if (validateCustomFields()) {
// Only call submit() if your validation passes
const response = await unzerPayment.submit();
// Handle response...
} else {
// Handle validation errors
console.log("Custom validation failed");
}
});
function validateCustomFields() {
// Implement your custom validation logic here
// Return true if validation passes, false otherwise
return true;
}
This approach allows you to:
- Perform custom validation before Unzer validation
- Control when the submit process starts
- Handle validation errors according to your application’s needs
- Integrate with existing form validation frameworks