mirror of
https://github.com/pretix/pretix.git
synced 2026-08-19 12:16:26 +00:00
add timeout to paypal after which a pending payment can be canceled
This commit is contained in:
@@ -23,7 +23,7 @@ import json
|
||||
import logging
|
||||
import urllib.parse
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from decimal import Decimal
|
||||
|
||||
from django import forms
|
||||
@@ -55,7 +55,7 @@ from pretix.base.forms import SecretKeySettingsField
|
||||
from pretix.base.forms.questions import guess_country
|
||||
from pretix.base.models import Event, Order, OrderPayment, OrderRefund, Quota
|
||||
from pretix.base.payment import BasePaymentProvider, PaymentException
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.base.settings import SettingsSandbox, settings_hierarkey
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.urls import mainreverse_absolute
|
||||
from pretix.multidomain.urlreverse import eventreverse, eventreverse_absolute
|
||||
@@ -193,6 +193,31 @@ class PaypalSettingsHolder(BasePaymentProvider):
|
||||
}
|
||||
)
|
||||
)),
|
||||
('allow_retries_during_compliance_hold',
|
||||
forms.BooleanField(
|
||||
label=_('Allow further payments during compliance hold'),
|
||||
help_text=_(
|
||||
'PayPals fraud prevention might block processing of individual payments for a considerable amount '
|
||||
'of time. The payment is marked as "pending" during this time window. You can allow your customers to '
|
||||
'start another payment attempts during that window. This might result in overpayment of orders if the'
|
||||
'original payment is approved.'
|
||||
),
|
||||
required=False
|
||||
)),
|
||||
('timeout_payment_during_compliance_hold',
|
||||
forms.IntegerField(
|
||||
label=_('Timeout further payment attempts'),
|
||||
help_text=_(
|
||||
'Time duration in minutes after which another payment attempt is possible, while the last payment is '
|
||||
'still under investigation.'
|
||||
),
|
||||
required=False,
|
||||
widget=forms.NumberInput(
|
||||
attrs={
|
||||
'data-checkbox-dependency': '#id_payment_paypal_allow_retries_during_compliance_hold',
|
||||
}
|
||||
)
|
||||
)),
|
||||
|
||||
]
|
||||
|
||||
@@ -517,6 +542,18 @@ class PaypalMethod(BasePaymentProvider):
|
||||
|
||||
@property
|
||||
def abort_pending_allowed(self):
|
||||
return True
|
||||
|
||||
def abort_pending_payment_allowed(self, payment) -> bool:
|
||||
if not self.settings.get('allow_retries_during_compliance_hold', as_type=bool, default=True):
|
||||
return False
|
||||
|
||||
if payment.info_data.get('create_time', False):
|
||||
create_time = datetime.fromisoformat(payment.info_data['create_time'])
|
||||
duration = self.settings.get('timeout_payment_during_compliance_hold', as_type=int, default=0)
|
||||
if datetime.now(tz=timezone.utc) - create_time > timedelta(minutes=duration):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _create_paypal_order(self, request, payment=None, cart_total=None):
|
||||
@@ -859,22 +896,34 @@ class PaypalMethod(BasePaymentProvider):
|
||||
logger.info('{}: {} - paypal payment processing time'.format(str(payment.global_id), str(duration)))
|
||||
|
||||
def payment_pending_render(self, request, payment) -> str:
|
||||
stuck_in_compliance = False
|
||||
retry = True
|
||||
|
||||
if payment.state == OrderPayment.PAYMENT_STATE_PENDING:
|
||||
retry = self.abort_pending_payment_allowed(payment) and self.abort_pending_allowed
|
||||
|
||||
try:
|
||||
if (
|
||||
payment.info
|
||||
and payment.info_data['purchase_units'][0]['payments']['captures'][0]['status'] == 'PENDING'
|
||||
):
|
||||
retry = False
|
||||
stuck_in_compliance = True
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
try:
|
||||
if payment.info and payment.info_data['status'] == "APPROVED":
|
||||
stuck_in_compliance = True
|
||||
except (KeyError):
|
||||
pass
|
||||
|
||||
error = payment.info_data.get("error", {})
|
||||
is_known_issue = error.get("name") == "RESOURCE_NOT_FOUND" or "RESOURCE_NOT_FOUND" in (error.get("message") or "")
|
||||
|
||||
template = get_template('pretixplugins/paypal2/pending.html')
|
||||
ctx = {'request': request, 'event': self.event, 'settings': self.settings,
|
||||
'retry': retry, 'order': payment.order, 'is_known_issue': is_known_issue}
|
||||
'stuck_in_compliance': stuck_in_compliance, 'retry': retry, 'order': payment.order,
|
||||
'is_known_issue': is_known_issue}
|
||||
return template.render(ctx)
|
||||
|
||||
def matching_id(self, payment: OrderPayment):
|
||||
@@ -1122,6 +1171,10 @@ class PaypalMethod(BasePaymentProvider):
|
||||
return self.settings.get('_invoice_text', as_type=LazyI18nString, default='')
|
||||
|
||||
|
||||
settings_hierarkey.add_default('payment_paypal_allow_retries_during_compliance_hold', True, bool)
|
||||
settings_hierarkey.add_default('payment_paypal_timeout_payment_during_compliance_hold', 0, int)
|
||||
|
||||
|
||||
class PaypalWallet(PaypalMethod):
|
||||
identifier = 'paypal'
|
||||
verbose_name = _('PayPal')
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
Your payment has failed due to a known issue within PayPal. Please try again, there is a high chance of the
|
||||
payment succeeding on a second or third attempt. You can also try other payment methods, if available.
|
||||
{% endblocktrans %}</div>
|
||||
{% else %}
|
||||
{% elif stuck_in_compliance %}
|
||||
<p>{% blocktrans trimmed %}
|
||||
Our attempt to execute your payment via PayPal has failed. Please try again or contact us.
|
||||
Your payment is being processed by PayPal. This takes longer than usual. You can wait until PayPal
|
||||
acknowledges the payment or you can try paying again with this or another payment method.
|
||||
This might result in you being charged twice in case PayPal allows your initial payment attempt.
|
||||
Please contact us, to resolve this case.
|
||||
{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
|
||||
Reference in New Issue
Block a user