A while ago I was asked to look into setting up a Subscription product for WooCommerce that would be a Quarterly subscription, but it would be renewed on a specific schedule. In this case, the payments would be taken on the 1st of February, May, August and November.

Synchronising the payments to fall on the 1st of the month is easy enough, it is a feature of WooCommerce Subscriptions already. However, making sure that payments aligned with the correct month as well as the correct day? That would require a bit of custom coding.

There is a filter hook that we can use to adjust the “Synced First Payment Date”. We can check to see what date is proposed, then adjust it if need be.

I start off by defining my filter and function:

add_filter( 'woocommerce_subscriptions_synced_first_payment_date', 'heavyside_align_first_payment_to_feb_qtr', 10, 2 );

function heavyside_align_first_payment_to_feb_qtr( $first_payment_date, $product ) {

// We are checking for product 1000 so if we aren't viewing that product, don't worry about the rest of the code

if ( '1000' == $product->get_id() ) {

// Add code here to check the Proposed First Payment Date

}

}

Since I wanted this to only apply to a specific product, I first need to check that I am performing the adjustment only on that Product.

The tricky bit is working out when and how to adjust the dates.

A standard “Quarterly” schedule would be month 3 (March), 6 (June), 9 (September), 12 (December).

The easiest way to check if you are looking at one of those months is to use the “Modulo” operator (%) to see if there is a remainder when dividing the ‘Month Number’ by 3 (for quarters). If it is cleanly divisible (ie the remainder is 0), then you are indeed looking at one of the Quarter Months.

For example:

March (Month 3) – ` 3 % 3 = 0`

which means March is a “Quarter Month”.

July (Month 7) – ` 7 % 3 = 1`

which means it is NOT a “Quarter Month”.

November (Month 11) – ` 11 % 3 = 2 `

which means it is NOT a “Quarter Month”.

Because we want to use a unique schedule of 2 (Feb), 5 (May), 8 (August), 11 (November). We need to “Offset” the Month number so it aligns to a proper Quarter. In this case, we are bringing the Quarter forward by a month, so we need to increase the Month Number by 1.

As an example:

February (Month 2) + Offset (1) % 3 = 0

Great Success!

But, now if we check March (Month 3) + Offset (1) % 3 = 1

This is interesting, it means that we can also easily work out how many months “away” from the next Quarter we are. Since Month 4 has a remainder of 1, it means that we are one month “Past” the Quarter, so since the quarters are every 3 months, we are (3 – 1) Months away from the next Quarter. In this instance, 2 Months.

We can then just use the `strtotime`

method to add the required number of Months to the proposed date.

The code I wrote is as follows:

add_filter( 'woocommerce_subscriptions_synced_first_payment_date', 'heavyside_align_first_payment_to_feb_qtr', 10, 2 );

function heavyside_align_first_payment_to_feb_qtr( $first_payment_date, $product ) {

// We are checking for product 1000 so if we aren't viewing that product, don't worry about the rest of the code

if ( '1000' == $product->get_id() ) {

// Find the "number" of the month that we are supposed to make our first payment. We can use this number to easily run checks on and make adjustments.

$proposedMonthInt = (int) date( 'n', $first_payment_date );

// Because the

$remainder = ($proposedMonthInt+1) % 3;

// Let's check if we are looking at the correct product, and test to see if we need to alter the first_payment_date (ie. $remainder > 0)

if($remainder > 0) {

// We work out how many months are needed to bring us up to an appropriate month and format that as a string which we can use in the strtotime()

$diff = "+" . (3 - $remainder) . " Month";

//We use the strtotime() function to adjust the first_payment_date by "+ 1Month" or "+2 Month"

$first_payment_date = strtotime( $diff, $first_payment_date );

}

}

return $first_payment_date;

}

Dive straight into the feedback!Login below and you can start commenting using your own user instantly