Accept PayPal Express with UI components
Use Unzer UI component to add Unzer PayPal Express payment to your checkout page.
Overview
Using UI components v2 for PayPal Express you get a ready-made form with the fields necessary to make this type of payment. Basic steps for integrating using UI components are the same for all payment methods and you can read about them here.
Legacy integration
This page describes the most recent integration of the UI Components, introduced in Jan 2025. For the Legacy integration guide go here.
Note for WebView- If you are integrating for mobile devices, please read the Guidelines for mobile integrations.
Before you begin
Before you begin- Check the basic integration requirements.
- Familiarize yourself with general guide on integrating using UI components.
Step 1: Add UI components v2 to your payment pageclient side
Step 1: Add UI components v2 to your payment page [client side]First, you need to initiate our UI components v2 library and add the needed payment type component to your payment page.
Initiate UI Components - PayPal Express
Initiate UI Components v2 - PayPal ExpressLoad the Unzer JS script
Load the Unzer JS scriptInclude the Unzer JS script on your website. This will load all the Unzer custom UI components with unzer- prefixed. For example, <unzer-paylater-invoice>.
Make sure to always include the script directly from the Unzer domain https://static-v2.unzer.com.
<script
type="module"
src="https://static-v2.unzer.com/v2/ui-components/index.js"
></script>
To make your website load faster, import the unzer script at the bottom of your HTML document.
Make sure to import the script as type=“module”.
To learn which URLs must be added to the allowlist for Unzer UI components in your content security policy, please refer to Content security policy section.
It is a good practice to put your website in loading state until the Unzer script is loaded, and the UI components are ready to use.
// Make sure initially your application is in loading state
// until all Unzer components are loaded
Promise.all([
customElements.whenDefined("unzer-payment"),
// Probably add any other Unzer components used here. For example:
// customElements.whenDefined("unzer-paylater-invoice"),
]).then(() => {
// Hide the loading state and proceed with next steps
}).catch((error) => {
// Handle any error that might occur during the
// loading process and initialization
});
UI setup and configuration
UI setup and configurationTo securely collect payment data from your customer, you need to add the <unzer-payment> component, inside which you insert the needed payment type components.
<unzer-checkout> element.
This will provide automatic handling for enabling/disabling the submit button depending on the current status,
and showing/hiding of brand icons.<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-payment>
<unzer-checkout id='unzer-checkout'>
<button type="submit" id="yourPaymentButtonId">Pay</button>
</unzer-checkout>
Following parameters need to be passed.
| Parameter | Type | Description | Default value |
|---|---|---|---|
publicKey (required) | String | The merchant public key. | - |
locale | String | The used locale. For more information on supported locales, see Localization. | Browser user defined locale. |
Customer Data APIs
The <unzer-payment> component exposes public APIs to programmatically access and manage customer data. These APIs enable multi-step checkout flows where customer information and payment are on separate steps.
Use case: Update customer details during checkout, pre-populate forms, or retrieve customer data before final submission.
Read APIs
The following methods return or check customer data:
| Method | Return Type | Description |
|---|---|---|
getCustomerData() | Object | Returns all customer data in PAPI format including personal info, addresses, and metadata (customerId, id, phone, language). |
getValidationErrors() | Object | Returns current validation errors as key-value pairs. Only includes fields with errors. Returns empty object {} if no errors. |
isCustomerValid() | Boolean | Checks if all customer data is valid. Returns true if valid, false if validation errors exist. |
Write APIs
The following methods update or clear customer data:
| Method | Parameters | Description |
|---|---|---|
setCustomerData(data) | data: Object in PAPI format | Pre-populates the entire customer form with provided data. Replaces all existing data. |
updateCustomerData(data) | data: Object in PAPI format | Partially updates customer data. Fields not included in the update are preserved. Useful for updating individual fields without affecting others. |
clearCustomerData() | - | Resets the customer form to empty state. Clears all personal info, addresses, and company data. |
Customer Settings Parameter
To render an empty customer form with a specific customer type (B2C or B2B), use the customerSettings parameter:
// Render empty B2B form (shows company fields)
unzerPayment.setCustomerData({ customerSettings: { type: 'B2B' } });
// Render empty B2C form (shows personal fields) - or omit for default B2C
unzerPayment.setCustomerData({ customerSettings: { type: 'B2C' } });
Valid values:
'B2C'- Individual customer (default if nothing is passed)'B2B'- Business customer
customerSettings.typeonly supports'B2C'and'B2B'- use this to pre-set the form type without pre-populating data- Default behavior: If no
customerSettingsis provided, the form defaults to B2C
All customer data APIs use the PAPI (Payment API) format. The data structure matches the customer resource format from the Unzer API. Fields like
customerId and id can be added or retrieved through these APIs.Example: Multi-step checkout
// Get reference to unzer-payment component
const unzerPayment = document.getElementById('unzer-payment');
// Pre-populate customer data on page load (e.g., from previous session)
const existingCustomerData = {
salutation: 'mr',
firstname: 'John',
lastname: 'Doe',
email: 'john.doe@example.com',
mobile: {
prefix: '+49',
phone: '1234567890'
},
billingAddress: {
firstname: 'John',
lastname: 'Doe',
street: 'Main Street 1',
zip: '12345',
city: 'Berlin',
country: 'DE'
},
shippingAddress: {
firstname: 'John',
lastname: 'Doe',
street: 'Shipping Street 2',
zip: '54321',
city: 'Munich',
country: 'DE'
}
};
unzerPayment.setCustomerData(existingCustomerData);
// Update only specific fields (other fields are preserved)
unzerPayment.updateCustomerData({
email: 'newemail@example.com',
mobile: {
prefix: '+49',
phone: '9876543210'
}
});
// Check if customer data is valid before proceeding to payment
if (unzerPayment.isCustomerValid()) {
const customerData = unzerPayment.getCustomerData();
console.log('Customer data is valid:', customerData);
// Proceed to payment step
} else {
const errors = unzerPayment.getValidationErrors();
console.log('Validation errors:', errors);
// Show errors to user: e.g., {email: "Invalid email format", mobile: "Phone number is required"}
}
// Clear customer form (e.g., on logout or form reset)
unzerPayment.clearCustomerData();
To implement a customer form, check customer UI components page for details.
Customized checkout flow - PayPal Express
Customized checkout flow - PayPal ExpressTo simplify the integration, you can use the <unzer-checkout> custom web component. This tag encapsulates core functionality needed to manage the checkout process efficiently.
<unzer-checkout> should be used with a checkout button as its immediate child.<unzer-checkout id='unzer-checkout'>
<button type="submit">Pay</button>
</unzer-checkout>
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);
});
Add Paypal Express payment method
To add the Paypal Express payment method insert unzer-paypal-express in the unzer-payment container.
<unzer-payment
publicKey="s-pub-xxxxxxxxxx"
locale="de-DE">
<unzer-paypal-express></unzer-paypal-express>
</unzer-payment>
<unzer-checkout>
<button type="submit" id="yourPaymentButtonId">Pay</button>
</unzer-checkout>
<unzer-checkout> element.
This will provide automatic handling of the submit whenever the user clicks the submit button, and passing back the
creation payload.Optional: Customize the UI components
You can easily customize the color of the button by setting the color attribute. Here you can set the following colors: blue (by default), white, silver, black, and gold.
<unzer-payment
publicKey="s-pub-xxxxxxxxxx"
locale="de-DE">
<unzer-paypal-express color="silver"></unzer-paypal-express>
</unzer-payment>
<unzer-checkout>
<button type="submit" id="yourPaymentButtonId">Pay</button>
</unzer-checkout>
Step 2: Create a basket resourceserver side
The basket resource stores information about the purchased products, used vouchers, and the shipment costs.
POST https://api.unzer.com/v2/baskets
Body:
{
"currencyCode": "EUR",
"orderId": "YourOrderId1234",
"totalValueGross":195.90,
"note": "basket note",
"basketItems": [
{
"title": "Notebook pro",
"basketItemReferenceId": "item-1",
"quantity": 1,
"amountPerUnitGross": 200.00,
"amountDiscountPerUnitGross": 10.00,
"vat": 1,
"unit": "pc",
"subTitle": "basket item 2",
"type": "goods"
},
{
"title": "Shipment costs",
"basketItemReferenceId": "item-2",
"quantity": 1,
"amountPerUnitGross": 5.90,
"vat": 1,
"subTitle": "Insured standard shipment",
"type": "shipment"
}
]
}
$unzer = new Unzer('s-priv-xxxxxxxxxx');
$basketItem = (new BasketItem())
->setBasketItemReferenceId('Item-d030efbd4963')
->setQuantity(10)
->setUnit('m')
->setAmountPerUnitGross(20.00)
->setAmountDiscountPerUnitGross(1.00)
->setVat(19.0)
->setTitle('SDM 6 CABLE')
->setSubTitle('This is brand new Mid 2019 version')
->setImageUrl('https://a.storyblok.com/f/91629/x/1ba8deb8cc/unzer_primarylogo__white_rgb.svg')
->setType(BasketItemTypes::GOODS);
$basket = (new Basket())
->setTotalValueGross(190.00)
->setCurrencyCode('EUR')
->setOrderId('Order-12345')
->setNote('Test Basket')
->addBasketItem($basketItem);
$unzer->createBasket($basket);
BasketItem basketItem = new BasketItem()
.setBasketItemReferenceId("Item-xxxxxxxxxx")
.setQuantity(10)
.setUnit("m")
.setAmountPerUnitGross(BigDecimal.valueOf(20.00))
.setAmountDiscountPerUnitGross(BigDecimal.valueOf(1.00))
.setVat(BigDecimal.valueOf(19.0))
.setTitle("SDM 6 CABLE")
.setSubTitle("This is brand new Mid 2019 version")
.setImageUrl(new URL("https://a.storyblok.com/f/91629/x/1ba8deb8cc/unzer_primarylogo__white_rgb.svg"))
.setType(BasketItem.Type.GOODS);
Basket basket = new Basket()
.setTotalValueGross(BigDecimal.valueOf(190.00))
.setCurrencyCode(Currency.getInstance("EUR"))
.setOrderId("Order-12345")
.setNote("Test Basket")
.addBasketItem(basketItem);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
unzer.createBasket(basket);
The response looks similar to the following example:
{
"id": "s-bsk-12345"
}
For a full description of basket resource, refer to the relevant server-side-integration documentation page.
Create Payment Type Resource
Create Payment Type ResourceFor each payment type that you use, you need to create a dedicated payment type resource.
After the submit button is clicked, the payment data are automatically submitted. You will then need
to query the unzer-checkout element and handle the response inside its onPaymentSubmit event listener.
// ...
const unzerCheckout = document.getElementById('unzer-checkout');
unzerCheckout.onPaymentSubmit = (response) => {
if (response.submitResponse && response.submitResponse.success) {
const paymentId = response.submitResponse.data.id;
// Submit the ID of the created payment type resource to your
// server side integration to perform the payment transaction.
} else {
// Handle resource creation error
}
};
Step 3: Make a paymentserver side
For PayPal Express, an extended 2-step process for PayPal Express is applicable. This is different from the standard PayPal transaction flow.
In a first step, you are forwarding the customer to the PayPal payment page, where the customer authenticates himself and approves the order initially. Once this is successfully done, PayPal provides the customer’s full address data, that you can use to complete the customer’s order in your shop. With the address data, you can recalculate the shipping costs according to the now known shipping address.
In a second step, you are finalizing the payment in a separate request to Unzer by providing the final transaction amount.
Depending on your use cases, you have the option to use two different transaction flows.
Option 1: You can charge the customer’s wallet directly and the payment is done once the customer confirmed the order, and you’ve successfully finalized the payment immediately during customer’s checkout. For more details, see Order fulfillment.
Option 2: For a delayed fulfillment, you can authorize the payment first and the amount is blocked on the customer’s wallet. When you ship the customer’s order, you can then charge the blocked amount and charge the customer’s wallet. For more details, see Order fulfillment.
For more details on managing PayPal payments, such as refunding them, see Manage PayPal payments.
Option 1: Make a charge transaction
Make a POST /payments/charges
Now, make a charge transaction with the Paypal typeId that you created and the returnUrl leading back to your shop after the customer has successfully approved the order on the PayPal payment page. With the charge transaction, an amount is reserved but is not yet transferred. Initially the transaction is pending and a payment resource is created.
POST https://api.unzer.com/v1/payments/charges
Body:
{
"amount": "195.90",
"currency": "EUR",
"returnUrl": "https://unzer.com/",
"paymentReference": "Example Reference",
"orderId": "ExampleOrderId1234",
"resources": {
"typeId": "s-ppl-twq0jsaafyny",
"basketId": "s-bsk-12345"
},
"additionalTransactionData": {
"paypal": {
"checkoutType": "express"
}
}
}
$unzer = new Unzer('s-priv-xxxxxxxxxx');
$charge = (new Charge(195.9, 'EUR', 'https://unzer.com/'))
->setCheckoutType('express', $paypal)
->setPaymentReference("Example Reference")
->setOrderId("ExampleOrderId1234");
$unzer->performCharge($charge, $paypal, null, null, $basket);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Charge charge = (Charge) new Charge()
.setAmount(BigDecimal.valueOf(195.9))
.setCurrencyCode(Currency.getInstance("EUR"))
.setReturnUrl("https://unzer.com/")
.setPaymentReference("Example Reference")
.setOrderId("ExampleOrderId1234");
.setAdditionalTransactionData(
new AdditionalTransactionData()
.setPaypal(new PaypalData().setCheckoutType(PaypalData.CheckoutType.EXPRESS))
);
unzer.charge(charge);
The response looks similar to the following example:
{
"id": "s-chg-1",
"isSuccess": false,
"isPending": true,
"isError": false,
"isResumed": false,
"redirectUrl": "https://sbx-payment.unzer.com/v1/redirect/paypal/s-ky7ZyEmKNjiC",
"message": {
"code": "COR.000.200.000",
"merchant": "Transaction pending",
"customer": "Your payment is currently pending. Please contact us for more information."
},
"amount": "195.9000",
"currency": "EUR",
"returnUrl": "https://unzer.com/",
"date": "2022-10-31 12:48:12",
"resources": {
"customerId": "s-cst-0b5fbac8e1fb",
"paymentId": "s-pay-8537",
"basketId": "s-bsk-12345",
"metadataId": "",
"traceId": "f24acee84b5d28324489a7860bc7ffb2",
"typeId": "s-ppl-skki29gw2fue"
},
"additionalTransactionData": {
"paypal": {
"checkoutType": "express"
}
},
"orderId": "YourOrderId1234",
"paymentReference": "Example Reference",
"processing": {
"uniqueId": "31HA07BC8137FFAF17A41E1EADC47133",
"shortId": "5311.4688.9455",
"traceId": "f24acee84b5d28324489a7860bc7ffb2"
}
}
Fetch the result of POST /payments/charges
We recommend subscribing to the charge event to receive notifications about any changes to the charge transaction. As soon as the event is triggered, you should fetch the charge and update the order status in your shop according to its status.
Transaction status isResumed:true indicates, customer has successfully approved the order on PayPal payment page, and you can proceed on this transaction.
GET /payments/s-pay-8538/charges/s-chg-1
Body:
{
}
$unzer = new UnzerSDK\Unzer('s-priv-xxxxxxxxxx');
$charge = $unzer->fetchChargeById($paymentId, $chargeId);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Charge charge = unzer.fetchCharge(paymentId, chargeId);
The response looks similar to the following example:
{
"id": "s-chg-1",
"isSuccess": true,
"isPending": false,
"isResumed": true,
"isError": false,
"card3ds": false,
"redirectUrl": "https://sbx-payment.unzer.com/v1/redirect/paypalot/s-jjlC9FrQrZ7q",
"message": {
"code": "COR.000.100.112",
"merchant": "Request successfully processed in 'Merchant in Connector Test Mode'",
"customer": "Your payments have been successfully processed in sandbox mode."
},
"amount": "195.9000",
"currency": "EUR",
"returnUrl": "https://unzer.com/",
"date": "2022-10-31 12:59:07",
"resources": {
"customerId": "s-cst-0b5fbac8e1fb",
"paymentId": "s-pay-8538",
"basketId": "s-bsk-5e612a68be0b",
"traceId": "e8664e249367acee81480e9ae4f74b1f",
"typeId": "s-ppl-skki29gw2fue"
},
"additionalTransactionData": {
"paypal": {
"checkoutType": "express"
}
},
"orderId": "YourOrderId1234",
"paymentReference": "YourReference",
"processing": {
"uniqueId": "31HA07BC8137FFAF17A44DC072DB429E",
"shortId": "5311.4754.5055",
"traceId": "e8664e249367acee81480e9ae4f74b1f"
}
}
Fetch customer data
Fetch the customer resource, which is now linked to the charges transaction. The customer resource is containing complete customer billing and shipping address data provided by PayPal.
For a full description of customer resource, refer to the relevant server-side-integration documentation page: Manage customer.
GET /customers/s-cst-0b5fbac8e1fb
Body:
{
}
$unzer = new Unzer('s-priv-xxxxxxxxxx');
$unzer->fetchCustomer($customerId);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Customer customer = unzer.fetchCustomer(customerId);
The response looks similar to the following example:
{
"id": "s-cst-0b5fbac8e1fb",
"lastname": "Doe",
"firstname": "John",
"salutation": "mr",
"company": "",
"customerId": "",
"birthDate": "1987-12-20",
"email": "John.Doe@unzer.com",
"phone": "+4962216471100",
"mobile": "",
"language": "de",
"billingAddress": {
"name": "John Doe",
"street": "Hugo-Junkers-Str. 5",
"state": "DE",
"zip": "60386",
"city": "Frankfurt am Main",
"country": "DE"
},
"shippingAddress": {
"name": "John Doe",
"street": "Hugo-Junkers-Str. 5",
"state": "DE",
"zip": "60386",
"city": "Frankfurt am Main",
"country": "DE",
"shippingType": "equals-billing"
},
"geoLocation": {
"clientIp": "87.163.144.231",
"countryIsoA2": "DE"
}
}
(Optional) Recalculate the charge amount
With customer’s address data you can now recalculate the charge amount, for example, if due to customer’s shipping address higher shipping costs are applied.
Optional: Update the basket and the charge amount
If you want to change the initial amount of your charges transaction, update the basket you’ve used in the POST /payments/charges request, display the updated basket and final charge amount to the customer. Ask the customer to finally confirm the changes.
Refer to the Update a basket guide to learn more on how to update basket resources.
Make a PATCH /payments/{paymentId}/charges
Once the customer has finally confirmed the changed amount in your shop, make a PATCH on the initially created charge transaction to complete the PayPal Express transaction.
Provide the updated charge amount in the PATCH /payments/{paymentId}/charges request.
An updated basket doesn’t need to be provided again, the basketId includes the changes that you’ve applied. These will be considered automatically.
With a successful PATCH /payments/{paymentId}/charges the payment status is set on completed now.
PATCH /payments/{paymentId}/charges with an empty body.PATCH /payments/s-pay-8538/charges/s-chg-1
Body:
{
"amount": "210.90"
}
$unzer = new Unzer('s-priv-xxxxxxxxxx');
$charge = new Charge(210.9);
$unzer->updateCharge($paymentId, $charge);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Charge charge = unzer.fetchCharge(paymentId, chargeId);
charge.setAmount(BigDecimal.valueOf(210.9));
unzer.updateCharge(charge);
The response looks similar to the following example:
{
"id": "s-chg-1",
"isSuccess": true,
"isPending": false,
"isError": false,
"card3ds": false,
"redirectUrl": "https://sbx-payment.unzer.com/v1/redirect/paypalot/s-jjlC9FrQrZ7q",
"message": {
"code": "COR.000.100.112",
"merchant": "Request successfully processed in 'Merchant in Connector Test Mode'",
"customer": "Your payments have been successfully processed in sandbox mode."
},
"amount": "210.9000",
"currency": "EUR",
"returnUrl": "https://unzer.com/",
"date": "2022-10-31 12:59:07",
"resources": {
"customerId": "s-cst-0b5fbac8e1fb",
"paymentId": "s-pay-8538",
"basketId": "s-bsk-5e612a68be0b",
"metadataId": "",
"payPageId": "",
"traceId": "e8664e249367acee81480e9ae4f74b1f",
"typeId": "s-ppl-skki29gw2fue"
},
"additionalTransactionData": {
"paypal": {
"checkoutType": "express"
}
}
"orderId": "YourOrderId1234",
"paymentReference": "YourReference",
"processing": {
"uniqueId": "31HA07BC8137FFAF17A44DC072DB429E",
"shortId": "5311.4754.5055",
"paypalBuyerId": "JHQEVB7FYWUHW",
"traceId": "e8664e249367acee81480e9ae4f74b1f"
}
}
Option 2: Make an authorize transaction
Make a POST /payments/authorize
Now, make an authorize transaction with the Paypal typeId and basketId that you created and the returnUrl leading back to your shop after the customer has successfully approved the order on the PayPal payment page. With authorize, the transaction is created, but the amount is not reserved yet in the wallet. Initially, the transaction is pending and a payment resource is created.
POST /payments/authorize
Body:
{
"amount": "195.90",
"currency": "EUR",
"returnUrl": "https://unzer.com/",
"paymentReference": "Example Reference",
"orderId": "ExampleOrderId1234",
"resources": {
"typeId": "s-ppl-twq0jsaafyny",
"basketId": "s-bsk-12345"
},
"additionalTransactionData": {
"paypal": {
"checkoutType": "express"
}
}
}
$unzer = new Unzer('s-priv-xxxxxxxxxx');
$authorize = (new Authorization(195.9, 'EUR', 'https://unzer.com/'))
->setCheckoutType('express', $paypal)
->setPaymentReference("Example Reference")
->setOrderId("ExampleOrderId1234");
$unzer->performAuthorization($authorize, $paypal, null, null, $basket);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Authorization authorization = (Authorization) new Authorization()
.setAmount(BigDecimal.valueOf(195.9))
.setCurrencyCode(Currency.getInstance("EUR"))
.setReturnUrl("https://unzer.com/")
.setPaymentReference("Example Reference")
.setOrderId("ExampleOrderId1234");
.setAdditionalTransactionData(
new AdditionalTransactionData()
.setPaypal(new PaypalData().setCheckoutType(PaypalData.CheckoutType.EXPRESS))
);
unzer.authorize(authorization);
The response looks similar to the following example:
{
"id": "s-aut-1",
"isSuccess": false,
"isPending": true,
"isError": false,
"isResumed": false,
"redirectUrl": "https://sbx-payment.unzer.com/v1/redirect/paypal/s-ky7ZyEmKNjiC",
"message": {
"code": "COR.000.200.000",
"merchant": "Transaction pending",
"customer": "Your payment is currently pending. Please contact us for more information."
},
"amount": "195.9000",
"currency": "EUR",
"returnUrl": "https://unzer.com/",
"date": "2022-10-31 12:48:12",
"resources": {
"customerId": "",
"paymentId": "s-pay-8537",
"basketId": "s-bsk-12345",
"metadataId": "",
"traceId": "f24acee84b5d28324489a7860bc7ffb2",
"typeId": "s-ppl-skki29gw2fue"
},
"additionalTransactionData": {
"paypal": {
"checkoutType": "express"
}
},
"orderId": "YourOrderId1234",
"paymentReference": "Example Reference",
"processing": {
"uniqueId": "31HA07BC8137FFAF17A41E1EADC47133",
"shortId": "5311.4688.9455",
"traceId": "f24acee84b5d28324489a7860bc7ffb2"
}
}
Fetch the result of POST /payments/authorize
We recommend subscribing to the authorize event to receive notifications about any changes to the authorize transaction. As soon as the event is triggered, you should fetch the authorize transaction and update the order status in your shop according to its status.
Transaction status isResumed:true indicates, customer has successfully approved the order on PayPal payment page, and you can proceed on this transaction.
GET /payments/s-pay-8538/authorize/s-aut-1
Body:
{
}
$unzer = new Unzer('s-priv-xxxxxxxxxx');
$unzer->fetchAuthorization($paymentId);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Charge charge = unzer.fetchAuthorization(paymentId);
The response looks similar to the following example:
{
"id": "s-aut-1",
"isSuccess": true,
"isPending": false,
"isResumed": true,
"isError": false,
"card3ds": false,
"redirectUrl": "https://sbx-payment.unzer.com/v1/redirect/paypalot/s-jjlC9FrQrZ7q",
"message": {
"code": "COR.000.100.112",
"merchant": "Request successfully processed in 'Merchant in Connector Test Mode'",
"customer": "Your payments have been successfully processed in sandbox mode."
},
"amount": "195.9000",
"currency": "EUR",
"returnUrl": "https://unzer.com/",
"date": "2022-10-31 12:59:07",
"resources": {
"customerId": "s-cst-0b5fbac8e1fb",
"paymentId": "s-pay-8538",
"basketId": "s-bsk-5e612a68be0b",
"traceId": "e8664e249367acee81480e9ae4f74b1f",
"typeId": "s-ppl-skki29gw2fue"
},
"additionalTransactionData": {
"paypal": {
"checkoutType": "express"
}
},
"orderId": "YourOrderId1234",
"paymentReference": "YourReference",
"processing": {
"uniqueId": "31HA07BC8137FFAF17A44DC072DB429E",
"shortId": "5311.4754.5055",
"traceId": "e8664e249367acee81480e9ae4f74b1f"
}
}
Fetch the customer data
Fetch the customer resource, which is now linked to the charges transaction. The customer resource is containing complete customer billing and shipping address data provided by PayPal.
For a full description of customer resource, refer to the relevant server-side-integration documentation page: Manage customer.
GET /customers/s-cst-0b5fbac8e1fb
Body:
{
}
$unzer = new Unzer('s-priv-xxxxxxxxxx');
$unzer->fetchCustomer($customerId);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Customer customer = unzer.fetchCustomer(customerId);
The response looks similar to the following example:
{
"id": "s-cst-0b5fbac8e1fb",
"lastname": "Doe",
"firstname": "John",
"salutation": "mr",
"company": "",
"customerId": "",
"birthDate": "1987-12-20",
"email": "John.Doe@unzer.com",
"phone": "+4962216471100",
"mobile": "",
"language": "de",
"billingAddress": {
"name": "John Doe",
"street": "Hugo-Junkers-Str. 5",
"state": "DE",
"zip": "60386",
"city": "Frankfurt am Main",
"country": "DE"
},
"shippingAddress": {
"name": "John Doe",
"street": "Hugo-Junkers-Str. 5",
"state": "DE",
"zip": "60386",
"city": "Frankfurt am Main",
"country": "DE",
"shippingType": "equals-billing"
},
"geoLocation": {
"clientIp": "87.163.144.231",
"countryIsoA2": "DE"
}
}
(Optional) Recalculate the authorize amount
With customer’s address data you can now recalculate the authorize amount, for example, if due to customer’s shipping address higher shipping costs are applied.
Optional: Update basket and authorize amount
If you want to change the initial amount of your authorize transaction, update the basket you’ve used in the POST /payments/authorize request, display the updated basket and final authorize amount to the customer. Ask the customer to finally confirm the changes.
Refer to the Update a basket guide to learn more on how to update basket resources.
Make a PATCH /payments/{paymentId}/authorize
Once the customer has finally confirmed the changed amount in your shop, make a PATCH on the initially created authorize transaction to complete the PayPal Express transaction.
Provide the updated authorize amount in the PATCH /payments/{paymentId}/authorize request.
An updated basket don’t need to be provided again, the basketId incl changes you’ve applied will be considered automatically.
With a successful PATCH /payments/{paymentId}/authorize the transaction status is set on success now - the payment status remains on pending as long as no charge has been initialized.
PATCH /payments/{paymentId}/charges with an empty body.PATCH /payments/s-pay-8538/authorize
Body:
{
"amount": "210.90"
}
$unzer = new Unzer('s-priv-xxxxxxxxxx');
$authorize = new Authorization(210.9);
$unzer->updateAuthorization($paymentId, $authorize);
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Authorization authorization = unzer.fetchAuthorization(paymentId);
authorization.setAmount(BigDecimal.valueOf(210.9));
unzer.updateAuthorization(authorization);
The response looks similar to the following example:
{
"id": "s-aut-1",
"isSuccess": true,
"isPending": false,
"isError": false,
"card3ds": false,
"redirectUrl": "https://sbx-payment.unzer.com/v1/redirect/paypalot/s-jjlC9FrQrZ7q",
"message": {
"code": "COR.000.100.112",
"merchant": "Request successfully processed in 'Merchant in Connector Test Mode'",
"customer": "Your payments have been successfully processed in sandbox mode."
},
"amount": "210.9000",
"currency": "EUR",
"returnUrl": "https://unzer.com/",
"date": "2022-10-31 12:59:07",
"resources": {
"customerId": "s-cst-0b5fbac8e1fb",
"paymentId": "s-pay-8538",
"basketId": "s-bsk-5e612a68be0b",
"metadataId": "",
"payPageId": "",
"traceId": "e8664e249367acee81480e9ae4f74b1f",
"typeId": "s-ppl-skki29gw2fue"
},
"additionalTransactionData": {
"paypal": {
"checkoutType": "express"
}
}
"orderId": "YourOrderId1234",
"paymentReference": "YourReference",
"processing": {
"uniqueId": "31HA07BC8137FFAF17A44DC072DB429E",
"shortId": "5311.4754.5055",
"paypalBuyerId": "JHQEVB7FYWUHW",
"traceId": "e8664e249367acee81480e9ae4f74b1f"
}
}
For a full description of the authorize transaction, see the relevant server-side-integration documentation page: Authorize a payment (direct API calls), Authorize a payment (PHP SDK), Authorize a payment (Java SDK).
Charge the authorization
Charge the authorizationBecause the customer already accepted the order with the authorize transaction, you can now charge the payment to transfer the money.
When you authorize before charge, the authorized amount is on hold for 29 days. Paypal however, recommends to charge the funds within the three-day honor period. The success of the charge is subject to risk and availability of funds on the authorized payment instrument.
POST https://api.unzer.com/v1/payments/s-pay-1/charges
{
"amount": 20,
"paymentReference": "Test charge transaction"
}
$unzer = new Unzer('s-priv-xxxxxxxxx');
$charge = $unzer->performChargeOnPayment('s-pay-1', new Charge());
Unzer unzer = new Unzer("s-priv-xxxxxxxxxx");
Charge charge = unzer.chargeAuthorization("s-pay-1");
The response looks similar to the following example:
{
"id": "s-chg-1",
"isSuccess": true,
"isPending": false,
"isError": false,
"message": {
"code": "COR.000.100.112",
"merchant": "Request successfully processed in 'Merchant in Connector Test Mode'",
"customer": "Your payments have been successfully processed in sandbox mode."
},
"amount": "20.0000",
"currency": "EUR",
"date": "2021-06-04 15:33:16",
"resources": {
"customerId": "s-cst-d6ae94482612",
"paymentId": "s-pay-1",
"basketId": "",
"metadataId": "",
"payPageId": "",
"traceId": "4ebfbfcae640ff2823e2e65febd54037",
"typeId": "s-ppl-4x3zn7i4bxyh"
},
"paymentReference": "Test charge transaction",
"processing": {
"uniqueId": "31HA07BC8198C2F9107E38AF14C45201",
"shortId": "4867.4719.2103",
"traceId": "4ebfbfcae640ff2823e2e65febd54037"
}
}
Step 4: Check status of the paymentserver side
Once the customer is redirected to the returnUrl, you can fetch the payment details from the API, by using the resources.paymentId from the charge response above to handle the payment according to its status. If the status of the payment is completed, the payment process has been finished successfully and can be considered as paid. Check all possible payment states here.
GET https://api.unzer.com/v1/payments/{payment_ID}
{
"id": "s-pay-131937",
"state": {
"id": 1,
"name": "completed"
},
"amount": {
"total": "20.0000",
"charged": "20.0000",
"canceled": "0.0000",
"remaining": "0.0000"
},
"currency": "EUR",
"orderId": "",
"invoiceId": "",
"resources": {
"customerId": "",
"paymentId": "s-pay-131937",
"basketId": "",
"metadataId": "",
"payPageId": "",
"traceId": "70ddf3152a798c554d9751a6d77812ae",
"typeId": "s-ppl-grpucjmy5zrk"
},
"transactions": [
{
"date": "2021-05-10 00:51:03",
"type": "charge",
"status": "success",
"url": "https://api.unzer.com/v1/payments/s-pay-131937/charges/s-chg-1",
"amount": "20.0000"
}
]
}
Step 5: Display the payment resultclient side
Step 5: Display the payment result [client side]Use the information from the Check status of the payment step to show payment result to your customer.
This can be the success or error page of your shop. If something went wrong you can use the client message from the API response and show it to the customer.
Notifications
NotificationsWe recommend subscribing to the payment event to receive notifications about any changes to the payment resource. As soon as the event is triggered you should fetch the payment and update the order status in your shop according to its status.
{
"event":"payment.pending",
"publicKey":"s-pub-xxxxxxxxxx",
"retrieveUrl":"https://api.unzer.com/v1/payments/s-pay-774",
"paymentId":"s-pay-774"
}
For more details on implementing webhooks to receive notifications, see Notifications page.
Error handling
Error handlingAll requests to the API can result in an error that should be handled. Refer to the Error handling guide to learn more about Unzer API (and other) errors and handling them.
Hands-on example
The following example contains a sample integration.
Select Edit in JSFiddle to try out the checkout component for yourself. Just replace the publicKey="INSERT YOUR SANDBOX PUBLIC KEY" with your sandbox public key and then select Run.
You can find your sandbox keys in Unzer Insights.
Once you submit the payment, you can view the payment ID in the console of the browser. For example, --- success paymentId s-ppl-00naqzqml7ex.
<script
type="module"
src="https://static-v2.unzer.com/v2/ui-components/index.js"
></script>
<div class="box">
<!-- id: Unique HTML identifier for the payment component. -->
<!-- publicKey: Merchant public key provided by Unzer. Replace with your own Unzer public key. -->
<!-- locale: Defines the language of the payment component interface and error messages(e.g., "de-DE" for German, "en-EN" for English). If not set, defaults to browser language. -->
<unzer-payment
id="unzer-payment"
publicKey="INSERT YOUR SANDBOX PUBLIC KEY"
locale="de-DE"
>
<unzer-paypal-express></unzer-paypal-express>
</unzer-payment>
<div class="pay-button">
<unzer-checkout id="unzer-checkout"
><button type="submit" id="yourPaymentButtonId">
Pay
</button></unzer-checkout
>
</div>
</div>
Promise.all([customElements.whenDefined('unzer-payment'), customElements.whenDefined('unzer-paypal-express')]).then(() => {
const unzerCheckout = document.getElementById('unzer-checkout');
unzerCheckout.onPaymentSubmit = response => {
if (response.submitResponse && response.submitResponse.success) {
/* Submit the ID of the created payment type resource to your server side integration to perform the payment transaction. */
const paymentId = response.submitResponse.data.id;
console.log("--- success paymentId", paymentId);
const saveInfoValue = response.saveInfoValue; /* If saveInfoValue == true, you can use the masked submitResponseData for storage in your server side integration and reuse for future transactions for this customer. */
} else {
/* Handle resource creation error */ }
};
}).catch(error => {
/* Handle loading and initialization error */
console.log("--- initialization error", error);
});
:root {
/* Font family */
--unzer-font: SFMono;
/* Brand color */
--unzer-brand-color: #ee1818;
/* Main text color */
--unzer-text-color: #f19316;
/* Background Color */
--unzer-background-color: #6a9472;
/* Link color */
--unzer-link-color: #1330ef;
/* Corner radius in pixels */
--unzer-corner-radius: 0;
/* Enables or disables shadows (1 or 0) */
--unzer-shadows: 1;
}
.box {
/* Changes the box width */
max-width: 300px;
/* Sets horizontal alignment */
margin: auto;
}
.pay-button {
/* Makes the button container full width and centers its content horizontally */
width: 100%;
display: flex;
justify-content: center;
margin-top: 8px;
}
#yourPaymentButtonId {
/* Button background color */
background: #2986e2;
/* Button text color */
color: #fff;
/* Removes border */
border: none;
/* Makes button corners fully rounded */
border-radius: 24px;
/* Button padding */
padding: 10px 32px;
/* Button font size */
font-size: 1rem;
/* Button font weight */
font-weight: 600;
/* Button font family */
font-family: 'Segoe UI', 'Arial', 'Helvetica Neue', 'sans-serif';
/* Shows pointer cursor on hover */
cursor: pointer;
/* Removes outline on focus */
outline: none;
/* Minimum button width */
min-width: 100px;
/* Minimum button height */
min-height: 40px;
/* Makes the button respect width/height and allows centering in flex */
display: inline-block;
}
#yourPaymentButtonId:hover {
/* Button background color on hover */
background: #1565c0;
}
Test & go live
Test & go liveYou should always test your integration before going live. First perform test transactions using test data. Next, check against Integration checklist and Go-live checklist to make sure the integration is complete and you’re ready to go live.
