Compare commits

...

2 Commits

Author SHA1 Message Date
Martin Gross
3113fdb53a Review notes: lock payment for execution/capture 2024-05-23 16:02:37 +02:00
Martin Gross
f2f1d548df PPv2 APM: Create referenced PPObjects for APM Orders; enable webhooks to capture them 2024-03-05 18:04:53 +01:00
2 changed files with 38 additions and 21 deletions

View File

@@ -31,6 +31,7 @@ from django import forms
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.core.cache import cache from django.core.cache import cache
from django.db import transaction
from django.http import HttpRequest from django.http import HttpRequest
from django.template.loader import get_template from django.template.loader import get_template
from django.templatetags.static import static from django.templatetags.static import static
@@ -55,6 +56,7 @@ from pretix.base.models import Event, Order, OrderPayment, OrderRefund, Quota
from pretix.base.payment import BasePaymentProvider, PaymentException from pretix.base.payment import BasePaymentProvider, PaymentException
from pretix.base.services.mail import SendMailException from pretix.base.services.mail import SendMailException
from pretix.base.settings import SettingsSandbox from pretix.base.settings import SettingsSandbox
from pretix.helpers import OF_SELF
from pretix.helpers.urls import build_absolute_uri as build_global_uri from pretix.helpers.urls import build_absolute_uri as build_global_uri
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
from pretix.plugins.paypal2.client.core.environment import ( from pretix.plugins.paypal2.client.core.environment import (
@@ -586,6 +588,9 @@ class PaypalMethod(BasePaymentProvider):
}, },
}) })
response = self.client.execute(paymentreq) response = self.client.execute(paymentreq)
if payment:
ReferencedPayPalObject.objects.get_or_create(order=payment.order, payment=payment, reference=response.result.id)
except IOError as e: except IOError as e:
if "RESOURCE_NOT_FOUND" in str(e): if "RESOURCE_NOT_FOUND" in str(e):
messages.error(request, _('Your payment has failed due to a known issue within PayPal. Please try ' messages.error(request, _('Your payment has failed due to a known issue within PayPal. Please try '
@@ -618,7 +623,13 @@ class PaypalMethod(BasePaymentProvider):
} }
return template.render(ctx) return template.render(ctx)
@transaction.atomic
def execute_payment(self, request: HttpRequest, payment: OrderPayment): def execute_payment(self, request: HttpRequest, payment: OrderPayment):
payment = OrderPayment.objects.select_for_update(of=OF_SELF).get(pk=payment.pk)
if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED:
logger.warning('payment is already confirmed; possible return-view/webhook race-condition')
return
try: try:
if request.session.get('payment_paypal_oid', '') == '': if request.session.get('payment_paypal_oid', '') == '':
raise PaymentException(_('We were unable to process your payment. See below for details on how to ' raise PaymentException(_('We were unable to process your payment. See below for details on how to '

View File

@@ -482,8 +482,8 @@ def webhook(request, *args, **kwargs):
amount=payment.amount - known_sum amount=payment.amount - known_sum
) )
elif payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED, elif payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED,
OrderPayment.PAYMENT_STATE_CANCELED, OrderPayment.PAYMENT_STATE_FAILED) \ OrderPayment.PAYMENT_STATE_CANCELED, OrderPayment.PAYMENT_STATE_FAILED):
and sale['status'] == 'COMPLETED': if sale['status'] == 'COMPLETED':
any_captures = False any_captures = False
all_captures_completed = True all_captures_completed = True
for purchaseunit in sale['purchase_units']: for purchaseunit in sale['purchase_units']:
@@ -505,6 +505,12 @@ def webhook(request, *args, **kwargs):
payment.confirm() payment.confirm()
except Quota.QuotaExceededException: except Quota.QuotaExceededException:
pass pass
elif sale['status'] == 'APPROVED':
request.session['payment_paypal_oid'] = payment.info_data['id']
try:
payment.payment_provider.execute_payment(request, payment)
except PaymentException as e:
logger.exception('PayPal2 - Could not capture/execute_payment from Webhook: {}'.format(str(e)))
return HttpResponse(status=200) return HttpResponse(status=200)