Broadleaf Microservices
  • v1.0.0-latest-prod

Creating a Payment Gateway Module

Before you begin, it is recommended that you fully understand how gateways work and the context of how these gateway integration libraries are used, before attempting to implement any of the Broadleaf interfaces. Please make sure to review the following docs to aid in this process:

Where to place your implementation?

When building a PaymentGatewayCommon implementation, we typically do so in an independent project, stored in a new repository. From there, we build a maven dependency & add it to PaymentTransactionServices to make all of the integration’s Spring beans & resources available. The independent project & repository help us to manage various versions of the integration, but this is optional and can be simplified in your integration.

Alternatively, you could introduce a new module in your project or implement the necessary interfaces directly in your PaymentTransactionServices project. With either route, you’ll need to add the PaymentGatewayCommon dependency to expose the relevant interfaces.

<dependency>
    <groupId>com.broadleafcommerce.microservices</groupId>
    <artifactId>broadleaf-payment-gateway-common</artifactId>
</dependency>

What interfaces to implement?

Review the list of PaymentGatewayCommon interfaces to get a better sense of the interfaces that you’ll need to implement.

Conventions & Implementation Considerations

Package Structure

  • All implementations of the Payment Gateway Common interfaces will be under com.broadleafcommerce.payment.service.gateway

  • All gateway specific services and constants will be under com.broadleafcommerce.vendor.<replace_with_gateway_name>.service

  • All gateway specific web components such as Spring MVC Controllers and Thymeleaf Processors will go under com.broadleafcommerce.vendor.<replace_with_gateway_name>.web

Using SDKs Provided by the Gateway Implementation

Some gateways don’t publish their Java SDK on Maven Central, so you may need to install it locally. In those cases, we suggest noting in your documentation that there is a dependency on their JAR for compilation.

Gateway Type & the GatewayTypeAware Interface

When implementing the PaymentGatewayCommon interfaces, it’s important to use a consistent gatewayType value. When PaymentTransactionServices performs an action against a Payment, it uses the gatewayType to identify the correct implementation of each interface.

Configuring your Integration

To interact with the payment gateway’s APIs, you’ll need to provide some form of authentication. We typically provide these values via Spring configuration properties using the following path: broadleaf.{my-gateway-name}.*.

To allow for differing values per tenant or application, we have our @ConfigurationProperties-annotated class extend the DiscriminatedProperties class.

Request Validation

When handling requests into the PaymentGatewayCommon interfaces (esp. PaymentGatewayTransactionService), it’s best to ensure that required request parameters are present. If they are not present, then it’s best to fail at the beginning of the request.

Error handling

  • Exceptions should never come out of the PaymentGatewayTransactionService. Instead, return a PaymentResponse.

  • You must consider when an error/exception occurs

    • During request creation is usually harmless

    • During gateway interaction leads to an unknown transaction state

    • During interpretation of the gateway response represents a transaction with an unknown result

  • "Catch-all" catch blocks (ie catch (Exception e)) are good for completely unknown exceptions, but make sure to use plenty of catch blocks that handle specific/known error scenarios.

  • When catching/handling exceptions, be very cautious about the contents of the exception’s message. Unless you’re 100% sure that it doesn’t, then you should assume that the exception message has sensitive data in it that you don’t want to log, persist, return in response payloads, etc.

    • Don’t divulge sensitive data in logs, exceptions, or error messages

    • This is always a balance between protecting potentially sensitive data & having good data for debugging if something goes wrong. Do your best to strike a good balance.

    • To help with this, think about error/exception handling at a granular level. For example, if your try/catch scopes are small, then you can be more confident in whether the caught exception has sensitive data. From there, you can provide a more meaningful error message, & you can likely better manage the implications of the exception.

  • When creating new error/exception messages, think about how you can link it to the overall process or to a specific entity to help with debugging. Including the transactionReferenceId and/or gatewayTransactionId is especially helpful!

Use AbstractExternalPaymentGatewayCall

In a lot of cases, you may notice that a lot of the transaction methods share common code to create a request that then calls an external API or SDK and parses the response. If this is the case, you may wish to create a parent class that all these extend to unify this boiler-plate code.

It’s also important to extend AbstractExternalPaymentGatewayCall if your service makes an SDK or external API call. This allows anyone using the framework to configure the ServiceMonitor AOP hooks and detect any outages to provide (email/logging) feedback when necessary. You can look at the JavaDocs on that class for further examples and documentation.

Capture the Raw Response on the PaymentResponse DTO

It’s often very helpful and in some cases required to capture the entire response back from a gateway. Make sure to serialize all the information coming back from the gateway in the rawResponse field. You may wish to use a PaymentGatewayWebResponsePrintService to translate an HttpServletRequest into a rawResponse string.

Use a MessageConstants Class

Create a MessageConstants class that define the gateway specific constants that you pass between the additional fields map of the request and response DTOs to the gateway services.

Integrate your module into PaymentTransactionServices

Once the necessary interfaces have been implemented and their Spring beans have been registered, then you’ll simply need to add the module’s Maven dependency to your PaymentTransactionServices project & provide any relevant configuration properties. From there, PaymentTransactionServices automatically picks up the relevant service beans and is able to process transactions for your gateway.

Debugging

If you’ve followed the package structure outlined above, it is helpful to enable trace debugging for the following:

<logger name="com.broadleafcommerce.payment.service.gateway"><level value="TRACE"/></logger>
<logger name="com.broadleafcommerce.vendor"><level value="TRACE"/></logger>