further fixes

This commit is contained in:
Lukas Bockstaller
2026-08-17 15:39:29 +02:00
parent 25e651f3e8
commit b4bf300855
2 changed files with 74 additions and 5 deletions
+15 -5
View File
@@ -33,6 +33,7 @@
# License for the specific language governing permissions and limitations under the License. # License for the specific language governing permissions and limitations under the License.
import json import json
import logging import logging
import re
from decimal import Decimal from decimal import Decimal
from django.contrib import messages from django.contrib import messages
@@ -361,7 +362,13 @@ def webhook(request, *args, **kwargs):
if event_json['resource_type'] == 'checkout-order': if event_json['resource_type'] == 'checkout-order':
payloadid = event_json['resource']['id'] payloadid = event_json['resource']['id']
elif event_json['resource_type'] == 'refund' or event_json['resource_type'] == 'capture': elif event_json['resource_type'] == 'refund' or event_json['resource_type'] == 'capture':
payloadid = get_link(event_json['resource']['links'], 'up')['href'].split('/')[-1] payloadid = get_order_id(event_json.get('resource', {}).get('links', []))
if payloadid is None:
# if we get a PAYMENT.CAPTURE.DECLINED webhook because a capture wasn't created due to
# violated validations, then it is labeled as a `capture` ressource_type but is in fact
# an `order` ressource_type as there is no `capture`. So we have to fall back
# See test_webhook_capture_declined for a redacted payload we've received
payloadid = event_json['resource']['id']
else: else:
return HttpResponse("Not interested in this resource type", status=200) return HttpResponse("Not interested in this resource type", status=200)
@@ -553,9 +560,12 @@ def isu_disconnect(request, **kwargs):
})) }))
def get_link(links, rel): ORDER_ID_RE = re.compile(r"/checkout/orders/([^/?]+)")
for link in links:
if link['rel'] == rel:
return link
def get_order_id(links):
for link in links:
match = ORDER_ID_RE.search(link.get("href", ""))
if match:
return match.group(1)
return None return None
+59
View File
@@ -837,3 +837,62 @@ def test_webhook_pending_payment(env, client, monkeypatch):
order.refresh_from_db() order.refresh_from_db()
with scopes_disabled(): with scopes_disabled():
assert order.payments.first().state == OrderPayment.PAYMENT_STATE_PENDING assert order.payments.first().state == OrderPayment.PAYMENT_STATE_PENDING
@pytest.mark.django_db
def test_webhook_capture_declined(env, client, monkeypatch):
order = env[1]
order.status = Order.STATUS_PENDING
order.save()
with scopes_disabled():
order.payments.update(state=OrderPayment.PAYMENT_STATE_CREATED)
pp_order = Result(get_test_order_review_pending())
mock_orders_get_request = MagicMock(return_value=pp_order)
monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", mock_orders_get_request)
monkeypatch.setattr("pretix.plugins.paypal2.payment.PaypalMethod.init_api", init_api)
with scopes_disabled():
ReferencedPayPalObject.objects.create(order=order, payment=order.payments.first(),
reference="806440346Y391300T")
assert order.payments.first().state == OrderPayment.PAYMENT_STATE_CREATED
client.post('/_paypal/webhook/', json.dumps(
{
"create_time": "2026-08-17T12:23:30.687Z",
"event_type": "PAYMENT.CAPTURE.DECLINED",
"event_version": "1.0",
"id": "WH-XXXXXXXXXXXX-XXXXXXXXX",
"links": [
{
"href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-XXXXXXXXXXXX-XXXXXXXXX",
"method": "GET",
"rel": "self"
},
{
"href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-XXXXXXXXXXXX-XXXXXXXXX/resend",
"method": "POST",
"rel": "resend"
}
],
"resource": {
"amount": {},
"custom_id": "Order ABC-12345",
"disbursement_mode": "INSTANT",
"final_capture": True,
"id": "806440346Y391300T",
"payee": {},
"seller_protection": {},
"seller_receivable_breakdown": {},
"status": "DECLINED",
"supplementary_data": {}
},
"resource_type": "capture",
"resource_version": "2.0",
"summary": "A payment capture for € 30.0 EUR was declined."
}), content_type='application_json')
order = env[1]
order.refresh_from_db()
with scopes_disabled():
assert order.payments.first().state == OrderPayment.PAYMENT_STATE_FAILED