如何为django oscar添加几种付款方式

如何为django oscar添加几种付款方式,django,django-oscar,Django,Django Oscar,我已经在我的项目中添加了货到付款方式。但我想再添加一个方法。我怎么能做到。目前,我有如下签出视图代码: class PaymentDetailsView(PaymentDetailsView): template_name = 'checkout/payment-details.html' template_name_preview = 'checkout/preview.html' def get_context_data(self, **kwargs): ctx = super(

我已经在我的项目中添加了货到付款方式。但我想再添加一个方法。我怎么能做到。目前,我有如下签出视图代码:

class PaymentDetailsView(PaymentDetailsView):
template_name = 'checkout/payment-details.html'
template_name_preview = 'checkout/preview.html'

def get_context_data(self, **kwargs):

    ctx = super(PaymentDetailsView, self).get_context_data(**kwargs)
    ctx['signature'] = gateway.hmac_gen(ctx)
    ctx['amount'] = '%s' % ctx['order_total'].incl_tax
    ctx['price'] = '%s' % ctx['basket'].lines.all()[0].price_incl_tax
    ctx['source'] = ctx

    return ctx

# def handle_payment_details_submission(self, request):
#     # Validate the submitted forms
#     shipping_address = self.get_shipping_address(
#         self.request.basket)
#     address_form = BillingAddressForm(shipping_address, request.POST)
#
#     if address_form.is_valid():
#         address_fields = dict(
#             (k, v) for (k, v) in address_form.instance.__dict__.items()
#             if not k.startswith('_') and not k.startswith('same_as_shipping'))
#         self.checkout_session.bill_to_new_address(address_fields)
#         return self.render_preview(request, billing_address_form=address_form)
#
#     # Forms are invalid - show them to the customer along with the
#     # validation errors.
#     return self.render_payment_details(
#         request, billing_address_form=address_form)

def handle_payment(self, order_number, total, **kwargs):
    reference = gateway.create_transaction(order_number, total)
    source_type, is_created = SourceType.objects.get_or_create(
        name='Cash on Delivery')
    source = Source(
        source_type=source_type,
        currency=total.currency,
        amount_allocated=total.incl_tax,
        amount_debited=total.incl_tax
    )
    self.add_payment_source(source)
    self.add_payment_event('Issued', total.incl_tax, reference=reference)
也许我可以通过增加运输方式来付款?

对我的实施是一个很好的启发

多个付款方式,意味着您必须首先列出某个地方的所有方式(以便于启用/禁用或其他方式),然后决定哪些方式适用于给定用户(取决于货币、篮子大小、位置等)。所有这些都可以通过扩展
PaymentMethodView
来处理

设置.py

from oscar import get_core_apps as get_oscar_apps

...
INSTALLED_APPS = [
    ...
] + get_oscar_apps([
    'checkout',
]) + [
    'cashondelivery', # https://github.com/ashishnitinpatil/django-oscar-cash-on-delivery
    'custom_payment', # my local app for custom payment gateway
]

OSCAR_PAYMENT_METHODS = (
    ('cod', _('Cash on delivery')),
    ('custom_payment', _('Credit / Debit card')),
)
...
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _


class PaymentMethodForm(forms.Form):
    """
    Extra form for the custom payment method.
    """
    payment_method = forms.ChoiceField(
        label=_("Select a payment method"),
        choices=settings.OSCAR_PAYMENT_METHODS,
        widget=forms.RadioSelect()
    )


def get_payment_method_display(payment_method):
    return dict(settings.OSCAR_PAYMENT_METHODS).get(payment_method)
from django.utils import six
from django.conf import settings
from django.shortcuts import redirect
from django.views.generic import FormView
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse, reverse_lazy

from . import forms
from custom_payment import facade
from cashondelivery import gateway as cod_gateway

from oscar.apps.checkout import exceptions
from oscar.core.loading import get_model, get_class


Source = get_model("payment", "Source")
SourceType = get_model("payment", "SourceType")
RedirectRequired = get_class("payment.exceptions", "RedirectRequired")
UnableToPlaceOrder = get_class('order.exceptions', 'UnableToPlaceOrder')
OscarPaymentMethodView = get_class("checkout.views", "PaymentMethodView")
OscarPaymentDetailsView = get_class("checkout.views", "PaymentDetailsView")
OscarShippingMethodView = get_class("checkout.views", "ShippingMethodView")

# Sample pre-condition
class CheckCountryPreCondition(object):
    """DRY class for check country in session pre_condition"""

    def get_pre_conditions(self, request):
        if 'check_country_in_session' not in self.pre_conditions:
            return self.pre_conditions + ['check_country_in_session']
        return super().get_pre_conditions(request)

    def check_country_in_session(self, request):
        if request.session.get('country', None) is None:
            raise exceptions.FailedPreCondition(
                    url=reverse('checkout:shipping-address'),
                )


# Inspired by https://github.com/django-oscar/django-oscar-docdata/blob/master/sandbox/apps/checkout/views.py
class PaymentMethodView(CheckCountryPreCondition, OscarPaymentMethodView, FormView):
    """
    View for a user to choose which payment method(s) they want to use.

    This would include setting allocations if payment is to be split
    between multiple sources. It's not the place for entering sensitive details
    like bankcard numbers though - that belongs on the payment details view.
    """
    template_name = "checkout/payment_method.html"
    step = 'payment-method'
    form_class = forms.PaymentMethodForm
    success_url = reverse_lazy('checkout:payment-details')

    pre_conditions = [
        'check_basket_is_not_empty',
        'check_basket_is_valid',
        'check_user_email_is_captured',
        'check_shipping_data_is_captured',
        'check_payment_data_is_captured',
    ]
    skip_conditions = ['skip_unless_payment_is_required']

    def get(self, request, *args, **kwargs):
        # if only single payment method, store that
        # and then follow default (redirect to preview)
        # else show payment method choice form
        if len(settings.OSCAR_PAYMENT_METHODS) == 1:
            self.checkout_session.pay_by(settings.OSCAR_PAYMENT_METHODS[0][0])
            return redirect(self.get_success_url())
        else:
            return FormView.get(self, request, *args, **kwargs)

    def get_success_url(self, *args, **kwargs):
        # Redirect to the correct payments page as per the method (different methods may have different views &/or additional views)
        return reverse_lazy('checkout:preview')

    def get_initial(self):
        return {
            'payment_method': self.checkout_session.payment_method(),
        }

    def form_valid(self, form):
        # Store payment method in the CheckoutSessionMixin.checkout_session (a CheckoutSessionData object)
        self.checkout_session.pay_by(form.cleaned_data['payment_method'])
        return super().form_valid(form)

class PaymentDetailsView(CheckCountryPreCondition, OscarPaymentDetailsView):

    def handle_payment(self, order_number, order_total, **kwargs):
        method = self.checkout_session.payment_method()
        if method == 'cod':
            return self.handle_cod_payment(order_number, order_total, **kwargs)
        elif method == 'custom_payment':
            return self.handle_custom_payment_payment(order_number, order_total, **kwargs)
        else:
            raise PaymentError(_('Bad payment method in handle_payment!'))

    def handle_cod_payment(self, order_number, total, **kwargs):
        reference = cod_gateway.create_transaction(order_number, total)
        source_type, is_created = SourceType.objects.get_or_create(
            name='Cash on delivery')
        source = Source(
            source_type=source_type,
            currency=total.currency,
            amount_allocated=total.incl_tax,
            amount_debited=total.incl_tax
        )
        self.add_payment_source(source)
        self.add_payment_event('awaiting-delivery', total.incl_tax, reference=reference)

    def handle_custom_payment_payment(self, order_number, total, **kwargs):
        submission = self.build_submission(order_number=order_number, **kwargs)

        # Save required payment gateway data
        # also validates that we have all we need
        custom_payment_payment_data = facade.get_gateway_url_and_parameters(
            submission, self.request.build_absolute_uri, live=settings.custom_payment['LIVE'])

        # Any raised exceptions are handled by the PaymentDetail.submit() code.
        custom_payment_order = facade.create_order(
            order_number=order_number,
            amount=total,
            payment_data=custom_payment_payment_data
        )
        # record payment data to session to double verify things on success / failure
        self.set_custom_payment_payment_data(custom_payment_payment_data, custom_payment_order)

        source = Source(
            source_type=facade.get_source_type(),
            currency=total.currency,
            amount_allocated=total.incl_tax,   # amount_* field depends on type of transaction.
            reference=custom_payment_order.id
        )
        self.add_payment_source(source)

        # Also record payment event.
        # This will be visible in the Dashboard
        self.add_payment_event('pre-auth', total.incl_tax, reference=custom_payment_order.id)

        # Regardless of whether the order is paid, write it in the database before redirecting.
        # Oscar actually skips this when redirecting the user to the payment provider.
        self._save_order(order_number, submission)

        # Redirect the user to the payment provider's gateway.
        raise RedirectRequired(facade.get_payment_url())

    def _save_order(self, order_number, submission):
        # Finalize the order that PaymentDetailsView.submit() started
        # If all is ok with payment, try and place order
        logger.info("Order #%s: payment started, placing order", order_number)

        try:
            # Call OrderPlacementMixin.handle_order_placement()
            return self.handle_order_placement(
                order_number, submission['user'], submission['basket'],
                submission['shipping_address'], submission['shipping_method'],
                submission['shipping_charge'], submission['billing_address'],
                submission['order_total'], **(submission['order_kwargs'])
            )
        except UnableToPlaceOrder as e:
            # It's possible that something will go wrong while trying to
            # actually place an order.  Not a good situation to be in as a
            # payment transaction may already have taken place, but needs
            # to be handled gracefully.
            logger.error("Order #%s: unable to place order - %s", order_number, e, exc_info=True)
            msg = six.text_type(e)
            self.restore_frozen_basket()
            return self.render_to_response(self.get_context_data(error=msg))

    # Can't update CheckoutSessionMixin nicely without causing trouble,
    # hence dumping the overridden / new methods from that class here
    def check_payment_data_is_captured(self, request):
        # We don't collect payment data by default so we don't have anything to
        # validate here. If your shop requires forms to be submitted on the
        # payment details page, then override this method to check that the
        # relevant data is available. Often just enforcing that the preview
        # view is only accessible from a POST request is sufficient.
        if not self.checkout_session.payment_method():
            raise FailedPreCondition(
                url=reverse('checkout:payment-method'),
                message=_("Please select a payment method for your order!")
            )

    def set_custom_payment_payment_data(self, payment_data, custom_payment_order):
        self.request.session['custom_payment'] = payment_data
        self.request.session['custom_payment_order_id'] = custom_payment_order.id
        self.request.session['ongoing_online_payment'] = True
        self.request.session.pop('custom_payment_error', None)

    def get_context_data(self, **kwargs):
        ctx = super(PaymentDetailsView, self).get_context_data(**kwargs)
        ctx.update({'payment_method': self.checkout_session.payment_method()})
        return ctx

    def send_confirmation_message(self, order, *args, **kwargs):
        # In case of custom_payment, delay sending the order confirmation till payment success!
        if not self.checkout_session.payment_method() == 'custom_payment':
            super(PaymentDetailsView, self).send_confirmation_message(order, *args, **kwargs)
签出/forms.py

from oscar import get_core_apps as get_oscar_apps

...
INSTALLED_APPS = [
    ...
] + get_oscar_apps([
    'checkout',
]) + [
    'cashondelivery', # https://github.com/ashishnitinpatil/django-oscar-cash-on-delivery
    'custom_payment', # my local app for custom payment gateway
]

OSCAR_PAYMENT_METHODS = (
    ('cod', _('Cash on delivery')),
    ('custom_payment', _('Credit / Debit card')),
)
...
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _


class PaymentMethodForm(forms.Form):
    """
    Extra form for the custom payment method.
    """
    payment_method = forms.ChoiceField(
        label=_("Select a payment method"),
        choices=settings.OSCAR_PAYMENT_METHODS,
        widget=forms.RadioSelect()
    )


def get_payment_method_display(payment_method):
    return dict(settings.OSCAR_PAYMENT_METHODS).get(payment_method)
from django.utils import six
from django.conf import settings
from django.shortcuts import redirect
from django.views.generic import FormView
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse, reverse_lazy

from . import forms
from custom_payment import facade
from cashondelivery import gateway as cod_gateway

from oscar.apps.checkout import exceptions
from oscar.core.loading import get_model, get_class


Source = get_model("payment", "Source")
SourceType = get_model("payment", "SourceType")
RedirectRequired = get_class("payment.exceptions", "RedirectRequired")
UnableToPlaceOrder = get_class('order.exceptions', 'UnableToPlaceOrder')
OscarPaymentMethodView = get_class("checkout.views", "PaymentMethodView")
OscarPaymentDetailsView = get_class("checkout.views", "PaymentDetailsView")
OscarShippingMethodView = get_class("checkout.views", "ShippingMethodView")

# Sample pre-condition
class CheckCountryPreCondition(object):
    """DRY class for check country in session pre_condition"""

    def get_pre_conditions(self, request):
        if 'check_country_in_session' not in self.pre_conditions:
            return self.pre_conditions + ['check_country_in_session']
        return super().get_pre_conditions(request)

    def check_country_in_session(self, request):
        if request.session.get('country', None) is None:
            raise exceptions.FailedPreCondition(
                    url=reverse('checkout:shipping-address'),
                )


# Inspired by https://github.com/django-oscar/django-oscar-docdata/blob/master/sandbox/apps/checkout/views.py
class PaymentMethodView(CheckCountryPreCondition, OscarPaymentMethodView, FormView):
    """
    View for a user to choose which payment method(s) they want to use.

    This would include setting allocations if payment is to be split
    between multiple sources. It's not the place for entering sensitive details
    like bankcard numbers though - that belongs on the payment details view.
    """
    template_name = "checkout/payment_method.html"
    step = 'payment-method'
    form_class = forms.PaymentMethodForm
    success_url = reverse_lazy('checkout:payment-details')

    pre_conditions = [
        'check_basket_is_not_empty',
        'check_basket_is_valid',
        'check_user_email_is_captured',
        'check_shipping_data_is_captured',
        'check_payment_data_is_captured',
    ]
    skip_conditions = ['skip_unless_payment_is_required']

    def get(self, request, *args, **kwargs):
        # if only single payment method, store that
        # and then follow default (redirect to preview)
        # else show payment method choice form
        if len(settings.OSCAR_PAYMENT_METHODS) == 1:
            self.checkout_session.pay_by(settings.OSCAR_PAYMENT_METHODS[0][0])
            return redirect(self.get_success_url())
        else:
            return FormView.get(self, request, *args, **kwargs)

    def get_success_url(self, *args, **kwargs):
        # Redirect to the correct payments page as per the method (different methods may have different views &/or additional views)
        return reverse_lazy('checkout:preview')

    def get_initial(self):
        return {
            'payment_method': self.checkout_session.payment_method(),
        }

    def form_valid(self, form):
        # Store payment method in the CheckoutSessionMixin.checkout_session (a CheckoutSessionData object)
        self.checkout_session.pay_by(form.cleaned_data['payment_method'])
        return super().form_valid(form)

class PaymentDetailsView(CheckCountryPreCondition, OscarPaymentDetailsView):

    def handle_payment(self, order_number, order_total, **kwargs):
        method = self.checkout_session.payment_method()
        if method == 'cod':
            return self.handle_cod_payment(order_number, order_total, **kwargs)
        elif method == 'custom_payment':
            return self.handle_custom_payment_payment(order_number, order_total, **kwargs)
        else:
            raise PaymentError(_('Bad payment method in handle_payment!'))

    def handle_cod_payment(self, order_number, total, **kwargs):
        reference = cod_gateway.create_transaction(order_number, total)
        source_type, is_created = SourceType.objects.get_or_create(
            name='Cash on delivery')
        source = Source(
            source_type=source_type,
            currency=total.currency,
            amount_allocated=total.incl_tax,
            amount_debited=total.incl_tax
        )
        self.add_payment_source(source)
        self.add_payment_event('awaiting-delivery', total.incl_tax, reference=reference)

    def handle_custom_payment_payment(self, order_number, total, **kwargs):
        submission = self.build_submission(order_number=order_number, **kwargs)

        # Save required payment gateway data
        # also validates that we have all we need
        custom_payment_payment_data = facade.get_gateway_url_and_parameters(
            submission, self.request.build_absolute_uri, live=settings.custom_payment['LIVE'])

        # Any raised exceptions are handled by the PaymentDetail.submit() code.
        custom_payment_order = facade.create_order(
            order_number=order_number,
            amount=total,
            payment_data=custom_payment_payment_data
        )
        # record payment data to session to double verify things on success / failure
        self.set_custom_payment_payment_data(custom_payment_payment_data, custom_payment_order)

        source = Source(
            source_type=facade.get_source_type(),
            currency=total.currency,
            amount_allocated=total.incl_tax,   # amount_* field depends on type of transaction.
            reference=custom_payment_order.id
        )
        self.add_payment_source(source)

        # Also record payment event.
        # This will be visible in the Dashboard
        self.add_payment_event('pre-auth', total.incl_tax, reference=custom_payment_order.id)

        # Regardless of whether the order is paid, write it in the database before redirecting.
        # Oscar actually skips this when redirecting the user to the payment provider.
        self._save_order(order_number, submission)

        # Redirect the user to the payment provider's gateway.
        raise RedirectRequired(facade.get_payment_url())

    def _save_order(self, order_number, submission):
        # Finalize the order that PaymentDetailsView.submit() started
        # If all is ok with payment, try and place order
        logger.info("Order #%s: payment started, placing order", order_number)

        try:
            # Call OrderPlacementMixin.handle_order_placement()
            return self.handle_order_placement(
                order_number, submission['user'], submission['basket'],
                submission['shipping_address'], submission['shipping_method'],
                submission['shipping_charge'], submission['billing_address'],
                submission['order_total'], **(submission['order_kwargs'])
            )
        except UnableToPlaceOrder as e:
            # It's possible that something will go wrong while trying to
            # actually place an order.  Not a good situation to be in as a
            # payment transaction may already have taken place, but needs
            # to be handled gracefully.
            logger.error("Order #%s: unable to place order - %s", order_number, e, exc_info=True)
            msg = six.text_type(e)
            self.restore_frozen_basket()
            return self.render_to_response(self.get_context_data(error=msg))

    # Can't update CheckoutSessionMixin nicely without causing trouble,
    # hence dumping the overridden / new methods from that class here
    def check_payment_data_is_captured(self, request):
        # We don't collect payment data by default so we don't have anything to
        # validate here. If your shop requires forms to be submitted on the
        # payment details page, then override this method to check that the
        # relevant data is available. Often just enforcing that the preview
        # view is only accessible from a POST request is sufficient.
        if not self.checkout_session.payment_method():
            raise FailedPreCondition(
                url=reverse('checkout:payment-method'),
                message=_("Please select a payment method for your order!")
            )

    def set_custom_payment_payment_data(self, payment_data, custom_payment_order):
        self.request.session['custom_payment'] = payment_data
        self.request.session['custom_payment_order_id'] = custom_payment_order.id
        self.request.session['ongoing_online_payment'] = True
        self.request.session.pop('custom_payment_error', None)

    def get_context_data(self, **kwargs):
        ctx = super(PaymentDetailsView, self).get_context_data(**kwargs)
        ctx.update({'payment_method': self.checkout_session.payment_method()})
        return ctx

    def send_confirmation_message(self, order, *args, **kwargs):
        # In case of custom_payment, delay sending the order confirmation till payment success!
        if not self.checkout_session.payment_method() == 'custom_payment':
            super(PaymentDetailsView, self).send_confirmation_message(order, *args, **kwargs)
签出/查看.py

from oscar import get_core_apps as get_oscar_apps

...
INSTALLED_APPS = [
    ...
] + get_oscar_apps([
    'checkout',
]) + [
    'cashondelivery', # https://github.com/ashishnitinpatil/django-oscar-cash-on-delivery
    'custom_payment', # my local app for custom payment gateway
]

OSCAR_PAYMENT_METHODS = (
    ('cod', _('Cash on delivery')),
    ('custom_payment', _('Credit / Debit card')),
)
...
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _


class PaymentMethodForm(forms.Form):
    """
    Extra form for the custom payment method.
    """
    payment_method = forms.ChoiceField(
        label=_("Select a payment method"),
        choices=settings.OSCAR_PAYMENT_METHODS,
        widget=forms.RadioSelect()
    )


def get_payment_method_display(payment_method):
    return dict(settings.OSCAR_PAYMENT_METHODS).get(payment_method)
from django.utils import six
from django.conf import settings
from django.shortcuts import redirect
from django.views.generic import FormView
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse, reverse_lazy

from . import forms
from custom_payment import facade
from cashondelivery import gateway as cod_gateway

from oscar.apps.checkout import exceptions
from oscar.core.loading import get_model, get_class


Source = get_model("payment", "Source")
SourceType = get_model("payment", "SourceType")
RedirectRequired = get_class("payment.exceptions", "RedirectRequired")
UnableToPlaceOrder = get_class('order.exceptions', 'UnableToPlaceOrder')
OscarPaymentMethodView = get_class("checkout.views", "PaymentMethodView")
OscarPaymentDetailsView = get_class("checkout.views", "PaymentDetailsView")
OscarShippingMethodView = get_class("checkout.views", "ShippingMethodView")

# Sample pre-condition
class CheckCountryPreCondition(object):
    """DRY class for check country in session pre_condition"""

    def get_pre_conditions(self, request):
        if 'check_country_in_session' not in self.pre_conditions:
            return self.pre_conditions + ['check_country_in_session']
        return super().get_pre_conditions(request)

    def check_country_in_session(self, request):
        if request.session.get('country', None) is None:
            raise exceptions.FailedPreCondition(
                    url=reverse('checkout:shipping-address'),
                )


# Inspired by https://github.com/django-oscar/django-oscar-docdata/blob/master/sandbox/apps/checkout/views.py
class PaymentMethodView(CheckCountryPreCondition, OscarPaymentMethodView, FormView):
    """
    View for a user to choose which payment method(s) they want to use.

    This would include setting allocations if payment is to be split
    between multiple sources. It's not the place for entering sensitive details
    like bankcard numbers though - that belongs on the payment details view.
    """
    template_name = "checkout/payment_method.html"
    step = 'payment-method'
    form_class = forms.PaymentMethodForm
    success_url = reverse_lazy('checkout:payment-details')

    pre_conditions = [
        'check_basket_is_not_empty',
        'check_basket_is_valid',
        'check_user_email_is_captured',
        'check_shipping_data_is_captured',
        'check_payment_data_is_captured',
    ]
    skip_conditions = ['skip_unless_payment_is_required']

    def get(self, request, *args, **kwargs):
        # if only single payment method, store that
        # and then follow default (redirect to preview)
        # else show payment method choice form
        if len(settings.OSCAR_PAYMENT_METHODS) == 1:
            self.checkout_session.pay_by(settings.OSCAR_PAYMENT_METHODS[0][0])
            return redirect(self.get_success_url())
        else:
            return FormView.get(self, request, *args, **kwargs)

    def get_success_url(self, *args, **kwargs):
        # Redirect to the correct payments page as per the method (different methods may have different views &/or additional views)
        return reverse_lazy('checkout:preview')

    def get_initial(self):
        return {
            'payment_method': self.checkout_session.payment_method(),
        }

    def form_valid(self, form):
        # Store payment method in the CheckoutSessionMixin.checkout_session (a CheckoutSessionData object)
        self.checkout_session.pay_by(form.cleaned_data['payment_method'])
        return super().form_valid(form)

class PaymentDetailsView(CheckCountryPreCondition, OscarPaymentDetailsView):

    def handle_payment(self, order_number, order_total, **kwargs):
        method = self.checkout_session.payment_method()
        if method == 'cod':
            return self.handle_cod_payment(order_number, order_total, **kwargs)
        elif method == 'custom_payment':
            return self.handle_custom_payment_payment(order_number, order_total, **kwargs)
        else:
            raise PaymentError(_('Bad payment method in handle_payment!'))

    def handle_cod_payment(self, order_number, total, **kwargs):
        reference = cod_gateway.create_transaction(order_number, total)
        source_type, is_created = SourceType.objects.get_or_create(
            name='Cash on delivery')
        source = Source(
            source_type=source_type,
            currency=total.currency,
            amount_allocated=total.incl_tax,
            amount_debited=total.incl_tax
        )
        self.add_payment_source(source)
        self.add_payment_event('awaiting-delivery', total.incl_tax, reference=reference)

    def handle_custom_payment_payment(self, order_number, total, **kwargs):
        submission = self.build_submission(order_number=order_number, **kwargs)

        # Save required payment gateway data
        # also validates that we have all we need
        custom_payment_payment_data = facade.get_gateway_url_and_parameters(
            submission, self.request.build_absolute_uri, live=settings.custom_payment['LIVE'])

        # Any raised exceptions are handled by the PaymentDetail.submit() code.
        custom_payment_order = facade.create_order(
            order_number=order_number,
            amount=total,
            payment_data=custom_payment_payment_data
        )
        # record payment data to session to double verify things on success / failure
        self.set_custom_payment_payment_data(custom_payment_payment_data, custom_payment_order)

        source = Source(
            source_type=facade.get_source_type(),
            currency=total.currency,
            amount_allocated=total.incl_tax,   # amount_* field depends on type of transaction.
            reference=custom_payment_order.id
        )
        self.add_payment_source(source)

        # Also record payment event.
        # This will be visible in the Dashboard
        self.add_payment_event('pre-auth', total.incl_tax, reference=custom_payment_order.id)

        # Regardless of whether the order is paid, write it in the database before redirecting.
        # Oscar actually skips this when redirecting the user to the payment provider.
        self._save_order(order_number, submission)

        # Redirect the user to the payment provider's gateway.
        raise RedirectRequired(facade.get_payment_url())

    def _save_order(self, order_number, submission):
        # Finalize the order that PaymentDetailsView.submit() started
        # If all is ok with payment, try and place order
        logger.info("Order #%s: payment started, placing order", order_number)

        try:
            # Call OrderPlacementMixin.handle_order_placement()
            return self.handle_order_placement(
                order_number, submission['user'], submission['basket'],
                submission['shipping_address'], submission['shipping_method'],
                submission['shipping_charge'], submission['billing_address'],
                submission['order_total'], **(submission['order_kwargs'])
            )
        except UnableToPlaceOrder as e:
            # It's possible that something will go wrong while trying to
            # actually place an order.  Not a good situation to be in as a
            # payment transaction may already have taken place, but needs
            # to be handled gracefully.
            logger.error("Order #%s: unable to place order - %s", order_number, e, exc_info=True)
            msg = six.text_type(e)
            self.restore_frozen_basket()
            return self.render_to_response(self.get_context_data(error=msg))

    # Can't update CheckoutSessionMixin nicely without causing trouble,
    # hence dumping the overridden / new methods from that class here
    def check_payment_data_is_captured(self, request):
        # We don't collect payment data by default so we don't have anything to
        # validate here. If your shop requires forms to be submitted on the
        # payment details page, then override this method to check that the
        # relevant data is available. Often just enforcing that the preview
        # view is only accessible from a POST request is sufficient.
        if not self.checkout_session.payment_method():
            raise FailedPreCondition(
                url=reverse('checkout:payment-method'),
                message=_("Please select a payment method for your order!")
            )

    def set_custom_payment_payment_data(self, payment_data, custom_payment_order):
        self.request.session['custom_payment'] = payment_data
        self.request.session['custom_payment_order_id'] = custom_payment_order.id
        self.request.session['ongoing_online_payment'] = True
        self.request.session.pop('custom_payment_error', None)

    def get_context_data(self, **kwargs):
        ctx = super(PaymentDetailsView, self).get_context_data(**kwargs)
        ctx.update({'payment_method': self.checkout_session.payment_method()})
        return ctx

    def send_confirmation_message(self, order, *args, **kwargs):
        # In case of custom_payment, delay sending the order confirmation till payment success!
        if not self.checkout_session.payment_method() == 'custom_payment':
            super(PaymentDetailsView, self).send_confirmation_message(order, *args, **kwargs)
您可以忽略
自定义支付的详细信息
,因为这是我的项目/支付网关的特定内容,您可能对此有不同的要求。我还留了一大块东西在里面,这样你们就可以据此来思考问题了

PS-请注意,我使用的是Python3.6,所以在以前的Python版本(特别是2.7版)中,一些东西(如
super
)可能无法像这里提到的那样工作

更新

我的
模板/checkout/payment\u method.html

{% extends "checkout/checkout.html" %}
{% load i18n %}

{% block checkout_title %}{{ form.payment_method.label }}{% endblock %}

{% block checkout_nav %}
    {% include 'checkout/nav.html' with step=3 %}
{% endblock %}

{% block content %}
    {% if error %}
        <div class="alert alert-error">
            {{ error }}
        </div>
    {% endif %}

    <form action="" method="post">
        {% csrf_token %}

        {% if form.payment_method.errors %}{{ form.payment_method.errors }}{% endif %}
        <div class="radio">
            <label for="id_payment_method_0">
                <input id="id_payment_method_0" name="payment_method" type="radio" value="cod" required="">{% trans "Cash on delivery" %}
            </label>
        </div>
        <div class="radio">
            <label for="id_payment_method_1">
                <input id="id_payment_method_1" name="payment_method" type="radio" value="custom_payment" required="">{% trans "Credit / Debit Card" %}
            </label>
        </div>
        <p><button type="submit" class="btn btn-large btn-primary">{% trans "Continue to confirmation" %}</button>

    </form>
{% endblock %}
{%extensed“checkout/checkout.html”%}
{%load i18n%}
{%block checkout\u title%}{{form.payment\u method.label}{%endblock%}
{%block checkout_nav%}
{%include'checkout/nav.html'和step=3%}
{%endblock%}
{%block content%}
{%if错误%}
{{error}}
{%endif%}
{%csrf_令牌%}
{%if form.payment_method.errors%}{{form.payment_method.errors}{%endif%}
{%trans“货到付款”%}
{%trans“信用卡/借记卡”%}
{%trans“继续确认”%}
{%endblock%}

我想问一下您是如何实现货到付款的,请遵循这一点,但付款页面没有responsive@olar19试试我的货到付款叉子,但是货到付款不能在仪表板上更新。你没有发布关于模板的任何信息。这几乎不需要,也不重要,因为你的观点无论如何都是非常重要的习惯。我会看看是否可以添加一个最小的模板。那太好了。你在这个例子中使用了奥斯卡结帐应用程序吗?你能分享文件夹结构吗?Thanks@benzkji在“签出”视图中,您可以使用
self.checkout\u session.payment\u method()
获取付款方法。至于保存方法,不需要将其存储在order下,因为方法已经保存在order下。而且,由于一个订单可以有多个付款,所以不建议将付款方式保存在订单对象下。