Cartridge provides integration hooks for various steps in the checkout process, such as when billing and shipping details are entered, when payment information is entered, and when an order is complete. Each of these steps trigger one or more handler functions, which can be configured via settings that define their dotted Python package / module / function name. These settings and their defaults are:
- SHOP_HANDLER_BILLING_SHIPPING = "cartridge.shop.checkout.default_billship_handler"
- SHOP_HANDLER_TAX = "cartridge.shop.checkout.default_tax_handler"
- SHOP_HANDLER_PAYMENT = "cartridge.shop.checkout.default_payment_handler"
- SHOP_HANDLER_ORDER = "cartridge.shop.checkout.default_order_handler"
By defining your own values for each of these settings, you can create your own handler functions for each of these aspects of the checkout steps. You can also set their values to None if you wish to disable any of the default handlers rather than overriding them.
The handler functions have the signature:
handler_function(request, form, order=None)
The arguments are:
The current cart object can also be retrieved from the request with the following code:
from cartridge.shop.models import Cart cart = Cart.objects.from_request(request)
With the request object, the user’s cart, the order form fields and order instance all available, you can then implement any custom integration required, such as validating shipping rules, calculating the shipping amount, integrating with your preferred payment gateway and implementing any custom order handling once the order is complete.
The setting SHOP_HANDLER_BILLING_SHIPPING is used to specify the handler function that will be called when the billing and shipping step of the checkout form is submitted, as this is the first point at which we can be certain we know the customer’s address. It defaults to the value cartridge.shop.checkout.default_billship_handler, which simply sets a flat rate shipping value, as defined by the setting SHOP_DEFAULT_SHIPPING_VALUE. The order instance is not passed to the billing / shipping handler as it is only available at the last step of the checkout process.
The function cartridge.shop.utils.set_shipping is used to set the shipping type and amount, typically from within the billing / shipping step handler. It has the signature:
set_shipping(request, shipping_type, shipping_value)
request is the current Django request object, shipping_type is a string indicating the type of shipping, and shipping_value is a float or integer for the monetary value of shipping that will be added to the order.
Under the hood, this simply assigns the given shipping values to the user’s session. Subsequently these values are saved to the user’s order upon successful completion.
The setting SHOP_HANDLER_TAX is used to specify the handler function that will be called for setting the tax amount for the order. Like the billing / shipping handler, it’s called at the billing and shipping step of the checkout, as this is the first point at which we can be certain we know the customer’s address.
The interface for tax handling mimics the one described above for specifying shipping, where the function cartridge.shop.utils.set_tax is provided for the tax handler function to set the tax type and amount in the user’s session, that will then appear in the totals for the order:
set_tax(request, tax_type, tax_value)
Just as with shipping, the tax_type variable is simply a string label, so using a value such as “Tax” here will usually suffice.
This tax implementation is geared towards countries where tax is not included in a product’s price, and therefore should be added separately to the overall order total.
Some countries however generally include tax in a product’s price. In this case you may still need to display the tax amount in the order totals, purely for informational purposes without it having any bearing on the overall order total (which will already include the tax amount, given its a sum of all products ordered).
In the latter case, the easiest approach to displaying tax without it affecting the order total is to simply override the templates shop/includes/order_totals.html and shop/includes/order_totals.txt, and include in these a template tag that calculates the tax amount for display purposes. These templates are responsible for displaying the order totals in all cases, such as the cart, checkout, and the order receipt emails and PDF downloads.
The setting SHOP_HANDLER_PAYMENT is used to specify the handler function that will be called when the payment step of the checkout form is submitted. It defaults to the value cartridge.shop.checkout.default_payment_handler, which does nothing.
The payment handler function can optionally return a transaction ID which will be stored against a successful order for display in the admin.
Unlike the billing / shipping handler, the payment handler has access to the order object which contains fields for the order sub total, shipping, discount and tax amounts. If there is a payment error (see Error Handling) then the order is deleted.
When Cartridge is configured to display a final confirmation step after payment info is entered, the handler function defined by SHOP_HANDLER_PAYMENT will not be called until after the confirmation step. If there is no confirmation step, then the handler function will be called directly upon the customer submitting payment info.
There are a few built-in payment handlers you may want to use:
SHOP_HANDLER_PAYMENT = 'cartridge.shop.payment.egate.process' EGATE_ACCESS_CODE = '' EGATE_MERCHANT = ''
SHOP_HANDLER_PAYMENT = 'cartridge.shop.payment.paypal.process' PAYPAL_USER = '' PAYPAL_SIGNATURE = ''
Requires the stripe library from pypi.
SHOP_HANDLER_PAYMENT = 'cartridge.shop.payment.stripe_api.process' STRIPE_API_KEY = ''
The setting SHOP_HANDLER_ORDER is used to specify the handler function that will be called when the order is complete. It defaults to the value cartridge.shop.checkout.default_order_handler, which does nothing. With your order handler function, you can implement any custom order processing required once an order successfully completes.
When the billing / shipping and payment handler functions are called, the exception cartridge.shop.checkout.CheckoutError is checked for and, if caught, presented to the customer as an error message. This can be used to indicate a payment error, or even to raise potential errors in shipping requirements at the billing / shipping checkout step.
It’s possible to override the checkout form class used via the setting SHOP_CHECKOUT_FORM_CLASS, which contains the Python dotted path to the form class that will be used. This defaults to cartridge.shop.forms.OrderForm, which for the most part, is a Django ModelForm for the Order model, along with all of the methods for handling the different checkout steps. When defining your own custom form class, subclassing cartridge.shop.forms.OrderForm is certainly recommended.
The following list contains the fields for the cartridge.shop.forms.OrderForm instance, that is passed to each of the checkout handler functions when a custom form class is not used.
The following list contains the fields for the Django order model instance that is passed to the checkout handler functions in the final step.