Django PayPal (4) – Subscriptions Django.How

Author avatar wrote on 01/06/2022

Resources:
  • https://overiq.com/django-paypal-subscriptions-with-django-paypal/
  • https://overiq.com/django-paypal-integration-with-django-paypal/
  • simple_ecommerce/django_project/ecommerce_app/views.py
  • from paypal.standard.forms import PayPalPaymentsForm
  • from django.views.decorators.csrf import csrf_exempt
  • from .models import Product, Order, LineItem
  • 
    def process_payment(request):
        #...
    
    
    @csrf_exempt
    def payment_done(request):
        return render(request, 'ecommerce_app/payment_done.html')
    
    
    @csrf_exempt
    def payment_canceled(request):
        return render(request, 'ecommerce_app/payment_cancelled.html')
    
    
    def checkout(request):
        if request.method == 'POST':
            form = CheckoutForm(request.POST)
            if form.is_valid():
                cleaned_data = form.cleaned_data
            #...
            #...
    
                cart.clear(request)
    
                request.session['order_id'] = o.id
                return redirect('process_payment')
    
    
        else:
            form = CheckoutForm()
            return render(request, 'ecommerce_app/checkout.html', locals())
    

    process_payment.html

    payment_done.html

    payment_cancelled.html

    simple_ecommerce/django_project/ecommerce_app/urls.py
    
    urlpatterns = [
        path('', views.index, name='index'),
        path('product///',
            views.show_product, name='show_product'),
        path('cart/', views.show_cart, name='show_cart'),
        path('checkout/', views.checkout, name='checkout'),
        path('process-payment/', views.process_payment, name='process_payment'),
        path('payment-done/', views.payment_done, name='payment_done'),
        path('payment-cancelled/', views.payment_canceled, name='payment_cancelled'),
    ]
    

    settings.py add
    
    PAYPAL_RECEIVER_EMAIL = '[email protected]'
    
    PAYPAL_TEST = True
    

    Subscription

    simple_ecommerce/django_project/ecommerce_app/forms.py
    
    class CheckoutForm(forms.ModelForm):
        #...
    
    subscription_options = [
        ('1-month', '1-Month subscription ($10 USD/Mon)'),
        ('6-month', '6-Month subscription Save $10 ($50 USD/Mon)'),
        ('1-year', '1-Year subscription Save $30 ($90 USD/Mon)'),
    ]
    
    
    class SubscriptionForm(forms.Form):
        plans = forms.ChoiceField(choices=subscription_options)
    

    simple_ecommerce/django_project/ecommerce_app/views.py
  • from .models import Product, Order, LineItem
  • from .forms import CartForm, CheckoutForm, SubscriptionForm
  • from . import cart
  • 
    @csrf_exempt
    def payment_canceled(request):
        return render(request, 'ecommerce_app/payment_cancelled.html')
    
    
    def subscription(request):
        if request.method == 'POST':
            f = SubscriptionForm(request.POST)
            if f.is_valid():
                request.session['subscription_plan'] = request.POST.get('plans')
                return redirect('process_subscription')
        else:
            f = SubscriptionForm()
        return render(request, 'ecommerce_app/subscription_form.html', locals()
    

    simple_ecommerce/django_project/ecommerce_app/templates/ecommerce_app/subscription_form.html

    simple_ecommerce/django_project/ecommerce_app/urls.py
    
    urlpatterns = [
        #...
        path('checkout/', views.checkout, name='checkout'),
        path('process-payment/', views.process_payment, name='process_payment'),
        path('payment-done/', views.payment_done, name='payment_done'),
        path('payment-cancelled/', views.payment_canceled, name='payment_cancelled'),
        path('subscribe/', views.subscription, name='subscription'),    
    ]
    
    

    simple_ecommerce/django_project/ecommerce_app/views.py
    
    def subscription(request):
        #...
    
    def process_subscription(request):
    
        subscription_plan = request.session.get('subscription_plan')
        host = request.get_host()
    
        if subscription_plan == '1-month':
            price = "10"
            billing_cycle = 1
            billing_cycle_unit = "M"
        elif subscription_plan == '6-month':
            price = "50"
            billing_cycle = 6
            billing_cycle_unit = "M"
        else:
            price = "90"
            billing_cycle = 1
            billing_cycle_unit = "Y"
    
    
        paypal_dict  = {
            "cmd": "_xclick-subscriptions",
            'business': settings.PAYPAL_RECEIVER_EMAIL,
            "a3": price,  # monthly price
            "p3": billing_cycle,  # duration of each unit (depends on unit)
            "t3": billing_cycle_unit,  # duration unit ("M for Month")
            "src": "1",  # make payments recur
            "sra": "1",  # reattempt payment on payment error
            "no_note": "1",  # remove extra notes (optional)
            'item_name': 'Content subscription',
            'custom': 1,     # custom data, pass something meaningful here
            '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, button_type="subscribe")
        return render(request, 'payment/process_subscription.html', locals())
    

    simple_ecommerce/django_project/ecommerce_app/templates/ecommerce_app/process_subscription.html

    simple_ecommerce/django_project/ecommerce_app/urls.py
    
    urlpatterns = [
        #...
        path('subscribe/', views.subscription, name='subscription'),
        path('process_subscription/', views.process_subscription, name='process_subscription'),
    ]
    

    simple_ecommerce/django_project/ecommerce_app/signals.py
  • from paypal.standard.ipn.signals import valid_ipn_received
  • from django.dispatch import receiver
  • from paypal.standard.models import ST_PP_COMPLETED
  • from django.core.mail import EmailMessage
  • from django.contrib.auth.models import User
  • from datetime import datetime
  • 
    @receiver(valid_ipn_received)
    def ipn_receiver(sender, **kwargs):
        ipn_obj = sender
    

    Check for Buy Now IPN
    
        if ipn_obj.txn_type == 'web_accept':
    
            if ipn_obj.payment_status == ST_PP_COMPLETED:
                # payment was successful
                print('great!')
                order = get_object_or_404(Order, id=ipn_obj.invoice)
    
                if order.get_total_cost() == ipn_obj.mc_gross:
                    # mark the order as paid
                    order.paid = True
                    order.save()
    

    Check for subscription signup IPN
    
        elif ipn_obj.txn_type == "subscr_signup":
    
            # get user id and activate the account
            id = ipn_obj.custom
            user = User.objects.get(id=id)
            user.active = True
            user.save()
    
            subject = 'Sign Up Complete'
    
            message = 'Thanks for signing up!'
    
            email = EmailMessage(subject,
                                 message,
                                 '[email protected]',
                                 [user.email])
    
            email.send()
    

    Check for subscription payment IPN
    
        elif ipn_obj.txn_type == "subscr_payment":
    
            # get user id and extend the subscription
            id = ipn_obj.custom
            user = User.objects.get(id=id)
            # user.extend()  # extend the subscription
    
            subject = 'Your Invoice for {} is available'.format(
                datetime.strftime(datetime.now(), "%b %Y"))
    
            message = 'Thanks for using our service. The balance was automatically ' \
                      'charged to your credit card.'
    
            email = EmailMessage(subject,
                                 message,
                                 '[email protected]',
                                 [user.email])
    
            email.send()
    

    Check for failed subscription payment IPN

    elif ipn_obj.txn_type == "subscr_failed":
    pass

    Check for subscription cancellation IPN

    elif ipn_obj.txn_type == "subscr_cancel":
    pass

    CRUDgen Django CRUD generator