BasePaymentProvider & PayPal2: allow to cancel pending payments on a per payment basis (Z#23240966) (#6472)

* move payment into pending on PENDING_REVIEW webhook

* mark approved payment as pending

* extend BasePaymentProvider to gate aborting pending payments on a payment per payment basis

* add timeout to paypal after which a pending payment can be canceled

* formatting

* add missing negation

* cleanup abort_pending_allowed methods

* Apply suggestions from code review

Co-authored-by: pajowu <pajowu@pajowu.de>

* check all capture elements

* rename method and change defaults

* remove left over Constant

* flake8 .

---------

Co-authored-by: pajowu <pajowu@pajowu.de>
This commit is contained in:
Lukas Bockstaller
2026-08-13 17:28:54 +02:00
committed by GitHub
co-authored by pajowu
parent 4e5fbacf6d
commit c4a5a9a84d
7 changed files with 239 additions and 24 deletions
+21 -4
View File
@@ -330,9 +330,24 @@ class BasePaymentProvider:
payment method. This returns ``False`` by default which is no guarantee that
aborting a pending payment can never happen, it just hides the frontend button
to avoid users accidentally committing double payments.
If the decision doesn't depend on the specific payment, then only implementing
``abort_pending_allowed`` is enough, ``payment_abort_pending_allowed(payment: OrderPayment)``
is expected to take this into account.
As a consumer only evaluate ``payment_abort_pending_allowed(payment: OrderPayment)``
to check if aborting this pending payment is possible.
"""
return False
def _payment_abort_pending_allowed(self, payment: OrderPayment) -> bool:
"""
Experimental: This might change during upcomming releases.
Whether or not a user can abort a payment in pending state to switch to another
payment method. This returns ``self.abort_pending_allowed`` by default which is
no guarantee that aborting a pending payment can never happen, it just hides the
frontend button to avoid users accidentally committing double payments.
"""
return self.abort_pending_allowed
@property
def requires_invoice_immediately(self):
"""
@@ -1019,10 +1034,12 @@ class BasePaymentProvider:
On success, you should set ``payment.state = OrderPayment.PAYMENT_STATE_CANCELED`` (or call the super method).
On failure, you should raise a PaymentException.
"""
if payment.state == OrderPayment.PAYMENT_STATE_PENDING and not self.abort_pending_allowed:
raise PaymentException(_(
"This payment is already being processed and can not be canceled any more."
))
if payment.state == OrderPayment.PAYMENT_STATE_PENDING:
if not self._payment_abort_pending_allowed(payment):
raise PaymentException(_(
"This payment is already being processed and cannot be canceled any more."
))
payment.state = OrderPayment.PAYMENT_STATE_CANCELED
payment.save(update_fields=['state'])