diff options
| -rw-r--r-- | blog/forms.py | 9 | ||||
| -rw-r--r-- | blog/urls.py | 3 | ||||
| -rw-r--r-- | blog/views.py | 64 | ||||
| -rw-r--r-- | requirements.txt | 3 | ||||
| -rw-r--r-- | templates/blog/donate.html | 63 | ||||
| -rw-r--r-- | templates/blog/partials/sidebar.html | 10 | ||||
| -rw-r--r-- | templates/blog/post.html | 33 |
7 files changed, 182 insertions, 3 deletions
diff --git a/blog/forms.py b/blog/forms.py new file mode 100644 index 00000000..a76e354b --- /dev/null +++ b/blog/forms.py @@ -0,0 +1,9 @@ +from django import forms +import datetime + +class PaymentForm(forms.Form): + amount = forms.CharField(required=True, widget=forms.NumberInput, label='Amount') + card_number = forms.CharField(max_length=16, min_length=16, required=True, widget=forms.NumberInput, label='Card Number') + card_expiry_mm = forms.ChoiceField(choices=[(i, i) for i in range(1, 13)], required=True, label='Expiry Month') + card_expiry_yyyy = forms.ChoiceField(choices=[(i, i) for i in range(datetime.datetime.now().year, datetime.datetime.now().year + 21)], required=True, label='Expiry Year') + card_cvv = forms.CharField(max_length=3, min_length=3, required=True, widget=forms.NumberInput, label='CVV') diff --git a/blog/urls.py b/blog/urls.py index 57abe74e..b056b74e 100644 --- a/blog/urls.py +++ b/blog/urls.py @@ -13,5 +13,6 @@ urlpatterns = [ path('articles/<str:slug>/comment', views.comment, name='comment'), path('articles/<str:slug>/edit_comment', views.edit_comment, name='edit_comment'), path('articles/<str:slug>/delete_comment/<int:comment_id>', views.delete_comment, name='delete_comment'), - path('users/~<str:username>', views.user_activity, name='user_activity'), + path('~<str:username>', views.user_activity, name='user_activity'), + path('donate', views.donate, name='donate'), ] diff --git a/blog/views.py b/blog/views.py index e3d1e951..d13d7c93 100644 --- a/blog/views.py +++ b/blog/views.py @@ -13,7 +13,17 @@ from users.forms import RegisterForm, UpdateUserDetailsForm from users.tokens import CaptchaTokenGenerator from django.contrib import messages from bs4 import BeautifulSoup +from .forms import PaymentForm import re +from dotenv import load_dotenv +import os +import stripe +import requests +import math +load_dotenv() + +stripe.api_key = os.getenv('STRIPE_SECRET_KEY') + def atoi(text): return int(text) if text.isdigit() else text @@ -254,4 +264,56 @@ def user_activity(request, username): comment.body = comment_processor(comment.body) return render(request, 'blog/activity.html', {'title': 'User Activity', 'activity_user': user, 'activity_user_profile': user_profile, 'activity_recent_comments': recent_comments, 'activity_user_email': user_email}) - + +def donate(request): + amount = request.GET.get('amount') + + try: + amount = int(amount) + except: + amount = 3 + amount = amount if amount > 0 else 3 + amount = amount if amount < 1000 else 1000 + payment_form = PaymentForm(initial={'amount': amount}) + + if request.method == 'POST': + try: + # create a payment using stripe + payment_method = stripe.PaymentMethod.create( + type='card', + card={ + 'number': request.POST['card_number'], + 'exp_month': request.POST['card_expiry_mm'], + 'exp_year': request.POST['card_expiry_yyyy'], + 'cvc': request.POST['card_cvv'], + }, + ) + + # get the current usd to inr conversion rate + rate = requests.get('https://api.exchangerate-api.com/v4/latest/USD').json()['rates']['INR'] + + # convert the amount to inr + init_amt = int(request.POST['amount']) + amount = init_amt * math.ceil(rate) * 100 + print(amount) + + # create a payment intent + payment_intent = stripe.PaymentIntent.create( + amount=amount, + currency='inr', + payment_method_types=['card'], + payment_method=payment_method.id, + confirm=True, + ) + + if payment_intent.status == 'succeeded': + return redirect(reverse('blog:donate') + '?tab=success&payment_method=' + payment_intent.payment_method_types[0] + '&payment_id=' + payment_intent.id + '&payment_status=' + payment_intent.status + '&payment_created=' + str(payment_intent.created) + '&payment_amount=' + str(int(payment_intent.amount / 100)) + '&payment_currency=' + payment_intent.currency + '&amount=' + str(init_amt)) + else: + return redirect(reverse('blog:donate') + '?tab=error&payment_method=' + payment_intent.payment_method_types[0] + '&payment_id=' + payment_intent.id + '&payment_status=' + payment_intent.status + '&payment_created=' + str(payment_intent.created) + '&payment_amount=' + str(int(payment_intent.amount / 100)) + '&payment_currency=' + payment_intent.currency + '&amount=' + str(init_amt)) + + except Exception as e: + print(e) + return redirect(reverse('blog:donate') + '?tab=error') + + return render(request, 'blog/donate.html', {'title': 'Donate', 'amount': amount, 'payment_form': payment_form}) + diff --git a/requirements.txt b/requirements.txt index 16235e50..093d9b23 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,5 @@ requests pillow pycryptodome pygments -bs4
\ No newline at end of file +bs4 +stripe diff --git a/templates/blog/donate.html b/templates/blog/donate.html new file mode 100644 index 00000000..d9004ed8 --- /dev/null +++ b/templates/blog/donate.html @@ -0,0 +1,63 @@ +{% extends 'blog/partials/base.html' %} {% block content %} +{% load static %} + +{% if not request.GET.payment_id %} +<div id="donation"> + <h2 style="text-transform: none; border: none; padding: 0; font-size: 16px;"> + {% if request.GET.referrer %} + Complete your donation of ${{ amount }}: + {% else %} + Donate to the blog: + {% endif %} + </h2> + <form method="POST" action="{% url 'blog:donate' %}"> + {% csrf_token %} + <table> + {{ payment_form.as_table }} + </table> + <input type="submit" value="Donate" class="button button-special" /> + </form> +</div> +<div id="fineprint" class="mtsbitem"> + <h2>Donation Fine Print</h2> + <ul> + <li>Donations are processed by Stripe, no credit card information is stored on this site.</li> + <li>Donations are processed in INR. All conversions will be made according to the latest rates.</li> + <li>Donations are non-refundable. If you donate by accident, please contact me and I will refund your donation.</li> + </ul> +</div> +<div id="testing-info" class="mtsbitem error"> + <h2>Testing Info</h2> + <p>The current payment form is only for testing purposes. It will not actually process a payment. If you would like to test the payment form, please use the following information:</p> + <ul> + <li>Card Number: 4242 4242 4242 4242</li> + <li>Expiration Date: Any date in the future</li> + <li>CVV: Any 3 digits</li> + </ul> +</div> +{% endif %} + +{% if request.GET.payment_id and request.GET.payment_status == 'succeeded' %} +<div id="donation-success" style="width: 400px; margin: 0 auto;"> + <h1>Thank you for your donation!</h1> + <p>Your donation of ${{ request.GET.amount }} has been processed successfully. Thank you for your support! You can find the transaction details below.</p> + <p><b>Transaction ID:</b> {{ request.GET.payment_id }}</p> + <p><b>Amount:</b> ${{ request.GET.amount }}</p> + <p><b>Amount Charged:</b> INR {{ request.GET.payment_amount }}</p> + <p><b>Payment Method:</b> <span style="text-transform:uppercase">{{ request.GET.payment_method }}</span></p> + <p><b>Payment Status:</b> <span style="text-transform:uppercase">{{ request.GET.payment_status }}</span></p> + <a href="{% url 'blog:home' %}" class="button button-special" style="text-decoration: none;">Return to Blog</a> +</div> +{% endif %} + +{% if request.GET.payment_id and request.GET.payment_status == 'failed' %} +<div id="donation-failed" style="width: 400px; margin: 0 auto;"> + <h1>Donation Failed</h1> + <p>Your donation of ${{ request.GET.amount }} has failed. You can find the transaction details below.</p> + <p><b>Transaction ID:</b> {{ request.GET.payment_id }}</p> + <p><b>Amount:</b> ${{ request.GET.amount }}</p> + <a href="{% url 'blog:donate' %}?amount={{ amount }}" class="button button-special" style="text-decoration: none;">Try Again</a> +</div> +{% endif %} + +{% endblock %} diff --git a/templates/blog/partials/sidebar.html b/templates/blog/partials/sidebar.html index af31552d..ffe34f7f 100644 --- a/templates/blog/partials/sidebar.html +++ b/templates/blog/partials/sidebar.html @@ -145,6 +145,16 @@ </li> <li> <span> + <img src="{% static 'images/site/icons/research.png' %}" alt="Archives" border="0"> + </span> + <span> + <a href="{% url 'blog:donate' %}"> + Donate + </a> + </span> + </li> + <li> + <span> <img src="{% static 'images/site/icons/setup.gif' %}" alt="Source Code" border="0"> </span> <span> diff --git a/templates/blog/post.html b/templates/blog/post.html index ee1fe7c0..ca3e7644 100644 --- a/templates/blog/post.html +++ b/templates/blog/post.html @@ -15,6 +15,24 @@ <div id="article-body">{{ post.body | safe }}</div> </div> +<div id="donate" style="clear: both;" class="mtsbitem"> + <h2 style="text-transform: none; border: none; margin: 0; padding: 0;">Liked this article?</h2> + <form action="{% url 'blog:donate' %}"> + <input type="hidden" name="referrer" value="{{ post.slug }}" id="referrer" style="display: none;"> + <p> + <span style="width:60px; display: inline-block;">Donate: </span> + <span style="width:90px; display: inline-block;"> + <input type="number" name="amount" id="amount" value="5" min="3" style="width: 60px; text-align: center;" oninput="parseAmount()"> + </span> + <span style="width:40px; display: inline-block;">USD</span> + <span style="width:100px; display: inline-block;"> + <input type="submit" id="donate_btn" value="Donate $5" class="button button-special"> + </span> + </p> + </form> +</div> + + <div id="comments" style="clear: both;" class="mtsbitem"> <h2>Comments</h2> {% for comment in comments %} @@ -127,6 +145,21 @@ } } + function parseAmount(amount) { + var amount_input = document.getElementById('amount'); + var donate_btn = document.getElementById('donate_btn'); + var amount = amount_input.value; + amount = parseInt(amount); + if (isNaN(amount)) { + amount = 3; + } + amount = Math.max(amount, 3); + amount = Math.min(amount, 1000); + amount_input.value = amount; + // update donate button value + donate_btn.value = 'Donate $' + amount; + } + // We are aiming to keep the JS to ES3 for compatibility with older browsers. // Get all textareas and bind the shortkeys. var textareas = document.getElementsByTagName('textarea'); |
