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] 
> <javascript:>> 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*
>>
>>
>
> -- 
> 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.

Reply via email to