Fundraising/Data and flow/Recurring

From Wikitech


Recurring donations are supported via PayPal (both legacy and Express Checkout integrations), via Ingenico (Connect ), Adyen, and via Dlocal.

In CiviCRM, recurring donations are stored in the civicrm_contribution_recur table and each installment has an entry in civicrm_contribution with a value in the contribution_recur_id pointing to the civicrm_contribution_recur row. We store them slightly idiosyncratically - while core CiviCRM's payment processors interpret the 'Completed' status to mean that there are no more installments expected, we use 'Completed' to mean that the subscription is in a good state.


At Payments-wiki, we interpret any truthy value sent for the query string parameter 'recurring' to mean we should set up a recurring payment for the donor.

Will start a recurring recurring=1 recurring=true

Will not start a recurring recurring=0 recurring=false


PayPal recurring donations are all driven by PayPal - that is, we indicate that we want to set up a monthly donation for a specific amount, and PayPal takes care of the scheduling, charging, and retry attempts on failed charges.

For PayPal Express Checkout, we use the MAXFAILEDPAYMENTS parameter to instruct PayPal to stop trying to charge the donor after N failed attempts. Express Checkout subscriptions set up prior to October 15th, 2019 had this value set to 3, and after that date have the value set to 0, meaning to retry indefinitely. On failure, PayPal retries every 5 days. PayPal's documentation


Ingenico recurring donations are driven by us - after setting up a recurring donation, Ingenico gives us a recurring_payment_tokento store and refer to in future donation attempts. We schedule the donations and make API calls to charge them once each month. Our hourly scheduled jobs check the civicrm_contribution_recur table for rows where the next_sched_contribution_date is now or before, and charge a batch of them.

Ingenico recurring donations (coded as gateway=ingenico) are tokenized. That is, we receive a payment token at the time of the setup and store it in the civicrm_payment_token table. The code to charge each monthly installment is in the CiviCRM extension org.wikimedia.smashpig, which uses the SmashPig library to interface with the Connect API. The scheduled job is configured in recurring_smashpig_charge.yaml, which runs drush cvapi job.process_smashpig_recurring.


Like Ingenico and dlocal, Adyen recurring jobs are driven by us.

There is more information on the details of Adyen recurring on the main Adyen page.


Like Adyen and Ingenico, Dlocal recurring jobs are driven by us.

More information on dlocal recurring on the main dlocal page

Monthly Convert is a system to give the donor an option to start a recurring donation after a one time donation. They do not need to reinput their payment information.

There is more information on the main monthly convert page.

Recurring queue

A somewhat misleading name, the recurring queue mainly handles Paypal recurrings. It also is where initial monthly convert messages are sent as well as [something for the dlocal flow figure out exactly what.]

Smashpig civi extension/Smashing recurring charge job/Recurring charge job

We call this many different things but it is the nightly job that charges all the donations we have control over the charging of. It lives in the civi extension org.wikimedia.smashpig

More documentation here:

Failures and retry logic

If we get a failure charging a recurring contribution, we set the contribution_recur status to 'Failed', increment the failure_count column, and set a failure_retry_date. The maximum number of failures and the retry interval is configurable. For Ingenico Connect, settings are in the Civi top menu under Administer->System Settings->SmashPig Settings. For WebCollect, see the 'Recurring GlobalCollect Processor' link in the left-hand menu. Both are currently set to retry failed payments 1 day later, and to stop retrying after 3 failures.

After 3 failures a recurring failure message is sent. The templates for this message can be found in Civi under Mailings -> Message Templates. On the Message Template page click on the System Workflow Message tab. The templates are named WMF Recurring failure message. The 'from' address and name used are the default CiviCRM core FROM address. This is configured under Admininster->Communications->FROM Email Addresses (the failure email uses the one marked as default).


You can send a test failure email by going to a recurring record in civi and clicking on the ... menu on the right hand side. This brings up the option to Send Failure Notification.

Failure Reasons

column description
(auto) maximum failures reached cancelled automatically after three retries
(auto) un-retryable card decline reason cancelled automatically when the processor returns a decline reason that is not retryable (fraud, lost card)
(auto) Expiration notification PayPal only, we were sent a message from PayPal that the recuring is expired
(auto) User Cancelled via Gateway PayPal only, we were sent a message from PayPal that the donor cancelled their recurring
(auto) backfilled automated cancel
(auto) backfilled automated Expiration notification