We came across an issue in https://freescout.pronamic.nl/conversation/11328?folder_id=55 where a subscription was updated from Open > Active > Open, while payment status had been updated correctly to Success.
The issue appears to be caused by concurrent processing of webhook and user return requests. Upon further investigation I suspect that the issue is a stale in-memory subscription object being saved after the webhook already persisted a newer status.
The gateway status update (in this case from Mollie) can use a payment and subscription while they are still pending. The first request (I suspect the webhook) updates the payment status to Success, which subsequently triggers the subscription status to be updated to Active through the pronamic_payment_status_update action. Shortly after that, the second request (the user return?) continues with its older in-memory version of the subscription (still Open) and saves it (through https://github.com/pronamic/wp-pronamic-pay-mollie/blob/939429d447b07591800b38d994316843d77ce9eb/src/Gateway.php#L1049 or https://github.com/pronamic/wp-pronamic-pay-mollie/blob/939429d447b07591800b38d994316843d77ce9eb/src/Gateway.php#L1284), unintentionally overwriting Active back to Open.
The payment status is protected more reliably, because previous payment status is read from post meta before firing status transition hooks:
|
$previous_status = $this->get_meta( $id, 'status' ); |
|
|
|
$this->update_meta( $id, 'status', $payment->status ); |
|
|
|
if ( $previous_status !== $payment->status ) { |
The same check for subscriptions can still result in a status update, when meta status is Active and in-memory subscription is Open:
|
$previous_status = $this->get_meta( $id, 'status' ); |
|
|
|
$this->update_meta( $id, 'status', $subscription->status ); |
|
|
|
if ( $previous_status !== $subscription->status ) { |
I was unable to find any paths that should update a subscription status back to Open. If that is never expected, the easiest way to resolve the issue is by updating a Open status in the in-memory subscription to the actual status from post meta during a subscription update:
|
public function update( $subscription ) { |
|
$id = $subscription->get_id(); |
|
|
|
if ( empty( $id ) ) { |
|
return false; |
|
} |
|
|
|
$result = \wp_update_post( |
|
$this->get_post_data( |
|
$subscription, |
|
[ |
|
'ID' => $id, |
|
] |
|
), |
|
true |
|
); |
An alternative solution could be to use locking in status updates to prevent concurrent processing, but I'm afraid that will be more prone to issues.
We came across an issue in https://freescout.pronamic.nl/conversation/11328?folder_id=55 where a subscription was updated from
Open>Active>Open, while payment status had been updated correctly toSuccess.The issue appears to be caused by concurrent processing of webhook and user return requests. Upon further investigation I suspect that the issue is a stale in-memory subscription object being saved after the webhook already persisted a newer status.
The gateway status update (in this case from Mollie) can use a payment and subscription while they are still pending. The first request (I suspect the webhook) updates the payment status to
Success, which subsequently triggers the subscription status to be updated toActivethrough thepronamic_payment_status_updateaction. Shortly after that, the second request (the user return?) continues with its older in-memory version of the subscription (stillOpen) and saves it (through https://github.com/pronamic/wp-pronamic-pay-mollie/blob/939429d447b07591800b38d994316843d77ce9eb/src/Gateway.php#L1049 or https://github.com/pronamic/wp-pronamic-pay-mollie/blob/939429d447b07591800b38d994316843d77ce9eb/src/Gateway.php#L1284), unintentionally overwritingActiveback toOpen.The payment status is protected more reliably, because previous payment status is read from post meta before firing status transition hooks:
wp-pay-core/src/Payments/PaymentsDataStoreCPT.php
Lines 531 to 535 in 2d14a84
The same check for subscriptions can still result in a status update, when meta status is
Activeand in-memory subscription isOpen:wp-pay-core/src/Subscriptions/SubscriptionsDataStoreCPT.php
Lines 536 to 540 in 2d14a84
I was unable to find any paths that should update a subscription status back to
Open. If that is never expected, the easiest way to resolve the issue is by updating aOpenstatus in the in-memory subscription to the actual status from post meta during a subscription update:wp-pay-core/src/Subscriptions/SubscriptionsDataStoreCPT.php
Lines 242 to 257 in 2d14a84
An alternative solution could be to use locking in status updates to prevent concurrent processing, but I'm afraid that will be more prone to issues.