Fundraising/Data and flow/Recurring
Recurring donations are supported via PayPal (both legacy and Express Checkout integrations), via Ingenico (Connect ), and via Adyen.
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.
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 something to 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.
Smashpig civi extension
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.