Skip to content

Commit

Permalink
Merge pull request #16 from oscaro/feature/ignore-duplicates
Browse files Browse the repository at this point in the history
Ignore duplicates
  • Loading branch information
maiksprenger committed Jul 14, 2015
2 parents f2e1038 + 956eaaf commit 9c18b90
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 30 deletions.
18 changes: 14 additions & 4 deletions adyen/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def _record_audit_trail(self, request, status, txn_details):

pass

def handle_payment_feedback(self, request, record_audit_trail):
def handle_payment_feedback(self, request):
"""
Validate, process, optionally record audit trail and provide feedback
about the current payment response.
Expand Down Expand Up @@ -167,9 +167,8 @@ def handle_payment_feedback(self, request, record_audit_trail):
success, status, details = response.process()
txn_details = self._unpack_details(details)

# ... and record the audit trail if instructed to...
if record_audit_trail:
self._record_audit_trail(request, status, txn_details)
# ... and record the audit trail.
self._record_audit_trail(request, status, txn_details)

# ... prepare the feedback data...
output_data = {
Expand Down Expand Up @@ -220,6 +219,17 @@ def assess_notification_relevance(self, request):
if request.POST.get(Constants.EVENT_CODE) != Constants.EVENT_CODE_AUTHORISATION:
return False, True

# Adyen duplicates many notifications. This bit makes sure we ignore them.
# "Duplicate notifications have the same corresponding values for their eventCode and
# pspReference fields." https://docs.adyen.com/display/TD/Accept+notifications
reference = request.POST[Constants.PSP_REFERENCE]
# The event code gets checked above, so we only need to check for the reference now.
if AdyenTransaction.objects.filter(reference=reference).exists():
# We already stored a transaction with this reference, so we can ignore the
# notification. As above, we still acknowledge it to Adyen, in case it missed
# our previous acknowledgment.
return False, True

# Seems legit, just do it :)
return True, True

Expand Down
7 changes: 1 addition & 6 deletions adyen/scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,7 @@ def _normalize_feedback(self, feedback):
return success, common_status, details

def handle_payment_feedback(self, request):
return self._normalize_feedback(
Facade().handle_payment_feedback(request, record_audit_trail=True))

def check_payment_outcome(self, request):
return self._normalize_feedback(
Facade().handle_payment_feedback(request, record_audit_trail=False))
return self._normalize_feedback(Facade().handle_payment_feedback(request))

def assess_notification_relevance(self, request):
return Facade().assess_notification_relevance(request)
Expand Down
39 changes: 19 additions & 20 deletions tests/test_adyen.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,26 +261,6 @@ def test_get_origin_ip_address(self):

def test_handle_authorised_payment(self):

# Before the test, there are no recorded transactions in the database.
num_recorded_transactions = AdyenTransaction.objects.all().count()
self.assertEqual(num_recorded_transactions, 0)

self.request.GET = deepcopy(AUTHORISED_PAYMENT_PARAMS_GET)
success, status, details = self.scaffold.check_payment_outcome(self.request)

self.assertTrue(success)
self.assertEqual(status, Scaffold.PAYMENT_STATUS_ACCEPTED)
self.assertEqual(details.get('amount'), 13894)
self.assertEqual(details.get('ip_address'), '127.0.0.1')
self.assertEqual(details.get('method'), 'adyen')
self.assertEqual(details.get('psp_reference'), '8814136447235922')
self.assertEqual(details.get('status'), 'AUTHORISED')

# After calling `check_payment_outcome`, there are still no recorded
# transactions in the database.
num_recorded_transactions = AdyenTransaction.objects.all().count()
self.assertEqual(num_recorded_transactions, 0)

self.request.GET = deepcopy(AUTHORISED_PAYMENT_PARAMS_GET)
success, status, details = self.scaffold.handle_payment_feedback(self.request)

Expand Down Expand Up @@ -441,3 +421,22 @@ def test_assess_notification_relevance(self):
self.request.POST['eventCode'] = 'REPORT_AVAILABLE'
must_process, must_ack = self.scaffold.assess_notification_relevance(self.request)
self.assertTupleEqual((must_process, must_ack), (False, True))

def test_assert_duplicate_notifications(self):
"""
This test tests that duplicate notifications are ignored.
"""
self.request.method = 'POST'
self.request.POST = deepcopy(AUTHORISED_PAYMENT_PARAMS_POST)

# We have a valid request. So let's confirm that we think we should process
# and acknowledge it.
assert (True, True) == self.scaffold.assess_notification_relevance(self.request)

# Let's process it then.
__, __, __ = self.scaffold.handle_payment_feedback(self.request)

# As we have already processed that request, we now shouldn't process the request
# any more. But we still acknowledge it.
assert (False, True) == self.scaffold.assess_notification_relevance(self.request)

0 comments on commit 9c18b90

Please sign in to comment.