Skip to content

Commit c452fc2

Browse files
committed
Merge branch 'develop' into trunk
2 parents a305903 + 4052cb5 commit c452fc2

13 files changed

+278
-91
lines changed

changelog.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
*** GoCardless for WooCommerce Changelog ***
22

3+
2025-08-05 - version 2.9.7
4+
* Add - Improved subscription cancellation by cancelling "Pending Submission" payments and preventing retries on non-cancellable payments.
5+
* Fix - Ensure webhook events are handled properly without any issues.
6+
* Dev - Bump WordPress minimum supported version to 6.7.
7+
* Dev - Bump WooCommerce "tested up to" version 10.1.
8+
* Dev - Bump WooCommerce minimum supported version to 9.9.
9+
310
2025-07-02 - version 2.9.6
411
* Add - Respond with an error to the GoCardless webhook call if the scheduling action fails.
512
* Add - A notice indicating that GoCardless is connected in sandbox mode if the merchant is connected to a sandbox account.

includes/class-wc-gocardless-api.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,23 @@ public static function create_payment( $params = array() ) {
401401
return self::_request( 'payments', $args );
402402
}
403403

404+
/**
405+
* Update payment.
406+
*
407+
* @param string $payment_id Payment ID.
408+
* @param array $params Parameters to update payment.
409+
*
410+
* @return array|WP_Error
411+
*/
412+
public static function update_payment( $payment_id, $params = array() ) {
413+
$args = array(
414+
'method' => 'PUT',
415+
'body' => wp_json_encode( array( 'payments' => $params ) ),
416+
);
417+
418+
return self::_request( 'payments/' . $payment_id, $args );
419+
}
420+
404421
/**
405422
* Get a single payment from given payment_id.
406423
*
@@ -417,7 +434,7 @@ public static function get_payment( $payment_id ) {
417434
*
418435
* @param string $payment_id Payment ID.
419436
*
420-
* @return array
437+
* @return array|WP_Error
421438
*/
422439
public static function cancel_payment( $payment_id ) {
423440
$args = array(

includes/class-wc-gocardless-gateway-addons.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ public function __construct() {
2929
// Allow store managers to manually set GoCardless as the payment method on a subscription.
3030
add_filter( 'woocommerce_subscription_payment_meta', array( $this, 'add_subscription_payment_meta' ), 10, 2 );
3131
add_action( 'woocommerce_subscription_validate_payment_meta', array( $this, 'validate_subscription_payment_meta' ), 10, 2 );
32+
33+
// Cancel in-progress payment on subscription cancellation.
34+
add_action( 'woocommerce_subscription_pending-cancel_' . $this->id, array( $this, 'maybe_cancel_subscription_payment' ) );
35+
add_action( 'woocommerce_subscription_cancelled_' . $this->id, array( $this, 'maybe_cancel_subscription_payment' ) );
3236
}
3337

3438
if ( class_exists( 'WC_Pre_Orders_Order' ) ) {
@@ -323,4 +327,63 @@ public function validate_subscription_payment_meta( $payment_method_id, $payment
323327
}
324328
}
325329
}
330+
331+
/**
332+
* Cancel the order payment on subscription cancellation.
333+
*
334+
* If the payment is pending submission, we can cancel it.
335+
* Otherwise, update the retry_if_possible to false to avoid the payment being retried (except for paid_out and cancelled payment states).
336+
*
337+
* @since 2.9.7
338+
*
339+
* @param WC_Subscription $subscription Subscription object.
340+
*/
341+
public function maybe_cancel_subscription_payment( $subscription ) {
342+
if ( ! $subscription ) {
343+
return;
344+
}
345+
wc_gocardless()->log( sprintf( '%s - Subscription cancelled/Pending cancellation, checking if payment should be cancelled', __METHOD__ ) );
346+
347+
$last_order = $subscription->get_last_order( 'all' );
348+
if ( ! $last_order ) {
349+
return;
350+
}
351+
352+
$payment_id = $this->get_order_resource( $last_order->get_id(), 'payment', 'id' );
353+
$payment = WC_GoCardless_API::get_payment( $payment_id );
354+
if ( is_wp_error( $payment ) || empty( $payment['payments'] ) ) {
355+
wc_gocardless()->log( sprintf( '%s - Failed to retrieve payment.', __METHOD__ ) );
356+
return;
357+
}
358+
359+
$gocardless_status = $payment['payments']['status'] ?? '';
360+
wc_gocardless()->log( sprintf( '%s - GoCardless payment status: %s', __METHOD__, $gocardless_status ) );
361+
362+
// If the payment is pending submission, we can cancel it.
363+
if ( 'pending_submission' === $gocardless_status ) {
364+
$response = WC_GoCardless_API::cancel_payment( $payment_id );
365+
if ( ! is_wp_error( $response ) ) {
366+
wc_gocardless()->log( sprintf( '%s - Payment cancelled', __METHOD__ ) );
367+
} else {
368+
wc_gocardless()->log( sprintf( '%s - Failed to cancel payment: %s', __METHOD__, $response->get_error_message() ) );
369+
}
370+
} elseif (
371+
! in_array(
372+
$gocardless_status,
373+
array(
374+
'paid_out',
375+
'cancelled',
376+
),
377+
true
378+
)
379+
) {
380+
// We can't cancel the payment, so we update it to not retry if possible to avoid the payment being retried.
381+
$response = WC_GoCardless_API::update_payment( $payment_id, array( 'retry_if_possible' => false ) );
382+
if ( ! is_wp_error( $response ) ) {
383+
wc_gocardless()->log( sprintf( '%s - Payment updated with retry_if_possible set to false', __METHOD__ ) );
384+
} else {
385+
wc_gocardless()->log( sprintf( '%s - Failed to update payment: %s', __METHOD__, $response->get_error_message() ) );
386+
}
387+
}
388+
}
326389
}

includes/class-wc-gocardless-gateway.php

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,24 +1632,43 @@ protected function _handle_webhook() {
16321632

16331633
wc_gocardless()->log( sprintf( '%s - Handling webhook. Payload: %s', __METHOD__, print_r( $payload, true ) ) );
16341634

1635-
$args = array( $payload );
1636-
1637-
// Process the webhook payload asynchronously.
1638-
$action_id = WC()->queue()->schedule_single(
1639-
WC()->call_function( 'time' ) + 1,
1640-
'woocommerce_gocardless_process_webhook_payload_async',
1641-
$args,
1642-
'woocommerce-gocardless-webhook'
1643-
);
1635+
$webhook_payload = $payload;
1636+
$schedule_failed = false;
1637+
1638+
/*
1639+
* Loop through each event to schedule the action for each event separately.
1640+
* This is to avoid the issue where the webhook payload is too large and the action not get scheduled.
1641+
*
1642+
* @see https://github.com/gocardless/woocommerce-gateway-gocardless/issues/79
1643+
*/
1644+
foreach ( $payload['events'] as $event ) {
1645+
// Update the webhook payload with the current event only.
1646+
$webhook_payload['events'] = array( $event );
1647+
1648+
// Schedule the action to process the webhook payload asynchronously.
1649+
$action_id = WC()->queue()->schedule_single(
1650+
time() + 1,
1651+
'woocommerce_gocardless_process_webhook_payload_async',
1652+
array( $webhook_payload ),
1653+
'woocommerce-gocardless-webhook'
1654+
);
16441655

1645-
// If the action is not scheduled, return error to GoCardless, to get a retry.
1646-
if ( empty( $action_id ) ) {
1656+
// If any action fail to schedule, mark the schedule as failed, and return error to GoCardless, to get a retry.
1657+
if ( empty( $action_id ) ) {
1658+
$schedule_failed = true;
1659+
break;
1660+
}
1661+
1662+
$webhook_payload['events'] = array();
1663+
wc_gocardless()->log( sprintf( '%s - Action scheduled with ID %d, to process webhook.', __METHOD__, $action_id ) );
1664+
}
1665+
1666+
// Return error to GoCardless, to get a retry.
1667+
if ( $schedule_failed ) {
16471668
wc_gocardless()->log( sprintf( '%s - Failed to schedule action to process webhook.', __METHOD__ ) );
16481669
header( 'HTTP/1.1 500 Internal Server Error' );
16491670
throw new Exception( esc_html__( 'Failed to schedule action to process webhook.', 'woocommerce-gateway-gocardless' ) );
16501671
}
1651-
1652-
wc_gocardless()->log( sprintf( '%s - Action scheduled with ID %d, to process webhook.', __METHOD__, $action_id ) );
16531672
} catch ( Exception $e ) {
16541673
wc_gocardless()->log( sprintf( '%s - Error when handling webhook: %s', __METHOD__, $e->getMessage() ) );
16551674
wp_send_json_error( array( 'message' => $e->getMessage() ) );

0 commit comments

Comments
 (0)