Skip to main content

Nirvana Lab

Home / Blog / The Ultimate Guide: Integrating Square Payments with Liferay Commerce (Client Extensions) 
Table of Contents

The Ultimate Guide: Integrating Square Payments with Liferay Commerce (Client Extensions) 

Square Payment for Liferay Comerce

Integrating an external payment gateway with Liferay Commerce involves more than simply calling a payment API. A production-ready implementation must also handle payment configuration, secure communication, callback processing, status synchronization, and multi-channel requirements.

In this implementation, we integrated Square Payments with Liferay Commerce using a Spring Boot Client Extension.

The objective was to keep the Square-specific integration outside the Liferay runtime while allowing Liferay Commerce to continue managing the checkout and order lifecycle.

This article explains the architecture, payment flow, webhook handling, and a few of the important design decisions involved in the implementation.

Liferay 7.4 introduced a massive paradigm shift in how we extend the platform: Client Extensions. By moving custom logic out of Liferay’s core OSGi container and into decoupled, cloud-native microservices, developers gain unprecedented flexibility.

In this comprehensive guide, we will walk through exactly how we built and deployed a production-ready Square Payment Integration for Liferay Commerce using a Spring Boot 3 Microservice.

Whether you are deploying to Liferay Cloud (LXC), a custom Kubernetes cluster, or an on-premise standalone server, this guide covers every configuration step, environment variable, and troubleshooting trick you need to know.

Prerequisites

Before you begin, ensure you have the following: 

  • Liferay Environment: Liferay DXP 7.4+ with Liferay Commerce enabled. 
  • Java: Java 21+ (Required for the Spring Boot 3 microservice). 
  • Square Account: A Square Developer Account (with Sandbox credentials for QA, and Production credentials for live deployments). 

Key Features

  • Native Commerce Integration: Seamlessly appears as a standard Payment Method within Liferay Commerce Channels. 
  • Webhook Failsafe: Ensures zero dropped orders. If a user completes a payment but their browser crashes or fails to redirect back to Liferay, Square’s asynchronous webhooks are securely captured, verified via HMAC-SHA256 signatures, and processed by the connector to finalize the Liferay order. 
  • Order ERC Synchronization: Automatically binds the Liferay Order External Reference Code (ERC) to the exact Square Order ID for permanent, reliable cross-referencing. 
  • OAuth2 Secured: All internal HTTP communication from Liferay to the microservice is fortified using OAuth2 Headless Server authentication. 
  • Cloud-Native Architecture: Designed for Liferay Cloud. Connects natively to LXC Config Trees without requiring hardcoded Docker environments. 
  • Sandbox Support: Easily toggle between Square Sandbox (QA) and Square Production. 

Why We Used a Spring Boot Client Extension

Liferay Client Extensions provide a clean way to connect Liferay with external services without deploying custom business logic directly inside the Liferay JVM.

For the Square payment integration, the Spring Boot Client Extension acts as an integration layer between Liferay Commerce and Square.

At a high level, the flow is:

Why We Used a Spring Boot Client Extension

The responsibilities are separated clearly. 

Liferay Commerce manages: 

  • Commerce orders 
  • Checkout 
  • Payment method configuration 
  • Commerce channels 
  • Payment lifecycle 

 

The Spring Boot Client Extension manages: 

  • Receiving payment requests from Liferay 
  • Reading Square configuration 
  • Calling Square APIs 
  • Returning the checkout URL 
  • Receiving Square webhooks 
  • Validating webhook requests 
  • Updating the corresponding payment state 

 

Square remains responsible for the actual payment processing. 

Configuring Square as a Commerce Payment Method

The Square payment method is configured in Liferay Commerce and can be enabled for the required Commerce channel.

One of the important requirements was to avoid hardcoding Square credentials inside the Spring Boot application.

Instead, payment configuration can be maintained as part of the payment method settings.

Depending on the implementation, the configuration can contain values such as: 

  • Square Application ID 
  • Square Access Token 
  • Square Location ID 
  • Environment 
  • Webhook-related configuration 

 

This is especially useful when the same Liferay installation contains multiple Commerce channels. 

For example: 

  • Channel A → Square Account A 
  • Channel B → Square Account B 
  • Channel C → Square Account C 

 

The same Spring Boot Client Extension can therefore be reused while each Commerce channel maintains its own Square configuration. 

Payment Initiation Flow

When the customer selects Square during checkout, Liferay invokes the payment Client Extension. The Spring Boot application receives the payment request along with the information needed to initiate the transaction.

The general flow is:

Payment Initiation Flow

The Spring Boot service prepares the Square request using information such as the order amount, currency, order reference, and channel-specific Square configuration.

Square then returns the hosted payment URL.

The Client Extension sends the required response back to Liferay, allowing the customer to continue the payment process on Square's hosted checkout page.

Why Hosted Checkout Is Useful

Using a payment page hosted by Square keeps sensitive payment details outside the Liferay application. Instead of collecting card information directly inside Liferay, the customer enters the payment information on Square's infrastructure.

This simplifies the integration and reduces the amount of sensitive card-related data handled by the Liferay application and the Client Extension.

The flow becomes:

Why Hosted Checkout Is Useful

Once the customer completes or cancels the transaction, the user can be redirected back to the appropriate Liferay page. However, that browser redirect should not be treated as the authoritative payment confirmation.

Handling Square Webhooks

The asynchronous webhook flow is one of the most important parts of the integration.

After a payment event occurs, Square sends a request to the webhook endpoint exposed by the Spring Boot Client Extension.

Handling Square Webhooks

A webhook event can contain information such as: 

  • Square payment ID 
  • Square order ID 
  • Payment status 
  • Event type 
  • Merchant-related information 

 

The Client Extension uses this information to identify the corresponding Liferay Commerce transaction and update the payment status. 

Validating That the Webhook Really Came from Square

A public webhook endpoint can technically be called by anyone.

For example, someone could send a request from Postman and manually add a header named: x-square-hmacsha256-signature

Therefore, checking whether the header exists is not sufficient.

The signature itself must be validated.

The general validation process is:

Validating That the Webhook Really Came from Square

Correlating Square Payments with Liferay Orders

Another important requirement is maintaining a reliable relationship between the Square transaction and the Liferay Commerce order.

During payment creation, the integration should maintain identifiers that make later reconciliation possible.

Typical values include: 

  • Liferay Commerce Order ID 
  • Liferay Payment Transaction ID 
  • Commerce Channel ID 
  • Square Order ID 
  • Square Payment ID 

 

This correlation is particularly important when the webhook arrives because the webhook originates from Square, not from Liferay. 

 

The integration needs to determine which Liferay order and Commerce channel belong to the incoming Square event. 

 

A stable correlation mechanism also helps during troubleshooting and payment reconciliation. 

Multi-Channel Payment Configuration

Multi-channel support adds another architectural consideration. During the initial Liferay-to-Client-Extension request, channel-specific configuration can be available to the payment integration.

However, when Square later calls the webhook, the request originates directly from Square.

Therefore, the webhook does not automatically include the Liferay payment method configuration.

The application must use available identifiers, such as the Square transaction or Liferay order reference, to resolve the correct configuration.

This is an important consideration when different Commerce channels are connected to different Square accounts.

A conceptual mapping can look like this:

Multi-Channel Payment Configuration

This makes webhook processing deterministic and prevents the application from using the wrong Square account configuration. 

Payment Status Should Not Depend on Browser Redirects

After completing a payment, Square can redirect the customer back to Liferay.

That redirect is useful for the customer experience, but it should not be considered final proof that payment succeeded.

The browser is client-controlled.

Therefore: 

  • Browser Redirect = Navigation / User Experience 
  • Webhook or Square API = Payment Confirmation 

 

The authoritative payment state should come from a validated server-to-server source. 

 

This is particularly important when updating a Liferay Commerce payment from pending to completed. 

Duplicate Webhook Handling

Payment providers can retry webhook delivery.

As a result, the same event may reach the Client Extension more than once.

The integration should therefore be idempotent.

For example:

Duplicate Webhook Handling

This prevents the same Square event from triggering duplicate payment updates. 

  

Idempotency is especially important for payment integrations because network retries and temporary service failures are normal operational scenarios. 

Error Handling and Observability

Payment integrations should also be designed for situations where external systems are temporarily unavailable.

Some scenarios that should be considered include:

  • Square API timeout 
  • Invalid Square configuration 
  • Customer abandons checkout 
  • Payment is declined 
  • Duplicate webhook delivery 
  • Invalid webhook signature 
  • Liferay is unavailable when a webhook arrives 
  • Payment succeeds but Liferay update temporarily fails 

 

Logging should make it possible to trace a transaction across systems. 

Useful identifiers include: 

  • Liferay Order ID 
  • Channel ID 
  • Square Order ID 
  • Square Payment ID 
  • Webhook Event ID 
  • Correlation ID 

 

At the same time, secrets such as Square access tokens, webhook signature keys, or authentication headers should never be written to application logs. 

Final Architecture

Unlike legacy OSGi plugins that ran inside Liferay, this integration consists of two decoupled pieces:

  1. Liferay Client Extension YAML: A simple configuration file deployed to Liferay that registers the Square Payment method and sets up the secure OAuth2 routing rules.
  2. Spring Boot Microservice: A standalone Java application that receives the checkout payload from Liferay, communicates with the Square API, and securely pushes order updates back to Liferay Headless APIs. 

     

    Data flows in both directions:
  • Liferay -> Spring Boot: When a user clicks “Checkout”, Liferay uses an OAuth2 User Agent application to securely route the payload to the microservice. 
  • Spring Boot -> Liferay: The microservice uses an OAuth2 Headless Server application (Client ID/Secret) to authenticate back to Liferay to update the Order Status. 

 

The overall implementation can be summarized as follows: 

The asynchronous payment update works in the opposite direction:

Final Architecture 2

This architecture keeps Liferay focused on the commerce lifecycle while the Client Extension handles Square-specific integration logic.

Step-by-Step Deployment Guide

Step 1: Configure the Routing Dynamically

Unlike legacy configurations where URLs were hardcoded in source files, you do not need to modify the client-extension.yaml file for your specific environment (QA vs Prod).

Simply deploy the client-extension.yaml as-is. Once it deploys, Liferay generates the OAuth2 applications. You can then dynamically configure the routing directly from the Liferay Admin UI:

  1. Go to Control Panel → Security → OAuth2 Administration. 
  2. Click Edit on the Square Payment Backend OAuth Application User Agent. 
  3. Update the Website URL field to point to your microservice: 
  • For On-Premise Kubernetes: Use your internal K8s Service Name (e.g., http://nl-liferay-dxp-square-payment-api-svc:8080). This completely bypasses external Load Balancers and avoids frustrating Hairpin NAT (Loopback) connection timeouts! 
  • For External Standalone/VMs: Use your public microservice URL (e.g., https://payment-api.mycompany.com). 

Pro-Tip: Updating the Website URL directly from the Liferay UI allows you to instantly change your routing on the fly without ever needing to redeploy the Client Extension YAML! 

10-Square OAuth Detail

Step 2: Deploy to Liferay and Gather Credentials

  1. Zip up your client extension workspace (or just the YAML) and deploy it to Liferay’s deploy/ folder.
  2. Go to Control Panel → Security → OAuth2 Administration.
  3. You will see two new applications generated by Liferay: a Headless Server and a User Agent.
  4. Click Edit on the Headless Server application and copy the Client ID and Client Secret.

Step 3: Run the Spring Boot Microservice

Deploy your Spring Boot .jar via Docker, Kubernetes, or standalone Java.

CRITICAL ENVIRONMENT VARIABLES:

To allow your Spring Boot code to securely call Liferay's Headless APIs, you must inject the Headless Server credentials into the container environment. The User Agent credentials are NOT required for the Spring Boot side. You must also inject your Square Webhook Signature Key (more on this in Step 4).

    square-payment-backend-oauth-application-headless-server.oauth2.headless.server.client.id=YOUR_HEADLESS_CLIENT_ID     
    square-payment-backend-oauth-application-headless-server.oauth2.headless.server.client.secret=YOUR_HEADLESS_CLIENT_SECRET     
          
    square.webhook.signature.key=YOUR_SQUARE_WEBHOOK_SIGNATURE_KEY     
          
    com.liferay.lxc.dxp.mainDomain=apps.yourdomain.com     
    com.liferay.lxc.dxp.server.protocol=https  

Configuring Square & Webhooks

Step 4: Webhook Configuration (The Failsafe)

Webhooks guarantee your Liferay orders are finalized even if the buyer closes their browser immediately after paying.

  1. Log into the Square Developer Dashboard. 
  2. Navigate to Webhooks → Subscriptions → Add Subscription. 
  3. Set the webhook URL to your Spring Boot microservice (e.g., https://your-domain.com/square/webhook).
  4. Select the payment.updated event, save, and copy the Webhook Signature Key.
  5. Add this key to your Spring Boot environment variables (as shown in Step 3). 

Step 5: Liferay Commerce Channel Settings

Finally, link your Square credentials to your Liferay storefront.

  1. In Liferay, navigate to Commerce → Channels → Payment Methods.
  2. Locate Square Payment and toggle the status to Active.
  3. Click the Configuration tab and paste your settings into the Type Settings box:

square.access.token=YOUR_SQUARE_ACCESS_TOKEN  

square.location.id=YOUR_SQUARE_LOCATION_ID     
square.api.version=2026-05-20     
square.currency=USD     
square.payment.link.api.url=https://connect.squareup.com/v2/online-checkout/payment-links  

THE SANDBOX TRAP:

If you are testing this integration using a Square Sandbox Access Token (which usually starts with EAAAl), you must change the api.url in the settings above to https://connect.squareupsandbox.com/v2/online-checkout/payment-links.

If you accidentally pass a Sandbox token to Square's production URL, Square will reject it with a 401 UNAUTHORIZED error, and Liferay will display a generic "An error occurred" message on the checkout page!

Square channels payment method
Speedwell portal

Frequently Asked Questions

Architecture & Design

Why use a Client Extension instead of implementing Square directly inside Liferay?

A Spring Boot Client Extension keeps external payment logic outside the Liferay JVM. It can be deployed, maintained, scaled, and upgraded independently while still integrating with the Liferay Commerce payment lifecycle. 

Yes. Channel-specific payment configuration can be maintained so that different Commerce channels can connect to different Square accounts while using the same Client Extension.

No. The presence of the header alone does not prove that the request originated from Square. The received signature must be cryptographically validated using Square’s webhook signature verification mechanism.

Yes, anyone who knows the public webhook URL can technically send an HTTP request to it. However, without a valid Square-generated signature, the request should fail signature validation and must not be processed.

Not necessarily for every event. A correctly validated webhook can be used to process the payment event. However, in some scenarios, retrieving the payment from Square again can provide an additional verification layer for status, amount, or transaction reconciliation.

No. The browser redirect should mainly control the customer experience. Payment completion should be based on trusted server-to-server confirmation, such as a validated Square webhook or Square API response.

The integration should maintain a correlation between Liferay identifiers and Square transaction identifiers when the payment is created. This allows the Client Extension to resolve the correct Commerce order when a webhook arrives.

The webhook handler should be idempotent. Already processed events or payment states should be detected so that the same payment update is not executed multiple times.

Deployment & Troubleshooting

Why was the Webhook Signature Key moved to Environment Variables?

Webhooks are triggered directly by Square, bypassing Liferay completely. Because Square doesn’t know about Liferay’s “Type Settings” payload, the Spring Boot application must read the Webhook Signature Key directly from its own environment variables to cryptographically verify the incoming Square request.

No. Spring Boot only uses the Headless Server credentials to authenticate its outbound calls back to Liferay (to update the order status). The User Agent credentials are used internally by Liferay to route the payload to Spring Boot.

No! This is a harmless warning from the com.liferay.client.extension.spring.boot starter library. It complains if it doesn’t find a global default client ID. As long as you provided the headless-server environment variables correctly and your application accepts requests, this warning can be safely ignored.

For security reasons, Liferay intentionally hides raw payment gateway errors (like “Invalid API Key”) from the frontend customer. To debug, check the logs of your Spring Boot microservice. You will see the exact JSON error returned by Square (e.g., UNAUTHORIZED if your token is invalid or belongs to the wrong Sandbox/Production environment).

This happens if your Ingress/Load Balancer is not stripping the URL prefix. The Spring Boot application strictly listens on the root path / (e.g., /ready). If your reverse proxy forwards the request as /square-payment/ready, Spring Security will reject the unknown path with a 401 Unauthorized. To fix this, configure your Ingress to strip the prefix (Rewrite Target) before forwarding to the container.

It will cause routing conflicts. You must uninstall or deactivate any old OSGi modules (com.nirvanalab.commerce.payment.method.square) from the Liferay App Manager to ensure Liferay strictly invokes the new Client Extension.

Author