Custom Recurring Payment Adapters
From Jadu Central 3.3, it is possible to plug in a PayBridge adapter to support custom recurring payments e.g. via Direct Debit or simpler recurring card payments.
Anatomy of recurring payments in PayBridge
A standard PayBridge card payment is a one-off payment. PayBridge redirects the user to a page hosted by the payment provider to collect the payment details. The user is then redirected back to Jadu Central to complete the web form.
Recurring payments are a bit more complex. Inherently, there is a concept of a long-running series of payments. From PayBridge's point of view, the third party is expected to handle future collection of payments. This means the process for setting them up is very similar to a standard payment.
The main difference from a standard payment is that a user may wish to change or cancel the payment in the future. This gives additional considerations:
- A recurring payment adapter will usually dovetail with one or more form actions, so it is possible to build an "Amend" form and use the actions to cancel/change the payment schedule as necessary.
- To support the above, it is necessary for the adapter to store subscription references somewhere permanent. This could either be in Jadu Central's database, or in an alternate system such as Jadu Connect.
Is the solution I want to integrate with recurring payments compatible with PayBridge?
There are a few key considerations to keep in mind when evaluating a solution to determine if it is compatible with PayBridge:
-
The solution must provide a hosted page for collection of card or Direct Debit details. Collecting and storing these details yourself via Jadu Central Forms is strongly discouraged, as this data is particularly sensitive. Collection of card details also requires PCI compliance.
-
The solution must redirect back from the hosted page to Jadu Central, so the web form can be completed.
-
Ideally, the solution should handle the recurrence itself e.g. collecting future payments
Jadu does not provide a scheduling engine for collecting payments in the future.
Building a custom recurring payment integration
Creating a recurring payment integration typically consists of a few parts:
Building a custom adapter
You should build an adapter following the custom adapter guidance to implement the API of your chosen solution. The adapter will handle the handoff of the user to the payment provider, as well as their return.
When making the request to the payment provider, it is likely that you will need to pass data about the payment schedule. To do this, PayBridge has an abstraction called a Plan, see "Building a Plan Service" below.
It is possible that your third party solution will use the hosted page to set up a mandate or initial instruction, and expect a schedule to be created via an API afterwards. If this is the case, Jadu recommends:
- building the adapter request to handle requirements of the hosted page
- using getResponse() and verifyDataReceived() to handle the response from the hosted page
- using parseResponse() to make API calls for setting up schedules with the returned values from the hosted page
NOTE: Be careful when implementing the response methods in this scenario. The API response to set up the schedule may fail, but it may not be appropriate to fail the PayBridge order via wasPaymentSuccessful() if the user has already paid an initial amount via the hosted page.
NOTE: Jadu strongly recommends logging exactly once in all execution paths through your response methods, to make debugging easier.
Converting this to a recurring payment adapter
-
Recurring payment adapters should implement
Jadu\PayBridge\DirectDebit\DirectDebitAdapterInterface. Add this to your adapter class. -
Implement the method on this interface:
public function getDirectDebitPlanService(): DirectDebitPlanServiceInterface. This should return an instance of your Plan Service, see below. -
Ensure that (if required for later use in custom actions), the adapter stores the relevant subscription reference after setup. This could be in Jadu Central's database, e.g.:
$product = $this->getProductMapper()->getByID($this->getFirstOrderItem()->productID);
$subscription = new UserDirectDebit();
$subscription->setUserID($this->getOrder()->userID);
$subscription->setOrderID($this->getOrder()->id);
$subscription->setActive(1);
$subscription->setDDSubscriptionRef($this->getJaduOrderRef());
$subscription->setPspID($product->pspID);
$this->getSubscriptionMapper()->save($subscription);Alternately, you could use a system like Jadu Connect to store the subscription reference, by mapping the 'PayBridge - Payment Reference' variable in your Create Case action template.
Building a Plan Service
Plans are PayBridge's model of a recurring payment's schedule. To this end, your Plan Service is responsible for handling the abstraction of these schedules.
Your plan service must extend Jadu\PayBridge\DirectDebit\AbstractDirectDebitPlanService. This will in turn require the implementation of the following methods:
-
abstract public function getAllPlans(): array;This function should return an array of ProviderPlan objects. A ProviderPlan has three properties:
- Identifier (a unique string that identifies the plan, used only internally by PayBridge)
- Name (shown when creating a Direct Debit service in PayBridge; administrators are shown a list of all plans based on this method's output)
- Fields (discussed in more detail below)
NOTE: Some payment providers may not have a concept of "plans" or anything similar. In this case, return a single plan from this method, making all fields in it configurable.
-
abstract public function validatePlan(array $fields): array;This function is passed an array, string-indexed by field name, where the keys are the values set in the plan fields when creating the Direct Debit service in the Control Centre. The method should validate these, returning an array of any errors indexed by field name.
-
abstract public function getPlanSummary(OrderItem $item): array;This function is passed an OrderItem object, and should build up a string array of key-value pairs to be shown in the summary of the recurring payment (on both the payment review and complete pages). The keys should be the labels to show e.g. 'Amount', and the values should be the corresponding value of that field e.g. '£10.00'.
Plan Fields
PayBridge provides a number of types of plan fields (all under the Jadu\PayBridge\Bundle\CoreBundle\Model\DirectDebit namespace):
-
FixedProviderPlanField
Used for fields that will always have the same value.
$field = new FixedProviderPlanField('frequency', 'Frequency', 'monthly'); -
FixedAmountProviderPlanField
As above, but specific to the amount.
$field = new FixedAmountProviderPlanField(10); -
FormAmountProviderField
An alternative to FixedAmountProviderPlanField, used to allow mapping of the amount from form data. Most common.
$field = new FormAmountProviderField(); -
TextProviderPlanField
Renders a text input for this field when setting up the Direct Debit service.
$field = new TextProviderPlanField('name', 'Name'); -
DropdownProviderPlanField
Renders a dropdown of choices for this field when setting up the Direct Debit service.
$field = new DropdownProviderPlanField('monthDay', 'Day of Month', ['1' => '1st', '2' => '2nd'...]);
Accessing Plans and Fields in the Adapter
PayBridge provides Jadu\PayBridge\DataMapper\DirectDebitPlanMapper as a data mapper to access plan objects. It may be useful to inject this into your Plan service.
As an example of usage, the getPlanSummary() method above could be implemented as:
public function getPlanSummary(OrderItem $item): array
{
$plan = $this->planMapper->getByServiceID($item->productID);
...
To get at plan fields, use Jadu\PayBridge\DataMapper\DirectDebitPlanFieldMapper. On this, getPlanFieldValue($planID, $name) is probably most useful.
NOTE: PayBridge adapters themselves do not support dependency injection. Either inject the mappers into your plan service, or utilise Jadu\PayBridge\PSP\Helpers\DirectDebitTrait, which provides getters for the mappers that construct them when called.
Custom Actions
As mentioned, you may wish to create custom actions to facilitate the amendment or cancellation of a recurring payment. This can be done following the custom action guidance.
Note the guidance above about storing the subscription reference; it is likely that this will be required during this form. Typically, a user may be asked to enter their subscription reference in the amend form, and this subscription reference can then be used to check the user ID on the matching UserDirectDebit object matches the current user. If using Jadu Connect, you may instead pull the reference back from a case via the 'Connect - Value of Case Field' logic.
NOTE: There are security implications if allowing anonymous amending or cancelling of recurring payments - please see guidance on anonymous direct debits.