diff --git a/doc/development/api/payment.rst b/doc/development/api/payment.rst index 92d358501e..a060eaed21 100644 --- a/doc/development/api/payment.rst +++ b/doc/development/api/payment.rst @@ -114,6 +114,8 @@ The provider class .. automethod:: api_payment_details + .. automethod:: matching_id + .. automethod:: shred_payment_info .. automethod:: cancel_payment diff --git a/src/pretix/base/exporters/orderlist.py b/src/pretix/base/exporters/orderlist.py index 6afadc7105..e13191fff2 100644 --- a/src/pretix/base/exporters/orderlist.py +++ b/src/pretix/base/exporters/orderlist.py @@ -531,10 +531,11 @@ class InvoiceDataExporter(MultiSheetListExporter): _('Foreign currency rate'), _('Total value (with taxes)'), _('Total value (without taxes)'), + _('Payment matching IDs'), ] qs = self.event.invoices.order_by('full_invoice_no').select_related( 'order', 'refers' - ).annotate( + ).prefetch_related('order__payments').annotate( total_gross=Subquery( InvoiceLine.objects.filter( invoice=OuterRef('pk') @@ -551,6 +552,16 @@ class InvoiceDataExporter(MultiSheetListExporter): ) ) for i in qs: + pmis = [] + for p in i.order.payments.all(): + if p.state in (OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_CREATED, + OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_REFUNDED): + pprov = p.payment_provider + if pprov: + mid = pprov.matching_id(p) + if mid: + pmis.append(mid) + pmi = '\n'.join(pmis) yield [ i.full_invoice_no, date_format(i.date, "SHORT_DATE_FORMAT"), @@ -581,6 +592,7 @@ class InvoiceDataExporter(MultiSheetListExporter): i.foreign_currency_rate, i.total_gross if i.total_gross else Decimal('0.00'), Decimal(i.total_net if i.total_net else '0.00').quantize(Decimal('0.01')), + pmi ] elif sheet == 'lines': yield [ diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 3cd63d4104..d28a95fe86 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -721,6 +721,16 @@ class BasePaymentProvider: """ return {} + def matching_id(self, payment: OrderPayment): + """ + Will be called to get an ID for a matching this payment when comparing pretix records with records of an external + source. This should return the main transaction ID for your API. + + :param payment: The payment in question. + :return: A string or None + """ + return None + class PaymentException(Exception): pass diff --git a/src/pretix/plugins/paypal/payment.py b/src/pretix/plugins/paypal/payment.py index c734ee944e..0e995f791d 100644 --- a/src/pretix/plugins/paypal/payment.py +++ b/src/pretix/plugins/paypal/payment.py @@ -396,6 +396,14 @@ class Paypal(BasePaymentProvider): 'retry': retry, 'order': payment.order} return template.render(ctx) + def matching_id(self, payment: OrderPayment): + sale_id = None + for trans in payment.info_data.get('transactions', []): + for res in trans.get('related_resources', []): + if 'sale' in res and 'id' in res['sale']: + sale_id = res['sale']['id'] + return sale_id or payment.info_data.get('id', None) + def api_payment_details(self, payment: OrderPayment): sale_id = None for trans in payment.info_data.get('transactions', []): diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py index 5e02675a59..24fff0f7a2 100644 --- a/src/pretix/plugins/stripe/payment.py +++ b/src/pretix/plugins/stripe/payment.py @@ -432,6 +432,9 @@ class StripeMethod(BasePaymentProvider): } return template.render(ctx) + def matching_id(self, payment: OrderPayment): + return payment.info_data.get("id", None) + def api_payment_details(self, payment: OrderPayment): return { "id": payment.info_data.get("id", None),