Skip to content

Technical notes

GoCardless sends dozens of webhooks and this extension only reacts to the following:

  • payments: confirmed and failed.
  • subscriptions: cancelled and finished.

The life-cycle would typically be:

Lifecycle image

It may be more helpful to view the image full size (Devs: create this file using dot from the graphviz package: dot -Tsvg -o lifecycle.svg lifecycle.dot.)

Typical Contribution Page flow for initial set-up of a Direct Debit

Sequence diagram visually explaining the process detailed below

Editable diagram source

  1. User interacts with a CiviCRM Contribution Page to set up regular contribution. On submitting the form, core creates a Pending order (Contribution), recur and, if new membership, membership, and calls our doPayment method, which:

  2. Implements cycle_day mangling.

  3. Coerces '7 day' interval into '1 week' instead (daily is not supported by GoCardless). Any daily interval that is divisible by 7 gets converted. issues/59
  4. creates a Billing Request (BRQ), including:
    • a mandate request
    • including prefill customer data where available
    • a return URL that we fabricate to be the Contribution Page's thank you page, plus the ID of the BRQ and the recur ID.
    • the description field from the form.
  5. stores the BRQ ID on the recur's processor_id
  6. creates a Billing Request Flow (BRF) and returns its authorization_uri.

The user is then redirected to this uri which points to GoCardless' site.

  1. User completes forms on GoCardless website, then gets redirected back to the thank you page at the URL we generated in the process above.

  2. The thank you page is intercepted via hook_civicrm_buildForm which detects the BRQ and cr IDs in the query string, looks them up, checks they match and fetches the BRQ object using the GoCardless API, to check its status is as expected. We then:

  3. Sets up a subscription at GoCardless. This uses data from the recur to determine interval, start/cycle date, amount etc. It copies the description from the BRQ fetched data. It offers extensions a chance to alter these subsription params via hook_alterPaymentProcessorParams

  4. Updates the recur to be In Progress, start_date is set to match the subscription, the cycle_day is possibly recalculated, the processor_id becomes the subscription ID.
  5. Updates the (still) pending contribution with the future receive_date of the start of the subscription.

A top-level code path overview is:

  • doPaymentgetBillingRequestFlowURL
  • user sent to GoCardless' site
  • back on civi (if successful): hook_civicrm_buildFormGoCardlessUtils::handleContributionFormThankshandleSuccessfulBillingRequest

Process for funds coming in (all via webhooks)

  1. GoCardless submits the charge for a payment to the customer's bank and eventually (4-5 working days after creation) this is confirmed. It sends a webhook for resource_type payments, action confirmed. At this point the extension will:

    • look up the payment with GoCardless to obtain its subscription ID.
    • look up the CiviCRM recurring contribution record in CiviCRM from this subscription ID (which is stored in the transaction ID field)
    • find the pending CiviCRM contribution record that belongs to the recurring contribution and update it, setting status to Completed, setting the receive date to the charge date from GoCardless (n.b. this is earlier than the date this payment confirmed webhook fires) and setting the transaction ID to the GoCardless payment ID. It also sets amount to the amount from GoCardless.
    • check that the status on the CiviCRM recurring contribution record is 'In Progress'. (It should be, but the check is there because we previously did things differently.)

Note: the following working day the GoCardless payment status is changed from confirmed to paid_out. Normally the confirmed webhook will have processed the payment before this happens, but the extension will allow processing of payment confirmed webhooks if the payment's status is paid_out too. This can be helpful if there has been a problem with the webhook and you need to replay some.

  1. A week/month/year later GoCardless sends another confirmed payment. This time:

    • look up payment, get subscription ID. As before.
    • look up recurring contribution record from subscription ID. As before.
    • there is no 'pending' contribution now, so a new Completed one is created, copying details from the recurring contribution record.
  2. Any failed payments work like confirmed ones but of course add or update Contributions with status Failed instead of Completed.

  3. The Direct Debit ends with either cancelled or completed. Cancellations can be that the subscription is cancelled, or the mandate is cancelled. The latter would affect all subscriptions. Probably other things too, e.g. delete customer. GoCardless will cancel all pending payments and inform CiviCRM via webhook. GoCardless will then cancel the subscription and inform CiviCRM by webhook. Each of these updates the status of the contribution (payment) or recurring contribution (subscription) records.

Notes on intervals, installments, cycle day and start dates

The core flow offers installments and intervals. intervals are restricted to weekly, monthly, yearly. If daily is encountered, it gets translated if possible to weekly, otherwise an exception is thrown. These fields are stored on the recur.

Core does not support cycle day (?) nor a future start_date. However this extension, via its settings can include a hack to offer a different day of month (cycle day) as part of the core flow.

At first, we store these values in the recur's cycle_day field

  • 0 means early as possible
  • 1-28
  • 32 menas the last day of the month

Then in handleSuccessfulBillingRequest this value will be changed if it was zero to the start date's day-of-month.

If you set the recur's start_date to a date ahead of the earliest possible date, then this is honoured.