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:
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¶
-
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:
-
Implements
cycle_day
mangling. - 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
- 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.
- stores the BRQ ID on the recur's
processor_id
- creates a Billing Request Flow (
BRF
) and returns itsauthorization_uri
.
The user is then redirected to this uri which points to GoCardless' site.
-
User completes forms on GoCardless website, then gets redirected back to the thank you page at the URL we generated in the process above.
-
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: -
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
- Updates the recur to be In Progress,
start_date
is set to match the subscription, thecycle_day
is possibly recalculated, theprocessor_id
becomes the subscription ID. - Updates the (still) pending contribution with the future
receive_date
of the start of the subscription.
A top-level code path overview is:
doPayment
→getBillingRequestFlowURL
- user sent to GoCardless' site
- back on civi (if successful):
hook_civicrm_buildForm
→GoCardlessUtils::handleContributionFormThanks
→handleSuccessfulBillingRequest
Process for funds coming in (all via webhooks)¶
-
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
, actionconfirmed
. 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.
-
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.
-
Any failed payments work like confirmed ones but of course add or update Contributions with status
Failed
instead ofCompleted
. -
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.
⚠️ Using test credentials on the live processor¶
Danger
Don’t do this on a live site! It will break your processing of live payments.
Add this to your civicrm.settings.php file:
define('CIVI_GOCARDLESS_FORCE_SANDBOX_ENVIRONMENT', 1);
With this constant defined all GoCardless API requests get sent to the sandbox endpoint. This enables you to put your sandbox credentials in the live processor's fields of the payment processor configuration.
This feature was added to support testing with Drupal 7’s CiviCRM WebForms since that does not allow use of a test processor. Normally, even in development, you should not need to use this.