Update documentation

pull/211/head
JamesRamm 2018-12-31 16:09:12 +01:00
rodzic f71948b9cd
commit 3741dc5234
24 zmienionych plików z 369 dodań i 422 usunięć

Wyświetl plik

@ -14,7 +14,7 @@ Checkout the [demo site](https://github.com/JamesRamm/longclaw_demo) and [docume
<a href="https://www.buymeacoffee.com/pHtXDM748" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a> <a href="https://www.buymeacoffee.com/pHtXDM748" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>
![Image of the dashboard](docs/images/dashboard.png) ![Image of the dashboard](docs/assets/dashboard.png)
## Quickstart ## Quickstart
@ -42,7 +42,7 @@ Setup a Longclaw project
### Screenshots ### Screenshots
![Order Detail](docs/images/order_detail.png) ![Order Detail](docs/assets/order_detail.png)
## Support ## Support

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 90 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 90 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 69 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 69 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 58 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 58 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 99 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 99 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 108 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 108 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 56 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 56 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 76 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 76 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 73 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 73 KiB

Wyświetl plik

@ -1,7 +0,0 @@
---
id: doc2
title: document number 2
---
This is a link to [another document.](doc3.md)
This is a link to an [external page.](http://www.example.com)

Wyświetl plik

@ -1,13 +0,0 @@
---
id: doc3
title: This is document number 3
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ac euismod odio, eu consequat dui. Nullam molestie consectetur risus id imperdiet. Proin sodales ornare turpis, non mollis massa ultricies id. Nam at nibh scelerisque, feugiat ante non, dapibus tortor. Vivamus volutpat diam quis tellus elementum bibendum. Praesent semper gravida velit quis aliquam. Etiam in cursus neque. Nam lectus ligula, malesuada et mauris a, bibendum faucibus mi. Phasellus ut interdum felis. Phasellus in odio pulvinar, porttitor urna eget, fringilla lectus. Aliquam sollicitudin est eros. Mauris consectetur quam vitae mauris interdum hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Duis et egestas libero, imperdiet faucibus ipsum. Sed posuere eget urna vel feugiat. Vivamus a arcu sagittis, fermentum urna dapibus, congue lectus. Fusce vulputate porttitor nisl, ac cursus elit volutpat vitae. Nullam vitae ipsum egestas, convallis quam non, porta nibh. Morbi gravida erat nec neque bibendum, eu pellentesque velit posuere. Fusce aliquam erat eu massa eleifend tristique.
Sed consequat sollicitudin ipsum eget tempus. Integer a aliquet velit. In justo nibh, pellentesque non suscipit eget, gravida vel lacus. Donec odio ante, malesuada in massa quis, pharetra tristique ligula. Donec eros est, tristique eget finibus quis, semper non nisl. Vivamus et elit nec enim ornare placerat. Sed posuere odio a elit cursus sagittis.
Phasellus feugiat purus eu tortor ultrices finibus. Ut libero nibh, lobortis et libero nec, dapibus posuere eros. Sed sagittis euismod justo at consectetur. Nulla finibus libero placerat, cursus sapien at, eleifend ligula. Vivamus elit nisl, hendrerit ac nibh eu, ultrices tempus dui. Nam tellus neque, commodo non rhoncus eu, gravida in risus. Nullam id iaculis tortor.
Nullam at odio in sem varius tempor sit amet vel lorem. Etiam eu hendrerit nisl. Fusce nibh mauris, vulputate sit amet ex vitae, congue rhoncus nisl. Sed eget tellus purus. Nullam tempus commodo erat ut tristique. Cras accumsan massa sit amet justo consequat eleifend. Integer scelerisque vitae tellus id consectetur.

Wyświetl plik

@ -1,6 +0,0 @@
---
id: doc4
title: Other Document
---
this is another document

Wyświetl plik

@ -1,6 +0,0 @@
---
id: doc5
title: Fifth Document
---
Another one

Wyświetl plik

@ -3,91 +3,96 @@ title: API Client
sidebar_label: API Client sidebar_label: API Client
--- ---
Using the API client
=====================
Longclaw comes with a handy API javascript client to simplify making HTTP requests. Longclaw comes with a handy API javascript client to simplify making HTTP requests.
To load the client into your HTML templates, you can use the template tags: To load the client into your HTML templates, you can use the template tags:
.. code-block:: django ```django
{% load longclawcore_tags %} {% load longclawcore_tags %}
{% longclaw_vendors_bundle %} {% longclaw_vendors_bundle %}
{% longclaw_client_bundle %} {% longclaw_client_bundle %}
```
This will render the ``<script>`` tags necessary to load the javascript. The api client This will render the `<script>` tags necessary to load the javascript. The api client
will be available as a global object called ``longclawclient``. will be available as a global object called `longclawclient`.
You can access the methods by calling the request name (e.g. ``basketList``) and the desired method:: You can access the methods by calling the request name (e.g. `basketList`) and the desired method::
longclawclient.basketList.get({ ... }) longclawclient.basketList.get({ ... })
longclawclient.basketList.post({ ... }) longclawclient.basketList.post({ ... })
API Reference ## API Reference
-------------
:orderDetail: ### orderDetail
supports GET methods. Requires an ``id`` url parameter
``longclawclient.orderDetail.get({ urlParams: { id: ... } })`` supports GET methods. Requires an `id` url parameter
:fulfillOrder: `longclawclient.orderDetail.get({ urlParams: { id: ... } })`
supports POST methods. Requires an ``id`` url param.
``longclawclient.fulfillOrder.post({ urlParams: { id: ... } })``
:checkout:
supports POST method. requires JSON data.
``longclawclient.checkout.post({ data: { ... } })``
:checkoutToken:
supports GET method.
:basketList:
supports GET and POST methods
:basketListCount:
supports GET methods
:basketDetailCount:
supports GET method. Requires ``id`` url param.
:basketDetail:
supports DEL method. Requires ``id`` url param.
:shippingCost:
supports GET method. requires ``country_code`` query param
``longclawclient.shippingCost.get({ queryParams: { country_code: 'gb' } })``
:shippingCountries:
supports GET method
:addressList:
supports GET and POST methods
:addressDetail:
supports GET, PUT, DEL methods
All methods take a ``config`` object of options which can optionally contain: ### fulfillOrder
supports POST methods. Requires an `id` url param.
`longclawclient.fulfillOrder.post({ urlParams: { id: ... } })`
:config.url: - Completely override the URL to use ### checkout
:config.prefix: - Use a different prefix or host for calling the endpoint. supports POST method. requires JSON data.
:config.urlParams: - replacement parameters for the endpoint. `longclawclient.checkout.post({ data: { ... } })`
for example, if the endpoint is specified as `booking/{id}/`, urlParams should
be an object containing an `id` key and the string to replace it with, e.g:
{ id: '123' } would result in the endpoint being modified to `booking/123/`
:config.queryParams: - An object containing key-value pairs which ### checkoutToken
will be mapped to a query string. E.g. supports GET method.
{
first_name: 'John',
last_name: 'Smith'
}
would result in the endpoint being modified to `booking/123/?first_name=John&last_name=Smith`
:config.data: - Data object to send with the POST/PUT request. Will be converted to JSON. ### basketList
supports GET and POST methods
It is important to note the ``config.prefix`` option. You will almost always need to specify this ### basketListCount
to match the ``API_URL_PREFIX`` setting in your django settings. This can be done by including it in supports GET methods
your template view as e.g. a data attribute on a element and then accessing it through the ``dataset``
attribute in javascript, e.g.::
### basketDetailCount
supports GET method. Requires `id` url param.
### basketDetail
supports DEL method. Requires `id` url param.
### shippingCost
supports GET method. requires `country_code` query param
`longclawclient.shippingCost.get({ queryParams: { country_code: 'gb' } })`
### shippingCountries
supports GET method
### addressList
supports GET and POST methods
### addressDetail
supports GET, PUT, DEL methods
All methods take a `config` object of options which can optionally contain:
| Parameter | Description
|:-----------:|----------------
| url | Completely override the URL to use
| prefix | Use a different prefix or host for calling the endpoint.
| urlParams | replacement parameters for the endpoint. for example, if the endpoint is specified as `booking/{id}/`, urlParams should be an object containing an `id` key and the string to replace it with, e.g: `{ id: '123' }` would result in the endpoint being modified to `booking/123/`
| queryParams | An object containing key-value pairs which will be mapped to a query string. E.g. `{ first_name: 'John', last_name: 'Smith' }` would result in the endpoint being modified to `booking/123/?first_name=John&last_name=Smith`
| data | Data object to send with the POST/PUT request. Will be converted to JSON.
It is important to note the `config.prefix` option. You will almost always need to specify this
to match the `API_URL_PREFIX` setting in your django settings. This can be done by including it in
your template view as e.g. a data attribute on a element and then accessing it through the `dataset`
attribute in javascript, e.g:
```javascript
document.getElementById('my-element').dataset.apiUrlPrefix document.getElementById('my-element').dataset.apiUrlPrefix
```
Usage with ES6 modules ### Usage with ES6 modules
----------------------
To use the client with other ES6+ modules, install the client library from npm::
To use the client with other ES6+ modules, install the client library from npm:
```bash
npm i longclawclient --save npm i longclawclient --save
```
Then import the api library:: Then import the api library:
```bash
import api from 'longclawclient' import api from 'longclawclient'
```

Wyświetl plik

@ -3,27 +3,26 @@ title: Basket
sidebar_label: Basket sidebar_label: Basket
--- ---
The basket (or 'shopping cart') is a collection of ``BasketItem`` objects, tied to the django session and as such, expires when the session expires. The basket (or 'shopping cart') is a collection of `BasketItem` objects, tied to the django session and as such, expires when the session expires.
Each ``BasketItem`` has a ``basket_id`` allowing items to be grouped together in a 'basket'. Each `BasketItem` has a `basket_id` allowing items to be grouped together in a 'basket'.
Fetching the basket ## Fetching the basket
-------------------
The function ``longclaw.basket.utils.get_basket_items`` will return all ``BasketItem`` for the current
session. This accepts a django ``request`` object and uses ``longclaw.basket.utils.basket_id`` to
fetch the underlying ``basket_id`` on which to filter the ``BasketItem`` objects.
On the front end, you can use the API endpoint ``<api_prefix>/basket/`` or the django view ``basket/``. You should The function `longclaw.basket.utils.get_basket_items` will return all `BasketItem` for the current
provide a template for the view title ``basket.html``. ``basket`` is also the name of the context variable session. This accepts a django `request` object and uses `longclaw.basket.utils.basket_id` to
fetch the underlying `basket_id` on which to filter the `BasketItem` objects.
On the front end, you can use the API endpoint `<api_prefix>/basket/` or the django view `basket/`. You should
provide a template for the view title `basket.html`. `basket` is also the name of the context variable
containing all basket items. containing all basket items.
A ``BasketItem`` has two fields of importance; ``quantity`` and ``variant``. The latter is a foreign key to the A `BasketItem` has two fields of importance; `quantity` and `variant`. The latter is a foreign key to the
``ProductVariant`` model. `ProductVariant` model.
In a django template, you can iterate over the basket items like so: In a django template, you can iterate over the basket items like so:
.. code-block:: django ```javascript
{% for item in basket %} {% for item in basket %}
{{ item.quantity }} {{ item.quantity }}
{{ item.variant.price }} {{ item.variant.price }}
@ -32,10 +31,10 @@ In a django template, you can iterate over the basket items like so:
{{ item.variant.product.title }} {{ item.variant.product.title }}
{{ item.variant.product.description }} {{ item.variant.product.description }}
{% endfor %} {% endfor %}
```
The API JSON response will contain all fields of the `ProductVariant` and `Product`:
The API JSON response will contain all fields of the ``ProductVariant`` and ``Product``: ```javascript
.. code-block:: json
{ {
quantity, quantity,
@ -51,31 +50,31 @@ The API JSON response will contain all fields of the ``ProductVariant`` and ``Pr
} }
} }
} }
```
## Adding and Removing items
Adding and Removing items
-------------------------
Items can be added or removed via the RESTful api: Items can be added or removed via the RESTful api:
POST to ``<api_prefix>/basket/`` to add an item and DELETE to ``<api_prefix>/basket/<variant_id>/`` to remove an item POST to `<api_prefix>/basket/` to add an item and DELETE to `<api_prefix>/basket/<variant_id>/` to remove an item
When adding an item, provide the ``variant_id`` in the request data. For both endpoints, you can optionally provide the ``quantity`` in When adding an item, provide the `variant_id` in the request data. For both endpoints, you can optionally provide the `quantity` in
the request data. the request data.
There is currently no django view for addition/deletion of basket items. There is currently no django view for addition/deletion of basket items.
Other API endpoints: ## Other API Endpoints
``<api_prefix>/basket/<variant_id>/count/`` ### `basket/:variant_id/count/`
get the quantity of a single item in the basket. Requires ``variant_id`` in the request data get the quantity of a single item in the basket. Requires `variant_id` in the request data
``<api_prefix>/basket/count/`` ### `basket/count/`
get total number of items in the basket get total number of items in the basket
All basket items can be deleted using the ``longclaw.basket.utils.destroy_basket`` function. All basket items can be deleted using the `longclaw.basket.utils.destroy_basket` function.
When an order is successfully placed, the basket will be automatically destroyed. When an order is successfully placed, the basket will be automatically destroyed.
.. note:: Longclaw does not automatically clean up abandoned baskets. This can occur when a session ends > Longclaw does not automatically clean up abandoned baskets. This can occur when a session ends
with items still in the basket (i.e the customer did not place an order). This allows you to provide checkout recovery, with items still in the basket (i.e the customer did not place an order). This allows you to provide checkout recovery,
with the caveat that you will need to do your own cleanup of rogue ``BasketItem`` objects when required. with the caveat that you will need to do your own cleanup of rogue `BasketItem` objects when required.

Wyświetl plik

@ -4,37 +4,37 @@ sidebar_label: Checkout
--- ---
Longclaw provides a simple, single checkout view. Longclaw provides a simple, single checkout view.
The URL for the checkout is ``'checkout/'``. The URL for the checkout is `'checkout/'`.
After a successful checkout, it is redirected to ``checkout/success/``. After a successful checkout, it is redirected to `checkout/success/`.
To implement the checkout, simply provide ``'checkout/checkout.html'`` and To implement the checkout, simply provide `'checkout/checkout.html'` and
``'checkout/success.html'`` templates. (Empty templates will have been created if `'checkout/success.html'` templates. (Empty templates will have been created if
you ran the longclaw CLI to start your project) you ran the longclaw CLI to start your project)
There are three forms provided in the checkout view: There are three forms provided in the checkout view:
:checkout_form: ### checkout_form
Captures the email address and optionally the shipping option for the checkout. Captures the email address and optionally the shipping option for the checkout.
Also captures a boolean indicating whether a different billing address should be used Also captures a boolean indicating whether a different billing address should be used
:shipping_form: ### shipping_form
Captures shipping information. Captures shipping information.
:billing_form: ### billing_form
A second address form for capturing alternate billing information. If you do not submit this form A second address form for capturing alternate billing information. If you do not submit this form
(e.g. by not rendering it on the template), the billing and shipping addresses are assumed to be the same. (e.g. by not rendering it on the template), the billing and shipping addresses are assumed to be the same.
Generally, you may need to use a little javascript to optionally render the form if the user selects Generally, you may need to use a little javascript to optionally render the form if the user selects
'different billing address'. 'different billing address'.
Shipping Options and Javascript ## Shipping Options and Javascript
--------------------------------
The shipping option dropdown has no options by default - this is because it is dependent on the shipping country. The shipping option dropdown has no options by default - this is because it is dependent on the shipping country.
The checkout form includes the necessary javascript to do this - you just need to include it on the page. The checkout form includes the necessary javascript to do this - you just need to include it on the page.
You will typically also need to include your chosen payment gateways' client javascript: You will typically also need to include your chosen payment gateways' client javascript:
..code-block:: django ```django
{% gateway_client_js as scripts %} {% gateway_client_js as scripts %}
{% for js in scripts %} {% for js in scripts %}
@ -46,31 +46,31 @@ You will typically also need to include your chosen payment gateways' client jav
<script type="text/javascript"> <script type="text/javascript">
initShippingOption('{% longclaw_api_url_prefix %}'); initShippingOption('{% longclaw_api_url_prefix %}');
</script> </script>
```
The first half uses the ``gateway_client_js`` template tag to load all the payment gateway javascript. There may be one or more. The first half uses the `gateway_client_js` template tag to load all the payment gateway javascript. There may be one or more.
The second half has three parts to it: The second half has three parts to it:
1. ``{% longclaw_client_bundle %}`` loads the longclaw client API, which is required by the checkout form javascript. 1. `{% longclaw_client_bundle %}` loads the longclaw client API, which is required by the checkout form javascript.
2. ``{{ checkout_form.media }}`` loads the javascript required by the checkout form. 2. `{{ checkout_form.media }}` loads the javascript required by the checkout form.
3. The last script initializes the checkout javascript. It requires the longclaw API url prefix (which you can customise in your settings) 3. The last script initializes the checkout javascript. It requires the longclaw API url prefix (which you can customise in your settings)
to be passed in - there is a template tag to do this for you. You can also optionally specify the ``shippingCountrySelectId`` and ``shippingOptionSelectId``. to be passed in - there is a template tag to do this for you. You can also optionally specify the `shippingCountrySelectId` and `shippingOptionSelectId`.
These are the ID's of the select element. You would only need to pass these in if you are going to change them from the defaults. These are the ID's of the select element. You would only need to pass these in if you are going to change them from the defaults.
After this, when you change the shipping country, the shipping option should change appropriately. After this, when you change the shipping country, the shipping option should change appropriately.
..note:: We will hopefully improve this in future releases - any ideas and PR's are welcome! > We will hopefully improve this in future releases - any ideas and PR's are welcome!
Payment forms ## Payment forms
-------------
It is up to you to render a payment form and then pass the token in the POST data. It is up to you to render a payment form and then pass the token in the POST data.
Normally, the payment gateway chosen will have a javascript integration to render a form for you Normally, the payment gateway chosen will have a javascript integration to render a form for you
and tokenize the payment method (e.g. braintrees 'hosted fields') and tokenize the payment method (e.g. braintrees 'hosted fields')
Longclaws' payment gateways provide some helpful utilities to load client javascript and generate tokens. Longclaws' payment gateways provide some helpful utilities to load client javascript and generate tokens.
Loading ``longclawcheckout_tags`` in your template will allow you to retrieve the gateways' javascript libraries Loading `longclawcheckout_tags` in your template will allow you to retrieve the gateways' javascript libraries
as script tags (``{% gateway_client_js %}`` and generate a client token (``{% gateway_token %}``). as script tags (`{% gateway_client_js %}` and generate a client token (`{% gateway_token %}`).
A little javascript is then required to setup your form and ask the gateway to tokenize the payment method for you. A little javascript is then required to setup your form and ask the gateway to tokenize the payment method for you.
You should then add this token to the request POST data (e.g. with a hidden input field). You should then add this token to the request POST data (e.g. with a hidden input field).

Wyświetl plik

@ -15,8 +15,8 @@ In the front end, you should:
The first two are relatively simple to achieve. Longclaw provides some utilities to help with the rest. The first two are relatively simple to achieve. Longclaw provides some utilities to help with the rest.
Payment Capture ## Payment Capture
===============
With Longclaw you can either tokenize the customers payment method (e.g. credit card) and With Longclaw you can either tokenize the customers payment method (e.g. credit card) and
send this to the server for the payment to be captured, or you can use a service such as paypal send this to the server for the payment to be captured, or you can use a service such as paypal
express checkout, which captures the payment directly and returns a token representing the transaction express checkout, which captures the payment directly and returns a token representing the transaction
@ -27,111 +27,106 @@ The second option is often easiest to integrate since the user is redirected to
The first option offers tightest integration with the look and feel of your site, but invariable involves more The first option offers tightest integration with the look and feel of your site, but invariable involves more
front end work and validation. front end work and validation.
Tokenizing the Payment ### Tokenizing the Payment
+++++++++++++++++++++++
To capture the payment with a 3rd party service, you will include some external javascript on your page To capture the payment with a 3rd party service, you will include some external javascript on your page
and often designate a button or ``div`` to initialise the popup/redirect. You will also specify a submit and often designate a button or `div` to initialise the popup/redirect. You will also specify a submit
handler to receive the token representing the transaction. handler to receive the token representing the transaction.
For example, the braintree javascript client allows express checkout using Paypal. Full details of how For example, the braintree javascript client allows express checkout using Paypal. Full details of how
to setup are `here <https://developers.braintreepayments.com/guides/paypal/checkout-with-paypal/javascript/v3>`_. to setup are [here](https://developers.braintreepayments.com/guides/paypal/checkout-with-paypal/javascript/v3).
Other providers such as Stripe offer similar services. Other providers such as Stripe offer similar services.
Once you have received this token, you should submit it, along with the shipping address, billing address, Once you have received this token, you should submit it, along with the shipping address, billing address,
email and shipping rate to the ``api/checkout/prepaid/`` endpoint. email and shipping rate to the `api/checkout/prepaid/` endpoint.
.. note:: The ``api/`` prefix can be configured in your django settings under ``API_URL_PREFIX``. > The `api/` prefix can be configured in your django settings under `API_URL_PREFIX`.
For example, if you want to distinguish the longclaw API from your own, you could set ``API_URL_PREFIX="api/longclaw/"`` For example, if you want to distinguish the longclaw API from your own, you could set `API_URL_PREFIX="api/longclaw/"`
The checkout url would then be ``api/longclaw/checkout/prepaid/`` The checkout url would then be `api/longclaw/checkout/prepaid/`
The JSON request data would look like: The JSON request data would look like:
.. code-block:: json ```json
{ {
transaction_id: "...", "transaction_id": "...",
shipping_rate: 0.0, "shipping_rate": 0.0,
email: "john@smith.com", "email": "john@smith.com",
address: { "address": {
shipping_name: "john smith", "shipping_name": "john smith",
shipping_address_line_1": "...", "shipping_address_line_1": "...",
shipping_address_city: "", "shipping_address_city": "",
shipping_address_zip: "", "shipping_address_zip": "",
shipping_address_country: "", "shipping_address_country": "",
billing_name: "john smith", "billing_name": "john smith",
billing_address_line_1: "...", "billing_address_line_1": "...",
billing_address_city: "", "billing_address_city": "",
billing_address_zip: "", "billing_address_zip": "",
billing_address_country: "", "billing_address_country": "",
} }
} }
```
`transaction_id` is the token returned from the payment processor e.g. paypal
transaction_id When using this method, you do not need to define the `PAYMENT_GATEWAY` setting.
The token returned from e.g. paypal
When using this method, you do not need to define the ``PAYMENT_GATEWAY`` setting. ### Tokenizing the Payment method
Tokenizing the Payment method
+++++++++++++++++++++++++++++
Alternatively, you can pass the payment method for Longclaw to manually capture the payment. Alternatively, you can pass the payment method for Longclaw to manually capture the payment.
Longclaw expects the payment details (i.e. credit card) to be passed as some kind of token in Longclaw expects the payment details (i.e. credit card) to be passed as some kind of token in
a POST request to ``api/checkout/``. a POST request to `api/checkout/`.
Longclaw will then use the payment gateway defined by the ``PAYMENT_GATEWAY`` setting to capture Longclaw will then use the payment gateway defined by the `PAYMENT_GATEWAY` setting to capture
the payment. the payment.
To create the initial token representing the customers payment information, you may be able to use To create the initial token representing the customers payment information, you may be able to use
the ``api/checkout/token/`` endpoint, passing the card information in the request data. This is dependent the `api/checkout/token/` endpoint, passing the card information in the request data. This is dependent
upon the backend and it may be preferable to use client javascript libraries provided by your payment upon the backend and it may be preferable to use client javascript libraries provided by your payment
gateway (e.g. ``stripe.js`` or ``braintree-web`` ) to generate a token. gateway (e.g. `stripe.js` or `braintree-web` ) to generate a token.
Once the token is generated, the request data to send to ``api/checkout/`` is very similar to that for Once the token is generated, the request data to send to `api/checkout/` is very similar to that for
``api/checkout/prepaid/``: `api/checkout/prepaid/`:
.. code-block:: json ```json
{ {
token: "...", "token": "...",
shipping_rate: 0.0, "shipping_rate": 0.0,
email: "john@smith.com", "email": "john@smith.com",
address: { "address": {
shipping_name: "john smith", "shipping_name": "john smith",
shipping_address_line_1: "...", "shipping_address_line_1": "...",
shipping_address_city: "", "shipping_address_city": "",
shipping_address_zip: "", "shipping_address_zip": "",
shipping_address_country: "", "shipping_address_country": "",
billing_name: "john smith", "billing_name": "john smith",
billing_address_line_1: "...", "billing_address_line_1": "...",
billing_address_city: "", "billing_address_city": "",
billing_address_zip: "", "billing_address_zip": "",
billing_address_country: "", "billing_address_country": "",
} }
} }
```
token | Parameter | Description
The token for customer details. The key name is dependent on the backend ("token" for stripe, "payment_method_nonce" for braintree) |:-------------:|------------------
| token | The token for customer details. The key name is dependent on the backend ("token" for stripe, "payment_method_nonce" for braintree)
| shipping_rate | Number or string representation of a number (will be cast to float). The shipping costs
| email | The customers' email
shipping_rate > The `"token"` key is dependent upon the payment backend and may be named differently.
Number or string representation of a number (will be cast to float). The shipping costs
email Both `api/checkout/` and `api/checkout/prepaid/` return a 201 response with `order_id` in the JSON data.
The customers' email If the payment fails, `api/checkout/` will return a 400 response with `order_id` and `message` in the JSON data.
.. note:: The ``"token"`` key is dependent upon the payment backend and may be named differently. ## Calculating Shipping Costs
Both ``api/checkout/`` and ``api/checkout/prepaid/`` return a 201 response with ``order_id`` in the JSON data.
If the payment fails, ``api/checkout/`` will return a 400 response with ``order_id`` and ``message`` in the JSON data.
Calculating Shipping Costs You will have noticed the need to send `shipping_rate` with the checkout. If you are using Longclaws' shipping
========================== settings, you can easily calculate the shipping cost either in python or by using the `api/shipping/cost/` endpoint.
You will have noticed the need to send ``shipping_rate`` with the checkout. If you are using Longclaws' shipping
settings, you can easily calculate the shipping cost either in python or by using the ``api/shipping/cost/`` endpoint.
Python example: Python example:
.. code-block:: python ```python
from longclaw.shipping import utils from longclaw.shipping import utils
from longclaw.configuration.models import Configuration from longclaw.configuration.models import Configuration
@ -148,12 +143,13 @@ Python example:
# but the supplied option doesnt match any # but the supplied option doesnt match any
pass pass
except InvalidShippingCountry: except InvalidShippingCountry:
# A shipping rate for this country does not exist and ``default_shipping_enabled`` # A shipping rate for this country does not exist and `default_shipping_enabled`
# is set to ``False`` in the longclaw admin settings # is set to `False` in the longclaw admin settings
```
Javascript example: Javascript example:
.. code-block:: javascript ```javascript
fetch( fetch(
"api/shipping/cost/", "api/shipping/cost/",
@ -170,7 +166,4 @@ Javascript example:
}) })
} }
).then(response => {...}) ).then(response => {...})
```

Wyświetl plik

@ -4,36 +4,36 @@ sidebar_label: Product Requests
--- ---
This module allows customers to 'request' products which are otherwise out of stock. This module allows customers to 'request' products which are otherwise out of stock.
The request date and product variant are stored, with the customer email optionally being stored (The ``ProductRequest`` model The request date and product variant are stored, with the customer email optionally being stored (The `ProductRequest` model
contains a field for this, but template tags by default do not collect this information - it is up to you to store it.) contains a field for this, but template tags by default do not collect this information - it is up to you to store it.)
To install, add it to your ``INSTALLED_APPS`` after other longclaw modules: To install, add it to your `INSTALLED_APPS` after other longclaw modules:
.. code-block:: python ```python
INSTALLED_APPS = ( INSTALLED_APPS = (
..., ...,
"longclaw.contrib.productrequests" "longclaw.contrib.productrequests"
) )
```
To show a 'request' button, you can use the following template tag on your product page: To show a 'request' button, you can use the following template tag on your product page:
.. code-block:: django ```django
{% load productrequests_tags %} {% load productrequests_tags %}
{% for variant in page.variants.all %} {% for variant in page.variants.all %}
{% make_request_btn variant_id=variant.id %} {% make_request_btn variant_id=variant.id %}
{% endfor %} {% endfor %}
```
You can also pass `btn_class` and `btn_text` to change the CSS class and text of the resulting `button` element.
You can also pass ``btn_class`` and ``btn_text`` to change the CSS class and text of the resulting ``button`` element. By default they are `btn btn-default` and `Request Product`.
By default they are ``btn btn-default`` and ``Request Product``.
This template tag will take care of making the AJAX call to register a request against the product variant. This template tag will take care of making the AJAX call to register a request against the product variant.
In order to collect further information - i.e the customer email, you will need to create the button and necessary javascript In order to collect further information - i.e the customer email, you will need to create the button and necessary javascript
yourself. You can use the API client function ``requestList`` to post the collected data. yourself. You can use the API client function `requestList` to post the collected data.
You can view all requests in the admin index page for your product collections. When hovering over a product, alongside You can view all requests in the admin index page for your product collections. When hovering over a product, alongside
the usual `Edit`, `View Live` and `Add Child Page` buttons is a new `View Requests` button. This will take you to a page the usual `Edit`, `View Live` and `Add Child Page` buttons is a new `View Requests` button. This will take you to a page

Wyświetl plik

@ -3,60 +3,52 @@ title: Payment Backends
sidebar_label: Integrations sidebar_label: Integrations
--- ---
Payment Backends
==================
Longclaw supports payment capture through Stripe, Braintree and Paypal (Using the Braintree VZero SDK). Longclaw supports payment capture through Stripe, Braintree and Paypal (Using the Braintree VZero SDK).
To select the payment gateway to use, you must specify the ``PAYMENT_GATEWAY`` attribute in your ``settings.py``. To select the payment gateway to use, you must specify the `PAYMENT_GATEWAY` attribute in your `settings.py`.
The options are: The options are:
- ``longclaw.checkout.gateways.base.BasePayment``. A do-nothing base implementation - `longclaw.checkout.gateways.base.BasePayment`. A do-nothing base implementation
- ``longclaw.checkout.gateways.stripe.StripePayment``. Capture payments using Stripe. - `longclaw.checkout.gateways.stripe.StripePayment`. Capture payments using Stripe.
- ``longclaw.checkout.gateways.braintree.BraintreePayment``. Capture payments using Braintree. - `longclaw.checkout.gateways.braintree.BraintreePayment`. Capture payments using Braintree.
- ``longclaw.checkout.gateways.braintree.PaypalVZeroPayment``. Capture Paypal payments using the braintree v.zero SDK. - `longclaw.checkout.gateways.braintree.PaypalVZeroPayment`. Capture Paypal payments using the braintree v.zero SDK.
Additional Settings and dependencies ## Additional Settings and dependencies
------------------------------------
To use payment gateways it is necessary to specify API keys and install client SDK's for the chosen payment provider. To use payment gateways it is necessary to specify API keys and install client SDK's for the chosen payment provider.
:Stripe: ## Stripe
``STRIPE_PUBLISHABLE`` - Your public api key `STRIPE_PUBLISHABLE` - Your public api key
``STRIPE_SECRET`` - Your secret api key `STRIPE_SECRET` - Your secret api key
You will need to install the stripe python sdk (``pip install stripe``) You will need to install the stripe python sdk (`pip install stripe`)
:Braintree: ## Braintree
``BRAINTREE_MERCHANT_ID`` - Your braintree merchant account ID. `BRAINTREE_MERCHANT_ID` - Your braintree merchant account ID.
``BRAINTREE_PUBLIC_KEY`` - Your public api key `BRAINTREE_PUBLIC_KEY` - Your public api key
``BRAINTREE_PRIVATE_KEY`` - Your secret api key `BRAINTREE_PRIVATE_KEY` - Your secret api key
:Paypal: ## Paypal
``VZERO_ACCESS_TOKEN`` - Your access token for the v.zero SDK. `VZERO_ACCESS_TOKEN` - Your access token for the v.zero SDK.
Paypal and braintree require the braintree python SDK (``pip install braintree``) Paypal and braintree require the braintree python SDK (`pip install braintree`)
## Custom Integrations
.. _custom-integrations:
Custom Integrations
===================
To implement your own payment integration, you must implement the payment gateway interface. This is simple: To implement your own payment integration, you must implement the payment gateway interface. This is simple:
- Inherit from ``longclaw.checkout.gateways.base.BasePayment`` - Inherit from `longclaw.checkout.gateways.base.BasePayment`
- Implement ``create_payment``. This should take a ``request`` object, an ``amount`` and optionally a ``description``. - Implement `create_payment`. This should take a `request` object, an `amount` and optionally a `description`.
It should use these to capture the payment using your chosen provider. For examples see the implementations in It should use these to capture the payment using your chosen provider. For examples see the implementations in
``longclaw.checkout.gateways`` `longclaw.checkout.gateways`
- Implement the ``get_token`` method. This method should generate tokens used by the payment provider. It accepts a ``request`` - Implement the `get_token` method. This method should generate tokens used by the payment provider. It accepts a `request`
object containing post data (``request.data``). Tokens returned may represent different things depending on the object containing post data (`request.data`). Tokens returned may represent different things depending on the
payment provider - e.g. it may be used to tokenize payment details or generate authentication tokens. payment provider - e.g. it may be used to tokenize payment details or generate authentication tokens.
You can define your own requirements for the request data to be submitted to the functions. You can define your own requirements for the request data to be submitted to the functions.
``create_payment`` is called in a POST request to the ``checkout/`` api. ``get_token`` is similarly called `create_payment` is called in a POST request to the `checkout/` api. `get_token` is similarly called
in a POST request to the ``checkout/token/`` api. in a POST request to the `checkout/token/` api.
Longclaw aims to be as minimal as possible in order to get the job done. This is why longclaw currently only offers the barest minimum Longclaw aims to be as minimal as possible in order to get the job done. This is why longclaw currently only offers the barest minimum
support necessary to directly create payments with the backend payment provider. support necessary to directly create payments with the backend payment provider.

Wyświetl plik

@ -3,59 +3,55 @@ title: Configuring Payment
sidebar_label: Payment sidebar_label: Payment
--- ---
Checkout with Braintree
============================
Longclaw offers integration with a few payment gateways and it is also fairly easy to integrate your own. Longclaw offers integration with a few payment gateways and it is also fairly easy to integrate your own.
For this tutorial, we will use Braintree to process payments. For this tutorial, we will use Braintree to process payments.
Settings and Dependencies ## Settings and Dependencies
-------------------------
The payment gateway to use must be set in the settings file: The payment gateway to use must be set in the settings file:
.. code-block:: python ```python
PAYMENT_GATEWAY = 'longclaw.checkout.gateways.braintree.BraintreePayment'
PAYMENT_GATEWAY = 'longclaw.checkout.gateways.braintree.BraintreePayment' ```
We also need to define settings for access tokens; We also need to define settings for access tokens;
.. code-block:: python ```python
BRAINTREE_SANDBOX = False BRAINTREE_SANDBOX = False
BRAINTREE_MERCHANT_ID = os.environ['BRAINTREE_MERCHANT_ID'] BRAINTREE_MERCHANT_ID = os.environ['BRAINTREE_MERCHANT_ID']
BRAINTREE_PUBLIC_KEY = os.environ['BRAINTREE_PUBLIC_KEY'] BRAINTREE_PUBLIC_KEY = os.environ['BRAINTREE_PUBLIC_KEY']
BRAINTREE_PRIVATE_KEY = os.environ['BRAINTREE_PRIVATE_KEY'] BRAINTREE_PRIVATE_KEY = os.environ['BRAINTREE_PRIVATE_KEY']
```
We will need to install this SDK as it is not an explicit dependency of longclaw:: We will need to install this SDK as it is not an explicit dependency of longclaw:
pip install braintree ```bash
pip install braintree
```
That is all we need to do to configure our backend! That is all we need to do to configure our backend!
Front end integration ## Front end integration
---------------------
We will first show how to setup a checkout page using the Checkout view provided by longclaw. We will first show how to setup a checkout page using the Checkout view provided by longclaw.
The code shown here is very similar to the implementation of the checkout page here: `Ramshackle Audio<https://github.com/JamesRamm/ramshacklerecording>`_ The code shown here is very similar to the implementation of the checkout page here: [Ramshackle Audio](https://github.com/JamesRamm/ramshacklerecording)
First, we should load some templatetags which will help us: First, we should load some templatetags which will help us:
.. code-block:: django ```django
{% load longclawcheckout_tags longclawcore_tags %}
```
{% load longclawcheckout_tags longclawcore_tags %} As an aside - you may wish to display the items in the basket on our checkout page. The basket items queryset is available as `basket`
As an aside - you may wish to display the items in the basket on our checkout page. The basket items queryset is available as ``basket``
in the views' context. in the views' context.
Next, we need to setup the forms to gather customer information. There are 2 forms in the context. We will Next, we need to setup the forms to gather customer information. There are 2 forms in the context. We will
display and submit them as a single form. Here is an example layout: display and submit them as a single form. Here is an example layout:
.. code-block:: django ```django
<form action="." method="post" id="checkout-form">
<form action="." method="post" id="checkout-form">
{% csrf_token %} {% csrf_token %}
{% for field in shipping_form %} {% for field in shipping_form %}
{% if field.is_hidden %} {% if field.is_hidden %}
@ -96,20 +92,21 @@ display and submit them as a single form. Here is an example layout:
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
```
You may wish to layout the form differently. We have purposefully ignored the ``different_billing_address`` field You may wish to layout the form differently. We have purposefully ignored the `different_billing_address` field
since the Braintree dropin-ui will collect a billing postcode anyway, for its' own security checks. since the Braintree dropin-ui will collect a billing postcode anyway, for its' own security checks.
Before we close our `<form>` element, there are 3 further items to add: Before we close our `<form>` element, there are 3 further items to add:
.. code-block:: django ```django
<!-- hidden field for submitting the token back to the server. Name will vary depending on integration-->
<!-- hidden field for submitting the token back to the server. Name will vary depending on integration--> <input type="hidden" id="payment_method_nonce" name="payment_method_nonce"></input>
<input type="hidden" id="payment_method_nonce" name="payment_method_nonce"></input> <h4 class="ui dividing header">Payment Details</h4>
<h4 class="ui dividing header">Payment Details</h4> <div id="dropin-container"></div>
<div id="dropin-container"></div> <input type="submit" id="submit-button" value="Place Order" class="ui button submit" />
<input type="submit" id="submit-button" value="Place Order" class="ui button submit" /> </form>
</form> ```
We add a hidden field. This field will contain a token (string of characters) given by braintree which represents the payment method. We add a hidden field. This field will contain a token (string of characters) given by braintree which represents the payment method.
Most payment gateways require something like this, although the name of the field will change between backends. Most payment gateways require something like this, although the name of the field will change between backends.
@ -120,16 +117,14 @@ most integrations offer some sort of 'dropin' which are increasingly customisabl
Finally, we add a submit button. Finally, we add a submit button.
The Javascript ### The Javascript
***************
OK, so now we have hidden elements, empty containers....we need to get this stuff populated! OK, so now we have hidden elements, empty containers....we need to get this stuff populated!
Each payment gateway integration provides the necessary javascript libraries to interact with the gateway. Each payment gateway integration provides the necessary javascript libraries to interact with the gateway.
They are made available via a template tag. They are made available via a template tag.
Add them like this: Add them like this:
.. code-block:: django ```django
<!--Load any client javascript provided by the payment gateway. <!--Load any client javascript provided by the payment gateway.
I have chosen braintree as my gateway so the template tag below I have chosen braintree as my gateway so the template tag below
should give me a list of script tags which load the braintree should give me a list of script tags which load the braintree
@ -142,13 +137,13 @@ Add them like this:
<!--Finally add the media from the checkout form.--> <!--Finally add the media from the checkout form.-->
{{ checkout_form.media }} {{ checkout_form.media }}
```
The checkout form also provides a little javascript to initialise shipping options (when the user selects a shipping country). The checkout form also provides a little javascript to initialise shipping options (when the user selects a shipping country).
Finally, we need to add a little of our own javascript to create the braintree dropin: Finally, we need to add a little of our own javascript to create the braintree dropin:
.. code-block:: django ```django
<script type="text/javascript"> <script type="text/javascript">
//Initialize shipping options - this function is from the //Initialize shipping options - this function is from the
@ -183,12 +178,13 @@ Finally, we need to add a little of our own javascript to create the braintree d
}); });
}); });
</script> </script>
```
Two things are happening in the above code. First, we initialise the shipping options. Note we are using a template tag Two things are happening in the above code. First, we initialise the shipping options. Note we are using a template tag
to pass the longclaw API url prefix, since this is customisable in your settings.py to pass the longclaw API url prefix, since this is customisable in your settings.py
Secondly, we initialise the braintree dropin. Again, we use a template tag to get a token for the gateway. Secondly, we initialise the braintree dropin. Again, we use a template tag to get a token for the gateway.
All payment backends provide the ``gateway_token`` template tag, although it is not always necessary. All payment backends provide the `gateway_token` template tag, although it is not always necessary.
You may wish to only show the braintree payment form if the user has anything in their basket. In which case you might qualify You may wish to only show the braintree payment form if the user has anything in their basket. In which case you might qualify
the above javascript with ``{% if basket.count > 0 %}`` in your template. the above javascript with ``{% if basket.count > 0 %}`` in your template.
@ -199,8 +195,8 @@ for v0.2, but welcome any suggestions on how to make it easier!
If you wish to forego the templatetags & forms (e.g. if making a fully React-based frontend), read on. Otherwise, that is the end of the tutorial! If you wish to forego the templatetags & forms (e.g. if making a fully React-based frontend), read on. Otherwise, that is the end of the tutorial!
Javascript-Only integration ## Javascript-Only integration
----------------------------
Below is a walkthrough of integrating a payment gateway (PayPal) without the aid of templatetags etc.. Below is a walkthrough of integrating a payment gateway (PayPal) without the aid of templatetags etc..
@ -219,21 +215,19 @@ We therefore have three things we need to do in our client-side javascript:
1. Call the longclaw API to generate a token 1. Call the longclaw API to generate a token
.. code-block:: javascript ```javascript
$.get({
$.get({ url: 'api/checkout/token/',
url: 'api/checkout/token/', success: function(response){
success: function(response){ ...
... }
} })
}) ```
1. Following this, configure the paypal express checkout functionality. This actually has two steps.
2. Following this, configure the paypal express checkout functionality. This actually has two steps.
We must first create a braintree `client` using our new token. We then use this to create a braintree We must first create a braintree `client` using our new token. We then use this to create a braintree
`paypal` instance. `paypal` instance.
.. code-block:: javascript ```javascript
braintree.client.create({ braintree.client.create({
authorization: token authorization: token
}, (err, client) => { }, (err, client) => {
@ -251,14 +245,14 @@ We therefore have three things we need to do in our client-side javascript:
console.log("Paypal instance": paypalInstance); console.log("Paypal instance": paypalInstance);
}); });
}); });
```
3. Once paypal has created the `nonce` for the entered payment details, we must submit this 1. Once paypal has created the `nonce` for the entered payment details, we must submit this
to our server so longclaw can capture the payment. to our server so longclaw can capture the payment.
To do this, we must have a button which we want to use to launch the paypal express checkout window. To do this, we must have a button which we want to use to launch the paypal express checkout window.
We 'attach' the paypal instance we just created to the button like so: We 'attach' the paypal instance we just created to the button like so:
.. code-block:: javascript ```javascript
paypalButton.addEventListener( paypalButton.addEventListener(
'click', 'click',
function (){ function (){
@ -279,14 +273,13 @@ We therefore have three things we need to do in our client-side javascript:
} }
}); });
}); });
```
In this example `paypalButton` is a DOM node referring to the button element we wish to attach paypal to and ``handleSubmit`` In this example `paypalButton` is a DOM node referring to the button element we wish to attach paypal to and ``handleSubmit``
is a function which will actually POST the payload to the longclaw api endpoint (``api/checkout/``) is a function which will actually POST the payload to the longclaw api endpoint (``api/checkout/``)
We can make all these nested API calls simpler if we use ES6 Promises and the fetch API: We can make all these nested API calls simpler if we use ES6 Promises and the fetch API:
.. code-block:: javascript ```javascript
// Wrap braintree js functions as promises // Wrap braintree js functions as promises
function braintreeClientCreate(token){ function braintreeClientCreate(token){
return new Promise(function(resolve, reject){ return new Promise(function(resolve, reject){
@ -414,6 +407,7 @@ We can make all these nested API calls simpler if we use ES6 Promises and the fe
function parseJSON(response) { function parseJSON(response) {
return response.json(); return response.json();
} }
```
The total amount, shipping address, shipping rate and email address of the customer are passed into the setup function; The total amount, shipping address, shipping rate and email address of the customer are passed into the setup function;
it is up to the front end developer to create the necessary forms to gather these. it is up to the front end developer to create the necessary forms to gather these.

Wyświetl plik

@ -10,16 +10,20 @@ Now we have created products and configured our shipping, we can start thinking
Longclaw provides a REST API endpoint for retrieving basket data and a django view. Longclaw provides a REST API endpoint for retrieving basket data and a django view.
To use the django view, we must provide a template titled ``basket/basket.html``. To use the django view, we must provide a template titled `basket/basket.html`.
It is common to provide a link to the basket page in the header. We can use the ``url`` tag in It is common to provide a link to the basket page in the header. We can use the `url` tag in
our site header to provide the link:: our site header to provide the link:
{% url 'longclaw-basket' %} ```
{% url 'longclaw-basket' %}
```
In the basket template, we have access to all basket items under the ``basket`` context:: In the basket template, we have access to all basket items under the `basket` context:
{% for item in basket %} ```
... {% for item in basket %}
{% endfor %} ...
{% endfor %}
```
For the full implementation of the basket template, take a look at the `longclaw demo repository <https://github.com/JamesRamm/longclaw_demo/blob/master/longclaw_demo/templates/basket/basket.html>`_ For the full implementation of the basket template, take a look at the [longclaw demo repository](https://github.com/JamesRamm/longclaw_demo/blob/master/longclaw_demo/templates/basket/basket.html)

Wyświetl plik

@ -6,119 +6,115 @@ sidebar_label: Adding Products
Longclaw makes as few assumptions as possible when it comes to modelling your products, since the Longclaw makes as few assumptions as possible when it comes to modelling your products, since the
requirements of different shops can be wide and varied. requirements of different shops can be wide and varied.
It is required that you create a ``ProductVariant`` model (it can be called anything) and implement It is required that you create a `ProductVariant` model (it can be called anything) and implement
a small number of fields Longclaw expects. a small number of fields Longclaw expects.
The easiest way to do this is by inheriting from ``longclaw.products.ProductVariantBase``. The easiest way to do this is by inheriting from `longclaw.products.ProductVariantBase`.
Longclaws' project template will have setup a ``products`` app for you, with a ``ProductVariant`` model. Longclaws' project template will have setup a `products` app for you, with a `ProductVariant` model.
You will also notice that the settings file has ``PRODUCT_VARIANT_MODEL`` set to the ``ProductVariant`` model. You will also notice that the settings file has `PRODUCT_VARIANT_MODEL` set to the `ProductVariant` model.
The project template has also created a ``ProductIndex`` and ``Product`` model. The project template has also created a `ProductIndex` and `Product` model.
``Product`` is a regular Wagtail ``Page`` which ``ProductVariant`` is an line model of. `Product` is a regular Wagtail `Page` which `ProductVariant` is an line model of.
.. note:: > This is just one way of modelling yor catalogue but you are not bound to it. `ProductVariant` is the only model
This is just one way of modelling yor catalogue but you are not bound to it. ``ProductVariant`` is the only model
required by Longclaw and precisely what this represents is up to your (e.g. it could be the product itself, or, as the name required by Longclaw and precisely what this represents is up to your (e.g. it could be the product itself, or, as the name
suggests, a variant of a product). You could create multiple 'index' pages, perhaps representing different lines suggests, a variant of a product). You could create multiple 'index' pages, perhaps representing different lines
aswell as multiple 'product' type pages, or do away with ``Product`` completely. aswell as multiple 'product' type pages, or do away with `Product` completely.
Creating the Product Index ## Creating the Product Index
--------------------------
Wagtails' ``Page`` models are organized in a tree structure. All our ``Product`` pages will therefore
need a parent. This is provided by the ``ProductIndex`` model.
.. note:: Wagtails' `Page` models are organized in a tree structure. All our `Product` pages will therefore
Read more about Wagtail pages in the `Wagtail docs <http://docs.wagtail.io/en/v1.9/topics/pages.html>`_ need a parent. This is provided by the `ProductIndex` model.
> Read more about Wagtail pages in the [Wagtail docs](http://docs.wagtail.io/en/v1.9/topics/pages.html)
To add a product index page: To add a product index page:
- Navigate to the admin and log in - Navigate to the admin and log in
- Select the homepage from the explorer menu - Select the homepage from the explorer menu
- Select ``add child page`` - Select `add child page`
- Select ``Product index`` and enter the title (e.g. `Products`) - Select `Product index` and enter the title (e.g. `Products`)
.. figure:: ../_static/images/product_index.png ![Image of the product list](assets/product_index.png)
We can now add ``Product`` models as children of ``ProductIndex``. Only pages of type ``Product`` can be created under ``ProductIndex``. We can now add `Product` models as children of `ProductIndex`. Only pages of type `Product` can be created under `ProductIndex`.
Adding a Product ### Adding a Product
----------------
Under the explorer homepage, we should now see our newly created ``ProductIndex``. We can select ``Add child page`` to add our first
``Product``. The ``Product`` model is fairly minimal. It has: Under the explorer homepage, we should now see our newly created `ProductIndex`. We can select `Add child page` to add our first
`Product`. The `Product` model is fairly minimal. It has:
- A title - A title
- A description - A description
- One or more ``ProductVariant``s - One or more `ProductVariant`s
- Optionally some images and tags - Optionally some images and tags
.. figure:: ../_static/images/product.png ![Image of the product](assets/product.png)
Customising ### Customising
------------
As mentioned above, we can customise the ``ProductIndex`` and ``Product`` model completely - they
are not strict requirements for longclaw to operate. We reccomend using something similar to
the project layout so that ``Product``'s will appear in the Wagtail admin page explorer.
``ProductVariant`` can be customised, as long as we inherit from ``ProductVariantBase`` in order to ensure As mentioned above, we can customise the `ProductIndex` and `Product` model completely - they
are not strict requirements for longclaw to operate. We recommend using something similar to
the project layout so that `Product`'s will appear in the Wagtail admin page explorer.
`ProductVariant` can be customised, as long as we inherit from `ProductVariantBase` in order to ensure
the fields longclaw expects are present. the fields longclaw expects are present.
``ProductVariantBase`` provides the ``price``, ``ref`` and ``slug`` fields. `ProductVariantBase` provides the `price`, `ref` and `slug` fields.
The ``ref`` field is intended to be used as a short description or sub-title to help distinguish a particular variant. The `ref` field is intended to be used as a short description or sub-title to help distinguish a particular variant.
The ``slug`` field is autogenerated from the ``ref`` and the parent ``Product`` title. The `slug` field is autogenerated from the `ref` and the parent `Product` title.
As we are creating a music shop, we are going to add a ``music_format`` field to the model. We will also As we are creating a music shop, we are going to add a `music_format` field to the model. We will also
remove the ``description`` field as we dont have any real need for it at the moment: remove the `description` field as we dont have any real need for it at the moment:
.. code-block:: python ```python
class ProductVariant(ProductVariantBase):
_MUSIC_FORMAT_CHOICES = (
(1, 'CD'),
(2, 'Vinyl'),
)
class ProductVariant(ProductVariantBase): music_format = models.IntegerField(max_length=3, choices=_MUSIC_FORMAT_CHOICES)
_MUSIC_FORMAT_CHOICES = ( ```
(1, 'CD'),
(2, 'Vinyl'),
)
music_format = models.IntegerField(max_length=3, choices=_MUSIC_FORMAT_CHOICES)
After making and running migrations, we can now select the format for each variant: After making and running migrations, we can now select the format for each variant:
.. figure:: ../_static/images/product_variant.png ![Product variant](assets/product_variant.png)
The actual model used for the product variant can be changed by changing the ``PRODUCT_VARIANT_MODEL`` setting in your ``settings.py`` The actual model used for the product variant can be changed by changing the `PRODUCT_VARIANT_MODEL` setting in your `settings.py`
Creating the Front End ## Creating the Front End
=======================
Since ``ProductIndex`` and ``Product`` are Wagtail pages, we write templates for them just like any other page.
The Wagtail documentation already comprehensively covers `writing templates <http://docs.wagtail.io/en/v1.9/topics/writing_templates.html>`_.
Our template project already has some basic templates for ``ProductIndex`` and ``Product``: Since `ProductIndex` and `Product` are Wagtail pages, we write templates for them just like any other page.
The Wagtail documentation already comprehensively covers [writing templates](http://docs.wagtail.io/en/v1.9/topics/writing_templates.html).
- ``my_shop/my_shop/templates/products/product_index.html`` Our template project already has some basic templates for `ProductIndex` and `Product`:
- ``my_shop/my_shop/templates/products/product.html``
- `my_shop/my_shop/templates/products/product_index.html`
- `my_shop/my_shop/templates/products/product.html`
They contain just enough information to demonstrate how to traverse the products and their fields. They contain just enough information to demonstrate how to traverse the products and their fields.
For a more complete template, take a look at the `demo project <https://github.com/JamesRamm/longclaw_demo>`_. For a more complete template, take a look at the [demo project](https://github.com/JamesRamm/longclaw_demo).
Adding Products to the Basket ### Adding Products to the Basket
-----------------------------
Longclaw offers a helpful template tag to create an ``Add To Basket`` button for your products.
In your template, load the basket tags::
.. code-block:: django Longclaw offers a helpful template tag to create an `Add To Basket` button for your products.
In your template, load the basket tags:
```django
{% load basket_tags %} {% load basket_tags %}
```
You can now use the tag to render a button for each product variant: You can now use the tag to render a button for each product variant:
.. code-block:: django ```django
{% add_to_basket_btn variant.id btn_text="Add To Basket" btn_class="btn btn-default" %} {% add_to_basket_btn variant.id btn_text="Add To Basket" btn_class="btn btn-default" %}
```
If you wish to create a button manually, you can handle the click event by making an AJAX call to the longclaw API. If you wish to create a button manually, you can handle the click event by making an AJAX call to the longclaw API.
Situations where you would prefer this over the tempaltetag might be to support non-button elements, such as Situations where you would prefer this over the tempaltetag might be to support non-button elements, such as
@ -127,8 +123,7 @@ dropdown buttons, or for React-based frontends.
Here is an example with a single button whose 'variant id' will change depending on the selection in a dropdown box. Here is an example with a single button whose 'variant id' will change depending on the selection in a dropdown box.
We can acheive the drop down like this: We can acheive the drop down like this:
.. code-block:: django ```django
<dl> <dl>
<dt>Format</dt> <dt>Format</dt>
<dd> <dd>
@ -141,16 +136,18 @@ We can acheive the drop down like this:
</div> </div>
</dd> </dd>
</dl> </dl>
```
Add a button: Add a button:
.. code-block:: django ```django
<button id="add-button">Add To Basket</button> <button id="add-button">Add To Basket</button>
```
We can then write a jquery function to handle the click event: We can then write a jquery function to handle the click event:
.. code-block:: javascript ```javascript
$('#add-button').click(function () { $('#add-button').click(function () {
// Selected variant // Selected variant
@ -159,8 +156,9 @@ We can then write a jquery function to handle the click event:
// Add to the basket // Add to the basket
$.post("api/add_to_basket/", { variant_id: variant_id }); $.post("api/add_to_basket/", { variant_id: variant_id });
}); });
```
This is a basic example of integrating with the basket. You will likely need to incorporate more This is a basic example of integrating with the basket. You will likely need to incorporate more
complex designs such as displaying a count of items in the basket, allowing the user to increase/decrease complex designs such as displaying a count of items in the basket, allowing the user to increase/decrease
quantity and so on. The :ref:`basket API <basket>` allows all such interactions and all front end design decisions such as these are left up to the developer. quantity and so on. The [basket API](#basket) allows all such interactions and all front end design decisions such as these are left up to the developer.
It is worthwhile looking at the longclaw demo source code to see how e.g. a basket & item count in the page header is implemented. It is worthwhile looking at the longclaw demo source code to see how e.g. a basket & item count in the page header is implemented.

Wyświetl plik

@ -3,50 +3,44 @@ title: Configuring Shipping
sidebar_label: Shipping sidebar_label: Shipping
--- ---
Configuring Shipping
====================
Now we can display products and add them to the basket, we must configure our shipping rates Now we can display products and add them to the basket, we must configure our shipping rates
before setting up the checkout process. before setting up the checkout process.
Per Country Rates ## Per Country Rates
------------------
Shipping rates are set on a per-country basis via the ``Shipping`` page in the wagtail admin.
Shipping rates are set on a per-country basis via the `Shipping` page in the wagtail admin.
Initially, no countries will be available - Longclaw comes with a set of country data which can be loaded into the database Initially, no countries will be available - Longclaw comes with a set of country data which can be loaded into the database
using the ``loadcountries`` command: using the `loadcountries` command:
.. code-block:: bash ```bash
python manage.py loadcountries python manage.py loadcountries
```
In the image below, we set a standard rate for the UK. It is possible to select multiple countries In the image below, we set a standard rate for the UK. It is possible to select multiple countries
for a rate to apply to. We can also create more than one shipping rate for the same country. for a rate to apply to. We can also create more than one shipping rate for the same country.
.. figure:: ../_static/images/shipping.png ![Shipping](assets/shipping.png)
Default Shipping Rate ## Default Shipping Rate
---------------------
We can configure a default shipping rate to apply to all countries we have not explicitly specified. We can configure a default shipping rate to apply to all countries we have not explicitly specified.
.. note:: By enabling default shipping, you imply that you ship to *all* countries. If you do not wish this > By enabling default shipping, you imply that you ship to *all* countries. If you do not wish this
you should *not* enable default shipping. you should *not* enable default shipping.
To enable default shipping: To enable default shipping:
- Select ``settings`` from the wagtail admin menu - Select `settings` from the wagtail admin menu
- Select ``Longclaw Settings`` - Select `Longclaw Settings`
- Fill in ``Default Shipping Rate`` and ``Default Shipping Carrier`` - Fill in `Default Shipping Rate` and `Default Shipping Carrier`
- Ensure ``Enable Default Shipping`` is checked. - Ensure `Enable Default Shipping` is checked.
.. figure:: ../_static/images/default_shipping.png ![Default shipping](assets/default_shipping.png)
Currency ### Currency
********
You can also define the currency in ``Longclaw Settings``. This applies site wide. It is mostly semantic - You can also define the currency in `Longclaw Settings`. This applies site wide. It is mostly semantic -
Longclaw assumes all calculations & prices are in the same currency - however some payment gateways require the Longclaw assumes all calculations & prices are in the same currency - however some payment gateways require the
currency to be specified. currency to be specified.

Wyświetl plik

@ -77,7 +77,7 @@ const siteConfig = {
highlight: { highlight: {
// Highlight.js theme to use for syntax highlighting in code blocks. // Highlight.js theme to use for syntax highlighting in code blocks.
theme: 'default', theme: 'atom-one-dark',
}, },
// Add custom scripts here that would be placed in <script> tags. // Add custom scripts here that would be placed in <script> tags.
@ -89,8 +89,8 @@ const siteConfig = {
cleanUrl: true, cleanUrl: true,
// Open Graph and Twitter card images. // Open Graph and Twitter card images.
ogImage: 'img/docusaurus.png', ogImage: 'img/shop.png',
twitterImage: 'img/docusaurus.png', twitterImage: 'img/shop.png',
// Show documentation's last contributor's name. // Show documentation's last contributor's name.
// enableUpdateBy: true, // enableUpdateBy: true,