Skip to content

Commit 71a6a8f

Browse files
wjrosaannemirasol
andauthored
Moving existing order lock and bailing out when it is active (#4462)
* Moving existing order lock and bailing out when it is active * Changelog and readme entries * Moving the unlocking call to the finally clause * Removing unlock call from finally and reverting to original positions * Removing redundancy in lock check * Reverting intent data removal * Removing it again due incompatibility --------- Co-authored-by: Anne Mirasol <[email protected]>
1 parent a6a26c9 commit 71a6a8f

File tree

4 files changed

+52
-19
lines changed

4 files changed

+52
-19
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
*** Changelog ***
22

33
= 9.7.0 - xxxx-xx-xx =
4+
* Fix - Moves the existing order lock functionality earlier in the order processing flow to prevent duplicate processing requests
45
* Add - Adds two new safety filters to the subscriptions detached debug tool: `wc_stripe_detached_subscriptions_maximum_time` and `wc_stripe_detached_subscriptions_maximum_count`
56
* Add - Show a notice when editing an active subscription that has no payment method attached
67
* Fix - Fixes a possible fatal error when trying to generate the order signature for a `WC_Order_Refund` object

includes/abstracts/abstract-wc-stripe-payment-gateway.php

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,26 +1747,15 @@ private function get_intent( $intent_type, $intent_id ) {
17471747
*
17481748
* @since 4.2
17491749
* @param WC_Order $order The order that is being paid.
1750-
* @param stdClass $intent The intent that is being processed.
17511750
* @return bool A flag that indicates whether the order is already locked.
17521751
*/
1753-
public function lock_order_payment( $order, $intent = null ) {
1754-
$order->read_meta_data( true );
1755-
1756-
$existing_lock = $order->get_meta( '_stripe_lock_payment', true );
1757-
1758-
if ( $existing_lock ) {
1759-
$parts = explode( '|', $existing_lock ); // Format is: "{expiry_timestamp}" or "{expiry_timestamp}|{pi_xxxx}" if an intent is passed.
1760-
$expiration = (int) $parts[0];
1761-
$locked_intent = ! empty( $parts[1] ) ? $parts[1] : '';
1762-
1763-
// If the lock is still active, return true.
1764-
if ( time() <= $expiration && ( empty( $intent ) || empty( $locked_intent ) || ( $intent->id ?? '' ) === $locked_intent ) ) {
1765-
return true;
1766-
}
1752+
public function lock_order_payment( $order ) {
1753+
if ( $this->is_order_payment_locked( $order ) ) {
1754+
// If the order is already locked, return true.
1755+
return true;
17671756
}
17681757

1769-
$new_lock = ( time() + 5 * MINUTE_IN_SECONDS ) . ( isset( $intent->id ) ? '|' . $intent->id : '' );
1758+
$new_lock = ( time() + 5 * MINUTE_IN_SECONDS );
17701759

17711760
$order->update_meta_data( '_stripe_lock_payment', $new_lock );
17721761
$order->save_meta_data();
@@ -1785,6 +1774,38 @@ public function unlock_order_payment( $order ) {
17851774
$order->save_meta_data();
17861775
}
17871776

1777+
/**
1778+
* Retrieves the existing lock for an order.
1779+
*
1780+
* @param WC_Order $order The order to retrieve the lock for
1781+
* @return mixed
1782+
*/
1783+
protected function get_order_existing_lock( $order ) {
1784+
$order->read_meta_data( true );
1785+
return $order->get_meta( '_stripe_lock_payment', true );
1786+
}
1787+
1788+
/**
1789+
* Checks if an order is locked for payment processing.
1790+
*
1791+
* @param WC_Order $order The order to check the lock for
1792+
* @return bool
1793+
*/
1794+
protected function is_order_payment_locked( $order ) {
1795+
$existing_lock = $this->get_order_existing_lock( $order );
1796+
if ( $existing_lock ) {
1797+
$parts = explode( '|', $existing_lock ); // Format is: "{expiry_timestamp}"
1798+
$expiration = (int) $parts[0];
1799+
1800+
// If the lock is still active, return true.
1801+
if ( time() <= $expiration ) {
1802+
return true;
1803+
}
1804+
}
1805+
1806+
return false;
1807+
}
1808+
17881809
/**
17891810
* Locks an order for refund processing for 5 minutes.
17901811
*

includes/payment-methods/class-wc-stripe-upe-payment-gateway.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,17 @@ private function process_payment_with_payment_method( int $order_id ) {
10331033

10341034
$this->validate_selected_payment_method_type( $payment_information, $order->get_billing_country() );
10351035

1036+
// Attempt to acquire lock, bail if already locked
1037+
$is_order_payment_locked = $this->lock_order_payment( $order );
1038+
if ( $is_order_payment_locked ) {
1039+
// If the request is already being processed, return an error.
1040+
return [
1041+
'result' => 'failure',
1042+
'redirect' => '',
1043+
'message' => __( 'Your payment is already being processed. Please wait.', 'woocommerce-gateway-stripe' ),
1044+
];
1045+
}
1046+
10361047
$payment_needed = $this->is_payment_needed( $order->get_id() );
10371048
$payment_method_id = $payment_information['payment_method'];
10381049
$payment_method_details = $payment_information['payment_method_details'];
@@ -1079,9 +1090,6 @@ private function process_payment_with_payment_method( int $order_id ) {
10791090
$this->update_saved_payment_method( $payment_method_id, $order );
10801091
}
10811092

1082-
// Lock the order before we create and confirm the payment/setup intents to prevent Stripe sending the success webhook before this request is completed.
1083-
$this->lock_order_payment( $order );
1084-
10851093
if ( $payment_needed ) {
10861094
// Throw an exception if the minimum order amount isn't met.
10871095
$this->validate_minimum_order_amount( $order );
@@ -1185,6 +1193,8 @@ private function process_payment_with_payment_method( int $order_id ) {
11851193
$response_args
11861194
);
11871195
} catch ( WC_Stripe_Exception $e ) {
1196+
// Ensure the order is unlocked in case of an exception.
1197+
$this->unlock_order_payment( $order );
11881198
return $this->handle_process_payment_error( $e, $order );
11891199
}
11901200
}

readme.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
111111
== Changelog ==
112112

113113
= 9.7.0 - xxxx-xx-xx =
114+
* Fix - Moves the existing order lock functionality earlier in the order processing flow to prevent duplicate processing requests
114115
* Add - Adds two new safety filters to the subscriptions detached debug tool: `wc_stripe_detached_subscriptions_maximum_time` and `wc_stripe_detached_subscriptions_maximum_count`
115116
* Add - Show a notice when editing an active subscription that has no payment method attached
116117
* Fix - Fixes a possible fatal error when trying to generate the order signature for a `WC_Order_Refund` object

0 commit comments

Comments
 (0)