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.