What is the Transaction class which your model inherits from ? Is it just a 
base model ?

Let’s do an experiment 

Print(“object created”) right after the create() method

Print(“callback requested”) on top of your callback method

Which one do you see first ? 

Sent from my iPhone

> On 21 Aug 2019, at 16:56, Ahmed Shahwan <[email protected]> wrote:
> 
> That's the class, nothing different other than HTTP requests
> 
> class CashPayment(BasePayment):
>     """Cash Payment Wrapper Class"""
> 
>     integration_id = CASH_INTEGRATION_ID
> 
>     def __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 us
>> 
>>> On Wed, Aug 21, 2019 at 4:37 PM mohamed habib <[email protected]> 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]> 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.
>>>>>> 
>>>>>> -- 
>>>>>> 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.
>>>>> 
>>>>> 
>>>>> -- 
>>>>> 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/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 [email protected].
> 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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/F1D65465-F196-485F-ADAC-0EE780BB0031%40gmail.com.

Reply via email to