I'm trying to implement a subscription feaure in django using paystack

I would have done this using stripe or paypal but my regions does not support the use of stripe and paystack is not beginner friendly neither is there any tutorial out there to help implement this feature and it’s kind off urgent. Ii have written some models and a views but i don’t know if that is the right way to do it. I read the paystack payment documentation and it really doesnt state anything clearly about django - paystack integration.

models.py

from django.db import models
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import AbstractUser
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from django.conf import settings
import datetime
from datetime import timedelta
from datetime import datetime as dt

today = datetime.date.today()


#### User Payment History
class PayHistory(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
    paystack_charge_id = models.CharField(max_length=100, default='', blank=True)
    paystack_access_code = models.CharField(max_length=100, default='', blank=True)
    payment_for = models.ForeignKey('Membership', on_delete=models.SET_NULL, null=True)
    amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    paid = models.BooleanField(default=False)
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.user.username

#### Membership
class Membership(models.Model):
     # Note that they are all capitalize//
    MEMBERSHIP_CHOICES = (
    	('Enterprise', 'Enterprise'),
    	('Team', 'Team'),
        ('Student', 'Student'),
        ('Free', 'Free')
    )
    PERIOD_DURATION = (
        ('Months', 'Months'),
        ('Years', 'Years'),
    )
    slug = models.SlugField(null=True, blank=True)
    membership_type = models.CharField(choices=MEMBERSHIP_CHOICES, default='Free', max_length=30)
    duration = models.PositiveIntegerField(default=30)
    duration_period = models.CharField(max_length=100, default='Months', choices=PERIOD_DURATION)
    price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

    def __str__(self):
       return self.membership_type

#### User Membership
class UserMembership(models.Model):
    user = models.OneToOneField(User, related_name='user_membership', on_delete=models.CASCADE)
    membership = models.ForeignKey(Membership, related_name='user_membership', on_delete=models.SET_NULL, null=True)
    reference_code = models.CharField(max_length=100, default='', blank=True)

    def __str__(self):
       return self.user.username

@receiver(post_save, sender=UserMembership)
def create_subscription(sender, instance, *args, **kwargs):
	if instance:
		Subscription.objects.create(user_membership=instance, expires_in=dt.now().date() + timedelta(days=instance.membership.duration))


#### User Subscription
class Subscription(models.Model):
    user_membership = models.ForeignKey(UserMembership, related_name='subscription', on_delete=models.CASCADE, default=None)
    expires_in = models.DateField(null=True, blank=True)
    active = models.BooleanField(default=True)

    def __str__(self):
      return self.user_membership.user.username

@receiver(post_save, sender=Subscription)
def update_active(sender, instance, *args, **kwargs):
	if instance.expires_in < today:
		subscription = Subscription.objects.get(id=instance.id)
		subscription.delete()

views.py

def subscribe(request):
	plan = request.GET.get('sub_plan')
	fetch_membership = Membership.objects.filter(membership_type=plan).exists()
	if fetch_membership == False:
		return redirect('subscription:subscribe')
	membership = Membership.objects.get(membership_type=plan)
	price = float(membership.price)*100 # We need to multiply the price by 100 because Paystack receives in kobo and not naira.
	price = int(price)

	def init_payment(request):
		url = 'https://api.paystack.co/transaction/initialize'
		headers = {
			'Authorization': 'Bearer '+settings.PAYSTACK_SECRET_KEY,
			'Content-Type' : 'application/json',
			'Accept': 'application/json',
			}
		datum = {
			"email": request.user.email,
			"amount": price
			}
		x = requests.post(url, data=json.dumps(datum), headers=headers)
		if x.status_code != 200:
			return str(x.status_code)
		
		results = x.json()
		return results
	initialized = init_payment(request)
	print(initialized['data']['authorization_url'])
	amount = price/100
	instance = PayHistory.objects.create(amount=amount, payment_for=membership, user=request.user, paystack_charge_id=initialized['data']['reference'], paystack_access_code=initialized['data']['access_code'])
	UserMembership.objects.filter(user=instance.user).update(reference_code=initialized['data']['reference'])
	link = initialized['data']['authorization_url']
	return HttpResponseRedirect(link)
	return render(request, 'subscription/subscribe.html')

urls.py


urlpatterns = [
    path('', subscription, name="pricing"),
    path('subscribe/', subscribe, name="subscribe"),
    path('payment/', call_back_url, name='payment'),
    path('subscribed/', subscribed, name='subscribed'),
]

pricing.html - Take note of the way i am getting the subscription plan ...href="{% url 'subscription:subscribe' %}?sub_plan=Enterprise"

<!-- Price and Info -->
<h5 class="mb-0">Enterprise</h5>
<div class="badge bg-dark mb-0 rounded-pill">Per Montth</div>
<!-- Card Button-->
<a type="button" href="{% url 'subscription:subscribe' %}?sub_plan=Enterprise" class="btn btn-light mb-0">Get Started</a>

Please if there is any other information that is needed i would provide it immediately and the views can also be changed if you wish too, i don’t mind

1 Like

Hello @desphixs . Yesterday this post was submit in the forum, I think it may help you.

thanks alot for your response, let me check it out

hello desphixs, how are you doing man.
please were you able to get feedback on this because i am in this mood now and i need to solve it urgently.
thanks.

I didn’t get any solution yet, but I’d recommend you stay away from PayStack and flutterwave it’s more easier to integrate and it’s user friendly

So which other options do u recommend

Did you try Django flexible subscriptions

I’m also in this same page … But I got an error at the end which states

“TypeError: string indices must be integers”

Do you have any idea please

i was trying to recommend flutterwave, i guess i did not use the right punctuation, paystack is not user friendly. I’d recommend Flutterwave.