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