Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion app/eventyay/api/views/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -1247,8 +1247,14 @@ def confirm(self, request, **kwargs):
return self.retrieve(request, [], **kwargs)

@action(detail=True, methods=['POST'])
@transaction.atomic
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting transaction.atomic here doesn't work, because this refund function already handle an PaymentException:

try:
    r.payment_provider.execute_refund(r)
except PaymentException as e:

Read the Avoid catching exceptions inside atomic! note.

def refund(self, request, **kwargs):
payment = self.get_object()
# Acquire row-level lock on payment to prevent concurrent refund race conditions.
# We reuse DRF's filtering & permission logic while issuing a single SELECT ... FOR UPDATE.
queryset = self.filter_queryset(self.get_queryset().select_for_update())
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
lookup_value = self.kwargs[lookup_url_kwarg]
payment = get_object_or_404(queryset, **{self.lookup_field: lookup_value})
amount = serializers.DecimalField(max_digits=10, decimal_places=2).to_internal_value(
request.data.get('amount', str(payment.amount))
)
Expand All @@ -1265,6 +1271,7 @@ def refund(self, request, **kwargs):

full_refund_possible = payment.payment_provider.payment_refund_supported(payment)
partial_refund_possible = payment.payment_provider.payment_partial_refund_supported(payment)
# Calculate available amount under lock - prevents TOCTOU race condition
available_amount = payment.amount - payment.refunded_amount

if amount <= 0:
Expand Down