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.

Reply via email to