Search code examples
djangodjango-paypal

What is the correct way to write signal(s) to receive PayPal IPNs from 3 different models


I am using Django-Paypal to receive payments for 3 different services. Each service has a separate model (say, ServiceA, ServiceB and ServiceC) but all are within the same app. I need to update the payment status of the correct model to ‘paid=True’ after receiving payment for the corresponding service.

I have 3 payment processing views (each per service) which are working well, as all payments are completed successfully. Similarly, I have created 3 signals/receiver functions (each per model) and placed them in signals.py. When I test one signal at a time, it works well. The payment status is successfully updated to ‘paid=True’. However, when I include all the three signals in signals.py, things stop working. I noted that each time a payment is received, the first 2 receiver functions are fired, leading to an error. See the code at the bottom

How do I specify which receiver function should be called when a payment is received from a specific model? Or which is the best way to implement the above successfully? When I am using inbuilt signals such as pre-save, it is possible to specify the model to be updated by adding the sender at the decorator e.g.

@receiver(pre-save, sender = MyModel)

As for the PayPal IPN signal, I am completely stuck and any assistance, including ideas, are welcome. Thanks in advance.

@receiver(valid_ipn_received)
def Servicea_order_item_paid(sender, **kwargs):
    ipn_obj = sender
    if ipn_obj.payment_status == ST_PP_COMPLETED:
        servicea = ServiceA.objects.get(order_id =ipn_obj.invoice)
        if (
            ipn_obj.mc_gross == servicea.total_cost() 
            and ipn_obj.mc_currency == 'USD' 
            and ipn_obj.receiver_email == "xxxxxx@business.example.com"
            ):
            servicea.paid = True
            servicea.save()

@receiver(valid_ipn_received)
def serviceb_order_item_paid(sender, **kwargs):
    ipn_obj = sender
    if ipn_obj.payment_status == ST_PP_COMPLETED:
        serviceb = ServiceB.objects.get(order_id =ipn_obj.invoice)
        if (
            ipn_obj.mc_gross == serviceb.total_cost() 
            and ipn_obj.mc_currency == 'USD' 
            and ipn_obj.receiver_email == "xxxxxx@business.example.com"
            ):
            serviceb.paid = True
            serviceb.save()

@receiver(valid_ipn_received)
def servicec_order_item_paid(sender, **kwargs):
    ipn_obj = sender
    if ipn_obj.payment_status == ST_PP_COMPLETED:
        servicec = ServiceC.objects.get(order_id =ipn_obj.invoice)
        if (
            ipn_obj.mc_gross == servicec.total_cost() 
            and ipn_obj.mc_currency == 'USD' 
            and ipn_obj.receiver_email == "xxxxxx@business.example.com"
            ):
            servicec.paid = True
            servicec.save()


Solution

  • Though I wasn't able to get an answer here, I finally solved the problem. I did so by combining the three signals into a single 'compound' signal using:

    • Filters
    • if-statements and
    • The exists() method

    The final code is lengthy but it's working well without any errors. Even the 'Duplicate txn_id' error finally disappeared. For the purposes of sharing code, I have replaced the actual services with ServiceA, B and C respectively. See the code below:

    @receiver(valid_ipn_received)
    def order_item_paid(sender, **kwargs):
        ipn_obj = sender
        if ipn_obj.payment_status == ST_PP_COMPLETED:
            print('Payment was sucessful!!!!, pending other verifications')
    
            if ServiceA.objects.filter(order_id =ipn_obj.invoice).exists():
                print('!!!! ServiceA Oder object exists')
                servicea = get_object_or_404(ServiceA, order_id =ipn_obj.invoice)
                
                if (
                    ipn_obj.mc_gross == servicea.total_cost() 
                    and ipn_obj.mc_currency == 'USD' 
                    and ipn_obj.receiver_email == "xxxxxxx@business.example.com"
                    ):
    
                    print('!!!. Amount, Currency and Email Verified')
    
                    servicea.paid = True
                    servicea.save()
            else:
                print('!!!! ServiceA order item does not exist')
    
            if ServiceB.objects.filter(order_id =ipn_obj.invoice).exists():
                print('!!!! ServiceB order item exists')
                serviceb = get_object_or_404(ServiceB, order_id =ipn_obj.invoice)
            
                if (
                    ipn_obj.mc_gross == serviceb.total_cost() 
                    and ipn_obj.mc_currency == 'USD' 
                    and ipn_obj.receiver_email == "xxxxxx@business.example.com"
                    ):
    
                    print('!!!!!!. Amount, Currency and Email Verified')
    
                    serviceb.paid = True
                    serviceb.save()
            else:
                 print('!!!!!!!!Service B Order object does not exist')
    
    
            if ServiceC.objects.filter(order_id =ipn_obj.invoice).exists():
                print('!!!!!!!!Service C order item exists')
                servicec = get_object_or_404(ServiceC, order_id =ipn_obj.invoice)
           
                if (
                    ipn_obj.mc_gross == servicec.total_cost() 
                    and ipn_obj.mc_currency == 'USD' 
                    and ipn_obj.receiver_email == "xxxxxxx@business.example.com"
                    ):
    
                    print('!!!!!!. Amount, Currency and Email Verified')
    
                    servicec.paid = True
                    servicec.save()
                
            else:
                print('!!! Service C order object does not exist')