In this part, we implement a simulation payment gateway into Django-Oscar.
From here we have a ground to easily apply the code to Stripe, PayPal, or any other payment service.
We assume that the payment gateway provides a way to configure:
- a cancellation link, if the user decides not to complete the payment
- a payment link, if the user confirms the transaction
Content:
1. Gateway Simulation
from django.views.generic import TemplateView
from django.shortcuts import get_object_or_404
from oscar.apps.basket.models import Basket
class Gateway(TemplateView):
template_name = "Store/gateway.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
basket_id = kwargs.get('basket_id')
if basket_id:
context['basket'] = get_object_or_404(Basket, id=basket_id)
return context
from django.urls import path
from .views import *
urlpatterns = [
path("gateway/<int:basket_id>/", Gateway.as_view(), name="gateway"),
]
...
urlpatterns = [
...
path('', include('Store.urls')),
]
{% load static %}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
</head>
<body style="background-color:DarkSlateGray; color:Cornsilk; margin:20% 20%; font-family:'Segoe UI';">
<h1>This could be any Payment Gateway!</h1>
<p>Basket ID: {{basket.id}} </p>
<a style="color:greenyellow!important;" href="">Cancel Payment</a> <br>
<a style="color:greenyellow!important;" href="">Complete Payment</a>
</body>
</html>
Later when we create the Cancel and Success views we can complete the href for the anchor tags.
2. Gateway Redirect View
from django.views.generic import RedirectView
from oscar.apps.checkout.session import CheckoutSessionMixin
from django.urls import reverse
from django.contrib import messages
class GatewayRedirect(CheckoutSessionMixin, RedirectView):
def get_redirect_url(self, **kwargs):
self.request.session['payment_method'] = "Gateway"
basket = self.build_submission()['basket']
shipping_address = self.get_shipping_address(basket)
shipping_method = self.get_shipping_method(basket, shipping_address=shipping_address)
shipping_charge = shipping_method.calculate(basket)
if basket.is_empty:
messages.error(self.request, "Your basket is empty")
return reverse('basket:summary')
else: basket.freeze()
return f"/gateway/{basket.id}"
Before the user is redirected to /gateway/ the basket is frozen making it impossible to be modified when the user is in the gateway.
urlpatterns = [
...
path("redirect/", GatewayRedirect.as_view(), name="gateway_redirect"),
]
...
{% block payment_details %}
{% block payment_details_content %}
...
<li>
<a href="{% url 'gateway_redirect' %}"> Gateway </a>
</li><br>
{% endblock payment_details_content %}
{% endblock payment_details %}
3. Cancelation
class GatewayCancel(RedirectView):
def get_redirect_url(self, **kwargs):
basket = get_object_or_404(Basket, id=kwargs['basket_id'], status=Basket.FROZEN)
basket.thaw()
return reverse('basket:summary')
urlpatterns = [
...
path("cancel/<int:basket_id>/", GatewayCancel.as_view(), name="gateway_cancel"),
]
...
<a style="color:greenyellow!important;" href="{% url 'gateway_cancel' basket.id %}">Cancel Payment</a> <br>
4. Payment
from oscar.apps.checkout.views import PaymentDetailsView
from oscar.apps.checkout.mixins import OrderPlacementMixin
from oscar.apps.partner.strategy import Selector
from oscar.apps.offer.applicator import Applicator
from oscar.apps.payment.models import Source, SourceType
class GatewaySuccess(PaymentDetailsView, OrderPlacementMixin):
def check_pre_conditions(self, request):
pass
def check_skip_conditions(self, request):
pass
def get(self, request, *args, **kwargs):
basket_id = kwargs.get('basket_id')
basket = Basket.objects.get(id=basket_id, status=Basket.FROZEN)
if Selector: basket.strategy = Selector().strategy(self.request)
Applicator().apply(basket, self.request.user, request=self.request)
return self.submit(**self.build_submission(basket=basket))
def handle_payment(self, order_number, total, **kwargs):
payment_method = self.request.session.get('payment_method', 'default_value')
source_type, _ = SourceType.objects.get_or_create(name=payment_method)
source = Source(source_type=source_type,
currency=total.currency,
amount_allocated=str(total.incl_tax),
amount_debited=str(total.incl_tax),
reference="-")
self.add_payment_source(source)
self.add_payment_event('Settled', str(total.incl_tax), reference="-")
def handle_order_placement(self, order_number, user, basket, shipping_address, shipping_method,
shipping_charge, billing_address, order_total, surcharges=None, **kwargs):
order = self.place_order(
order_number=order_number,
user=user,
basket=basket,
shipping_address=shipping_address,
shipping_method=shipping_method,
shipping_charge=shipping_charge,
order_total=order_total,
billing_address=billing_address,
surcharges=surcharges,
**kwargs,
)
basket.submit()
return self.handle_successful_order(order)
urlpatterns = [
...
path('success/<int:basket_id>/', GatewaySuccess.as_view(), name='gateway_success'),
]
...
<a style="color:greenyellow!important;" href="{% url 'gateway_success' basket.id %}">Complete Payment</a> <br>
Tested with: Django 5.2 Django-Oscar 4
09 Sept. 2025
|
Last Updated: 06 Jan. 2026
|
jaimedcsilva Related