spookylukey / django-paypal

A pluggable Django application for integrating PayPal Payments Standard or Payments Pro
MIT License
729 stars 208 forks source link

Django signal not being fired #170

Closed carlosmain closed 7 years ago

carlosmain commented 7 years ago

Hi,

Im having trouble with getting the signals of my django app being fired.

I have my settings.py

Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'shop',
    'cart',
    'orders',
    'paypal.standard.ipn',
    'payment',
)

My views.py file

from decimal import Decimal
from django.conf import settings
from django.core.urlresolvers import reverse
from django.shortcuts import render, get_object_or_404
from paypal.standard.forms import PayPalPaymentsForm
from orders.models import Order
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def payment_done(request):
    return render(request, 'payment/done.html')

@csrf_exempt
def payment_canceled(request):
    return render(request, 'payment/canceled.html')

def payment_process(request):
    order_id = request.session.get('order_id')
    order = get_object_or_404(Order, id=order_id)
    host = request.get_host()

    paypal_dict = {
        'business': settings.PAYPAL_RECEIVER_EMAIL,
        'amount': '%.2f' % order.get_total_cost().quantize(Decimal('.01')),
        'item_name': 'Order {}'.format(order.id),
        'invoice': str(order.id),
        'currency_code': 'USD',
        'notify_url': 'http://{}{}'.format(host, reverse('paypal-ipn')),
        'return_url': 'http://{}{}'.format(host, reverse('payment:done')),
        'cancel_return': 'http://{}{}'.format(host, reverse('payment:canceled')),
    }
    form = PayPalPaymentsForm(initial=paypal_dict)
    return render(request, 'payment/process.html', {'order': order,
                                                    'form':form})

The signals.py file


from django.shortcuts import get_object_or_404
from paypal.standard.models import ST_PP_COMPLETED
from paypal.standard.ipn.signals import valid_ipn_received, invalid_ipn_received
from orders.models import Order
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
from django.conf import settings
import weasyprint
from io import BytesIO

def payment_notification(sender, **kwargs):
    ipn_obj = sender
    print(ipn_obj.payment_status)
    if ipn_obj.payment_status == ST_PP_COMPLETED:
        # payment was successful
        order = get_object_or_404(Order, id=ipn_obj.invoice)
        # mark the order as paid
        order.paid = True
        order.save()
        # create invoice e-mail
        subject = 'My Shop - Invoice no. {}'.format(order.id)
        message = 'Please, find attached the invoice for your recent purcharse.'
        email = EmailMessage(subject, message, 'admin@myshop.com', [order.email])
        # generate PDF
        html = render_to_string('orders/order/pdf.html', {'order': order})
        out = BytesIO()
        stylesheets = [weasyprint.CSS(settings.STATIC_ROOT + 'css/pdf.css')]
        weasyprint.HTML(string=html).write_pdf(out, stylesheets=stylesheets)
        # attach PDF file
        email.attach('order_{}.pdf'.format(order.id), out.getvalue(), 'application/pdf')
        # send e-mail
        email.send()

valid_ipn_received.connect(payment_notification)

The apps.py file

from django.apps import AppConfig

class PaymentConfig(AppConfig):
    name = 'payment'
    verbose_name = 'Payment'

    def ready(self):
        # import signal handlers
        import payment.signals

And at last but not least, my __init__.py file


default_app_config = 'payment.apps.PaymentConfig'

I'm using IPN, django 1.8.6, and django-paypal 0.2.5. This an example i grabbed from a book to get it running.

I can't figure out what is wrong.

spookylukey commented 7 years ago

I edited your comment so I could read the code (see https://guides.github.com/features/mastering-markdown/ )

Is there a reason you are running an old version of django-paypal? I would always start with the newest version.

I normally can't give support help like this on GitHub, or at all. That said, the missing information here:

1) What you did 2) What you expected to happen 3) What actually happened

In other words, how are you attempting to use this code? For a signal to be fired, PayPal has to send a request to your app. Is that request being sent? Is it being received by your app at all? Have you tested this from a development using the sandbox and got it to work? Those are the questions I would ask. I'm not able to provide further assistance I'm afraid. Here's an article about debugging webhooks in general. https://hackernoon.com/handling-webhooks-using-django-and-ngrok-b7ff27a6fd47

spookylukey commented 7 years ago

BTW - in your code, you need to check the amount and the sender etc. as well. You cannot rely on these matching the invoice - see https://django-paypal.readthedocs.io/en/stable/standard/ipn.html

xcelsiorbosi commented 4 years ago

@spookylukey I think this is a common problem with all general setup/walkthroughs. I have followed various including the documentation. My integration works as I can see the IPN in admin/models. I believe it all falls over in the docs at this point: The standard way to do this is to create an AppConfig class and add a ready() method, in which you can register your signal handlers or import a module that does this - see https://django-paypal.readthedocs.io/en/stable/standard/ipn.html As paypal send the request but it is blank in peoples views as the class is not used when the shopper returns to the site.

this is the best response I have found if anyone else comes here looking: https://stackoverflow.com/questions/42943041/understanding-where-to-put-the-ipn-reciever-function-in-django-paypal