How do i notify the user about a webhook In django

无人久伴 提交于 2021-01-29 17:41:48

问题


Hello ive been having problems with web hooks in django i need a way to notify a user that he/she has made a successful payment am notified about a successful web hook from adding a URL to my processor that sends a payment successful request to my web hook how can i show this to the user to notify if its successful

my code is: this is where the user sends checkout details to my server hopping to add a waiting spinner that resolves to a tick or an X depending if the user has paid

<script>
        document.getElementById("checkout-btn").addEventListener('click', analyzeText());
        function analyzeText(){
            var wallet = document.getElementById('wallet').value;
            var phone = document.getElementById('phone').value;
            var order_id = document.getElementById('order_id').value;
            $.ajax({
                type: "POST",
                url: "{% url 'orders:Checkout' %}",   /* Call python function in this script */
                data: {csrfmiddlewaretoken: '{{ csrf_token }}',
                        wallet: wallet,
                        phone:phone,
                        order_id:order_id,
                                        },   /* Passing the text data */
                success: callback
            });
        }
        function callback(response){
            console.log(response);
        }
</script>

the view this is sent to does this and its basically just calling the checkout API which now deals with the checkout if succesful or failed a webhook should be sent to my callback url

def processOrder(request):
    def spektraCheckoutLink(amount, reference, transaction_id, order_id):
        url = "https://api.spektra.co/api/v1/payments/pay-in"

        # request payload
        # spektraAccountName is Optional, used for subaccount-specific payments
        payload = {
            "amount": amount,
            "account": phone,
            "currency": "KES",
            "description": reference,

        }
        # pass authorization token obtained during authentication in headers
        authcode = str(auth())
        headers = {
            'Content-Type': "application/json",
            'Authorization': "Bearer " + authcode
        }
        response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
        # Print response data
        print(response.json())
        data = response.json()
        return True

    if request.method == "POST":
        global order_id
        try:
            order_id = request.POST['order_id']
        except Exception  as e:
            print(e)
            print(traceback.format_exc())

            raise HttpResponseServerError()
        order = Order.objects.get(id=order_id)
        try:
            wallet = request.POST['wallet']
            order.wallet = wallet
        except Exception  as e:
            print(e)
            print(traceback.format_exc())

            raise HttpResponseServerError()
        try:
            phone = request.POST['phone']
            order.phone = phone
        except Exception as e:
            print(e)
            print(traceback.format_exc())

            raise HttpResponseServerError()
        order.save()
    try:
        current_user = get_user_model().objects.get(id=request.user.id)
        Transaction.objects.get_or_create(user=current_user, order=order, amount=order.price, phone=order.phone)
        trans = Transaction.objects.get(order=order)
        link = spektraCheckoutLink(order.price, order.order_reference, trans.id, order.id)
        return redirect(link)
    except Exception as e:
        print(e)
        print(traceback.format_exc())

        # redirect to 404
        raise HttpResponseServerError()

what i don't know is how i'm going to change the spinner status on the users front end when the callback arrives to notify them if the transaction was successful or not

this is my webhook view which updates the order to paid my question is how can i notify the user in his browser that the payment is complete i dont know how to do it

def processOrderStatus(request):
    # updates transaction number and status
    data = json.loads(request.body)
    order = Order.objects.get(phone=data['account'])
    order.order_status = order.PAID
    order.save
    id = request.user.id
    return redirect('orders:Orders', user_id=id)

回答1:


Knowing your needs I will try to put you on a couple of possible ways to accomplish what you need, the first one is using a javascript interval with and ajax call to get the order status every 5 seconds, the second approach will use a long-polling strategy.

The following code must be adapted to your needs and it's not tested, take it as a guide.

AJAX requests using an interval of 5 seconds

Create a view in your django app which will receive an order PK and return it's status in a json like

{
    'pk': XX,
    'status': ['processing_payment', 'paid', 'refused', ...]
}

(Adjust the status values on your own)

Python view

class OrderStatusView(View):
    """ just return a json with the current status of the order """        
    def get(self, request, *args, **kwargs):
        order_pk = kwargs.get('pk')
        order = Order.objects.get(pk=order_pk)
        ret_values = {'pk': order.pk, 'status': order.status}
        return HttpResponse(json.dumps(ret_values))

The javascript part

var check_order_status = function (order_pk) {
    $.ajax({
        url: '/ajax/order/' + order_pk + '/status/',
        dataType: 'json',
        success: function (data) {
            switch (data['status']) {
               case 'processing_payment':
                   // just keep the spinner on
               break;
               case 'paid':
                   // Change the spinner to a green check or whatever you prefer
                   // Stop sending requests
                   clearInterval(order_status_interval);
               break;
               case 'refused':
                   // Change the spinner to a red cross or whatever fit better to your app
                   // Stop sending requests
                   clearInterval(order_status_interval);
               break;
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {}
    });

};
$(function () {
    var order_status_interval = setInterval(function() { check_order_status(ORDER_PK); }, 5000);
});

As you can see we will send a request to the server every 5 seconds, if it returns an 'ending' status like paid or refused you should update your layout and not do further requests, otherwise, keep looking for a change.

Long-polling

Using a long-polling strategy will be similar but instead of sending a request to your backend every 5 seconds, your view should implement a sleep time and check for a status change keeping the thread with the client open, if a timeout is raised, the front-end must re-send the request again.

Your view shold look something like this

class OrderStatusView(View):
    """ Wait until order status changes to paid or refused to long poll for a change """

    def get(self, request, *args, **kwargs):
        order_pk = kwargs.get('pk')
        order = Order.objects.get(pk=order_pk)
        while order.status not in ['paid', 'refused']:
            time.sleep(5)  # sleep for 5 seconds
            order = Order.objects.get(pk=order_pk)
        # order status is a final one
        ret_values = {'pk': order.pk, 'status': order.status}
        return HttpResponse(json.dumps(ret_values))

Your javascript code should control if a timeout is returned, in that case, do a new request.

var check_order_status = function (order_pk) {
    $.ajax({
        url: '/ajax/order/' + order_pk + '/status/',
        dataType: 'json',
        success: function (data) {
            switch (data['status']) {
               case 'processing_payment':
                   // just keep the spinner on
               break;
               case 'paid':
                   // Change the spinner to a green check or whatever you prefer
                   // Stop sending requests
                   clearInterval(order_status_interval);
               break;
               case 'refused':
                   // Change the spinner to a red cross or whatever fit better to your app
                   // Stop sending requests
                   clearInterval(order_status_interval);
               break;
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {
            switch (jqXHR.status) {
                // timeout http codes
                // 408 Request timeout
                // 504 Gateway timeout
                case 408:
                case 504:
                    check_order_status(ORDER_PK);
                break;
                default:
                    // Throw some error
            }
        }
    });

};
$(function () {
    check_order_status(ORDER_PK);
});

Keep in mind that this approach can consume lot's of threads in high traffic conditions which can, at some point, affect your server performance.


WebSockets

The most over-head option will be to implement websockets using django-channels or centrifugo but I will not give it a try just for this.


THE SIMPLEST ONE

Since you don't know when your webhook will be called, consider to just send an email to your user notifying the payment result and with a CTA to the order details, I think that this is the easiest way to keep you user informed.


Hope this puts U on the right way



来源:https://stackoverflow.com/questions/59352512/how-do-i-notify-the-user-about-a-webhook-in-django

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!