-
Notifications
You must be signed in to change notification settings - Fork 2
feat: adding extra information to payment processor response #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -22,11 +22,10 @@ | |||
| from django.utils.translation import ugettext_lazy as _ | ||||
| from django.views.decorators.csrf import csrf_exempt | ||||
| from django.views.generic import View | ||||
| from oscar.apps.partner import strategy | ||||
| from oscar.core.loading import get_class, get_model | ||||
|
|
||||
| from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin | ||||
| from ecommerce.extensions.checkout.utils import get_receipt_page_url | ||||
| from oscar.apps.partner import strategy | ||||
| from oscar.core.loading import get_class, get_model | ||||
|
|
||||
| from .processors import HyperPay, HyperPayMada | ||||
|
|
||||
|
|
@@ -144,29 +143,29 @@ def _verify_status(self, resource_path): | |||
| logger.warning( | ||||
| 'Received a pending status code %s from HyperPay for payment id %s.', | ||||
| result_code, | ||||
| response_data['id'] | ||||
| response_data.get('id', 'id-not-found') | ||||
| ) | ||||
| status = PaymentStatus.PENDING | ||||
| elif self.PENDING_NOT_CHANGEABLE_SOON_CODES_REGEX.search(result_code): | ||||
| logger.warning( | ||||
| 'Received a pending status code %s from HyperPay for payment id %s. As this can change ' | ||||
| 'after several days, treating it as a failure.', | ||||
| result_code, | ||||
| response_data['id'] | ||||
| response_data.get('id', 'id-not-found') | ||||
| ) | ||||
| status = PaymentStatus.FAILURE | ||||
| elif self.SUCCESS_CODES_REGEX.search(result_code): | ||||
| logger.info( | ||||
| 'Received a success status code %s from HyperPay for payment id %s.', | ||||
| result_code, | ||||
| response_data['id'] | ||||
| response_data.get('id', 'id-not-found') | ||||
| ) | ||||
| elif self.SUCCESS_MANUAL_REVIEW_CODES_REGEX.search(result_code): | ||||
| logger.error( | ||||
| 'Received a success status code %s from HyperPay which requires manual verification for payment id %s.' | ||||
| 'Treating it as a failed transaction.', | ||||
| result_code, | ||||
| response_data['id'] | ||||
| response_data.get('id', 'id-not-found') | ||||
| ) | ||||
|
|
||||
| # This is a temporary change till we get clarity on whether this should be treated as a failure. | ||||
|
|
@@ -175,7 +174,7 @@ def _verify_status(self, resource_path): | |||
| logger.error( | ||||
| 'Received a rejection status code %s from HyperPay for payment id %s', | ||||
| result_code, | ||||
| response_data['id'] | ||||
| response_data.get('id', 'id-not-found') | ||||
| ) | ||||
| status = PaymentStatus.FAILURE | ||||
|
|
||||
|
|
@@ -229,16 +228,38 @@ def _get_check_status(self, request): | |||
| del request.session['hyperpay_dont_check_status'] | ||||
| return check_status | ||||
|
|
||||
| def _generate_user_data(self, user): | ||||
| """ | ||||
| This extracts the basic user information. | ||||
|
|
||||
| Args: | ||||
| user <User model>: User instance. | ||||
|
|
||||
| Returns | ||||
| dictionary: Basic user data. | ||||
| """ | ||||
|
|
||||
| if not user: | ||||
| return {} | ||||
|
|
||||
| return { | ||||
| "id": user.id, | ||||
| "email": user.email, | ||||
| "username": user.username, | ||||
| } | ||||
|
|
||||
| def get(self, request, encrypted_resource_path=None): | ||||
| """ | ||||
| Handle the response from HyperPay and redirect to the appropriate page based on the status. | ||||
| """ | ||||
| if encrypted_resource_path is None: | ||||
| self.payment_processor.record_processor_response(request.GET, transaction_id=request.GET.get('id')) | ||||
|
|
||||
| verification_response = '' | ||||
| verification_response = {} | ||||
| basket = None | ||||
| error = None | ||||
| transaction_id = 'Unknown' | ||||
| status = PaymentStatus.PENDING | ||||
|
|
||||
| resource_path = self._get_resource_path(request, encrypted_resource_path) | ||||
| if resource_path is None: | ||||
|
|
@@ -248,21 +269,19 @@ def get(self, request, encrypted_resource_path=None): | |||
| check_status = self._get_check_status(request) | ||||
|
|
||||
| try: | ||||
| status = PaymentStatus.PENDING | ||||
| if check_status: | ||||
| verification_response, status = self._verify_status(resource_path) | ||||
| if (verification_response and isinstance(verification_response, dict) and | ||||
| verification_response.get('merchantTransactionId')): | ||||
| transaction_id = verification_response['merchantTransactionId'] | ||||
|
|
||||
| if verification_response and 'merchantMemo' in verification_response: | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think is better check if ecommerce-hyperpay/hyperpay/views.py Line 126 in 48b02da
doesn't return a dict for response...
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That could be possible if someone makes a big change in the |
||||
| transaction_id = verification_response['merchantMemo'] | ||||
| basket_id = OrderNumberGenerator().basket_id(transaction_id) | ||||
| basket = self._get_basket(basket_id) | ||||
|
|
||||
| if status == PaymentStatus.FAILURE: | ||||
| return redirect(reverse('payment_error')) | ||||
| if status == PaymentStatus.PENDING: | ||||
| elif status == PaymentStatus.PENDING: | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it better elif than the previous if ?, this is only curiosity of the change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in this case it doesn't make any difference, probably pylint errors since that elif is after a return statement , but this is part of a testing that I made so I shouldn't have included |
||||
| return self._handle_pending_status(request, encrypted_resource_path, resource_path) | ||||
|
|
||||
| basket_id = OrderNumberGenerator().basket_id(verification_response['merchantMemo']) | ||||
| basket = self._get_basket(basket_id) | ||||
|
|
||||
| transaction_id = verification_response['id'] | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why not all transaction_id are with the id? I mean this line is not inside and if block, and it's the last one that changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. me neither There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andrey-canon what do you think in that behavior is ok that good transactions keep the id(processor sent) and the others keep the 'merchantxxxx' in
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. personally I prefer to keep the same transaction id for all the operations, it's easier to filter error and success cases, however that is out of the scope of this pr and I would like to keep changes at minimum |
||||
|
|
||||
| if not basket: | ||||
|
|
@@ -276,12 +295,22 @@ def get(self, request, encrypted_resource_path=None): | |||
| request.user.username, | ||||
| ) | ||||
| raise Http404 | ||||
| except Exception as exc: | ||||
| error = exc.__class__.__name__ | ||||
| finally: | ||||
| verification_response.update({ | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||
| "request_user": self._generate_user_data(request.user), | ||||
| "basket_user": self._generate_user_data(getattr(basket, 'owner', None)), | ||||
| "status": status.name, | ||||
| "error": error, | ||||
| }) | ||||
|
|
||||
| payment_processor_response = self.payment_processor.record_processor_response( | ||||
| verification_response, | ||||
| transaction_id=transaction_id, | ||||
| basket=basket | ||||
| ) | ||||
|
|
||||
| try: | ||||
| with transaction.atomic(): | ||||
| try: | ||||
|
|
||||



There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove this default?
Is there not a case where the default, if check_status status is false, verify_status does not return a status?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't remove that, I consider this is as part of the initial conditions so I moved that line here