the response to my callback quicker than saving the object?! it's highly unlikely! there's something else I don't understand. The step that `pay_request()` is the last before getting the response from XPAY's server. I tried to put it after saving the transaction object. same error occurred!
I also tried to put the transaction save operation inside the `with transaction.atomic()` statement, but same error. also, I tried refresh_from_db() method on the transaction after saving it, and saving it again! and using some print statements The weird thing is I get the transaction object printed before and after XPAY's server hitting my callback-url (with the same error) using the ipdb and fetching the object from DB I get the same error, after trying the same statement for 2 or 3 times, it works! On Wednesday, August 21, 2019 at 3:38:33 PM UTC+2, mohamed habib 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 possibility > > Its 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 are > > On Wed, Aug 21, 2019 at 4:19 PM Ahmed Shahwan <[email protected] > <javascript:>> 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 <[email protected]> wrote: >>> >>>> Transaction model: >>>> >>>> >>>> class WeacceptTransaction(Transaction): >>>> """Weaccept's Transaction model""" >>>> >>>> # before frontend interaction >>>> auth_token = models.TextField() # got from step 1 >>>> order_id = models.IntegerField(verbose_name="Accept's Order ID") # >>>> id from step 2 >>>> order_url = models.CharField(max_length=255, null=True) # >>>> order_url from step 2 >>>> billing_data = JSONField() # provided in request for paymet key, >>>> step 3 >>>> payment_key = models.TextField() # payment_token from step 3 >>>> hmac = 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.CardPaymentSerializer >>>> required_fields = ["amount_cents", "billing_data"] >>>> >>>> def post(self, request): >>>> # Validate required fields >>>> utils.validate(self.required_fields, request) >>>> payment = utils.CashPayment(**request.data) >>>> payment.prepare() >>>> payment.pay_request() >>>> # create a new transaction >>>> models.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... later >>>> t_obj = request.data.get("obj") # transaction object >>>> incoming_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 ipdb >>>> >>>> ipdb.set_trace() >>>> # XXX: The error happens here >>>> transaction = 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_obj >>>> transaction.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 status >>>> try: >>>> 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 >>>>> >>>>> On 21 Aug 2019, at 14:46, Ahmed Shahwan <[email protected]> wrote: >>>>> >>>>> 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 URL >>>>> >>>>> and 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/card >>>>> myservice/payment/wallet >>>>> myservice/payment/kiosk >>>>> myservice/payment/cash >>>>> >>>>> myservice/payment/callback-url >>>>> myservice/payment/redirect-url >>>>> >>>>> These 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 transaction >>>>> 2. time.sleep some time after making the request and saving the >>>>> transaction object to the db >>>>> 3. time.sleep in the callback-url before getting the transaction from >>>>> DB >>>>> >>>>> I 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.2 >>>>> Postgres Docker container >>>>> DRF >>>>> >>>>> I 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 [email protected]. >>>>> To view this discussion on the web visit >>>>> https://groups.google.com/d/msgid/django-users/e43be322-bab9-4162-9fbc-05c6c9b95c94%40googlegroups.com >>>>> >>>>> <https://groups.google.com/d/msgid/django-users/e43be322-bab9-4162-9fbc-05c6c9b95c94%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>> . >>>>> >>>>> -- >>>> 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 [email protected]. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/django-users/a379e013-a01c-4c58-bf2b-0b4cd01f0fae%40googlegroups.com >>>> >>>> <https://groups.google.com/d/msgid/django-users/a379e013-a01c-4c58-bf2b-0b4cd01f0fae%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> >>> >>> -- >>> 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 [email protected] <javascript:>. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/django-users/987c6ce7-70b6-49a9-ab3e-506e369d060e%40googlegroups.com >> >> <https://groups.google.com/d/msgid/django-users/987c6ce7-70b6-49a9-ab3e-506e369d060e%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > > > -- > 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 [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/9c95bd9f-3b1e-44a1-8f4d-ab9d707abeb9%40googlegroups.com.

