Add payment provider specific details to the API

This commit is contained in:
Raphael Michel
2019-08-30 17:04:22 +02:00
parent e3a4ec93fc
commit aa99dbc830
9 changed files with 81 additions and 13 deletions
+10 -1
View File
@@ -231,6 +231,15 @@ created datetime Date and time o
payment_date datetime Date and time of completion of this payment (or ``null``)
provider string Identification string of the payment provider
payment_url string The URL where an user can continue with the payment (or ``null``)
details object Payment-specific information. This is a dictionary
with various fields that can be different between
payment providers, versions, payment states, etc. If
you read this field, you always need to be able to
deal with situations where values that you expect are
missing. Mostly, the field contains various IDs that
can be used for matching with other systems. If a
payment provider does not implement this feature,
the object is empty.
===================================== ========================== =======================================================
.. versionchanged:: 2.0
@@ -239,7 +248,7 @@ payment_url string The URL where a
.. versionchanged:: 3.1
The attribute ``payment_url`` has been added.
The attributes ``payment_url`` and ``details`` have been added.
.. _order-refund-resource:
+2
View File
@@ -108,6 +108,8 @@ The provider class
.. automethod:: execute_refund
.. automethod:: api_payment_details
.. automethod:: shred_payment_info
.. autoattribute:: is_implicit
+11 -1
View File
@@ -297,12 +297,22 @@ class PaymentURLField(serializers.URLField):
})
class PaymentDetailsField(serializers.Field):
def to_representation(self, value: OrderPayment):
pp = value.payment_provider
if not pp:
return {}
return pp.api_payment_details(value)
class OrderPaymentSerializer(I18nAwareModelSerializer):
payment_url = PaymentURLField(source='*', allow_null=True, read_only=True)
details = PaymentDetailsField(source='*', allow_null=True, read_only=True)
class Meta:
model = OrderPayment
fields = ('local_id', 'state', 'amount', 'created', 'payment_date', 'provider', 'payment_url')
fields = ('local_id', 'state', 'amount', 'created', 'payment_date', 'provider', 'payment_url',
'details')
class OrderRefundSerializer(I18nAwareModelSerializer):
+12 -10
View File
@@ -608,22 +608,24 @@ class Event(EventMixin, LoggedModel):
question_map=question_map
)
def get_payment_providers(self) -> dict:
def get_payment_providers(self, cached=False) -> dict:
"""
Returns a dictionary of initialized payment providers mapped by their identifiers.
"""
from ..signals import register_payment_providers
responses = register_payment_providers.send(self)
providers = {}
for receiver, response in responses:
if not isinstance(response, list):
response = [response]
for p in response:
pp = p(self)
providers[pp.identifier] = pp
if not cached or not hasattr(self, '_cached_payment_providers'):
responses = register_payment_providers.send(self)
providers = {}
for receiver, response in responses:
if not isinstance(response, list):
response = [response]
for p in response:
pp = p(self)
providers[pp.identifier] = pp
return OrderedDict(sorted(providers.items(), key=lambda v: str(v[1].verbose_name)))
self._cached_payment_providers = OrderedDict(sorted(providers.items(), key=lambda v: str(v[1].verbose_name)))
return self._cached_payment_providers
def get_html_mail_renderer(self):
"""
+1 -1
View File
@@ -1196,7 +1196,7 @@ class OrderPayment(models.Model):
"""
Cached access to an instance of the payment provider in use.
"""
return self.order.event.get_payment_providers().get(self.provider)
return self.order.event.get_payment_providers(cached=True).get(self.provider)
def _mark_paid(self, force, count_waitinglist, user, auth, ignore_date=False, overpaid=False):
from pretix.base.signals import order_paid
+20
View File
@@ -657,6 +657,15 @@ class BasePaymentProvider:
obj.info = '{}'
obj.save(update_fields=['info'])
def api_payment_details(self, payment: OrderPayment):
"""
Will be called to populate the ``details`` parameter of the payment in the REST API.
:param payment: The payment in question.
:return: A serializable dictionary
"""
return {}
class PaymentException(Exception):
pass
@@ -720,6 +729,12 @@ class BoxOfficeProvider(BasePaymentProvider):
def order_change_allowed(self, order: Order) -> bool:
return False
def api_payment_details(self, payment: OrderPayment):
return {
"pos_id": payment.info_data.get('pos_id', None),
"receipt_id": payment.info_data.get('receipt_id', None),
}
def payment_control_render(self, request, payment) -> str:
if not payment.info:
return
@@ -864,6 +879,11 @@ class OffsettingProvider(BasePaymentProvider):
def order_change_allowed(self, order: Order) -> bool:
return False
def api_payment_details(self, payment: OrderPayment):
return {
"orders": payment.info_data.get('orders', []),
}
def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:
return _('Balanced against orders: %s' % ', '.join(payment.info_data['orders']))
+14
View File
@@ -395,6 +395,20 @@ class Paypal(BasePaymentProvider):
'retry': retry, 'order': payment.order}
return template.render(ctx)
def api_payment_details(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 {
"payer_email": payment.info_data.get('payer', {}).get('payer_info', {}).get('email'),
"payer_id": payment.info_data.get('payer', {}).get('payer_info', {}).get('payer_id'),
"cart_id": payment.info_data.get('cart', None),
"payment_id": payment.info_data.get('id', None),
"sale_id": sale_id,
}
def payment_control_render(self, request: HttpRequest, payment: OrderPayment):
template = get_template('pretixplugins/paypal/control.html')
ctx = {'request': request, 'event': self.event, 'settings': self.settings,
+6
View File
@@ -411,6 +411,12 @@ class StripeMethod(BasePaymentProvider):
}
return template.render(ctx)
def api_payment_details(self, payment: OrderPayment):
return {
"id": payment.info_data.get("id", None),
"payment_method": payment.info_data.get("payment_method", None)
}
def payment_control_render(self, request, payment) -> str:
if payment.info:
payment_info = json.loads(payment.info)
+5
View File
@@ -160,6 +160,10 @@ TEST_PAYMENTS_RES = [
"payment_date": "2017-12-01T10:00:00Z",
"provider": "stripe",
"payment_url": None,
"details": {
"id": None,
"payment_method": None
},
"state": "refunded",
"amount": "23.00"
},
@@ -169,6 +173,7 @@ TEST_PAYMENTS_RES = [
"payment_date": None,
"provider": "banktransfer",
"payment_url": None,
"details": {},
"state": "pending",
"amount": "23.00"
}