Developing a Payment Driver For Helium Rapid


Overview

Helium provides a payment framework with standardised functionality to:

  • Integrate with third party payment gateways
  • Configure DSL apps for payment
  • Trigger payments from DSL apps
  • Post payment statuses back to DSL apps
  • Keep an audit trail of payments and report on account specific payments



Developing Payment Drivers


Development Process

To develop and test any Helium features locally a local Helium instance will be required. The following setup guide can be used as reference: /wiki/spaces/HP/pages/5075618

In general, any change made to the Helium codebase requires a feature branch to be made from the develop branch. Ideally, the branch name should include a relevant Jira ticket number.

Changes can then be made to the feature branch and once implemented a pull request to the develop branch needs to be submitted to the Helium team lead.

Once a feature has been implemented, tested successfully and adopted into Helium, the deployment of the new feature can be scheduled with the Helium team.

To allow beta testing and reduce risks for new deployments the Helium dev team has a fixed time slot and process for deployments:

  • First Thursday of every month: Beta deployment to all rc, dev and preprod environments
  • Second Thursday of every month: Deployment to all production environments

The implementation should conform the the standards defined here: /wiki/spaces/TPA/pages/5865762



General Payment Workflow

The payment framework supports a general workflow for payment integrations that is as follows:

  • Payment request is sent to payment gateway
  • A synchronous response is received by Helium. If the response indicates a failure the payment is mark as failed but updating the payment status to "Manual".
  • If the payment has not failed the payment gateway posts async responses back to Helium callback URL
  • With each async response Helium updates the payment status internally
  • Relevant payment status updates are posted back to the DSL app until the payment is completed or fails

For more information on how the above integrates with a Helium Rapid DSL app see the following tutorial documentation: Lesson 12: Payments, Widget Visiblity

Validate Payment Workflow

The payment framework supports an optional validation workflow for payment integrations that is as follows:

  • Payment request is sent to payment gateway
  • A synchronous response is received by Helium. If the response indicates a failure the payment is mark as failed but updating the payment status to "Manual".
  • Helium wait for a validation request on {gatewayName}/validate endpoint. A custom response can be sent back to the payment gateway. 
  • If the payment has not failed the payment gateway posts async responses back to Helium callback URL
  • With each async response Helium updates the payment status internally
  • Relevant payment status updates are posted back to the DSL app until the payment is completed or fails

For more information on how the above integrates with a Helium Rapid DSL app see the following tutorial documentation: Lesson 12: Payments, Widget Visiblity



Code Additions

In order to develop a payment driver, additions need to be made to the files, packages and projects in Helium's codebase described in the next section.

The following change sets in Helium's codebase represent previously added payment drivers and can be used as additional reference material:



Configuration Specific

Most base model entities that will be required by a new payment driver is already in Helium in the pay-model-lib-legacy project. In addition to those already available, the following driver specific model additions are required to pay-model-lib-legacy:

com.mezzanine.helium.pay.model.config:

  • A driver specific config needs to be added to this package
  • It should be split into an interface and implementation
  • The interface should extend the existing PaymentConfiguration base interface
  • The interface should also be added as a Json subtype to the base PaymentConfiguration
  • Any sensitive values such as payment account specific values should make use of crypto types that are built into Helium such as CryptoString.
  • Any additional model entities or enums that are required should also be added to this package
  • See the existing classes in this package for examples

com.mezzanine.helium.pay.model.result:

  • A driver specific async response needs to be added to this package
  • It should be split into an interface and implementation
  • The interface should extend the existing PaymentInstallmentResult base interface

com.mezzanine.helium.pay.model.visitor:

  • Appropriate additions should be made to the PayEntityVisitor and StubPayEntityVisitor classes in this package

Considerations for Configurability

It should be noted that payment configurations can be updated using an API without any downtime to Helium. It's therefore important to keep the following in mind:

  • Any account specific details should be configurable and not hard coded in drivers
  • If a gateway supports a sandbox integration, a flag should be added to the configuration to indicate whether the config represents a sandbox or non-sandbox integration
  • In general, URLs that are integrated with should be configurable
  • If a driver for a payment Gateway can be reused for different payment types, the payment type can also be added as a configurable value
  • Any other value that might change or that will be differ between applications making use of the same driver should be configurable

Enable validate payment flow

  • Set validationRequired to true from your *PaymentConfigure class that extends AbstractPaymentConfiguration
  • Implement a factory in PaymentValidationBuilderFactoryFactory for your payment driver. See  SafaricomMpesaRestValidationFactory as an example.
  • Create your own builder that implements PaymentValidationResultBuilder interface. See SafaricomPaymentValidationResultBuilder as an example.
  • if required PaymentValidateResponse class can be extend to return a custom response.



Outbound Driver Specific

In order to add a payment driver the following code additions need to be made to the Helium-ejb project:

com.mezzanine.helium.ea.service.payment.driver:

  • A new driver that contains the actual integration logic needs to be added to this package.
  • The driver class should extend the existing PaymentDriver class parameterised with the type of configuration applicable to the driver. See previous section for details on implementing configuration classes.
  • A private inner class called SingletonContainer that instantiates a single static final instance of the driver class should be added
  • A public static method called getInstance that return the above mentioned driver instance should also be added

com.mezzanine.helium.ea.service.payment.strategy:

  • Appropriate updates need to be made to the InitiateStrategy class. This will allow the driver to be called from rest of the payment framework.
  • Appropriate updated need to be made to the ResultStrategy class. This will allow payment status transitions for the driver to be handled.
  • Both of these classes need to implement the existing PaymentStrategy interface.



Gateway Result Specific

In order for async result callbacks from the payment gateway back to Helium to be supported, the following classes in the Helium-war project need to be updated:

  • PaymentResultBuilderFactoryFactory
  • SupportedPaymentGateways



Manual Payment Status Result Requests

Since Helium 1.48, framework support to manually trigger a payment status check from the DSL is also available. See HE-10562 for details and links to documentation.

As part of this an additional updateStatus method needs to be implemented in the payment driver class.

In addition, relevant updates to PaymentStatusRequestAsyncDelegate needs to be made supported by a relevant ...PaymentStatusRequestResultBuilderFactory implemented in the com.mezzanine.helium.ea.service.payment.strategy.result package of the Helium-ejb project. See MtnMomoPaymentStatusRequestResultBuilderFactory as an example.


Testing Payment Drivers


Unit Tests

As best practice, each payment driver requires a unit / integration test that can be used to test different use cases of payments through the drivers. Tests for payment drivers need to be added to the Helium code base to test package com.mezzanine.helium.ea.service.payment.driver. See existing tests for examples.



Payment Test App

In order to test payments locally the following test payment app can be used:

https://bitbucket.org/mezzanine-helium/helium-payment-app/src/master/

The app can be deployed using the "Administrator" role and in addition to deploying the app locally it has to be configured for payments.

For a quick summary of the different payment configs that Helium requires / exposes see the following document: /wiki/spaces/HP/pages/5074892

The curl calls below demonstrates how this specific app can be configured for payments. The API client credentials set up as part of the Helium Setup Guide can be used for the calls below.

1) Configure the payment config grouping for the app

curl -u 'usr:pwd' \
-H "Content-Type: application/json" \
-X PUT "<base url>/api/v2/payment/client" \
-d '{
  "id":"ecb14ecf-9a1f-4876-959c-ab864f9115c4",
  "name":"My Payment Accounts",
  "description":"My Payment Accounts"
}'

In the payload above the value for id is a randomly generated v4 uuid.

2) Configure the payment routing and DSL payment config for the app

curl -u 'usr:pwd' \
-H "Content-Type: application/json" \
-X PUT "<base url>/api/v2/payment/route" \
-d @route_config.json

See the content of route_config here: route_config.json

In the payload above:

  • The value for id is a randomly generated v4 uuid.
  • The value for clientId is the unique id of the app.


curl -u 'usr:pwd' \
-H "Content-Type: application/json" \
-X PUT "<base url>/api/v2/payment/configuration/dsl" \
-d '{
  "id":"6316a570-5163-42fd-998f-98bef09c6559",
  "appId":"b1f28c43-1343-4503-91d2-09fb531e20bc",
  "paymentObjects":{
    "Payee":{
      "matcherFields":null,
      "identifierType":"MSISDN",
      "identifierField":"mobileNumber"
    },
    "Payer":{
      "matcherFields":[
        "paymentConfigurationUuid"
      ],
      "identifierType":"MSISDN",
      "identifierField":"mobileNumber"
    }
  }
}'

In the payload above:

  • The value for id is a randomly generated v4 uuid.
  • The value for appId is the unique id of the app.

3) Configure one or more payment account configurations

curl -u 'usr:pwd' \
-H "Content-Type: application/json" \
-X PUT "<base url>/api/v2/payment/configuration" \
-d '{
  "id":"9c2d6182-48ab-43b8-846f-a02f46f1146f",
  "type":"payConfigurationYourDriver",
  "name":"Payment account 1",
  "endpoint":"some.payment.gateway",
  "userName":"usr",
  "password":"pwd",
  "paymentClientId":"b1f28c43-1343-4503-91d2-09fb531e20bc",
  "validationRequired": false,
  .
  .
  .
}'

The payload above is abbreviated and shows only fields that are required for the base PaymentConfiguration class. Any additional fields will be from the driver specific config that has been added.

The for the fields shown above the following applies:

  • The value for id is a randomly generated v4 uuid.
  • The value id represents the json subtype for your payment config. In code (configuration class for your driver) this will be represented by, for example: 

    String JSON_TYPE_NAME = PaymentConfiguration.JSON_TYPE_PREF_CONFIGURATION + "MoMo";

  • The value for name is a text name / description of this payment account configuration.
  • The value for userName is a user name relevant to the account.
  • The value for password is a password relevant to the account.
  • The value for paymentClientId is the id for the payment configuration grouping that was added above in step 1.
  • The value for validationRequired being true/false indicate if validate payment flow should be followed.



Receiving Async Validate Payment Responses

The implementation for the API can be found in the Helium-war project's PaymentResource class and the endpoint exposed by the API is as follows:

<base_url>/api/v2/payment/myDriver/validate

In the above "myDriver" represents the payment gateway name that was added to the SupportedPaymentGateways class. This will in turn be used as input to the PaymentValidationBuilderFactoryFactory class to determine which result builder should be used for the incoming payload.

Receiving Async Responses

Helium already contains a built in API for the purpose of receiving async payment result callbacks from aggregators. The implementation for the API can be found in the Helium-war project's PaymentResource class and the endpoint exposed by the API is as follows:

<base_url>/api/v2/payment/myDriver/result

In the above "myDriver" represents the payment gateway name that was added to the SupportedPaymentGateways class. This will in turn be used as input to the PaymentResultBuilderFactoryFactory class to determine which result builder should be used for the incoming payload.