|
| 1 | +# Payload E-Commerce Template |
| 2 | + |
| 3 | +A template for [Payload](https://github.com/payloadcms/payload) to power e-commerce businesses. This repo may have been created by running `npx create-payload-app` and selecting the "e-commerce" template or by cloning this template on [Payload Cloud](https://payloadcms.com/new/clone/blank). |
| 4 | + |
| 5 | +Core features: |
| 6 | + |
| 7 | +- [Pre-configured Payload Config](#how-it-works) |
| 8 | +- [Authentication](#users-authentication) |
| 9 | +- [Access Control](#access-control) |
| 10 | +- [Shopping Cart](#shopping-cart) |
| 11 | +- [Checkout](#checkout) |
| 12 | +- [Paywall](#paywall) |
| 13 | +- [Layout Builder](#layout-builder) |
| 14 | +- [SEO](#seo) |
| 15 | + |
| 16 | +For details on how to get this template up and running locally, see the [development](#development) section. |
| 17 | + |
| 18 | +## How it works |
| 19 | + |
| 20 | +The Payload config is tailored specifically to the needs of an e-commerce business. It is pre-configured in the following ways: |
| 21 | + |
| 22 | +### Collections |
| 23 | + |
| 24 | +See the [collections documentation](https://payloadcms.com/docs/configuration/collections) for details on how to extend this functionality. |
| 25 | + |
| 26 | +- #### Users (Authentication) |
| 27 | + |
| 28 | + Users are auth-enabled and encompass both admins and customers based on the value of their `roles` field. Only `admin` users can access your admin panel to manage your store whereas `customer` can authenticate on your front-end to create [shopping carts](#shopping-cart) and place [orders](#orders) but have limited access to the platform. See [Access Control](#access-control) for more details. |
| 29 | + |
| 30 | + For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth/cms#readme) or the [authentication docs](https://payloadcms.com/docs/authentication/overview#authentication-overview). |
| 31 | + |
| 32 | +- #### Products |
| 33 | + |
| 34 | + Each product is linked to Stripe via a select field that is dynamically populated in the sidebar. This field fetches all available products in the background and displays them as options. Once a product has been selected, prices get automatically synced between Stripe and Payload. All products are layout-builder enabled so you can generate unique pages for each product using layout-building blocks, see [Layout Builder](#layout-builder) for more details. Products can also gate their content or digital assets behind a paywall, see [Paywall](#paywall) for more details. |
| 35 | + |
| 36 | +- #### Orders |
| 37 | + |
| 38 | + When an order is placed in Stripe, a webhook is fired that Payload listens for. This webhook creates a new order in Payload with the same data as the invoice. See the [Stripe section](#stripe) for more details. |
| 39 | + |
| 40 | +- #### Pages |
| 41 | + |
| 42 | + All pages are layout-builder enabled so you can generate unique layouts for each page using layout-building blocks, see [Layout Builder](#layout-builder) for more details. |
| 43 | + |
| 44 | +- #### Media |
| 45 | + |
| 46 | + This is the uploads-enabled collection used by products and pages to contain media, etc. |
| 47 | + |
| 48 | +- #### Categories |
| 49 | + |
| 50 | + A taxonomy used to group products together. Categories can be nested inside of one another, for example "Shirts > Red". See the official [Payload Nested Docs Plugin](https://github.com/payloadcms/plugin-nested-docs) for more details. |
| 51 | + |
| 52 | +### Globals |
| 53 | + |
| 54 | +See the [globals documentation](https://payloadcms.com/docs/configuration/globals) for details on how to extend this functionality. |
| 55 | + |
| 56 | +- `Header` |
| 57 | + |
| 58 | + The data required by the header on your front-end, i.e. nav links, etc. |
| 59 | + |
| 60 | +- `Footer` |
| 61 | + |
| 62 | + Same as above but for the footer of your site. |
| 63 | + |
| 64 | +## Access control |
| 65 | + |
| 66 | +Basic role-based access control is setup to determine what users can and cannot do based on their roles, which are: |
| 67 | + |
| 68 | +- `admin`: They can access the Payload admin panel to manage your store. They can see all data and make all operations. |
| 69 | +- `customer`: They cannot access the Payload admin panel and have a limited access to operations based on their user (see below). |
| 70 | + |
| 71 | +This applies to each collection in the following ways: |
| 72 | + |
| 73 | +- `users`: Only admins and the user themselves can access their profile. Anyone can create a user but only admins can delete users. |
| 74 | +- `orders`: Only admins and the user who placed the order can access it. Once placed, orders cannot be edited or deleted. |
| 75 | +- `products`: Everyone can access products, but only admins can create, update, or delete them. Paywall-enabled products may also have content that is only accessible by users who have purchased the product. See [Paywall](#paywall) for more details. |
| 76 | + |
| 77 | +For more details on how to extend this functionality, see the [Payload Access Control](https://payloadcms.com/docs/access-control/overview#access-control) docs. |
| 78 | + |
| 79 | +## Shopping cart |
| 80 | + |
| 81 | +Logged-in users can have their shopping carts saved to their profiles as they shop. This way they can continue shopping at a later date or on another device. When not logged in, the cart can be saved to local storage and synced to Payload on the next login. This works by maintaining a `cart` field on the `user`: |
| 82 | + |
| 83 | +```ts |
| 84 | +{ |
| 85 | + name: 'cart', |
| 86 | + label: 'Shopping Cart', |
| 87 | + type: 'object', |
| 88 | + fields: [ |
| 89 | + { |
| 90 | + name: 'items', |
| 91 | + label: 'Items', |
| 92 | + type: 'array', |
| 93 | + fields: [ |
| 94 | + // product, quantity, etc |
| 95 | + ] |
| 96 | + }, |
| 97 | + // other metadata like `createdOn`, etc |
| 98 | + ] |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +## Stripe |
| 103 | + |
| 104 | +Payload itself handles no currency exchange. All payments are processed and billed using [Stripe](https://stripe.com). This means you must have access to a Stripe account via an API key, see [Connect Stripe](#connect-stripe) for how to get one. When you create a product in Payload that wish to sell, it must be connected to a Stripe product by selecting one from the field in the product's sidebar. This field fetches all available products in the background and displays them as options, see [Products](#products) for more details. Once set, data is automatically synced between the two platforms in the following ways: |
| 105 | + |
| 106 | +1. Stripe to Payload using [Stripe Webhooks](https://stripe.com/docs/webhooks): |
| 107 | + |
| 108 | + - `invoice.created` |
| 109 | + - `invoice.updated` |
| 110 | + - `product.created` |
| 111 | + - `product.updated` |
| 112 | + - `price.updated` |
| 113 | + |
| 114 | +1. Payload to Stripe using [Payload Hooks](https://payloadcms.com/docs/hooks/overview): |
| 115 | + - `user.create` |
| 116 | + |
| 117 | +For more details on how to extend this functionality, see the the official [Payload Stripe Plugin](https://github.com/payloadcms/plugin-stripe). |
| 118 | + |
| 119 | +## Checkout |
| 120 | + |
| 121 | +A custom endpoint is opened at `/api/checkout` which initiates the checkout process. This endpoint creates a [`PaymentIntent`](https://stripe.com/docs/payments/payment-intents) with the items in the cart using the Stripe's [Invoices API](https://stripe.com/docs/api/invoices). First, an invoice is drafted, then each item in your cart is appended as a line-item to the invoice. The total price is recalculated on the server to ensure accuracy and security, and once completed, passes the `client_secret` back in the response for your front-end to finalize the payment. |
| 122 | + |
| 123 | +## Paywall |
| 124 | + |
| 125 | +Products can optionally gate content or digital assets behind a paywall. This will require the product to be purchased before it's resources are accessible. To do this, we add a `paywall` field to the `product` collection with `read` access control to check for associated purchases on each request. A `purchases` field is maintained on each user to determine their access which can be manually adjusted as needed. |
| 126 | + |
| 127 | +```ts |
| 128 | +{ |
| 129 | + name: 'paywall', |
| 130 | + label: 'Paywall', |
| 131 | + type: 'blocks', |
| 132 | + access: { |
| 133 | + read: checkUserPurchases, |
| 134 | + }, |
| 135 | + fields: [ |
| 136 | + // assets |
| 137 | + ] |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +## Layout builder |
| 142 | + |
| 143 | +Products and pages can be built using a powerful layout builder. This allows you to create unique layouts for each product or page. This template comes pre-configured with the following layout building blocks: |
| 144 | + |
| 145 | +- Hero |
| 146 | +- Content |
| 147 | +- Media |
| 148 | +- Call To Action |
| 149 | +- Archive |
| 150 | + |
| 151 | +## SEO |
| 152 | + |
| 153 | +This template comes pre-configured with the official [Payload SEO Plugin](https://github.com/payloadcms/plugin-seo) for complete SEO control. |
| 154 | + |
| 155 | +## Development |
| 156 | + |
| 157 | +To spin up the template locally, follow these steps: |
| 158 | + |
| 159 | +1. First clone the repo |
| 160 | +1. Then `cd YOUR_PROJECT_REPO && cp .env.example .env` |
| 161 | +1. Next `yarn && yarn dev` (or `docker-compose up`, see [Docker](#docker)) |
| 162 | +1. Now `open http://localhost:8000/admin` to access the admin panel |
| 163 | +1. Create your first admin user using the form on the page |
| 164 | + |
| 165 | +That's it! Changes made in `./src` will be reflected in your app—but your database is blank and your app is not yet connected to Stripe, more details on that [here](#stripe). You can optionally seed the database with a few products and pages, more details on that [here](#seed). |
| 166 | + |
| 167 | +### Connect Stripe |
| 168 | + |
| 169 | +To integrate with Stripe, follow these steps: |
| 170 | + |
| 171 | +1. You will first need to create a [Stripe](https://stripe.com) account if you do not already have one. |
| 172 | +1. Retrieve your Stripe Secret Key from the Stripe admin panel and paste it into your `env`: |
| 173 | + ```bash |
| 174 | + STRIPE_SECRET_KEY= |
| 175 | + ``` |
| 176 | +1. In another terminal, listen for webhooks: |
| 177 | + ```bash |
| 178 | + stripe login # follow the prompts |
| 179 | + yarn stripe:webhooks |
| 180 | + ``` |
| 181 | +1. Paste the given webhook signing secret into your `env`: |
| 182 | + ```bash |
| 183 | + STRIPE_WEBHOOKS_ENDPOINT_SECRET= |
| 184 | + ``` |
| 185 | +1. Reboot Payload to ensure that Stripe connects and the webhooks are registered. |
| 186 | + |
| 187 | +See the official [Payload Stripe Plugin](https://github.com/payloadcms/plugin-stripe) for more details. |
| 188 | + |
| 189 | +### Docker |
| 190 | + |
| 191 | +Alternatively, you can use [Docker](https://www.docker.com) to spin up this template locally. To do so, follow these steps: |
| 192 | + |
| 193 | +1. Follow [steps 1 and 2 from above](#development), the docker-compose file will automatically use the `.env` file in your project root |
| 194 | +1. Next run `docker-compose up` |
| 195 | +1. Follow [steps 4 and 5 from above](#development) to login and create your first admin user |
| 196 | + |
| 197 | +That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams. |
| 198 | + |
| 199 | +### Seed |
| 200 | + |
| 201 | +To seed the database with a few products and pages you can run `yarn seed`. |
| 202 | + |
| 203 | +> NOTICE: seeding the database is destructive because it drops your current database to populate a fresh one from the seed template. Only run this command if you are starting a new project or can afford to lose your current data. |
| 204 | +
|
| 205 | +## Production |
| 206 | + |
| 207 | +To run Payload in production, you need to build and serve the Admin panel. To do so, follow these steps: |
| 208 | + |
| 209 | +1. First invoke the `payload build` script by running `yarn build` or `npm run build` in your project root. This creates a `./build` directory with a production-ready admin bundle. |
| 210 | +1. Then run `yarn serve` or `npm run serve` to run Node in production and serve Payload from the `./build` directory. |
| 211 | + |
| 212 | +### Deployment |
| 213 | + |
| 214 | +The easiest way to deploy your project is to use [Payload Cloud](https://payloadcms.com/new/import), a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo. You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/production/deployment) for full details. |
| 215 | + |
| 216 | +## Questions |
| 217 | + |
| 218 | +If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions). |
0 commit comments