Last active
October 29, 2023 20:21
-
-
Save jerinisready/398e9c26a169c411977061d4693bde03 to your computer and use it in GitHub Desktop.
How to integrate Stripe payment in Django Oscar!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PAYMENT_EVENT_PURCHASE = 'Purchase' | |
PAYMENT_METHOD_STRIPE = 'Stripe' | |
STRIPE_EMAIL = 'stripeEmail' | |
STRIPE_TOKEN = 'stripeToken' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AbstractFacade(object): | |
session = None | |
def __init__(self): | |
""" | |
Initializing Function. Set all api Keys here or create a client connection with name 'self.session' | |
""" | |
@staticmethod | |
def get_friendly_decline_message(error): | |
return 'The transaction was declined by your bank - please check your bankcard details and try again' | |
@staticmethod | |
def get_friendly_error_message(error): | |
return 'An error occurred when communicating with the payment gateway.' | |
def charge(self, order_number, total, token, currency='INR', description=None, metadata=None, **kwargs): | |
""" | |
'Facade.charge()' Function charges the required payment~ | |
order_number: str => Reference number we should pass to provider. | |
total: float => Total Amount (inclusive of all taxs) to be charged for order. | |
token: string => Token generated by third party authentication Mechanism, | |
which is needed for triggering transaction. | |
currency: string => Currency code. eg: INR | |
description: string => Description shown along with the transaction. | |
metadata: dict => Description shown along with the transaction. | |
""" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.conf import settings | |
from oscar.apps.payment.exceptions import UnableToTakePayment, InvalidGatewayRequestError | |
import razorpay | |
from apps.checkout.facade__abstract_facade import AbstractFacade | |
class RazorPayFacade(AbstractFacade): | |
def __init__(self): | |
super().__init__() | |
self.session = razorpay.Client(auth=(settings.RAZOR_PAY_PUBLIC_KEY, settings.RAZOR_PAY_SECRET_KEY)) | |
@staticmethod | |
def get_friendly_decline_message(error): | |
return 'The transaction was declined by your bank - please check your bankcard details and try again' | |
@staticmethod | |
def get_friendly_error_message(error): | |
return 'An error occurred when communicating with the payment gateway.' | |
def charge(self, | |
order_number, total_amount_incl_tax, token, currency=settings.PAYMENT_CURRENCY, | |
description=None, metadata=None, **kwargs): | |
self.session.payment.capture(token, "5000") | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.conf import settings | |
from oscar.apps.payment.exceptions import UnableToTakePayment, InvalidGatewayRequestError | |
import stripe | |
from apps.checkout.facade__abstract_facade import AbstractFacade | |
class StripeFacade(AbstractFacade): | |
def __init__(self): | |
stripe.api_key = settings.RAZORPAY_SECRET_KEY | |
@staticmethod | |
def get_friendly_decline_message(error): | |
return 'The transaction was declined by your bank - please check your bankcard details and try again' | |
@staticmethod | |
def get_friendly_error_message(error): | |
return 'An error occurred when communicating with the payment gateway.' | |
def charge(self, | |
order_number, | |
total, | |
token, | |
currency=settings.PAYMENT_CURRENCY, | |
description=None, | |
metadata=None, | |
**kwargs): | |
try: | |
return stripe.Charge.create( | |
amount=(total.incl_tax * 100).to_integral_value(), | |
currency=currency, | |
card=token, | |
description=description, | |
metadata=(metadata or {'order_number': order_number}), | |
**kwargs).id | |
except stripe.CardError as e: | |
raise UnableToTakePayment(self.get_friendly_decline_message(e)) | |
except stripe.StripeError as e: | |
raise InvalidGatewayRequestError(self.get_friendly_error_message(e)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django import forms | |
class StripeTokenForm(forms.Form): | |
stripeEmail = forms.EmailField(widget=forms.HiddenInput()) | |
stripeToken = forms.CharField(widget=forms.HiddenInput()) | |
stripeTokenType = forms.CharField(widget=forms.HiddenInput(), required=False) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.conf import settings | |
from oscar.core.loading import get_model | |
from django.utils.decorators import method_decorator | |
from django.views.decorators.csrf import csrf_exempt | |
from oscar.apps.checkout.views import PaymentDetailsView as CorePaymentDetailsView | |
from oscar.templatetags.currency_filters import currency | |
from apps.checkout.facade__stripe import StripeFacade as Facade | |
from . import PAYMENT_METHOD_STRIPE, PAYMENT_EVENT_PURCHASE, STRIPE_EMAIL, STRIPE_TOKEN | |
from apps.checkout import forms | |
SourceType = get_model('payment', 'SourceType') | |
Source = get_model('payment', 'Source') | |
""" | |
REQUIRED! | |
settings.STRIPE_PUBLIC_KEY = 'pk_test_pww_________________2235QtrBk9' | |
settings.STRIPE_SECRET_KEY = 'sk_test_5Li________________s00Lkbs47HE' | |
settings.PAYMENT_CURRENCY = 'INR' | |
""" | |
class PaymentDetailsView(CorePaymentDetailsView): | |
@method_decorator(csrf_exempt) | |
def dispatch(self, request, *args, **kwargs): | |
return super(PaymentDetailsView, self).dispatch(request, *args, **kwargs) | |
def get_context_data(self, **kwargs): | |
ctx = super(PaymentDetailsView, self).get_context_data(**kwargs) | |
if self.preview: | |
ctx['stripe_token_form'] = forms.StripeTokenForm(self.request.POST) | |
else: | |
ctx['stripe_publishable_key'] = settings.STRIPE_PUBLIC_KEY | |
ctx['order_total_incl_tax_cents'] = ( | |
ctx['order_total'].incl_tax * 100 # Rs 100.00 is needed as 10000 | |
).to_integral_value() | |
ctx['shop_name'] = 'Project Name' | |
ctx['description'] = f'Payment with \'Project Name\' with an amount of {currency(ctx["order_total"].incl_tax)} INR ' | |
return ctx | |
def handle_payment(self, order_number, total, **kwargs): | |
if self.request.POST['payment_method'] == 'cod': | |
return self.handle_cod(order_number, total, **kwargs) | |
stripe_ref = Facade().charge( | |
order_number, | |
total, | |
card=self.request.POST[STRIPE_TOKEN], | |
description=self.payment_description(order_number, total, **kwargs), | |
metadata=self.payment_metadata(order_number, total, **kwargs) | |
) | |
source_type, __ = SourceType.objects.get_or_create(name=PAYMENT_METHOD_STRIPE) | |
source = Source( | |
source_type=source_type, | |
currency=settings.PAYMENT_CURRENCY, | |
amount_allocated=total.incl_tax, | |
amount_debited=total.incl_tax, | |
reference=stripe_ref) | |
self.add_payment_source(source) | |
self.add_payment_event(PAYMENT_EVENT_PURCHASE, total.incl_tax) | |
def handle_cod(self, order_number, total, **kwargs): | |
# https://github.com/jerinisready/django-oscar-cash-on-delivery/tree/master/cashondelivery | |
# https://stackoverflow.com/questions/43408588/implementing-django-oscar-cod | |
self.amount = float(total.excl_tax) | |
gateway_code = self.request.POST.get('gateway_code', None) | |
if gateway_code and gateway_code == 'cash-on-delivery': | |
# Record payment source and event | |
source_type, is_created = SourceType.objects.get_or_create( | |
name='cash-on-delivery') | |
source = source_type.sources.model( | |
source_type=source_type, | |
amount_allocated=total.excl_tax) | |
self.add_payment_source(source) | |
self.add_payment_event('CREATED', total.excl_tax) | |
return | |
def payment_description(self, order_number, total, **kwargs): | |
from datetime import datetime | |
from pytz import timezone | |
ist = timezone('Asia/Kolkata') | |
ist_time = datetime.now(ist) | |
return f"Payment with 'Project Name' against order #{order_number} with an amount of {total.incl_tax} INR on {ist_time.strftime('%Y-%m-%d_%H-%M-%S')}" | |
def payment_metadata(self, order_number, total, **kwargs): | |
return {'order_number': order_number, 'amount': total.incl_tax} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice to meet you I am currently trying to introduce Stripe to django-oscar. I was unsuccessful and found this code.
But it doesn't work. If you don't mind, could you also show other code such as Settings.py and urls.py?
I'm a beginner, so I can't understand well with only the published code.
I'm sorry, but please.