Skip to main content

Saferpay payment dashboard for django-oscar.

Project description

django-oscar-saferpay

Saferpay dashboard for django-oscar

This package is a django-oscar dashboard component for saferpay.
First follow the install instructions for django-saferpay (pypi, github) .

Install with pip install django-oscar-saferpay.

Update your settings.py:

INSTALLED_APPS = [
    ...
    'saferpay_oscar',
]

in your project urls.py:

from saferpay_oscar.dashboard.app import application as saferpay_dashboard_application
...
        url(r'^dashboard/saferpay/', saferpay_dashboard_application.urls),
...

add a new payment method to oscar in settings.py:

...
OSCAR_PAYMENT_METHODS = (
    ('saferpay', _('Creditcard (Visa, Mastercard, etc..)')),  # all your activated saferpay methods
)
...

Example implementation

Here is an example implementation not meant for for copy & paste. A oscar shop can be setup totally different.

Update your checkout myshop/checkout/app.py

from django.conf.urls import url
from oscar.apps.checkout import app
from myshop.checkout import views


class CheckoutApplication(app.CheckoutApplication):
    shipping_address_view = views.ShippingAddressView
    shipping_method_view = views.ShippingMethodView
    payment_details_view = views.PaymentDetailsView
    payment_method_view = views.PaymentMethodView
    preview_view = views.PreviewView  # normally just in PaymentDetailsView, custom to this use-case
    payment_capture_view = views.PaymentCapture  # new view not in oscar
    thankyou_view = views.ThankYouView
    payment_failed_view = views.PaymentFailed  # new view not in oscar

    def get_urls(self):
        urls = [
            url(r'^$', self.index_view.as_view(), name='index'),

            # Shipping/user address views
            url(r'shipping-address/$', self.shipping_address_view.as_view(), name='shipping-address'),
            url(r'user-address/edit/(?P<pk>\d+)/$', self.user_address_update_view.as_view(), name='user-address-update'),
            url(r'user-address/delete/(?P<pk>\d+)/$', self.user_address_delete_view.as_view(), name='user-address-delete'),

            # Shipping method views
            url(r'shipping-method/$', self.shipping_method_view.as_view(), name='shipping-method'),

            # Payment views
            url(r'payment-details/$', self.payment_details_view.as_view(), name='payment-details'),
            url(r'payment-method/$', self.payment_method_view.as_view(), name='payment-method'),
            url(r'payment-failed/$', self.payment_failed_view.as_view(), name='payment-failed'),

            # Preview and thankyou
            url(r'preview/$', self.preview_view.as_view(), name='preview'),
            url(r'payment-capture/$', self.payment_capture_view.as_view(), name='payment-capture'),
            url(r'thank-you/$', self.thankyou_view.as_view(), name='thank-you'),
        ]
        return self.post_process_urls(urls)

application = CheckoutApplication()

Update your views

Add it to your custom extended oscar checkout app views.

in myshop/checkout/views.py:

...
from saferpay.gateway import SaferpayService
from saferpay import execptions as sp_execptions
...

def billing_address_for_saferpay(billing_address):
    data = {}
    data['FirstName'] = billing_address.first_name
    data['LastName'] = billing_address.last_name
    data['Street'] = billing_address.line1
    if billing_address.line2:
        data['Street2'] = billing_address.line2
    data['Zip'] = billing_address.postcode
    data['City'] = billing_address.city
    return data

# in your PaymentDetailsView in handle_payment()
class PaymentDetailsView(OrderPlacementMixin, generic.TemplateView):
    ...

    def handle_payment(self, order_number, total, **kwargs):
        payment_method = self.checkout_session._get('payment', 'payment_method')
        # invoice
        if payment_method == 'invoice':
            pass
            # nothing to do for invoice, no money collected

        # saferpay
        elif payment_method == 'saferpay
            language_code = translation.get_language()[0:2]
            saferpay_service = SaferpayService(
                order_id=order_number, amount=total.incl_tax, currency=total.currency,
                language_code=language_code
            )
            billing_address = self.get_billing_address(
                shipping_address=self.get_shipping_address(self.request.basket)
            )
            billing_address_data = billing_address_for_saferpay(billing_address)
            payload = saferpay_service.payload_init(billing_address=billing_address_data)

            # redirects to payment page
            try:
                token = saferpay_service.paymentpage_init(payload)
                self.checkout_session._set('payment', 'saferpay_token', token)
                raise RedirectRequired(saferpay_service.paymentpage_redirect().url)
            except sp_execptions.GatewayError as e:
                self.restore_frozen_basket()
                return redirect(reverse_lazy('checkout:payment-failed'))

# new class
class PaymentCapture(views.PaymentDetailsView):
    success_url = reverse_lazy('checkout:thank-you')
    pre_conditions = []

    def load_frozen_basket(self, basket_id):
        # Lookup the frozen basket that this txn corresponds to
        try:
            basket = Basket.objects.get(id=basket_id, status=Basket.FROZEN)
        except Basket.DoesNotExist:
            return None

        if Selector:
            basket.strategy = Selector().strategy(self.request)

        Applicator().apply(basket, self.request.user, request=self.request)

        return basket

    def get(self, request, *args, **kwargs):
        token = self.checkout_session._get('payment', 'saferpay_token')
        saferpay_service = SaferpayService.init_from_transaction(token=token)
        try:
            status = saferpay_service.paymentpage_assert()
        except (
            sp_execptions.GatewayError, sp_execptions.TransactionDeclined,
            sp_execptions.UnableToTakePayment, sp_execptions.PaymentError
        ) as e:
            self.restore_frozen_basket()
            return redirect(reverse_lazy('checkout:payment-failed'))
        if status == 'CAPTURED':
            source_type, is_created = models.SourceType.objects.get_or_create(
                name='saferpay')
            source = source_type.sources.model(
                source_type=source_type,
                amount_allocated=saferpay_service.amount,
                currency=saferpay_service.currency
            )
            self.add_payment_source(source)
            self.add_payment_event('CAPTURED', saferpay_service.amount)

            post_payment.send_robust(sender=self, view=self)

            # If all is ok with payment, try and place order
            logger.info("Order #%s: payment successful, placing order", saferpay_service.order_id)

            try:
                basket_id = self.checkout_session.get_submitted_basket_id()
                basket = self.load_frozen_basket(basket_id)
                shipping_address = self.get_shipping_address(basket)
                billing_address = self.get_billing_address(shipping_address=shipping_address)
                shipping_method = self.get_shipping_method(
                    basket=basket, shipping_address=shipping_address
                )
                shipping_charge = shipping_method.calculate(basket)
                order_total = Price(
                    saferpay_service.currency, saferpay_service.amount, saferpay_service.amount
                )
                order_kwargs = self.checkout_session._get('payment', 'order_kwargs')
                order_number = saferpay_service.order_id
                return self.handle_order_placement(
                    order_number, self.request.user,
                    basket, shipping_address, shipping_method, shipping_charge,
                    billing_address, order_total, **order_kwargs
                )
            except UnableToPlaceOrder as e:
                # It's possible that something will go wrong while trying to
                # actually place an order.
                msg = six.text_type(e)
                logger.error("Order #%s: unable to place order - %s",
                             order_number, msg, exc_info=True)
                self.restore_frozen_basket()
                return self.render_preview(
                    self.request, error=msg
                )

# updated to add payment_method
class ThankYouView(CheckoutSessionMixin, views.ThankYouView):
    def get_context_data(self, **kwargs):
        ctx = super().get_context_data()
        ctx['payment_method'] = self.checkout_session._get('payment', 'payment_method')
        ctx.update(kwargs)
        return ctx

# new class, redirect to preview on payment failure
class PaymentFailed(OrderPlacementMixin, View):
    def get(self, request, *args, **kwargs):
        self.restore_frozen_basket()
        error_txt = _("The payment was not successful, please try again or use a different payment method.")
        messages.error(
            request,
            error_txt
        )
        return redirect(reverse_lazy('checkout:preview'))

Project details


Release history Release notifications

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Filename, size & hash SHA256 hash help File type Python version Upload date
django-oscar-saferpay-0.1.0.tar.gz (6.3 kB) Copy SHA256 hash SHA256 Source None

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN SignalFx SignalFx Supporter DigiCert DigiCert EV certificate StatusPage StatusPage Status page