Wow that is interesting, thank you for this update. I will be reading more about this.
On Thu, Aug 22, 2019 at 2:15 PM Ahmed Shahwan <a@shahwan.me> wrote:
I solved the problem.--It's related to the atomic requests. I had "ATOMIC_REQUESTS": True in the database settings and XPAY's service responded so fast while the first atomic-request hadn't finished yet, so it prevented the other callback-url from accessing the same instance.Thank you very much for your time and effort
On Wednesday, August 21, 2019 at 4:32:03 PM UTC+2, mohammed habib wrote:What is the Transaction class which your model inherits from ? Is it just a base model ?Let's do an experimentPrint("object created") right after the create() methodPrint("callback requested") on top of your callback methodWhich one do you see first ?Sent from my iPhoneThat's the class, nothing different other than HTTP requests--class CashPayment(BasePayment):"""Cash Payment Wrapper Class"""integration_id = CASH_INTEGRATION_IDdef __init__(self, amount_cents: int, billing_data: dict):super().__init__(amount_cents, billing_data)def pay_request(self):"""Step 4. issue Pay request"""payload = {"source": {"identifier": "cash", "subtype": "CASH"},"payment_token": self.payment_key_res.get("token"),}self.pay_req_res = self.hit_accept(self._api_url + "acceptance/payments/pay", payload=payload)
On Wednesday, August 21, 2019 at 3:39:13 PM UTC+2, mohamed habib wrote:if you are able to share more code from the `utils.CashPayment` call that may help usOn Wed, Aug 21, 2019 at 4:37 PM mohamed habib <moe....@gmail.com> wrote:I highly doubt its related to ORM or postgres, the creation of the record is synchronous so you can be sure that the record exists after the create() call.Since you are sure that you are fetching the data properly we can rule out this possibilityIts probably a race condition of the callback arriving too soon *before* you hit the create() call. So do you know at what point your payment gateway will make the callback request and what would trigger it? Make sure you areOn Wed, Aug 21, 2019 at 4:19 PM Ahmed Shahwan <a...@shahwan.me> wrote:I tried, same happened. it's related to how Postgres works internally with the Django-ORM.--
On Wednesday, August 21, 2019 at 3:07:47 PM UTC+2, mohamed habib wrote:Wild guess in the dark here, as I am unfamiliar with this API. you are doing:`payment.pay_request()`before creating the WeacceptTransaction object.Maybe this line needs to move below: `models.WeAcceptTransaction.objects.create(...)` ?At what point is the callback request triggered ?On Wed, Aug 21, 2019 at 3:36 PM Ahmed Shahwan <a...@shahwan.me> wrote:Transaction model:--class WeacceptTransaction(Transaction):"""Weaccept's Transaction model"""# before frontend interactionauth_token = models.TextField() # got from step 1order_id = models.IntegerField(verbose_name="Accept's Order ID") # id from step 2order_url = models.CharField(max_length=255, null=True) # order_url from step 2billing_data = JSONField() # provided in request for paymet key, step 3payment_key = models.TextField() # payment_token from step 3hmac = models.TextField(null=True)# after (from callback)success_status = models.BooleanField(default=False)is_refunded = models.BooleanField(default=False)reference_number = models.IntegerField(null=True)processed_clbk_json = JSONField(null=True)
The cash_payment View:class CashPayment(APIView):"""Cash Payment endpoint"""serializer_class = serializers.CardPaymentSerializerrequired_fields = ["amount_cents", "billing_data"]def post(self, request):# Validate required fieldsutils.validate(self.required_fields, request)payment = utils.CashPayment(**request.data)payment.prepare()payment.pay_request()# create a new transactionmodels.WeacceptTransaction.objects.create(merchant_order_id=payment.merchant_order_id,amount=payment.amount_cents,auth_token=payment.auth_res.get("token"),order_id=payment.order_reg_res.get("id"),order_url=payment.order_reg_res.get("order_url"),billing_data=payment.billing_data,payment_key=payment.payment_key_res.get("token"),hmac=payment.pay_req_res.get("hmac"),)return Response({"message": "Our representative will go to the address you provided ""to collect the cash from you",**payment.pay_req_res,})Please note: the first 3 steps are wrapped into the method `prepare()`the 4th step is `pay_request()`the callback-url endpoint (where the 5th step happens)class TransactionProcessedCallback(APIView):"""Processed callback that will recieve "TRANSACTION", "TOKEN", "ORDER","DELIVERY_STATUS" objects"""def post(self, request):# XXX extend to handle TOKEN, DELIVERY_STATUS objects... latert_obj = request.data.get("obj") # transaction objectincoming_hmac = request.query_params.get("hmac")calculated_hmac = utils.calculate_hmac_transaction(t_obj)# if not equal hmac, not coming from Accept!if incoming_hmac != calculated_hmac:return Response({"message": "invalid data"}, status=status.HTTP_400_BAD_REQUEST)import ipdbipdb.set_trace()
# XXX: The error happens heretransaction = models.WeacceptTransaction.objects.get(merchant_order_id=t_obj.get("order").get("merchant_order_id"))transaction.success_status = bool(t_obj.get("success"))transaction.is_refunded = bool(t_obj.get("is_refunded"))transaction.reference_number = int(t_obj.get("data").get("transaction_no") or 1)transaction.processed_clbk_json = t_objtransaction.save(update_fields=["success_status","is_refunded","reference_number","processed_clbk_json",])email = t_obj.get("order").get("shipping_data").get("email")# TODO: send mail based upon success statustry:send_mail("Transaction Processed","Your transaction is processed","SERNDER MAIL",[email],fail_silently=False,)except Exception as ex:print(">>>>>>>>>>FAILED TO SEND MAIL")print(ex)return Response({"message": "Transaction Updated!"}, status=status.HTTP_200_OK)The 6th step (redirect-url) is irrelevant here, and not included in the XPAY's service for this payment option.
On Wednesday, August 21, 2019 at 2:03:20 PM UTC+2, mohammed habib wrote:Are you sure the callback request passes the right parameters ?Could you share some of your views code from the 6 steps, and your Transaction model ?Sent from my iPhone--Hi,I have a very strange problem that I don't know why it happens.I'm creating a REST API service that wraps another REST API payment service, let's call the other services XPAY. It has various payment options (Card, eWallet, Kiosk, and Cash). The steps required to use the services (to make a transaction)1. authentication 2. order registration 3. payment key 4. prepare frontend based on payment type 5. Callback URL 6. Redirect URLand each step depends on its predecessor. The first 4 steps are performed through HTTP Requests that I issue from my service. I store some of the information I receive in those steps in a Model called Transaction. 5th and 6th steps are Callback endpoints that I create to receive further information about the status of the transaction that I started in the first 4 steps.My endpoints design is:myservice/payment/cardmyservice/payment/walletmyservice/payment/kioskmyservice/payment/cashmyservice/payment/callback-urlmyservice/payment/redirect-urlThese endpoints are wrappers for the payment methods available in XPAY's service that my frontend apps will use to provide those payment options for users.I implemented the first 3 endpoints with no problem. inside those endpoints, I create a transaction object with a unique id and then update it from inside the callback-url endpoint after I receive the information from XPAY's service. Note that it takes some time between creating the Transaction object and saving it into the database from inside my endpoint, and updating it from inside the callback url endpoint.The weird thing happens in the 4th endpoint `cash` endpoint. XPAY's service responds very quickly on my callback-url endpoint and when it gets to the updating step, I get Transaction.DoesNotExist Exception while getting the transaction object using Transaction.objects.get(id=id).After some debugging time using ipdb I found that the instance has already been saved to the database! and I can retrieve it with its id. but when I let it run ordinarily the saving operation doesn't finish until that time (I got to know that using some print() statements), which is very strange, it's impossible that my laptop is that slow!Solutions I tried:1. atomic transaction2. time.sleep some time after making the request and saving the transaction object to the db3. time.sleep in the callback-url before getting the transaction from DBI checked any possible logic error related to it and there's nothing illogical.the issue is related to Postgres and how it operates, and also with Django's ORM. I don't know.Main technologies I use:Django 2.2Postgres Docker containerDRFI hope someone gives me an insight into why this is happening and how to work around it.Thanks
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/e43be322-bab9-4162-9fbc-05c6c9b95c94%40googlegroups.com.
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/a379e013-a01c-4c58-bf2b-0b4cd01f0fae%40googlegroups.com.
--Best regards,
Mohammed M. Habib, PhD
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/987c6ce7-70b6-49a9-ab3e-506e369d060e%40googlegroups.com.
--Best regards,
Mohammed M. Habib, PhD--Best regards,
Mohammed M. Habib, PhD
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/044d543f-5b6e-40d7-9ee6-36b7e84f5647%40googlegroups.com.
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/79ed22d5-675d-4362-834d-5f70f5e23856%40googlegroups.com.
Best regards,
Mohammed M. Habib, PhD
Mohammed M. Habib, PhD
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CA%2BpnY8a5sg9T5fJQ99keKxhKo7aZugRnQAFfGaSe4PVWONryeQ%40mail.gmail.com.
No comments:
Post a Comment