Skip to content

Commit

Permalink
Process payment intent webhook for successful card purchases asynchro…
Browse files Browse the repository at this point in the history
…nously (#3575)

* Refactor process_payment_intent_success() to default to processing succeeded webhook async

* Don't call process_response() with a null charge object if not found

* Add changelog entry

* Trim whitespace off the end and tweak wording of log
  • Loading branch information
mattallan committed Nov 7, 2024
1 parent 61385aa commit b9b4d1f
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 19 deletions.
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
= 8.8.2 - xxxx-xx-xx =
* Fix - Prevent marking renewal orders as processing/completed multiple times due to handling the Stripe webhook in parallel.
* Dev - Refactor lock_order_payment() to use order meta instead of transients.
* Update - Process successful payment intent webhooks asynchronously.

= 8.8.1 - 2024-10-28 =
* Tweak - Disables APMs when using the legacy checkout experience due Stripe deprecation by October 29, 2024.
Expand Down
38 changes: 19 additions & 19 deletions includes/class-wc-stripe-webhook-handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -928,22 +928,20 @@ public function process_payment_intent_success( $notification ) {
break;
case 'payment_intent.succeeded':
case 'payment_intent.amount_capturable_updated':
$charge = $this->get_latest_charge_from_intent( $intent );

WC_Stripe_Logger::log( "Stripe PaymentIntent $intent->id succeeded for order $order_id" );

/**
* Check if the order is awaiting further action from the customer. If so, do not process the payment via the webhook, let the redirect handle it.
*
* This is a stop-gap to fix a critical issue, see https://github.com/woocommerce/woocommerce-gateway-stripe/issues/2536. It would
* be better if we removed the need for additional meta data in favor of refactoring this part of the payment processing.
*/
$is_awaiting_action = $order->get_meta( '_stripe_upe_waiting_for_redirect' ) ?? false;
$process_webhook_async = apply_filters( 'wc_stripe_process_payment_intent_webhook_async', true, $order, $intent, $notification );
$is_awaiting_action = $order->get_meta( '_stripe_upe_waiting_for_redirect' ) ?? false;

// Process the webhook now if it's for a voucher or wallet payment , or if filtered to process immediately and order is not awaiting action.
if ( $is_voucher_payment || $is_wallet_payment || ( ! $process_webhook_async && ! $is_awaiting_action ) ) {
$charge = $this->get_latest_charge_from_intent( $intent );

// Voucher payments are only processed via the webhook so are excluded from the above check.
// Wallets are also processed via the webhook, not redirection.
if ( ! $is_voucher_payment && ! $is_wallet_payment && $is_awaiting_action ) {
WC_Stripe_Logger::log( "Stripe UPE waiting for redirect. Scheduled deferred webhook processing. The status for order $order_id might need manual adjustment." );
do_action( 'wc_gateway_stripe_process_payment', $charge, $order );

$this->process_response( $charge, $order );
} else {
WC_Stripe_Logger::log( "Processing $notification->type ($intent->id) asynchronously for order $order_id." );

// Schedule a job to check on the status of this intent.
$this->defer_webhook_processing(
Expand All @@ -954,14 +952,11 @@ public function process_payment_intent_success( $notification ) {
]
);

do_action( 'wc_gateway_stripe_process_payment_intent_incomplete', $order );
return;
if ( $is_awaiting_action ) {
do_action( 'wc_gateway_stripe_process_payment_intent_incomplete', $order );
}
}

do_action( 'wc_gateway_stripe_process_payment', $charge, $order );

// Process valid response.
$this->process_response( $charge, $order );
break;
default:
if ( $is_voucher_payment && 'payment_intent.payment_failed' === $notification->type ) {
Expand Down Expand Up @@ -1123,6 +1118,11 @@ protected function handle_deferred_payment_intent_succeeded( $order, $intent_id

$charge = $this->get_latest_charge_from_intent( $intent );

if ( ! $charge ) {
WC_Stripe_Logger::log( "Skipped processing deferred webhook for Stripe PaymentIntent {$intent_id} for order {$order->get_id()} - no charge found." );
return;
}

WC_Stripe_Logger::log( "Processing Stripe PaymentIntent {$intent_id} for order {$order->get_id()} via deferred webhook." );

do_action( 'wc_gateway_stripe_process_payment', $charge, $order );
Expand Down
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,6 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
= 8.8.2 - xxxx-xx-xx =
* Fix - Prevent marking renewal orders as processing/completed multiple times due to handling the Stripe webhook in parallel.
* Dev - Refactor lock_order_payment() to use order meta instead of transients.
* Update - Process successful payment intent webhooks asynchronously.

[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/trunk/changelog.txt).

0 comments on commit b9b4d1f

Please sign in to comment.