Merge branch 'master' into develop

This commit is contained in:
olivermrbl
2022-06-19 12:59:08 +02:00
56 changed files with 2304 additions and 676 deletions

54
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,54 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'type: bug'
assignees: ''
---
<!--
Thank you for submitting an issue!
Please make sure your issue is understandable and reproducible.
To make your issue readable make sure you use valid Markdown syntax: https://guides.github.com/features/mastering-markdown/
Please ensure you have also read and understood our contribution guide: https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md
-->
## Bug report
### Describe the bug
A clear and concise description of what the bug is.
### System information
Medusa version (including plugins):
Node.js version:
Database:
Operating system:
Browser (if relevant):
### Steps to reproduce the behavior
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
### Expected behavior
A clear and concise description of what you expected to happen
### Screenshots
If applicable, add screenshots to help explain your problem
### Code snippets
If applicable, add code samples to help explain your problem
### Additional context
Add any other context about the problem here

48
.github/ISSUE_TEMPLATE/docs.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: 📚 Documentation
description: Report errors you find in our documentation or ideas for enhancing our documentation
labels: ["type: docs"]
body:
- type: markdown
attributes:
value: |
# Hello 👋
Thanks for taking the time to fill out this issue.
Please fill out each section below. This info helps Medusa maintainers understand the issue better and fix it in a short time.
Useful links:
- Contribution Guidelines: https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md
- Documentation: https://docs.medusajs.com/
- type: checkboxes
attributes:
label: Preliminary Checks
description: Please make sure that you verify each checkbox and follow the instructions for them.
options:
- label: "This issue is not a duplicate. Before opening a new issue, please search existing issues: https://github.com/medusajs/medusa/issues"
required: true
- type: textarea
validations:
required: true
attributes:
label: Issue Summary
description: |
What is the error you found in our documentation or what do you think should be enhanced?
- type: textarea
validations:
required: false
attributes:
label: How can this issue be resolved?
description: If you have an idea of how the issue can be resolved, please details the steps below.
value: |
1.
2.
3.
...
- type: checkboxes
attributes:
label: Are you interested in working on this issue?
description: If you're interested in not only reporting this issue but also fix it, check the box below.
options:
- label: "I would like to fix this issue"
required: false

View File

@@ -0,0 +1,29 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'type: feature or enhancement'
assignees: ''
---
<!--
Thank you for submitting an issue!
Please make sure your issue is understandable.
To make your issue readable make sure you use valid Markdown syntax: https://guides.github.com/features/mastering-markdown/
Please ensure you have also read and understood our contribution guide: https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md
-->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,5 +1,9 @@
name: Medusa Pipeline
on: [pull_request]
on:
pull_request:
paths-ignore:
- 'docs/**'
- 'www/**'
jobs:
unit-tests:

View File

@@ -2,6 +2,8 @@
Thank you for considering contributing to Medusa! This document will outline how to submit changes to this repository and which conventions to follow. If you are ever in doubt about anything we encourage you to reach out either by submitting an issue here or reaching out [via Discord](https://discord.gg/xpCwq3Kfn8).
If you're contributing to our documentation, make sure to also check out the [contribution guidelines on our documentation website](https://docs.medusajs.com/contribution-guidelines).
## Prerequisites
- **You're familiar with GitHub Issues and Pull Requests**

View File

@@ -180,4 +180,4 @@ If you try entering an email and clicking Subscribe, the email will be subscribe
## Whats Next 🚀
- Check out SendGrid plugin for more Email functionalities.
- [Learn more about plugins.](https://docs.medusajs.com/guides/plugins)
- [Learn more about plugins.](../advanced/backend/plugins/overview.md)

View File

@@ -76,7 +76,7 @@ You will not be able to access the Secret Key after closing the pop-up. So, make
In the directory of your Medusa server, run the following command to install the MinIO plugin:
```bash
```bash npm2yarn
npm install medusa-file-minio
```

View File

@@ -3963,5 +3963,5 @@ You can also track analytics related to emails sent from the SendGrid dashboard.
## Whats Next 🚀
- Learn more about how [Notifications work in Medusa](../how-to/notification-api.md).
- Learn more about how [Notifications work in Medusa](../advanced/backend/notification/overview.md).
- Install the [Medusa admin](https://github.com/medusajs/admin#-setting-up-admin) for functionalities like Gift Cards creation, swaps, claims, order return requests, and more.

View File

@@ -33,7 +33,7 @@ Medusa's event system works by pushing data into a queue that is based on [Redis
As the Slack plugin will listen to the `order.placed` event to know when to send notifications, you'll need to have Redis installed and configured with your Medusa server.
You can read the [Set up your development enviornment guideline](https://docs.medusajs.com/tutorial/set-up-your-development-environment) to learn more about how you can install and setup Redis.
You can read the [Set up your development enviornment guideline](../tutorial/0-set-up-your-development-environment.mdx#redis) to learn more about how you can install and setup Redis.
## Create Slack App

View File

@@ -124,5 +124,5 @@ If youre on a Twilio trial make sure that the phone number you entered on che
## Whats Next 🚀
- Learn more about how [Notifications work in Medusa](../how-to/notification-api).
- Learn more about how [Notifications work in Medusa](../advanced/backend/notification/overview.md).
- Install the [Medusa admin](../admin/quickstart.md) for functionalities like Gift Cards creation, swaps, claims, order return requests, and more.

View File

@@ -101,6 +101,12 @@ ADMIN_CORS=<YOUR_ADMIN_URL>
Make sure to replace `<YOUR_ADMIN_URL>` with your URL.
:::info
For more details about the Admin CORS configuration, check out the [Configure your Server documentation](../usage/configurations.md#admin-cors).
:::
## Admin Features Overview
### Order Management

View File

@@ -1,8 +1,4 @@
---
title: Add Endpoint for Admin
---
# Add Endpoint for Admin
# Create Endpoint for Admin
In this document, youll learn how to add a custom endpoint in the Backend that you can use from the Admin.

View File

@@ -1,8 +1,4 @@
---
title: Add Endpoint for Storefront
---
# Add Endpoint for Storefront
# Create Endpoint for Storefront
In this document, youll learn how to add a custom endpoint in the Backend that you can use from the Storefront.

View File

@@ -0,0 +1,160 @@
# How to Create Entities
In this document, youll learn about entities in Medusa and how you can create your own entity.
## Overview
Entities in medusa represent tables in the database as classes. An example of this would be the `Order` entity which represents the `order` table in the database. Entities provide a uniform way of defining and interacting with data retrieved from the database.
Aside from Medusas core entities, you can also create your own entities to use in your Medusa server. Custom entities must reside in the `src/models` directory of your Medusa server.
Entities are TypeScript files and they are based on [Typeorms Entities](https://typeorm.io/entities) and use Typeorm decorators.
All entities must extend either the `BaseEntity` or `SoftDeletableEntity` classes. The `BaseEntity` class holds common columns including the `id`, `created_at`, and `updated_at` columns.
The `SoftDeletableEntity` class extends the `BaseEntity` class and adds another column `deleted_at`. If an entity can be soft deleted, meaning that a row in it can appear to the user as deleted but still be available in the database, it should extend `SoftDeletableEntity`.
## How to Create a Custom Entity
### Prerequisites
Its recommended to create a `tsconfig.json` file in the root of your Medusa server with the following content:
```jsx
{
"compilerOptions": {
"experimentalDecorators": true
}
}
```
This will remove any errors that show up in your IDE related to experimental decorators.
### Create the Entity
To create an entity, create a TypeScript file in `src/models`. For example, heres a `Post` entity defined in the file `src/models/post.ts`:
```tsx
import { BeforeInsert, Column, Entity, PrimaryColumn } from "typeorm";
import { BaseEntity} from "@medusajs/medusa";
import { generateEntityId } from "@medusajs/medusa/dist/utils"
@Entity()
export class Post extends BaseEntity {
@Column({type: 'varchar'})
title: string | null;
@BeforeInsert()
private beforeInsert(): void {
this.id = generateEntityId(this.id, "post")
}
}
```
This entity has one column `title` defined. However, since it extends `BaseEntity` it will also have the `id`, `created_at`, and `updated_at` columns.
Medusas core entities all have the following format for IDs: `<PREFIX>_<RANDOM>`. For example, an order might have the ID `order_01G35WVGY4D1JCA4TPGVXPGCQM`.
To generate an ID for your entity that matches the IDs generated for Medusas core entities, you should add a `BeforeInsert` event handler. Then, inside that handler use Medusas utility function `generateEntityId` to generate the ID. It accepts the ID as a first parameter and the prefix as a second parameter. The `Post` entity IDs will be of the format `post_<RANDOM>`.
If you want the entity to also be soft deletable then it should extend `SoftDeletableEntity` instead:
```tsx
import { SoftDeletableEntity } from "@medusajs/medusa";
@Entity()
export class Post extends SoftDeletableEntity {
//...
}
```
You can learn more about what decorators and column types you can use in [Typeorms documentation](https://typeorm.io/entities).
### Create the Migration
Additionally, you must create a migration for your entity. Migrations are used to update the database schema with new tables or changes to existing tables.
You can learn more about Migrations, how to create them, and how to run them in the [Migration documentation](migrations.md).
### Create a Repository
Entities data can be easily accessed and modified using Typeorm [Repositories](https://typeorm.io/working-with-repository). To create a repository, create a file in `src/repositories`. For example, heres a repository `PostRepository` that resides in `src/repositories/post.ts`:
```tsx
import { EntityRepository, Repository } from "typeorm"
import { Post } from "../models/post"
@EntityRepository(Post)
export class PostRepository extends Repository<Post> { }
```
This repository is created for the `Post` and that is indicated using the decorator `@EntityRepository`.
:::tip
Be careful with your file names as it can cause unclear errors in Typeorm. Make sure all your file names are small letters for both entities and repositories to avoid any issues with file names.
:::
## Access Your Custom Entity
:::note
Before trying this step make sure that youve created and run your migrations. You also need to re-build your code using:
```bash npm2yarn
npm run build
```
:::
You can access your custom entity data in the database in services or subscribers using the repository. For example, heres a service that lists all posts:
```tsx
import { TransactionBaseService } from "medusa-interfaces";
class PostService extends TransactionBaseService {
constructor({ postRepository, manager }) {
super({ postRepository, manager });
this.postRepository = postRepository;
this.manager_ = manager;
}
async list() {
const postRepository = this.manager_.getCustomRepository(this.postRepository);
return await postRepository.find();
}
}
export default PostService;
```
In the constructor, you can use dependency injection to get access to instances of services and repositories. Here, you initialize class fields `postRepository` and `manager`. The `manager` is a [Typeorm Entity Manager](https://typeorm.io/working-with-entity-manager).
Then, in the method `list`, you can obtain an instance of the `PostRepository` using `this.manager_.getCustomRepository` passing it `this.postRepository` as a parameter. This lets you use [Custom Repositories with Typeorm](https://typeorm.io/custom-repository) to create custom methods in your repository that work with the data in your database.
After getting an instance of the repository, you can then use [Typeorms Repository methods](https://typeorm.io/repository-api) to perform CRUD (Create, Read, Update, Delete) operations on your entity.
If you need access to your entity in endpoints, you can then use the methods you define in the service.
:::note
This same usage of repositories can be done in subscribers as well.
:::
### Deleting Soft-Deletable Entities
To delete soft-deletable entities that extend the `SoftDeletableEntity` class, you can use the repository method `softDelete` method:
```tsx
await postRepository.softDelete(post.id);
```
## Whats Next 🚀
- Learn more about [Services](services/create-service.md) and how to use them.
- Learn how to create an endpoint for [storefront](endpoints/add-storefront.md) and [admin](endpoints/add-admin.md).
- Learn about [migrations](migrations.md).

View File

@@ -57,12 +57,12 @@ In this section, youll learn how to create your own migrations using [Typeorm
To create a migration that makes changes to your Medusa schema, run the following command:
```bash
npx typeorm migration:create -n UserChanged --dir src/path
npx typeorm migration:create -n UserChanged --dir src/migrations
```
:::tip
The migration file should be inside the src directory to make sure it is built when the build command is run next.
The migration file must be inside the `src/migrations` directory. When the build command is run, it will be transpiled into the directory `dist/migrations`. The `migrations run` command can only pick up migrations under the `dist/migrations` directory.
:::

View File

@@ -0,0 +1,272 @@
# How to Create a Notification Provider
In this document, youll learn how to add a Notification Provider to your Medusa server. If youre unfamiliar with the Notification architecture in Medusa, it is recommended to check out the [architecture overview](overview.md) first.
## Overview
A Notification Provider is the custom or third-party service used to handle sending alerts to customers or users when an event occurs.
For example, you can use SendGrid to send a confirmation email to a customer after they place an order. SendGrid in this example is a Notification Provider.
:::tip
If youre interested in using SendGrid to send Notifications, you can use the [SendGrid](../../../add-plugins/sendgrid.mdx) plugin instead of using your own.
:::
Adding a Notification Provider is as simple as creating a [Service](../services/create-service.md) file in `src/services`. A Notification Provider is essentially a Service that extends the `NotificationService` from `medusa-interfaces`.
:::info
Notification Providers are loaded and installed at the server startup.
:::
After creating the Notification Provider Service, you must create a [Subscriber](../subscribers/create-subscriber.md) that subscribes the Notification Provider to specific events.
## Prerequisites
Before you start creating a Notification Provider, you need to install a [Medusa server](../../../quickstart/quick-start.md).
You also need to install and configure Redis. You can check out the documentation “[Set Up Your Development Environment](../../../tutorial/0-set-up-your-development-environment.mdx)” for help.
## Create a Notification Provider
The first step to creating a Notification Provider is to create a file in `src/services` with the following content:
```jsx
import { NotificationService } from "medusa-interfaces";
class EmailSenderService extends NotificationService {
}
export default EmailSenderService;
```
Where `EmailSenderService` is the name of your Notification Provider Service.
Notification Providers must extend `NotificationService` from `medusa-interfaces`.
:::info
Following the naming convention of Services, the name of the file should be the slug name of the Notification Provider, and the name of the class should be the camel case name of the Notification Provider suffixed with “Service”. In the example above, the name of the file should be `email-sender.js`. You can learn more in the [service documentation](../services/create-service.md).
:::
### identifier
Notification Provider Services must have a static property `identifier`.
The `NotificationProvider` entity has 2 properties: `identifier` and `is_installed`. The value of the `identifier` property in the Service class is used when the Notification Provider is created in the database.
The value of this property is also used later when you want to subscribe the Notification Provider to events in a Subscriber.
For example, in the class you created in the previous code snippet you can add the following property:
```jsx
static identifier = "email-sender";
```
### constructor
You can use the `constructor` of your Notification Provider to have access to different Services in Medusa through dependency injection.
You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party providers APIs, you can initialize it in the constructor and use it in other methods in the Service.
Additionally, if youre creating your Notification Provider as an external plugin to be installed on any Medusa server and you want to access the options added for the plugin, you can access it in the constructor. The options are passed as a second parameter.
:::info
You can learn more about plugins and how to create them in the [Plugins](../plugins/overview.md) documentation.
:::
Continuing on with the previous example, if you want to use the [`OrderService`](../../../references/services/classes/OrderService.md) later when sending notifications, you can inject it into the constructor:
```jsx
constructor({ orderService }, options) {
//you can access options here in case you're
//using a plugin
super();
this.orderService = orderService;
}
```
### sendNotification
When an event is triggered that your Notification Provider is registered as a handler for, the [`NotificationService`](../../../references/services/classes/NotificationService.md) in Medusas core will execute the `sendNotification` method of your Notification Provider.
In this method, you can perform the necessary operation to send the Notification. Following the example above, you can send an email to the customer when they place an order.
This method receives 3 parameters:
1. `eventName`: This is the name of the event that was triggered. For example, `order.placed`.
2. `eventData`: This is the data payload of the event that was triggered. For example, if the `order.placed` event is triggered, the `eventData` object contains the property `id` which is the ID of the order that was placed.
3. `attachmentGenerator`: If youve previously attached a generator to the `NotificationService` using the [`registerAttachmentGenerator`](../../../references/services/classes/NotificationService.md#registerattachmentgenerator) method, you have access to it here. You can use the `attachmentGenerator` to generate on-demand invoices or other documents. The default value of this parameter is null.
:::info
You can learn more about what events are triggered in Medusa and their data payload in the [Events List](../subscribers/events-list.md) documentation.
:::
This method must return an object containing two properties:
1. `to`: a string that represents the receiver of the Notification. For example, if you sent an email to the customer then `to` is the email address of the customer. In other cases, it might be a phone number or a username.
2. `data`: an object that contains the data used to send the Notification. For example, if you sent an order confirmation email to the customer, then the `data` object might include the order items or the subject of the email. This `data` is necessary if the notification is resent later as you can use the same data.
Continuing with the previous example you can have the following implementation of the `sendNotification` method:
```jsx
async sendNotification(eventName, eventData, attachmentGenerator) {
if (eventName === 'order.placed') {
//retrieve order
const order = await this.orderService.retrieve(eventData.id);
//TODO send email
console.log('Notification sent');
return {
to: order.email,
data: {
//any data necessary to send the email
//for example:
subject: 'You placed a new order!',
items: order.items
}
};
}
}
```
In this code snippet, you check first if the event is `order.placed`. This can be helpful if youre handling multiple events using the same Notification Provider.
You then retrieve the order using the ID and send the email. Here, the logic related to sending the email is not implemented as it is generally specific to your Notification Provider.
Finally, you return an object with the `to` property set to the customer email and the `data` property is an object that contains data necessary to send the email such as a `subject` or `items`.
:::note
The `to` and `data` properties are used in the `NotificationService` in Medusas core to create a new `Notification` record in the database. You can learn more about the `Notification` entity in the [Architecture Overview](overview.md#notification-entity-overview) documentation.
:::
### resendNotification
Using the [Resend Notification endpoint](https://docs.medusajs.com/api/admin/notification/resend-notification), an admin user can resend a Notification to the customer. The [`NotificationService`](../../../references/services/classes/NotificationService.md) in Medusas core then executes the `resendNotification` method in your Notification Provider.
This method receives 3 parameters:
1. `notification`: This is the original Notification record that was created after you sent the notification with `sendNotification`. You can get an overview of the entity and its attributes in the [architecture overview](overview.md#notification-entity-overview), but most notably it includes the `to` and `data` attributes which are populated originally using the `to` and `data` properties of the object you return in `sendNotification`.
2. `config`: In the Resend Notification endpoint you may specify an alternative receiver of the notification using the `to` request body parameter. For example, you may want to resend the order confirmation email to a different email. If thats the case, you have access to it in the `config` parameter object. Otherwise, `config` will be an empty object.
3. `attachmentGenerator`: If youve previously attached a generator to the Notification Service using the [`registerAttachmentGenerator`](../../../references/services/classes/NotificationService.md#registerattachmentgenerator) method, you have access to it here. You can use the `attachmentGenerator` to generate on-demand invoices or other documents. The default value of this parameter is null.
Similarly to the `sendNotification` method, this method must return an object containing two properties:
1. `to`: a string that represents the receiver of the Notification. You can either return the same `to` available in the `notification` parameter or the updated one in the `config` parameter.
2. `data`: an object that contains the data used to send the Notification. You can either return the same `data` in the `notification` parameter or make any necessary updates to it.
Continuing with the previous example you can have the following implementation of the `resendNotification` method:
```jsx
resendNotification(notification, config, attachmentGenerator) {
//check if the receiver of the notification should be changed
const to = config.to ? config.to : notification.to;
//TODO resend the notification using the same data that is saved under notification.data
console.log('Notification resent');
return {
to,
data: notification.data //you can also make changes to the data
}
}
```
In the above snippet, you check if the `to` should be changed by checking if the `config` parameter has a `to` property. Otherwise, you keep the same `to` address stored in the `notification` parameter.
You then resend the email. Here, the logic related to sending the email is not implemented as it is generally specific to your Notification Provider.
Finally, you return an object with the `to` property set to the email (either new or old) and the `data` property is the same data used before to send the original notification. you can alternatively make any changes to the data.
:::note
The `to` and `data` properties are used in the `NotificationService` in Medusas core to create a new `Notification` record in the database. No changes are made to the original `Notification` record created after the `sendNotification` method. This new record is associated with the original `Notification` record using the `parent_id` attribute in the entity. You can learn more about the `Notification` entity in the [Architecture Overview](overview.md#notification-entity-overview) documentation.
:::
## Create a Subscriber
After creating your Notification Provider Service, you must create a Subscriber that registers this Service as a notification handler of events.
:::note
This section will not cover the basics of Subscribers. You can read the [Subscribers](../subscribers/create-subscriber.md) documentation to learn more about them and how to create them.
:::
Following the previous example, to make sure the `email-sender` Notification Provider handles the `order.placed` event, create the file `src/subscribers/notification.js` with the following content:
```jsx
class NotificationSubscriber {
constructor({ notificationService }) {
notificationService.subscribe('order.placed', 'email-sender');
}
}
export default NotificationSubscriber;
```
This subscriber accesses the `notificationService` using dependency injection. The `notificationService` contains a `subscribe` method that accepts 2 parameters. The first one is the name of the event to subscribe to, and the second is the identifier of the Notification Provider that is subscribing to that event.
:::tip
Notice that the value of the `identifier` static property defined in the `EmailSenderService` is used to register the Notification Provider to handle the `order.placed` event.
:::
## Test Sending Notifications with your Notification Provider
Make sure you've configured Redis with your Medusa server as explained in the Prerequisites section and that the Redis service is running.
Then, start by running your Medusa server:
```bash
npm run start
```
Then, place an order either using the [REST APIs](https://docs.medusajs.com/api/store/auth) or using the storefront.
:::tip
If you dont have a storefront installed you can get started with either our [Next.js](../../../starters/nextjs-medusa-starter.md) or [Gatsby](../../../starters/gatsby-medusa-starter.md) starter storefronts in minutes.
:::
After placing an order, you can see in your console the message “Notification Sent”. If you added your own notification sending logic, you should receive an email or alternatively the type of notification youve set up.
## Test Resending Notifications with your Notification Provider
To test resending a notification, first, retrieve the ID of the notification you just sent using the [List Notifications admin endpoint](https://docs.medusajs.com/api/admin/notification/list-notifications). You can pass as a body parameter the `to` or `event_name` parameters to filter out the notification you just sent.
:::tip
You must be authenticated as an admin user before sending this request. You can use the [Authenticate a User](https://docs.medusajs.com/api/admin/auth/) endpoint to get authenticated.
:::
![List Notifications Request](https://i.imgur.com/iF1rZX1.png)
Then, send a request to the [Resend Notification](https://docs.medusajs.com/api/admin/notification/resend-notification) endpoint using the ID retrieved from the previous request. You can pass the `to` parameter in the body to change the receiver of the notification. You should see the message “Notification Resent” in your console and if you implemented your own logic for resending the notification it will be resent.
![Resend Notifications Request](https://i.imgur.com/0zFfPed.png)
This request returns the same notification object as the List Notifications endpoint, but it now has a new object in the `resends` array. This is the resent notification. If you supplied a `to` parameter in the request body, you should see its value in the `to` property of the resent notification object.
## Whats Next 🚀
- Check out the [list of events](../subscribers/events-list.md) you can listen to.
- Check out the [SendGrid](../../../add-plugins/sendgrid.mdx) plugin for easy integration of email notifications.
- Learn how to [create your own plugin](../plugins/create.md).
- Learn more about [Subscribers](../subscribers/create-subscriber.md) and [Services](../services/create-service.md).

View File

@@ -0,0 +1,88 @@
# Architecture Overview
This document gives an overview of the notification architecture and how it works.
## Introduction
Medusa provides a Notification API to mainly handle sending and resending notifications when an event occurs. For example, sending an email to the customer when they place an order.
The Notification architecture is made up of 2 main components: the Notification Provider and the Notification. Simply put, the Notification Provider handles the sending and resending of a Notification.
## Notification Provider
A Notification Provider is a provider that handles sending and resending of notifications. You can either create and integrate your own provider or install a Notification Provider through a third-party plugin.
An example of a notification provider is SendGrid. When an order is placed, the SendGrid plugin sends an email to the customer.
### How Notification Provider is Created
A Notification Provider is essentially a Medusa [Service](../services/create-service.md) with a unique identifier, and it extends the [`NotificationService`](../../../references/services/classes/NotificationService.md) provided by the `medusa-interfaces` package. It can be created as part of a [Plugin](../plugins/overview.md), or it can be created just as a Service file in your Medusa server.
As a developer, you mainly work with the Notification Provider when integrating a third-party service that handles notifications in Medusa.
When you run your Medusa server, the Notification Provider is registered on your server if it isnt already. This means that it will be inserted into the `notification_provider` table in your database.
### NotificationProvider Entity Overview
The `NotificationProvider` entity only has 2 attributes: `id` and `is_installed`.
`id` is the value of the static property `identifier` defined inside the notification Service class.
`is_installed` indicates whether the Notification Provider is installed or not. When you install a Notification Provider, the value of this attribute is `true`.
If you installed a Notification provider and then removed the Service files or plugin that registered the Notification Provider, the Notification Provider remains in your database, but the value of the `is_installed` field changes to `false`.
## Notification
A notification is a form of an alert sent to the customers or users to inform them of an action that has occurred. For example, if an order is placed, the notification, in this case, can be an email that confirms their order and lists the order details.
Notifications can take on other forms such as an SMS or a Slack message.
### How Notification is Created
Notifications are created in the `NotificationService` class in Medusas core after the Notification has been handled by the Notification Provider.
The data and additional details that the Notification Provider returns to the `NotificationService` is used to fill some of the attributes of the Notification in the database.
A Notification also represents a resent notification. So, when a notification is resent, a new one is created that references the original Notification as a parent. This Notification is also created by the `NotificationService` class.
### Notification Entity Overview
The 2 most important properties in the `Notification` entity are the `to` and `data` properties.
The `to` property is a string that represents the receiver of the Notification. For example, if the Notification was sent to an email address, the `to` property holds the email address the Notification was sent to.
The `to` property can alternatively be a phone number or a chat username. It depends on the Notification Provider and how it sends the Notification.
The `data` property is an object that holds all the data necessary to send the Notification. For example, in the case of an order confirmation Notification, it can hold data related to the order.
The `data` property is useful when a notification is resent later. The same `data` can be used to resend the notification.
In the case of resent notifications, the resent notification has a `parent_id` set to the ID of the original Notification. The value of the `parent_id` property in the original Notification is `null`.
The `Notification` entity has some properties that determine the context of this Notification. This includes the `event_name` property which is the event that triggered the sending of this notification.
Additionally, the `resource_type` property is used to determine what resource this event is associated with. For example, if the `event_name` is `order.placed`, the `resource_type` is `order`.
You can also access the specific resource using the `resource_id` property, which is the ID of the resource. So, in case of the `order.placed` event, the `resource_id` is the ID of the order that was created.
The `Notification` entity also includes properties related to the receiver of the Notification. In case the receiver is a customer, the `customer_id` property is used to identify which customer.
## Automating Flows with Notifications
With Medusa you can create notifications as a reaction to a wide spectrum of events, allowing you to automate communication and processes.
An example of a flow that can be implemented using Medusa's Notification API is automated return flows:
- A customer requests a return by sending a `POST` request to the `/store/returns` endpoint.
- The Notification Provider listens to the `order.return_requested` event and sends an email to the customer with a return invoice and return label generated by the Fulfillment Provider.
- The customer returns the items triggering the `return.recieved` event.
- The Notification Provider listens to the `return.received` event and sends an email to the customer with confirmation that their items have been received and that a refund has been issued.
## Whats Next 🚀
- Learn how to create your own Notification Provider.
- Check out the [list of events](../subscribers/events-list.md) in Medusa.
- Check the [`NotificationService`](../../../references/services/classes/NotificationService.md) API reference for more details on how it works.
- Check out the [SendGrid](../../../add-plugins/sendgrid.mdx) Notification plugin.
- Learn more about [Subscribers](../subscribers/create-subscriber.md) and [Services](../services/create-service.md) in Medusa.

View File

@@ -1,117 +0,0 @@
# Frontend Payment Flow in Checkout
## Introduction
The purpose of this guide is to describe a checkout flow in Medusa. It is assumed that you've completed our [Quickstart](https://docs.medusajs.com/quickstart/quick-start) or [Tutorial](https://docs.medusajs.com/tutorial/set-up-your-development-environment) and are familiar with the technologies we use in our stack. Additionally, having an understanding of the [core API](https://docs.medusajs.com/api/store/auth) would serve as a great foundation for this walkthrough.
:::note
All code snippets in the following guide, use the JS SDK distributed through **npm**. To install it, run:
```bash npm2yarn
npm install @medusajs/medusa-js
```
:::
## Glossary
- **Cart**: The Cart contains all the information needed for customers to complete an Order. In the Cart customers gather the items they wish to purchase, they add shipping and billing details and complete payment information.
- **LineItem**: Line Items represent an expense added to a Cart. Typically this will be a Product Variant and a certain quantity of the same variant. Line Items hold descriptive fields that help communicate its contents and price.
- **PaymentSession**: A Payment Session is a Medusa abstraction that unifies the APIs of multiple payment gateways. Payment Sessions are _initialized_ when the customer begins a checkout and are _authorized_ prior to an Order being placed. Payment Sessions are created based on the available Payment Providers configured in a Cart's region.
- **ShippingOption**: A Shipping Option represents a way in which an Order can be fulfilled. Shipping Options have a price and are associated with a Fulfillment Provider that will handle the shipment later in the Order flow. Once a customer selects a Shipping Option it becomes a Shipping Method.
- **ShippingMethod**: Shipping Methods are unique to each Cart and can thereby hold either overwrites for fields in a Shipping Option (e.g. price) or additional details (e.g. an id representing a parcel pickup location).
## Checkout flow
To create an order from a cart, we go through the following flow.
:::note
At this point, it assumed that the customer has created a cart, added items and is now at the initial step of the checkout flow.
:::
### Initializing the checkout
The first step in the flow is to _initialize_ the configured Payment Sessions for the Cart. If you are using the `medusa-starter-default` starter, this call will result in the `cart.payment_sessions` array being filled with one Payment Session for the manual payment provider.
```javascript
const { cart } = await medusa.carts.createPaymentSessions("cart_id")
```
To give a more real life example, it is assumed that `medusa-payment-stripe` is installed in your project in which case the call will result in a [Stripe PaymentIntent](https://stripe.com/docs/api/payment_intents) being created. The unique provider data for each Payment Session can be found in the Payment Session's `data` field; this data can be used in front end implementations e.g. if using Stripe Elements the `client_secret` can be retrieved through `session.data.client_secret`.
### Adding customer information
After initializing the checkout flow, you would typically have one big step or several smaller steps for gathering user information; email, address, country, and more. To store this data you may update the cart with each of field or all fields at the same time.
```javascript
const { cart } = await medusa.carts.update("cart_id", {
email: "lebron@james.com",
shipping_address: {
first_name: "",
last_name: "",
...
}
})
```
### Selecting payment provider
This step is only applicable if you have multiple Payment Sessions installed in your project. In cases where only one Payment Provider is configured the Payment Session will be preselected. In all other cases your implementations should call:
```javascript
const { cart } = await medusa.carts.setPaymentSession("cart_id", {
provider_id: "stripe"
})
```
### Choosing a shipping method
Before reaching the payment step, you would typically require the customer to choose a Shipping Method from a number of options. In Medusa you can set rules that must be met for a Shipping Option to be available for a Cart. To get the available shipping options for a Cart you should call:
```javascript
const { shipping_options } = await medusa.carts.listCartOptions("cart_id")
```
Choosing a Shipping Option, will create a Shipping Method and attach it to the Cart. The second argument to the function in the snippet below holds the id of the selected option.
```javascript=
const { cart } = await medusa.carts.addShippingMethod("cart_id", { option_id: "option_id"})
```
### Collecting payment details
The following snippet shows how we use Stripe to collect payment details from the customer. Note that we are using the `client_secret` from the Stripe PaymentIntent in `data` on the payment session as this is required by Stripe Elements.
```jsx
import { CardElement, useStripe, useElements } from "@stripe/react-stripe-js";
...
const stripe = useStripe();
const elements = useElements();
const handleSubmit = () => {
...
const { paymentIntent, error } = await stripe.confirmCardPayment(
cart.payment_session.data.client_secret,
{
payment_method: {
card: elements.getElement(CardElement),
},
}
);
...
}
return <CardElement id="card-element" />
```
After collecting the payment details, the customer can complete the checkout flow.
### Completing the cart
When all relevant customer information has been captured, your implementation should proceed to the final step: completing the cart.
```javascript
const { order } = await medusa.carts.complete("cart_id")
```
If all information is collected correctly throughout the checkout flow, the call will place an Order based on the details gathered in the Cart.
## Summary
You now have a solid foundation for creating your own checkout flows using Medusa. Throughout this guide, we've used Stripe as a Payment Provider. Stripe is one of the most popular providers and we have an official plugin that you can easily install in your project.
## What's next?
See the checkout flow, explained in the previous sections, in one of our frontend starters:
- [Nextjs Starter](https://github.com/medusajs/nextjs-starter-medusa)
- [Gatsby Starter](https://github.com/medusajs/gatsby-starter-medusa)

View File

@@ -70,7 +70,7 @@ Following the naming convention of Services, the name of the file should be the
As mentioned in the overview, Payment Providers should have a static `identifier` property.
The `PaymentProvider` model has 2 properties: `identifier` and `is_installed`. The value of the `identifier` property in the class will be used when the Payment Provider is created in the database.
The `PaymentProvider` entity has 2 properties: `identifier` and `is_installed`. The value of the `identifier` property in the class will be used when the Payment Provider is created in the database.
The value of this property will also be used to reference the Payment Provider throughout the Medusa server. For example, the identifier is used when a [Payment Session in a cart is selected on checkout](https://docs.medusajs.com/api/store/cart/select-a-payment-session).
@@ -384,4 +384,4 @@ async retrieveSavedMethods(customer) {
## Whats Next 🚀
- Check out the Payment Providers for [Stripe](https://github.com/medusajs/medusa/tree/2e6622ec5d0ae19d1782e583e099000f0a93b051/packages/medusa-payment-stripe) and [PayPal](https://github.com/medusajs/medusa/tree/2e6622ec5d0ae19d1782e583e099000f0a93b051/packages/medusa-payment-paypal) for implementation examples.
- Learn more about the [frontend checkout flow](./frontend-payment-flow-in-checkout.md).
- Learn more about the [frontend checkout flow](./../../storefront/how-to-implement-checkout-flow.mdx).

View File

@@ -1,4 +1,4 @@
# Architecture Overview
# Payment Architecture Overview
In this document, youll learn about the payment architecture in Medusa, specifically its 3 main components and the idempotency key.
@@ -16,17 +16,15 @@ An important part in the Payment architecture to understand is the **Idempotency
## Payment Provider
### Overview
A Payment Provider in Medusa is a method to handle payments in selected regions. It is not associated with a cart, customer, or order in particular. It provides the necessary implementation to create Payment Sessions and Payments, as well as authorize and capture payments, among other functionalities.
Payment Providers can be integrated with third-party services that handle payment operations such as capturing a payment. An example of a Payment Provider is Stripe.
Payment Providers can also be related to a custom way of handling payment operations. An example of that is cash on delivery (COD) payment methods or Medusas [manual payment provider plugin](https://github.com/medusajs/medusa/tree/master/packages/medusa-payment-manual) which provides a minimal implementation of a payment provider and allows store operators to manually handle order payments.
### How it is Created
### How Payment Provider is Created
A Payment Provider is essentially a Medusa [service](../services/create-service.md) with a unique identifier, and it extends the `PaymentService` provided by the `medusa-interfaces` package. It can be created as part of a [plugin](../../../guides/plugins.md), or it can be created just as a service file in your Medusa server.
A Payment Provider is essentially a Medusa [service](../services/create-service.md) with a unique identifier, and it extends the `PaymentService` provided by the `medusa-interfaces` package. It can be created as part of a [plugin](../plugins/overview.md), or it can be created just as a service file in your Medusa server.
As a developer, you will mainly work with the Payment Provider when integrating a payment method in Medusa.
@@ -40,21 +38,19 @@ Its important to choose a payment provider in the list of payment providers i
:::
### Model Overview
### PaymentProvider Entity Overview
The `PaymentProvider` model only has 2 attributes: `is_installed` to indicate if the payment provider is installed and its value is a boolean; and `id` which is the unique identifier that you define in the Payment Provider service.
The `PaymentProvider` entity only has 2 attributes: `is_installed` to indicate if the payment provider is installed and its value is a boolean; and `id` which is the unique identifier that you define in the Payment Provider service.
## Payment Session
### Overview
Payment Sessions are linked to a customers cart. Each Payment Session is associated with a payment provider that is available in the customer carts region.
They hold the status of the payment flow throughout the checkout process which can be used to indicate different statuses such as an authorized payment or payment that requires more actions from the customer.
After the checkout process is completed and the Payment Session has been authorized successfully, a Payment instance will be created to be associated with the customers order and will be used for further actions related to that order.
### How it is Created
### How Payment Session is Created
After the customer adds products to the cart, proceeds with the checkout flow, and reaches the payment method section, Payment Sessions are created for each Payment Provider available in that region.
@@ -64,15 +60,15 @@ Payment Sessions can hold data that is necessary for the customer to complete th
Among the Payment Sessions available only one will be selected based on the customers payment provider choice. For example, if the customer sees that they can pay with Stripe or PayPal and chooses Stripe, Stripes Payment Session will be the selected Payment Session of that cart.
### Model Overview
### PaymentSession Entity Overview
The `PaymentSession` model belongs to a `Cart`. This is the customers cart that was used for checkout which lead to the creation of the Payment Session.
The `PaymentSession` entity belongs to a `Cart`. This is the customers cart that was used for checkout which lead to the creation of the Payment Session.
The `PaymentSession` also belongs to a `PaymentProvider`. This is the Payment Provider that was used to create the Payment Session and that controls it for further actions like authorizing the payment.
The `data` attribute is an object that holds any data required for the Payment Provider to perform payment operations like authorizing or capturing payment. For example, when a Stripe payment session is initialized, the `data` object will hold the payment intent among other data necessary to authorize the payment.
The `is_selected` attribute in the `PaymentSession` model is a boolean value that indicates whether this Payment Session was selected by the customer to pay for their purchase. Going back to the previous example of having Stripe and PayPal as the available Payment Providers, when the customer chooses Stripe, Stripes Payment Session will have `is_selected` set to true whereas PayPals Payment Session will have `is_selected` set to false.
The `is_selected` attribute in the `PaymentSession` entity is a boolean value that indicates whether this Payment Session was selected by the customer to pay for their purchase. Going back to the previous example of having Stripe and PayPal as the available Payment Providers, when the customer chooses Stripe, Stripes Payment Session will have `is_selected` set to true whereas PayPals Payment Session will have `is_selected` set to false.
The `status` attributes indicates the current status of the Payment Session. It can be one of the following values:
@@ -86,21 +82,19 @@ These statuses are important in the checkout flow to determine the current step
## Payment
### Overview
A Payment is used to represent the amount authorized for a customers purchase. It is associated with the order placed by the customer and will be used after that for all operations related to the orders payment such as capturing or refunding the payment.
Payments are generally created using data from the Payment Session and it holds any data that can be necessary to perform later payment operations.
### How it is Created
### How Payment is Created
Once the customer completes their purchase and the payment has been authorized, a Payment instance will be created from the Payment Session. The Payment is associated first with the cart and then with the order once its created and placed.
When the store operator then chooses to capture the order from the Medusa Admin, the Payment is used by the Payment Provider to capture the payment. This is the same case for refunding the amount, canceling the order, or creating a swap.
### Model Overview
### Payment Entity Overview
The `Payment` model belongs to the `Cart` that it was originally created from when the customers payment was authorized. It also belongs to an `Order` once its placed. Additionally, it belongs to a `PaymentProvider` which is the payment provider that the customer chose on checkout.
The `Payment` entity belongs to the `Cart` that it was originally created from when the customers payment was authorized. It also belongs to an `Order` once its placed. Additionally, it belongs to a `PaymentProvider` which is the payment provider that the customer chose on checkout.
In case a `Swap` is created for an order, `Payment` will be associated with that swap to handle payment operations related to it.
@@ -126,5 +120,5 @@ This prevents any payment issues from occurring with the customers and allows fo
## Whats Next 🚀
- [Check out how the checkout flow is implemented on the frontend.](./frontend-payment-flow-in-checkout.md)
- [Check out how the checkout flow is implemented on the frontend.](./../../storefront/how-to-implement-checkout-flow.mdx)
- Check out payment plugins like [Stripe](../../../add-plugins/stripe.md), [Paypal](/add-plugins/paypal), and [Klarna](../../../add-plugins/klarna.md).

View File

@@ -0,0 +1,371 @@
# Create a Plugin
In this document, youll learn how to create a plugin and publish it. If youre interested to learn more about what plugins are and where to find available official and community plugins, check out the [overview document](overview.md).
## Prerequisites
This guide uses the Medusa CLI throughout different steps. If you dont have the Medusa CLI installed you can install it with the following command:
```bash npm2yarn
npm install @medusajs/medusa-cli -g
```
## Initialize Project
The recommended way to create a plugin is using the Medusa CLI. Run the following command to create a new Medusa project:
```bash
medusa new medusa-plugin-custom
```
Where `medusa-plugin-custom` is the name of the plugin youre creating. In Medusa, plugins are named based on their functionalities.
By convention, all plugin names start with `medusa` followed by a descriptive name of what the plugin does. For example, the Stripe plugin is named `medusa-payment-stripe`.
### Project Structure
The command above creates a new directory `medusa-plugin-custom` that holds essentially the same codebase you would have for a Medusa server. This is because a plugin has the same directory structure as a Medusa server.
Under the `src` directory is where the code of your plugin resides. After running the previous command, you should have at least 3 directories inside the `src` directory:
- `api` is where you can add custom endpoints.
- `services` is where you can add custom services.
- `subscribers` is where you can add custom subscribers.
You can also add more directories and files to your plugin including:
- `src/models` for adding custom entities or extending existing entities.
- `src/migrations` for migrations that make changes to the database schema.
## Change Dependencies in package.json
A basic Medusa server installed with the `medusa new` command has dependencies similar to this:
```json
"dependencies": {
"@medusajs/medusa": "^1.3.1",
"@medusajs/medusa-cli": "^1.3.0",
"medusa-fulfillment-manual": "^1.1.31",
"medusa-interfaces": "^1.3.0",
"medusa-payment-manual": "^1.0.16",
"medusa-payment-stripe": "^1.1.38",
"mongoose": "^5.13.3",
"typeorm": "^0.2.36"
},
"devDependencies": {
"@babel/cli": "^7.14.3",
"@babel/core": "^7.14.3",
"@babel/preset-typescript": "^7.14.5",
"babel-preset-medusa-package": "^1.1.19"
}
```
For a plugin, a lot of these dependencies are not necessary or should be labeled as [peer dependencies](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#peerdependencies). Therefore, its important to make changes to the dependencies of your plugin.
The recommended change is the following:
```json
"peerDependencies": {
"@medusajs/medusa": "^1.3.1",
"medusa-interfaces": "^1.3.0",
"typeorm": "^0.2.36"
},
"devDependencies": {
"@babel/cli": "^7.14.3",
"@babel/core": "^7.14.3",
"@babel/preset-typescript": "^7.14.5",
"babel-preset-medusa-package": "^1.1.19",
}
```
The packages `@medusajs/medusa` and `medusa-interfaces` act as peer dependencies. Theyll be installed while you develop your package, and they are required when your plugin is installed in another NPM project.
You remove the packages `medusa-fulfillment-manual`, `medusa-payment-manual`, and `medusa-payment-stripe` as they are fulfillment and payment plugins necessary for a Medusa server, but not for a plugin.
Additionally, you remove `@medusajs/medusa-cli` as you dont need to use the Medusa CLI while developing a plugin.
Once youre done making these changes, re-run the install command to update your `node_modules` directory:
```bash npm2yarn
npm install
```
## Recommended Changes in package.json
This section includes recommended changes to your `package.json`. You can skip any of these changes if you dont find them necessary to your plugin.
### Change Basic Info
`package.json` holds information that further describes the package or the author that created the package. It is recommended to make the following changes:
- `description`: Change this to a sentence that describes what your plugin does.
- `author`: Your name and email.
- `repository`: The repository that holds your plugins codebase.
- `keywords`: This should hold the keywords that are related to your plugin. Its recommended that all plugins use the keywords `medusa-plugin` or `medusa`.
### Change scripts
A basic Medusa installation comes with the following scripts:
```json
"scripts": {
"seed": "medusa seed -f ./data/seed.json",
"build": "babel src -d dist --extensions \".ts,.js\"",
"start": "medusa develop"
}
```
The `seed` and `start` scripts are not necessary for plugin development so you can remove them.
Its also recommended to add the `watch` script that automatically compiles your files if they are changed:
```json
"watch": "babel -w src --out-dir . --ignore **/__tests__"
```
This is helpful when testing the plugin.
:::note
Testing the plugin is covered in a later section.
:::
Another recommended script is the `prepare` script that builds your files under a “production” environment:
```json
"prepare": "cross-env NODE_ENV=production npm run build"
```
You would typically run this script before publishing your plugin.
This script requires installing the package `cross-env` as a development dependency:
```bash npm2yarn
npm install --save-dev cross-env
```
## Develop your Plugin
Now, You can start developing your plugin. This can include adding services, endpoints, entities, or anything that is relevant to your plugin.
This guide does not cover how to create each of those files or components. If youre interested in learning how to do that, you can check out these guides:
- How to create endpoints for [storefront](../endpoints/add-storefront.md) and [admin](../endpoints/add-admin.md)
- How to [create a service](../services/create-service.md)
- How to [create a subscriber](../subscribers/create-subscriber.md)
- How to create an entity
- How to [create a migration](../migrations.md)
## Add Plugin Configuration
Plugins often allow developers that will later use them to enter their own configuration. For example, you can allow developers to specify the API key of a service youre integrating.
To pass a plugin its configurations on a Medusa server, you have to add it to the `plugins` array in `medusa-config.js`:
```jsx
const plugins = [
//...
{
resolve: `medusa-plugin-custom`,
options: {
name: 'My Store'
}
}
];
```
Then, you can have access to your plugin configuration in the constructor of services in your plugin:
```jsx
//In a service in your plugin
constructor({}, options) {
//options contains plugin configurations
this.name = options.name
}
```
You can also have access to the configurations in endpoints in your plugin:
```jsx
//in an endpoint in your plugin
export default (rootDirectory, options) => {
//options contain the plugin configurations
const router = Router()
router.get("/hello-world", (req, res) => {
res.json({
message: `Welcome to ${options.name ? options.name : 'Medusa'}!`
})
})
return router;
}
```
:::tip
Make sure to include in the README of your plugin the configurations that can be passed to a plugin.
:::
## Test Your Plugin
While you develop your plugin, youll need to test it on an actual Medusa server. This can be done by using the [npm link](https://docs.npmjs.com/cli/v8/commands/npm-link) command.
In the root of your plugin directory, run the following command:
```bash npm2yarn
npm link
```
Then, change to the directory of the Medusa server you want to test the plugin on and run the following command:
```bash npm2yarn
npm link medusa-plugin-custom
```
Where `medusa-plugin-custom` is the package name of your plugin.
After linking to your plugin in a local Medusa server, either run the `build` or `watch` commands in your plugin directory:
```bash npm2yarn
# in the directory of the plugin
npm run watch
```
:::tip
If youre running the `watch` command, you dont need to run the `build` command every time you make a change to your plugin.
:::
Then, add your plugin into the array of plugins in `medusa-config.js`:
```jsx
const plugins = [
//...
{
resolve: `medusa-plugin-custom`,
//if your plugin has configurations
options: {
name: 'My Store'
}
}
];
```
:::note
If your plugin has migrations, you must run them before you start the server. Check out the [Migrations guide](../migrations.md#run-migration) for more details.
:::
Finally, start your server and test your plugins functionalities:
```bash npm2yarn
npm run start
```
## NPM Ignore File
Not all files that you use while developing your plugin are necessary to be published.
For example, the files you add in the `src` directory are compiled to a `dist` directory before publishing. Then, when a developer installs your plugin, theyll just be using the files under the `dist` directory.
So, you can ignore files and directories like `src` from the final published NPM package.
To do that, create the file `.npmignore` with the following content:
```bash
/lib
node_modules
.DS_store
.env*
/*.js
!index.js
yarn.lock
src
.gitignore
.eslintrc
.babelrc
.prettierrc
#These are files that are included in a
#Medusa project and can be removed from a
#plugin project
medusa-config.js
Dockerfile
medusa-db.sql
develop.sh
```
## Publish Plugin
Once youre done developing your plugin you can publish the package on NPMs registry so that other developers can benefit from it and use it.
Before you publish a plugin, you must [create an account on NPM](https://www.npmjs.com/signup).
### Login
In your terminal, log in with your NPM account:
```bash
npm login
```
Youll be asked to enter your NPM email and password.
### Publish Plugin Package
Once youre logged in, you can publish your package with the following command:
```bash
npm publish
```
Your package is then published on NPM and everyone can use it and install it.
### Update Plugin
To update your plugin at a later point, you can run the following command to change the NPM version:
```bash
npm version <type>
```
Where `<type>` indicates the type of version update youre publishing. For example, it can be `major` or `minor`.
You can see the [full list of types in NPMs documentation](https://docs.npmjs.com/cli/v8/commands/npm-version).
Then, publish the new update:
```bash
npm publish
```
## Add Plugin to Medusas Repository
All officially-supported plugins are available in the [`packages` directory of the Medusa GitHub repository](https://github.com/medusajs/medusa/tree/master/packages).
If youre interested in adding your plugin, you need to create a new pull request (PR) where you add your plugin inside the `packages` directory. Our team will then review your plugin, and if its approved the PR will be merged and your plugin will be available on Medusas repository.
:::note
Before contributing to the Medusa repository, please check out the [contribution guidelines](https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md).
:::
## Install a Plugin
To install any published plugin, you can run the following command on any Medusa server project:
```bash npm2yarn
npm install medusa-plugin-custom
```
## Whats Next 🚀
- Check out [available Services in Medusa](references/services/../../../../../references/services/classes/AuthService.md) that you can use in your plugin.
- Check out [available events](../subscribers/events-list.md) that you can listen to in Subscribers.
- Check out [available official plugins](https://github.com/medusajs/medusa/tree/master/packages).

View File

@@ -0,0 +1,54 @@
# Plugins
In this document, youll get an overview of plugins in Medusa, where to find them, and how to install them. If you want to learn how to create a plugin, check out [this guide](create.md) instead.
## Overview
Medusa was built with flexibility and extendibility in mind. All different components and functionalities in Medusa are built with an abstraction layer that gives developers the freedom to choose what services they want to use or how to implement a certain component in their ecommerce store.
Developers can use plugins to take advantage of this abstraction, flexibility, and extendibility. Plugins allow developers to implement custom features or integrate third-party services into Medusa.
For example, if you want to use Stripe as a payment provider in your store, then you can install the Stripe plugin on your server and use it.
An alternative approach is developing a custom way of handling payment on your ecommerce store. Both approaches are achievable by either creating a plugin or using an existing plugin.
Plugins run within the same process as the core Medusa server eliminating the need for extra server capacity, infrastructure, and maintenance. As a result, plugins can use all other services as dependencies and access the database.
## Using Existing Plugins
### Official Plugins
Medusa has official plugins that cover different aspects and functionalities such as payment, CMS, fulfillment, and notifications. You can check out the available plugins under the [packages directory in the Medusa repository on GitHub](https://github.com/medusajs/medusa/tree/master/packages).
:::tip
To feature your plugin in our repository, you can send a pull request that adds your plugin into the `packages` directory. Our team will review your plugin and, if approved, will merge the pull request and add your plugin in the repository.
:::
### Community Plugins
You can find community plugins by [searching NPM for the `medusa` or `medusa-plugin` keywords](https://www.npmjs.com/search?q=keywords%3Amedusa%2Cmedusa-plugin).
You can also check the [Awesome Medusa repository](https://github.com/adrien2p/awesome-medusajs#plugins) for a list of community plugins among other resources.
## How to Install a Plugin
To install an existing plugin, in your Medusa server run the following command:
```bash npm2yarn
npm install <plugin_name>
```
Where `<plugin_name>` is the package name of the plugin. For example, if youre installing the Stripe plugin `<plugin_name>` is `medusa-payment-stripe`.
### Plugin Configuration
If youre installing an official plugin from the Medusa repository, you can find in its `README.md` file a list of configurations that are either required or optional. You can also refer to the documentation related to that plugin for more details on how to install, configure, and use it.
For community plugins, please refer to the installation instructions of that plugin to learn about any required configurations.
## Whats Next 🚀
- Learn how to [create your own plugin](create.md).
- Learn how to [create a fulfillment provider](../shipping/add-fulfillment-provider.md) or a [payment provider](../payment/how-to-create-payment-provider.md).

View File

@@ -1,6 +1,6 @@
# How to Add a Fulfillment Provider
In this document, youll learn how to add a fulfillment provider to a Medusa server. If youre unfamiliar with the Shipping architecture in Medusa, make sure to [check out the overview first](https://docs.medusajs.com/advanced/backend/shipping/overview/).
In this document, youll learn how to add a fulfillment provider to a Medusa server. If youre unfamiliar with the Shipping architecture in Medusa, make sure to [check out the overview first](overview.md).
## Overview
@@ -45,7 +45,7 @@ Following the naming convention of Services, the name of the file should be the
As mentioned in the overview, fulfillment providers should have a static `identifier` property.
The `FulfillmentProvider` model has 2 properties: `identifier` and `is_installed`. The `identifier` property in the class will be used when the fulfillment provider is created in the database.
The `FulfillmentProvider` entity has 2 properties: `identifier` and `is_installed`. The `identifier` property in the class will be used when the fulfillment provider is created in the database.
The value of this property will also be used to reference the fulfillment provider throughout Medusa. For example, it is used to [add a fulfillment provider](https://docs.medusajs.com/api/admin/region/add-fulfillment-provider) to a region.

View File

@@ -1,46 +1,57 @@
---
# Shipping Architecture Overview
title: Shipping Architecture Overview
---
# Architecture Overview
This document gives an overview of the shipping architecture and its 3 most important components.
This document gives an overview of the shipping architecture and its 4 most important components.
## Introduction
In Medusa, the Shipping architecture relies on 3 components: **Shipping Profiles**, **Shipping Options**, and **Shipping Methods**.
In Medusa, the Shipping architecture relies on 4 components: **Fulfillment Provider**, **Shipping Profiles**, **Shipping Options**, and **Shipping Methods**.
The distinction between the 3 is important. It has been carefully planned and put together to support all the different ecommerce use cases and shipping providers that can be integrated.
The distinction between the 4 is important. It has been carefully planned and put together to support all the different ecommerce use cases and shipping providers that can be integrated.
Its also constructed to support multiple regions, provide different shipment configurations and options for different product types, provide promotional shipments for your customers, and much more.
## Summary
- **Shipping Profiles:** created by the admin. They are used to group products that should be shipped in a different manner than the default. Shipping profiles can have multiple shipping options.
- S**hipping Options:** created by the admin and belong to a shipping profile. They are specific to certain regions and can have cart conditions. They use an underlying fulfillment provider. Once a customer checks out, they can choose the shipping option thats available and most relevant to them.
- **Shipping Method:** created when the customer chooses a shipping option on checkout. The shipping method is basically a copy of the shipping option, but with values specific to the customer and the cart its associated with. When the order is placed, the shipping method will then be associated with the order and fulfilled based on the integration with the fulfillment provider.
- **Fulfillment Provider:** Fulfillment providers are plugins or [Services](../services/create-service.md) used to ship the products to your customers, whether physically or virtually. An example of a fulfillment provider would be FedEx.
- **Shipping Profiles:** created by the admin. They are used to group products that should be shipped in a different manner than the default. Shipping profiles can have multiple shipping options.
- **Shipping Options:** created by the admin and belong to a shipping profile. They are specific to certain regions and can have cart conditions. They use an underlying fulfillment provider. Once a customer checks out, they can choose the shipping option thats available and most relevant to them.
- **Shipping Method:** created when the customer chooses a shipping option on checkout. The shipping method is basically a copy of the shipping option, but with values specific to the customer and the cart its associated with. When the order is placed, the shipping method will then be associated with the order and fulfilled based on the integration with the fulfillment provider.
:::note
![Shipping Architecture](https://i.imgur.com/QII2Hvn.png)
Fulfillment providers are used to ship the products to your customers, whether physically or virtually. An example of a fulfillment provider would be FedEx.
## Fulfillment Provider
:::
A Fulfillment Provider in Medusa is a method to handle shipping products in selected regions. It is not associated with a cart, customer, or order in particular.
![Shipping.png](https://i.imgur.com/RnC2esy.png)
It provides the necessary implementation to create Fulfillments for orders and ship items to customers. They can also be used for order returns and swaps.
Fulfillment Providers can be integrated with third-party services that handle the actual shipment of products. An example of a Fulfillment Provider is FedEx.
Fulfillment Providers can also be related to a custom way of handling fulfillment operations. An example of that is Medusas [manual fulfillment provider plugin](https://github.com/medusajs/medusa/tree/master/packages/medusa-fulfillment-manual) which provides a minimal implementation of a fulfillment provider and allows store operators to manually handle order fulfillments.
### How Fulfillment Provider is Created
A Fulfillment Provider is essentially a Medusa [Service](../services/create-service.md) with a unique identifier, and it extends the `FulfillmentService` provided by the `medusa-interfaces` package. It can be created as part of a [plugin](../plugins/overview.md), or it can be created just as a Service file in your Medusa server.
As a developer, you will mainly work with the Fulfillment Provider when integrating a fulfillment method in Medusa.
When you run your Medusa server, the Fulfillment Provider will be registered on your server if it hasnt been already.
Once the Fulfillment Provider is added to the server, the store operator will be able to associate on the [Medusa Admin](../../../quickstart/quick-start.md) the Fulfillment Provider with shipping options.
### FulfillmentProvider Entity Overview
The `FulfillmentProvider` entity only has 2 attributes: `is_installed` to indicate if the fulfillment provider is installed and its value is a boolean; and `id` which is the unique identifier that you define in the Fulfillment Provider Service.
## Shipping Profile
### Overview
Shipping profiles are the highest in the hierarchy in the shipping architecture.
Shipping profiles are created by the admin. The admin can specify the name of the shipping profile which will be a name that the customer can see.
A shipping profile is not associated with any fulfillment providers. It has multiple shipping options that can be associated with different providers.
### Purpose
### Purpose of Shipping Profile
Shipping profiles are used to group products that can be shipped in the same manner.
@@ -50,18 +61,16 @@ Although this might be the general case, there are still some use cases where yo
For example, shipping heavy items might be more expensive than others, which would enforce different price rates. In that case, you can create a new shipping profile that groups together heavy products. This would allow you to give these products more suitable price rates when creating their shipping options.
### Model Overview
### ShippingProfile Entity Overview
The `ShippingProfile` model can have a set of `Product` instances. These would be the products the shipping profile is providing shipping options for.
The `ShippingProfile` entity can have a set of `Product` instances. These would be the products the shipping profile is providing shipping options for.
The `ShippingProfile` has a `type` attribute that can be `default`, `gift_card`, or `custom`.
The `ShippingProfile` model also has an array of `ShippingOption` instances.
The `ShippingProfile` entity also has an array of `ShippingOption` instances.
## Shipping Option
### Overview
After the admin adds a shipping profile, they can add shipping options that belong to that shipping profile from the admin dashboard.
Shipping options have a set of conditions like the region theyre available in or cart-specific conditions. For example, if your company operates in the United States as well as Germany, you might use a different shipping option for each of the two countries.
@@ -70,7 +79,7 @@ Among the configurations that the admin has to set when creating a shipping opti
Shipping options are only shown to a customer during checkout if their cart satisfies the options conditions. Also, as they belong to a shipping profile, theyre only shown when products that belong to the same shipping profile are in the cart.
### Purpose
### Purpose of Shipping Option
The first purpose that a shipping option has is showing the customer during checkout what shipping options are available for them.
@@ -78,17 +87,17 @@ Then, once the customer chooses a shipping option, that shipping option is used
Think of a shipping option as a template defined by the admin that indicates what data and values the shipping method should have when its chosen by the customer during checkout.
### Model Overview
### ShippingOption Entity Overview
The `ShippingOption` model belongs to the `ShippingProfile` model.
The `ShippingOption` entity belongs to the `ShippingProfile` entity.
The `ShippingOption` model also belongs to a `FulfillmentProvider`. This can be either a custom third-party provider or one of Medusas default fulfillment providers.
The `ShippingOption` entity also belongs to a `FulfillmentProvider`. This can be either a custom third-party provider or one of Medusas default fulfillment providers.
It has the `price_type` attribute to indicate whether the shipping options rate is `calculated` by the provider or a fixed `flat_rate` price. It also has the `amount` attribute to set an amount for the shipping option if the `price_type` is `flat_rate`.
`ShippingOption` also belongs to a `Region`, which resembles one or more countries. This defines where the shipping option is available.
`ShippingOption` has a set of `ShippingOptionRequirement` instances. The `ShippingOptionRequirement` model allows defining cart rules which determine whether the shipping option will be available or not for a customer during checkout. For example, you can set a minimum subtotal amount for a shipping option to be available for a customers cart.
`ShippingOption` has a set of `ShippingOptionRequirement` instances. The `ShippingOptionRequirement` entity allows defining cart rules which determine whether the shipping option will be available or not for a customer during checkout. For example, you can set a minimum subtotal amount for a shipping option to be available for a customers cart.
The `is_return` attribute is used to indicate whether the shipping option is used for shipping orders or returning orders. Shipping options can only be used for one or the other.
@@ -98,15 +107,13 @@ The `data` attribute does not have any specific format. Its up to you to choo
## Shipping Method
### Overview
Unlike the previous two components, a shipping method is not created by the admin. Its created when a `POST` request is sent to `/store/carts/:id/shipping-methods` after the customer chooses a shipping option.
The shipping method will be created based on the chosen shipping option and itll be associated with the customers cart. Then, when the order is placed, the shipping method is associated with the order.
A shipping method can be fulfilled automatically or manually through the admin dashboard. This is based on the fulfillment provider associated with the shipping option the shipping method is based on.
### Purpose
### Shipping Method Purpose
Its important to understand the distinction between shipping methods and shipping options. Shipping options are templates created by the admin to indicate what shipping options should be shown to a customer. This provides customization capabilities in a store, as an admin is free to specify configurations for that option such as what fulfillment provider it uses or what are its rates.
@@ -114,17 +121,17 @@ When handling the order and fulfilling it, you, as a developer, will be mostly i
This separation allows for developers to implement the custom integration with third-party fulfillment providers as necessary while also ensuring that the admin has full control of their store.
## Model Overview
## ShippingMethod Entity Overview
A lot of the shipping methods attributes are similar to the shipping options attribute.
The `ShippingMethod` model belongs to a `ShippingOption`.
The `ShippingMethod` entity belongs to a `ShippingOption`.
Similar to the `data` attribute explained for the `ShippingOption` model, a `ShippingMethod` has a similar `data` attribute that includes all the data to be sent to the fulfillment provider when fulfilling the order.
Similar to the `data` attribute explained for the `ShippingOption` entity, a `ShippingMethod` has a similar `data` attribute that includes all the data to be sent to the fulfillment provider when fulfilling the order.
The `ShippingMethod` belongs to a `Cart`. This is the cart the customer is checking out with.
The `ShippingMethod` also belongs to the `Order` model. This association is accomplished when the order is placed.
The `ShippingMethod` also belongs to the `Order` entity. This association is accomplished when the order is placed.
The `ShippingMethod` instance holds a `price` attribute, which will either be the flat rate price or the calculated price.

View File

@@ -167,7 +167,7 @@ Triggered when a cart and data associated with it (payment sessions, shipping me
</td>
<td>
The entire cart as an object. You can refer to the [Cart model](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/models/cart.ts) for an idea of what fields to expect.
The entire cart as an object. You can refer to the [Cart entity](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/models/cart.ts) for an idea of what fields to expect.
</td>
</tr>
@@ -476,7 +476,7 @@ Triggered when a customer is created.
</td>
<td>
The entire customer passed as an object. You can refer to the [Customer model](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/models/customer.ts) for an idea of what fields to expect.
The entire customer passed as an object. You can refer to the [Customer entity](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/models/customer.ts) for an idea of what fields to expect.
</td>
</tr>
@@ -494,7 +494,7 @@ Triggered when a customer is updated including their information or password, or
</td>
<td>
The entire customer passed as an object. You can refer to the [Customer model](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/models/customer.ts) for an idea of what fields to expect.
The entire customer passed as an object. You can refer to the [Customer entity](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/models/customer.ts) for an idea of what fields to expect.
</td>
</tr>
@@ -1338,7 +1338,7 @@ Triggered when a product and data associated with it (options, variant orders, e
</td>
<td>
The entire product passed as an object. You can refer to the [Product model](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/models/product.ts) for an idea of what fields to expect.
The entire product passed as an object. You can refer to the [Product entity](https://github.com/medusajs/medusa/blob/master/packages/medusa/src/models/product.ts) for an idea of what fields to expect.
</td>
</tr>
@@ -1862,4 +1862,4 @@ Object of the following format:
## Whats Next 🚀
- Learn how you can [use services in subscribers](create-subscriber.md#using-services-in-subscribers).
- Learn how to [create notifications](../../../how-to/notification-api.md) in Medusa.
- Learn how to [create notifications](../notification/overview.md) in Medusa.

View File

@@ -58,7 +58,7 @@ const dotenv = require('dotenv')
This new version of Medusa allows store operators to adjust line items in an order or a swap which provides more customization capabilities.
It introduces a new model `LineItemAdjustment` which gives more flexibility to adjust the pricing of line items in a cart, order, or swap. A discount can be added, removed, or modified and the price will reflect on the total calculation of the cart, order, or swap.
It introduces a new entity `LineItemAdjustment` which gives more flexibility to adjust the pricing of line items in a cart, order, or swap. A discount can be added, removed, or modified and the price will reflect on the total calculation of the cart, order, or swap.
This also introduces an optimization to the calculation of totals, as it is no longer necessary to calculate the discounts every time the totals are retrieved.
@@ -76,7 +76,7 @@ node ./node_modules/@medusajs/medusa/dist/scripts/line-item-adjustment-migration
This new version of Medusa holds advanced promotions functionalities to provide store operators with even more customization capabilities when creating discounts. You can now add even more conditions to your discounts to make them specific for a set of products, collections, customer groups, and more.
This change required creating a new model `DiscountCondition` which belongs to `DiscountRule` and includes a few relationships with other models to make the aforementioned feature possible.
This change required creating a new entity `DiscountCondition` which belongs to `DiscountRule` and includes a few relationships with other entities to make the aforementioned feature possible.
### Actions Required

View File

@@ -0,0 +1,8 @@
import DocCardList from '@theme/DocCardList';
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
# Upgrade Guides
Find in this page the upgrade guides that require necessary steps when upgrading to a new version.
<DocCardList items={useCurrentSidebarCategory().items}/>

View File

@@ -0,0 +1,329 @@
# How to Implement Checkout Flow
This document will guide you through the steps needed to implement the checkout flow in a Medusa storefront, including steps related to adding a custom payment provider.
## Overview
A checkout flow is composed of the necessary steps to allow a customer to perform a successful checkout. Its generally made up of 2 primary steps: the shipping and payment steps.
This document will take you through the general process of a checkout flow. You should follow along with this document if youre creating a custom storefront, if youre adding a custom payment provider, or if youre just interested in learning more about how checkout works in Medusa.
:::note
Its recommended to go through the [Shipping Architecture Overview](../backend/shipping/overview.md) and [Payment Architecture Overview](../backend/payment/overview.md) first to have a better understanding of Medusas architecture.
:::
## Prerequisites
This document assumes youve already taken care of the add-to-cart flow. So, you should have a [cart created](https://docs.medusajs.com/api/store/cart/create-a-cart) for the customer with at least [one product in it](https://docs.medusajs.com/api/store/cart/add-a-line-item).
To follow along with this tutorial, you can make use of the [Medusa JS Client](https://www.npmjs.com/package/@medusajs/medusa-js). You can install it with this command:
```bash npm2yarn
npm install @medusajs/medusa-js
```
Theres also an alternative approach in this document using [JavaScripts Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) in case youre unable to use Medusas JS Client. Make sure to replace `<SERVER_URL>` in those examples with your server URL.
## Shipping Step
In this step, the customer generally enters their shipping info, then chooses the available shipping option based on the entered info.
### Add Shipping Address
After the customer enters their shipping address information, you must send a `POST` request to the [Update a Cart](https://docs.medusajs.com/api/store/cart/update-a-cart) API endpoint passing it the new shipping address:
<Tabs groupId="request-tyoe">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.carts.update(cart.id, {
shipping_address: {
company,
first_name,
last_name,
address_1,
address_2,
city,
country_code,
province,
postal_code,
phone
},
}).then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<SERVER_URL>/store/carts/${cart.id}`, {
method: 'POST',
body: JSON.stringify({
shipping_address: {
company,
first_name,
last_name,
address_1,
address_2,
city,
country_code,
province,
postal_code,
phone
},
}),
headers: {
'Content-Type': 'application/json'
}
}).then((response) => response.json())
.then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
</Tabs>
You can have access to the updated cart in `response.cart`, which now has the shipping address you added in `response.cart.shipping_address`.
### List Shipping Options
After updating the cart with the customers address, the list of available [shipping options](../backend/shipping/overview.md#shipping-option) for that cart might change. So, you should retrieve the updated list of options by sending a `GET` request to the [Retrieve Shipping Options for Cart API](https://docs.medusajs.com/api/store/shipping-option/retrieve-shipping-options-for-cart) endpoint:
<Tabs groupId="request-tyoe">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.shippingOptions.listCartOptions(cart.id)
.then((response) => {
//shipping options available in response.shipping_options
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<SERVER_URL>/store/shipping-options/${cart.id}`)
.then((response) => response.json())
.then((response) => {
//shipping options available in response.shipping_options
})
```
</TabItem>
</Tabs>
You can access all shipping options available with their info in `response.shipping_options` which is an array of [shipping options](https://docs.medusajs.com/api/store/shipping-option). Typically you would display those options to the customer to choose from.
### Choose Shipping Option
Once the customer chooses one of the available shipping options, send a `POST` request to the [Add a Shipping Method](https://docs.medusajs.com/api/store/cart/add-a-shipping-method) API endpoint. This will create a [shipping method](../backend/shipping/overview.md#shipping-method) based on the shipping option chosen and will associate it with the customers cart:
<Tabs groupId="request-tyoe">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.carts.addShippingMethod(cart.id, {
option_id: shipping_option.id //shipping_option is the select option
}).then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<SERVER_URL>/store/carts/${cart.id}/shipping-methods`, {
method: 'POST',
body: JSON.stringify({
option_id: shipping_option.id //shipping_option is the select option
}),
headers: {
'Content-Type': 'application/json'
}
}).then((response) => response.json())
.then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
</Tabs>
You can have access to the updated cart in `response.cart`, which now has one item in the array value of the property `shipping_methods`.
## Payment Step
In this step, the customer generally chooses a payment method to complete their purchase. The implementation of payment providers is done differently for each provider, so this section will just show the general steps you should follow when implementing this step.
### Initialize Payment Sessions
When the page opens and before the payment providers are displayed to the customer to choose from, you must initialize the [payment sessions](./../backend/payment/overview.md#payment-session) for the current cart. Each payment provider will have a payment session associated with it. These payment sessions will be used later when the customer chooses the payment provider they want to complete their purchase with.
To initialize the payment sessions, send a `POST` request to the [Initialize Payment Sessions](https://docs.medusajs.com/api/store/cart/initialize-payment-sessions) API endpoint:
<Tabs groupId="request-tyoe">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.carts.createPaymentSessions(cart.id)
.then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<SERVER_URL>/store/carts/${cart.id}/payment-sessions`, {
method: 'POST'
}).then((response) => response.json())
.then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
</Tabs>
You can then access the initialized payment sessions under the `payment_sessions` array in `response.cart`.
### Select Payment Session
When the customer chooses the payment provider they want to complete purchase with, you should select the payment session associated with that payment provider. To do that, send a `POST` request to the [Select a Payment Session](https://docs.medusajs.com/api/store/cart/select-a-payment-session) API endpoint:
<Tabs groupId="request-tyoe">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.carts.setPaymentSession(cart.id, {
provider_id: payment_session.provider_id //payment_session is the session chosen by the customer
}).then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<SERVER_URL>/store/carts/${cart.id}/payment-session`, {
method: 'POST',
body: JSON.stringify({
provider_id: payment_session.provider_id //payment_session is the session chosen by the customer
}),
headers: {
'Content-Type': 'application/json'
}
}).then((response) => response.json())
.then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
</Tabs>
You can then access the selected payment session in `response.cart.payment_session`.
:::tip
If you have one payment provider or if only one payment provider is available for the current cart, its payment session will be automatically selected in the “[Initialize Payment Session](#initialize-payment-sessions)” step and this step becomes unnecessary. You can check whether there is a payment session selected or not by checking whether `cart.payment_session` is `null` or not.
:::
### Update Payment Session
This step is optional and is only necessary for some payment providers. As mentioned in the [Payment Architecture](../backend/payment/overview.md#overview) documentation, the `PaymentSession` model has a `data` attribute that holds any data required for the Payment Provider to perform payment operations such as capturing payment.
If you need to update that data at any point before the purchase is made, send a request to [Update a Payment Session](https://docs.medusajs.com/api/store/cart/update-a-payment-session) API endpoint passing it the updated data object:
<Tabs groupId="request-tyoe">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.carts.updatePaymentSession(cart.id, cart.payment_session.provider_id, {
data: {
//pass any data you want to add in the `data` attribute
//for example:
"test": true
}
}).then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<SERVER_URL>/store/carts/${cart.id}/payment-sessions/${cart.payment_session.provider_id}`, {
method: 'POST',
body: JSON.stringify({
data: {
//pass any data you want to add in the `data` attribute
//for example:
"test": true
}
}),
headers: {
'Content-Type': 'application/json'
}
}).then((response) => response.json())
.then((response) => {
//updated cart is in response.cart
})
```
</TabItem>
</Tabs>
You can have access to the updated data in the payment session in `response.cart.payment_session.data`.
### Complete Cart
The last step is to place the order by completing the cart. When you complete the cart, your Medusa server will try to authorize the payment first, then place the order if the authorization is successful. So, you should perform any necessary action with your payment provider first to make sure the authorization is successful when you send the request to complete the cart.
To complete a cart, send a `POST` request to the [Complete a Cart](https://docs.medusajs.com/api/store/cart/complete-a-cart) API endpoint:
<Tabs groupId="request-tyoe">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.carts.complete(cart.id)
.then((response) => {
//order details is in response.data
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<SERVER_URL>/store/carts/${cart.id}/complete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}).then((response) => response.json())
.then((response) => {
//order details is in response.data
})
```
</TabItem>
</Tabs>
If the order is placed successfully, you can access the order data in `response.data` and the value for `response.type` is `order`. Otherwise, `response.data` holds the cart details and `response.type` is `cart`.
## Whats Next 🚀
- Check out available plugins for popular payment providers such as [Stripe](../../add-plugins/stripe.md) and [PayPal](/add-plugins/paypal.md).
- Learn more about the [Payment](../backend/payment/overview.md) and [Shipping](../backend/shipping/overview.md) Architectures.

View File

@@ -0,0 +1,118 @@
# Contribution Guidelines
Thank you for your interest in contributing to the documentation! You will be helping the open source community and other developers interested in learning more about Medusa and using it.
:::tip
This guide is specific to contributing to the documentation. If youre interested in contributing to Medusas codebase, check out the [contributing guidelines in the Medusa GitHub repository](https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md).
:::
## Site Setup
The documentation website is built with [Docusaurus](https://docusaurus.io/), a framework that optimizes documentation creation. If youre not familiar with Docusaurus, its recommended to check out the [Installation documentation](https://docusaurus.io/docs/installation) on their website to better understand Docusaurus, how it works, its structure, and more details.
The documentation codebase is hosted as part of the [medusa repository](https://github.com/medusajs/medusa) on GitHub. Youll find the code that runs the docusaurus website under the [www/docs](https://github.com/medusajs/medusa/tree/master/www/docs) directory.
## Documentation Content
The documentation content is written in Markdown format and is located in the [docs/content](https://github.com/medusajs/medusa/tree/master/docs/content) directory of the same repository. If youre not familiar with Markdown, check out [this cheat sheet](https://www.markdownguide.org/cheat-sheet/) for a quick start.
Youll also find MDX files. MDX files combine the power of Markdown with React. So, the content of the file can contain JSX components and import statements, among other features. You can learn more about [MDX in docusauruss guide.](https://docusaurus.io/docs/markdown-features/react)
## What You Can Contribute To
- You can contribute to the Docusaurus codebase to add a new feature or fix a bug in the documentation website.
- You can contribute to the documentation content either by fixing errors you find or adding documentation pages.
## What You Cant Contribute To
The [Services Reference](/references/services/classes/AuthService) is an automatically generated API reference using Typedoc. So, you cant contribute to it by making changes to its markdown files.
You can, however, contribute to the script generating it if you find any issues in it.
## Style Guide
When you contribute to the documentation content, make sure to follow the [documentation style guide](https://www.notion.so/Style-Guide-Docs-fad86dd1c5f84b48b145e959f36628e0).
## How to Contribute
If youre fixing errors in an existing documentation page, you can scroll down to the end of the page and click on the “Edit this page” link. Youll be redirected to the GitHub edit form of that page and you can make edits directly and submit a pull request (PR).
If youre adding a new page or contributing to the codebase, fork the repository, create a new branch, and make all changes necessary in your repository. Then, once youre done creating a PR in the Medusa repository.
For more details on how to contribute, check out [the contribution guidelines on our repository](https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md).
### Branch Name
When you make edit to an existing documentation page or fork the repository to make changes to the documentation, you have to create a new branch.
Make sure that the branch name starts with `docs/`. For example, `docs/fix-services`.
### Pull Request Conventions
When you create a pull request, prefix the title with “docs:”. Make sure to keep “docs” in small letters.
In the body of the PR, explain clearly what the PR does. If the PR solves an issue, use [closing keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) with the issue number. For example, “Closes #1333”.
## Sidebar
When you add a new page to the documentation, you must add the new page in `www/docs/sidebars.js` under the `tutorialSidebar`. You can learn more about the syntax used [here](https://docusaurus.io/docs/sidebar/items).
### Terminology
When the documentation page is a conceptual or overview documentation, the label in the sidebar should start with a noun.
When the documentation page is a tutorial documentation, the label in the sidebar should start with a verb. An exception of this rule are integration documentations and upgrade guides.
## Notes and Additional Information
When displaying notes and additional information in a documentation page, use [Admonitions](https://docusaurus.io/docs/markdown-features/admonitions). Make sure the type of admonition used matches the notes importance to the current document.
If the note is something developers have to be careful of doing or not doing, use the `caution` or `danger` admonitions based on how critical it is.
If the note is defining something to the developer in case theyre not familiar with it, use the `info` admonition.
If the note displays helpful information and tips use the `tip` admonition.
If the admonition does not match any of the mentioned criteria, always default to the `note` admonition.
## Images
If you are adding images to a documentation page, you can host the image on [Imgur](https://imgur.com) for free.
## NPM and Yarn Code Blocks
If youre adding code blocks that use NPM and Yarn, you must use the [npm2yarn syntax](https://docusaurus.io/docs/markdown-features/code-blocks#npm2yarn-remark-plugin).
For example:
~~~md
```bash npm2yarn
npm run start
```
~~~
The code snippet must be written using NPM, and the `npm2yarn` plugin will automatically transform it to Yarn.
### Expand Commands
Don't use commands in their abbrivated terms. For example, instead of `npm i` use `npm install`.
### Run Command
Make sure to always use the `run` command when the command runs a script.
For example, even though you can run the `start` script using NPM with `npm start`, however, to make sure its transformed properly to a Yarn command, you must add the `run` keyword before `start`.
### Global Option
When a command uses the global option `-g`, add it at the end of the NPM command to ensure that its transformed to a Yarn command properly. For example:
```bash
npm install @medusajs/medusa-cli -g
```
## Need Additional Help?
If you need any additional help while contributing, you can join our [Discord server](https://discord.gg/medusajs) and ask Medusas core team as well as the community any questions.

View File

@@ -4,7 +4,7 @@ title: Create a file plugin
# File Plugin
This guide will give an introduction about the File Service and steps required to create a custom file uploader plugin for Medusa. It build on article about [creating custom plugins](https://docs.medusajs.com/guides/plugins).
This guide will give an introduction about the File Service and steps required to create a custom file uploader plugin for Medusa. It build on article about [creating custom plugins](../advanced/backend/payment/overview.md).
As an example, we will create a File plugin that uploads the product images to Cloudinary.

View File

@@ -6,7 +6,7 @@ title: Fulfillment API
This guide will give an overview of Medusa's Fulfillment API and how it can be implemented to work with different fulfillment providers. Before digging deeper into the API it should be clarified what is meant by a fulfillment provider; in Medusa a fulfillment provider is typically a 3rd party service that can handle order data for the purpose of shipping the items in the order to a customer. Examples of fulfillment providers are: a carrier like UPS, a logistics platform like ShipBob or a 3PL solution.
Implementations of the Fulfillment API can be distributed as npm packages for easy installation through the plugin system, there are already a couple of examples of fulfillment plugins in the Medusa monorepo, you can identify these by looking for `medusa-fulfillment-*`. For further details on building and publishing plugins [please check this guide](https://docs.medusajs.com/guides/plugins).
Implementations of the Fulfillment API can be distributed as npm packages for easy installation through the plugin system, there are already a couple of examples of fulfillment plugins in the Medusa monorepo, you can identify these by looking for `medusa-fulfillment-*`. For further details on building and publishing plugins [please check this guide](../advanced/backend/payment/overview.md).
## Fulfillment Service Interface
@@ -68,4 +68,4 @@ If the shipping option is configured to dynamically calculate the price of the t
## What's next?
Now that you have an overview of the Fulfillment API you can start developing your own fulfillment plugin. For a guide on how to create plugins [check this guide](https://docs.medusajs.com/how-to/plugins). If you have questions or issues please feel free to [join our Discord server](https://discord.gg/medusajs) for direct access to the engineering team.
Now that you have an overview of the Fulfillment API you can start developing your own fulfillment plugin. For a guide on how to create plugins [check this guide](../advanced/backend/payment/overview.md). If you have questions or issues please feel free to [join our Discord server](https://discord.gg/medusajs) for direct access to the engineering team.

View File

@@ -1,150 +0,0 @@
---
title: Plugins
---
# Plugins
The purpose of this guide is to give an introduction to the structure of a plugin and the steps required to create one. It builds upon our article describing the process of [adding custom functionality](https://docs.medusajs.com/tutorial/adding-custom-functionality). It can be seen as the proceeding steps for extracting your custom functionality to a reusable package for other developers to use.
## What is a plugin?
Plugins offer a way to extend and integrate the core functionality of Medusa.
In most commerce solutions, you can extend the basic features but it often comes with the expense of having to build standalone web applications. Our architecture is built such that plugins run within the same process as the core eliminating the need for extra server capacity, infrastructure and maintenance. As a result, the plugins can use all other services as dependencies and access the database.
:::note
You will notice that plugins vary in naming. The name should signal what functionality they provide.
:::
In the following sections, we will go through the basics of implementing a generic plugin. And finally, how to use it as part of your commerce setup.
## Building a plugin
A plugin is essentially a Node.js project of their own. They contain a file in root, `package.json`, that holds all metadata and dependencies of the project.
The first step in creating a plugin is to initialize the Node.js project:
```bash npm2yarn
npm init
```
This command will ask you to fill out your project's metadata, which will eventually be used when publishing the package to NPM. After this command completes, you are ready to start implementing the functionality.
### Implementation
We've already gone through the process of building custom services, endpoints, and subscribers in another tutorial, so this will not be repeated. The process is the same for the logic within a plugin, meaning that the functionality is loaded as part of the core if the correct naming convention is followed.
To quickly get started with the implementation, we advise you to copy `/services/welcome.js`, `/api/index.js`, `/subscribers/welcome.js` and the config files from the tutorial and add them in `/src`. As a result, you should have the following folder structure:
:::note
Since the container resolution paths are automatically generated from the used directories and filenames you should avoid pre- or suffixing your file (e.g. `services/welcomeService.js` would result in the service being registered as `WelcomeServiceService`).
:::
```js
.
├── src
│ ├── api
│ └── index.js
│ └── services
│ └── welcome.js
│ └── subscribers
│ └── welcome.js
├── .babelrc
├── .gitignore
├── medusa-config.js
├── README.md
└── package.json
```
Please note that you will need some build step before being able to properly load your plugin, since Medusa expects to find the directories (`api`, `services`, `subscribers`, `loaders`…) within the npm package root. In the simplest case, this could be you manually copying the folders from `src`.
It is worth mentioning the difference between building a generic and a non-generic plugin. A non-generic plugin has a specific purpose such as processing payments or creating fulfillments. Medusa core depends on a specific implementation from such plugins, which is why we've created interfaces that enforce this. These can be found in `medusa-interfaces`.
:::note
Non-generic plugins are required to extend the correct interface, otherwise they will not be loaded correctly as part of your Medusa setup.
:::
For a more comprehensive walkthrough of the implementation of such plugins, see our guides:
- How to build a fulfillment provider (Coming soon!)
- How to build a payment provider (Coming soon!)
### Publishing
In order for your plugin to become a part of the Medusa plugin ecosystem, you need to publish it to NPM. Make sure that you've included the `package.json` file. NPM uses the details of this file to configure the publishing. Please include `medusa` and `medusa-plugin` and possibly more in the `keywords` field of the `package.json`.
```bash
{
"name": "medusa-payment-stripe",
...
"keywords": [
"medusa",
"medusa-payment",
"medusa-plugin"
],
"description": "Stripe Payment provider for Medusa Commerce",
...
}
```
Finally, you should add a README for the plugin, such that the community understands the purpose of the plugin and how to install it.
## Installation and configuration
Official Medusa plugins can be found within the [mono repo](https://github.com/medusajs/medusa/tree/master/packages) and community plugins can be found by searching NPM for keywords such as `medusa` or `medusa-plugin`.
Note: For plugins to become a part of the mono repo, we require you to submit a PR request. If approved, we will publish it under the Medusa organisation on Github.
Plugins are distributed as NPM packages making it possible for developers to simply install and use a plugin via:
```bash npm2yarn
npm install
```
After installing a plugin using your preferred package manager, it should be added to `medusa-config.js`. We allow you to provide options for plugins. These options can be used for anything ranging from provider requirements such as API keys or custom configuration used in the plugin's logic. These options are injected into the services, subscribers, and APIs of the plugin.
The following steps will install the official Contentful plugin for your Medusa engine:
### Step 1: Installation
First, we add the plugin as a dependency to your project:
```bash npm2yarn
npm install medusa-plugin-contentful
```
### Step 2: Configuration
In the README of the plugin, you will see the options for the plugin. Some are required and some are optional.
In your `medusa-config.js`, add the plugin and the required options:
```js
const plugins = [
...
{
resolve: `medusa-plugin-contentful`,
options: {
space_id: "some_space_id",
access_token: "some_access_token",
environment: "some_environment",
},
},
...
]
```
### Step 3: Usage
Depending on the purpose of the plugin, you will now be able to use the extended functionality as part of your commerce setup.
In this case, you will need to add a content type in Contentful with the fields described in the README. Products created in Medusa Admin will now be synced to Contentful, such that you can enrich them with more details enabling you to enhance the customer experience of your webshop.
## Summary
As a result of following this guide, you should now be able to both implement and install plugins for you Medusa project.

81
docs/content/homepage.md Normal file
View File

@@ -0,0 +1,81 @@
---
id: homepage
title: Overview
description: 'What is Medusa?'
slug: /
hide_table_of_contents: true
---
Welcome to Medusa, the open source Shopify alternative!
Medusa is an open-source headless commerce engine that enables developers to create amazing digital commerce experiences.
:::tip
Get started with Medusa in a few minutes with our [Quickstart guide](./quickstart/quick-start.md)!
:::
## Features
Medusa comes with a set of building blocks that allow you to create unique digital commerce experiences, below is a list of some of the features that Medusa comes with out of the box:
- **Headless**: Medusa is a highly customizable commerce API which means that you may use any presentation layer such as a website, app, chatbots, etc.
- **Regions** allow you to specify currencies, payment providers, shipping providers, tax rates, and more for one or more countries for truly international sales.
- **Orders** come with all the functionality necessary to perform powerful customer service operations with ease.
- **Carts** allow customers to collect products for purchase, add shipping details, and complete payments.
- **Products** come with relevant fields for customs, stock keeping, and sales. Medusa supports multiple options and unlimited variants.
- **Swaps** allow customers to exchange products after purchase (e.g. for incorrect sizes). Accounting, payment, and fulfillment plugins handle all the tedious work for you for automated customer service.
- **Claims** can be created if customers experience problems with one of their products. Plugins make sure to automate sending out replacements, handling refunds, and collecting valuable data for analysis.
- **Returns** allow customers to send back products and can be configured to function in 100% automated flow-through accounting and payment plugins.
- **Fulfillment API** makes it easy to integrate with any fulfillment provider by creating fulfillment plugins.
- **Payments API** makes it easy to integrate with any payment provider by creating payment plugins, we already support Stripe, Paypal, and Klarna.
- **Notification API** allows integrations with email providers, chatbots, Slack channels, etc.
- **Customer Login** gives customers a way of managing their data, viewing their orders, and saving payment details.
- **Shipping Options & Profiles** enable powerful rules for free shipping limits, multiple fulfillment methods, and more.
- **Medusa's Plugin Architecture** makes it intuitive and easy to manage your integrations, switch providers and grow with ease.
- **Customization** is supported for those special use cases that all the other e-commerce platforms can't accommodate.
## Where to Get Started
### The Medusa Server
You can follow our [quickstart guide](quickstart/quick-start.md) to install and run a Medusa server.
It's also recommended to learn how to [set up your development environment](tutorial/set-up-your-development-environment) with the requirements tools and services to run a Medusa server, then [configure your Medusa server](usage/configurations.md).
### The Medusa Admin
The Medusa Admin provides you with a lot of functionalities and configurations such as Product Management, Order Management, Discounts and Promotions, and more.
You can install the Medusa admin in 2 steps by following our [Medusa Admin quickstart guide](admin/quickstart.md).
### The Storefront
The final step is to set up a storefront to sell your products.
Medusa provides 2 starter storefronts, one built with [Next.js](./starters/nextjs-medusa-starter.md) and one with [Gatsby](./starters/gatsby-medusa-starter.md), that you can use to quickly set up your store and start selling.
Alternatively, you can build your own storefront with any frontend framework of your choice just by connecting to your server with the [Storefront REST APIs](https://docs.medusajs.com/api/store/collection).
## Whats Next 🚀
- Customize your Medusa server by creating your own [endpoints](./advanced/backend/endpoints/add-storefront.md), [services](./advanced/backend/services/create-service.md), and [subscribers](./advanced/backend/subscribers/create-subscriber.md).
- Check out guides under the Integrations section to install plugins for [CMS](./add-plugins/strapi.md), [Payment](./add-plugins/stripe.md), [Search Engines](./add-plugins/algolia.md), and more.
- Deploy your Medusa server in seconds on [Heroku](./how-to/deploying-on-heroku.md), [Qovery](./how-to/deploying-on-qovery.md), or [Digital Ocean](./how-to/deploying-on-digital-ocean.md).
## Open Source Contribution
As Medusa is an open source platform, contributions to improve it and its documentation are welcome. In the GitHub repository heres where youll find the different components you can contribute to:
- The core of the Medusa server resides in [`packages/medusa`](https://github.com/medusajs/medusa/tree/master/packages/medusa).
- You can also find all existing plugins under [the `packages` directory](https://github.com/medusajs/medusa/tree/master/packages).
- The documentation content resides in [`docs/content`](https://github.com/medusajs/medusa/tree/master/docs/content). The code for the documentation website is in [`www/docs`](https://github.com/medusajs/medusa/tree/master/www/docs).
You can find more details about contributing in [CONTRIBUTING.md](https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md).
## Community & Support
If you need any support during your development with Medusa, you can join our [Discord Server](https://discord.gg/medusajs). You will get help directly from our core team as well as our community.
By joining our Discord Server, youll also have the chance to participate in many events such as Bug Hunts and showcase your work with Medusa.

View File

@@ -1,97 +0,0 @@
---
id: homepage
title: Overview
description: 'What is Medusa?'
slug: /
hide_table_of_contents: true
---
import useBaseUrl from '@docusaurus/useBaseUrl'
import Link from '@docusaurus/Link'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
Medusa is an open-source Shopify alternative.
It provides you with the primitives to create amazing digital commerce experiences.
## Architecture overview
Medusa is composed of 3 components: The headless backend, the admin dashboard, and the storefront.
You can learn more about Medusa's architecture in [our introduction](/introduction).
![Medusa's Architecture](https://i.imgur.com/ZHvM2bu.png)
<div className="container" style={{ padding: 0 }}>
<div className="row is-multiline">
<div className="col col--6">
<Link className="card box-link" to="/tutorial/set-up-your-development-environment" style={{ height: '100%' }}>
<div className="card__body">
<h4>Tutorial</h4>
<p>Set up your local development environment</p>
</div>
</Link>
</div>
<div className="col col--6">
<Link className="card box-link" to="/tutorial/adding-custom-functionality" style={{ height: '100%' }}>
<div className="card__body">
<h4>Make it your own</h4>
<p>Create custom endpoints, services, or subscribers.</p>
</div>
</Link>
</div>
<div className="col col--6">
<Link className="card box-link" to="/guides/plugins" style={{ height: '100%' }}>
<div className="card__body">
<h4>Plugins</h4>
<p>Add or build a plugin to make your engine more powerful.</p>
</div>
</Link>
</div>
<div className="col col--6">
<Link className="card box-link" to="/how-to/deploying-on-heroku" style={{ height: '100%' }}>
<div className="card__body">
<h4>Deploy in seconds</h4>
<p>Use one of our guides to deploy your Medusa project in seconds.</p>
</div>
</Link>
</div>
</div>
</div>
## Quickstart
Visit our [Quickstart](https://github.com/medusajs/medusa#-quickstart) to get up and running in minutes with only a couple of commands.
## What you'll find here
<div className="container" style={{ padding: 0 }}>
<div className="row is-multiline">
<div className="col col--4">
<Link className="card box-link" to="/quickstart/quick-start" style={{ height: '100%' }}>
<div className="card__body">
<h4>Quickstart</h4>
<p>A short guide to get you quickly started.</p>
</div>
</Link>
</div>
<div className="col col--4">
<Link className="card box-link" to="/how-to/notification-api" style={{ height: '100%' }}>
<div className="card__body">
<h4>How-to and guides</h4>
<p>Guides and walkthroughs of concepts, tools, deployment and APIs.</p>
</div>
</Link>
</div>
{/* Ref */}
<div className="col col--4">
<a className="card box-link" href="https://docs.medusajs.com/api/store" target="_blank" style={{ height: '100%' }}>
<div className="card__body">
<h4>Reference</h4>
<p>Technical documentation of the Medusa API.</p>
</div>
</a>
</div>
</div>
</div>

View File

@@ -116,6 +116,6 @@ To learn more about Medusa, go through our docs to get some inspiration and guid
- [Find out how to set up a Medusa project with Gatsby and Contentful](https://docs.medusajs.com/how-to/headless-ecommerce-store-with-gatsby-contentful-medusa)
- [Move your Medusa setup to the next level with some custom functionality](https://docs.medusajs.com/tutorial/adding-custom-functionality)
- [Create your own Medusa plugin](https://docs.medusajs.com/guides/plugins)
- [Create your own Medusa plugin](../advanced/backend/payment/overview.md)
If you have any follow-up questions or want to chat directly with our engineering team, we are always happy to meet you at our [Discord](https://discord.gg/DSHySyMu).

View File

@@ -1,103 +0,0 @@
---
title: Notifications and automated flows
---
# Notification API and how to structure email flows
### Introduction
Plugins offer a way to extend and integrate the core functionality of Medusa. For a walkthrough of the implementation details behind these, please see [Plugins in Medusa](https://docs.medusajs.com/guides/plugins).
Medusa makes it possible for plugins to implement the Notification API. The API allows for different types of implementations of notifications (emails, text messages, Slack messages, etc), that are sent as a reaction to events in Medusa. All Notifications are stored in the database with information about the receiver of the notification and what plugin was in charge of sending it. This allows merchants to resend notifications, but also gives an overview of what communication has been sent to customers.
### How it works
The Notification API works by subscribing to all events that are emitted to the Event Bus and channeling them through to notification plugins that listen to the events. In a plugin you can subscribe to a notification event by exporting a subscriber class that calls `notificationService.subscribe`:
```jsx
// src/subscribers/my-notification.js
class MyNotification {
constructor({ notificationService }) {
// Subscribe to order.placed events
notificationService.subscribe("order.placed", "my-service");
}
}
export default MyNotification;
```
The above code tells the notification service to send `order.placed` events to the `my-service` notification service implemented in your plugin.
For a plugin to work with the Notification API you must implement 2 methods `sendNotification` and `resendNotification`;
```jsx
// src/services/my-notification.js
class MyService extends NotificationService {
static identifier = "my-service";
constructor({ orderService }, options) {
super();
this.options_ = options;
this.orderService_ = orderService;
}
async sendNotification(eventName, eventData, attachmentGenerator) {
let sendData;
switch (eventName) {
case "order.placed":
sendData = await this.orderService_.retrieve(eventData.id);
break;
default:
// If the return value is undefined no notification will be stored
return;
}
await CoolEmailSender.send({
email: sendData.email,
templateData: sendData,
});
return { to: sendData.email, data: sendData };
}
async resendNotification(notification, config, attachmentGenerator) {
const recipient = config.to || notification.to;
await CoolEmailSender.send({
email: recipient,
templateData: notification.data,
});
return { to: sendOptions.to, data: notification.data };
}
}
export default MyService;
```
:::note
A notification service must have a static property called `identifier` this is used to determine which classes are called when subscribing to different events. In this case the service identifier is `my-service` so to subscribe to notifications you must use:
`notificationService.subscribe([eventname], "my-service")`
:::
The above class is an example implementation of a NotificationService. It uses a fictional email service called `CoolEmailSender` to send emails to a customer whenever an order is placed. The `sendNotification` implementation gets the event name and fetches relevant data based on what event is being processed; in this case it retrieves an order, which is later used when requesting `CoolEmailSender` to dispatch an email. The address to send the email to is likewise fetched from the order.
The return type of `sendNotification` and `resendNotification` is an object with `to` and `data`. The `to` prop should identify the receiver of the notification, in this case an email, but it could also be a phone number, an ID, a channel name or something similar depending on the type of notification provider. The `data` prop contains the data as it was gathered when the notification was sent; the same data will be provided if the notification is resent at a later point.
When `resendNotification` is called the original Notification is provided along with a config object. The config object may contain a `to` property that can be used to overwrite the original `to`.
## Creating automated notification flows
When running an ecommerce store you typically want to send communication to your customers when different events occur, these include messages like order confirmations and shipping updates. With Medusa you can create transactional notifications as a reaction to a wide spectrum of events, allowing you to automate communication and processes. An example of a flow that can be implemented using Medusa's Notification API is automated return flows. Below is an outline of how an automated return flow might work.
- Customer requests a return with `POST /store/returns`
- Notification Service listens for `order.return_requested` and sends email to the customer with a return invoice and return label generated by fulfillment provider
- Customer returns items triggering `return.recieved`
- Notification Service listens for `return.received` and sends email to the customer with confirmation that their items have been received and that a refund has been issued.
Check out `medusa-plugin-sendgrid` for an Notification API implementation that works with Sendgrid.

View File

@@ -18,27 +18,29 @@ node -v
You can install Node from the [official website](https://nodejs.org/en/).
## Getting started
## Create a Medusa Server
1. **Install Medusa CLI**
### 1. Install Medusa CLI
```bash npm2yarn
npm install -g @medusajs/medusa-cli
```
2. **Create a new Medusa project**
### 2. Create a new Medusa project
```bash
medusa new my-medusa-store --seed
```
3. **Start your Medusa engine**
### 3. Start your Medusa server
```bash
cd my-medusa-store
medusa develop
```
### Test Your Server
After these 3 steps and in only a couple of minutes, you now have a complete commerce engine running locally. You can test it out by sending a request using a tool like Postman or through the command line:
```bash
@@ -55,6 +57,22 @@ curl localhost:9000/store/products
:::
## Additional Steps
### File Service Plugin
To upload product images to your Medusa server, you must install and configure one of the following file service plugins:
- [MinIO](../add-plugins/minio.md) (Recommended for local development)
- [S3](../add-plugins/s3.md)
- [DigitalOcean Spaces](../add-plugins/spaces.md)
### Server Configurations
It's important to configure your Medusa server properly and learn how environment variables are loaded.
You can learn more about configuring your server and loading environment variables in the [Configure your Server documentation](../usage/configurations.md).
## What's next :rocket:
- Install our [Next.js](http://localhost:3000/starters/nextjs-medusa-starter) or [Gatsby](http://localhost:3000/starters/gatsby-medusa-starter) storefronts to set up your ecommerce storefront quickly.

View File

@@ -69,6 +69,12 @@ Then, on your server, update the environment variable `STORE_CORS` to the URL wi
STORE_CORS=http://localhost:<PORT>
```
:::info
For more details about the Store CORS configuration, check out the [Configure your Server documentation](../usage/configurations.md#storefront-cors).
:::
### Development Resources
If youre not familiar with Gatsby, you can learn more about it through the following resources:

View File

@@ -67,6 +67,12 @@ Then, on your server, update the environment variable `STORE_CORS` to the URL
STORE_CORS=http://localhost:<PORT>
```
:::info
For more details about the Store CORS configuration, check out the [Configure your Server documentation](../usage/configurations.md#storefront-cors).
:::
### Development Resources
You can learn more about development with Next.js through [their documentation](https://nextjs.org/docs/getting-started).

View File

@@ -177,6 +177,12 @@ PostgreSQL is an open-source relational database system with more than 30 years
Although you can use an SQLite database with Medusa which would require no necessary database installations, it is recommended to use a PostgreSQL database for your server.
:::tip
After installing PostgreSQL, check out the [Configure your Server documentation](../usage/configurations.md#postgresql-configurations) to learn how to configure PostgreSQL to work with Medusa.
:::
<Tabs groupId="operating-systems" wrapperClassName={styles.osTabs}>
<TabItem value="windows" label="Windows">
@@ -216,6 +222,12 @@ Medusa uses Redis as the event queue in the server. If you want to use subscribe
If you dont install and configure Redis with your Medusa server, then it will work without any events-related features.
:::tip
After installing Redis, check out the [Configure your Server documentation](../usage/configurations.md#redis) to learn how to configure Redis to work with Medusa.
:::
<Tabs groupId="operating-systems" wrapperClassName={styles.osTabs}>
<TabItem value="windows" label="Windows">
@@ -290,75 +302,8 @@ Here are some other options:
It is not important which editor you use as long as you feel comfortable working with it.
## Configuring Your Server
After installing all the requirements mentioned above and following along with our [quickstart guide](../quickstart/quick-start.md), you need to configure some information on your server to connect it to some of the requirements you installed.
### PostgreSQL
After creating a new database schema in PostgreSQL, you need to add the URL to connect to it on your Medusa server.
To do that, add the following environment variable to the `.env` file on the root of your Medusa server:
```bash
DATABASE_URL=postgres://<USER>:<PASSWORD>@<HOST>:<PORT>/<DB_NAME>
```
Notice that there are some details in the URL above you need to fill in yourself:
- `<USER>`: the username of the user that has access to the database schema you created.
- `<PASSWORD>`: the password of the user that has access to the database schema you created.
- `<HOST>`: the hostname where the PostgreSQL database is hosted. In local development, you can use `localhost`.
- `<PORT>`: the port where the PostgreSQL database can be contacted on the host. By default, its 5432**.**
- `<DB_NAME>`: the name of the database schema you created.
Then, in `medusa-config.js`, change the following properties in the object `projecConfig`:
```jsx
module.exports = {
projectConfig: {
...,
database_url: DATABASE_URL,
database_type: "postgres",
// comment out or remove these lines:
// database_database: "./medusa-db.sql",
// database_type: "sqlite",
},
plugins,
};
```
The last recommended step is running the following command to migrate Medusas database schema into your database and seed the database with dummy data:
```bash npm2yarn
npm run seed
```
### Redis
After installing Redis and running the Redis server, you must configure Medusa to use it.
In `.env` add a new environment variable:
```bash
REDIS_URL=redis://localhost:6379
```
This is the default Redis URL to connect to, especially in development. However, if youre deploying your server, have configured your Redis installation differently, or just need to check the format of the connection URL, you can check [this guide](https://github.com/lettuce-io/lettuce-core/wiki/Redis-URI-and-connection-details) for more details.
:::tip
If you use the default connection string mentioned here then you can skip over adding the environment variable.
:::
Then, in `medusa-config.js`, comment out the following line in the object `projectConfig`:
```jsx
redis_url: REDIS_URL,
```
## Whats Next 🚀
- Learn how to [configure your Medusa server](../usage/configurations.md).
- Learn how to install a storefront with [Next.js](../starters/nextjs-medusa-starter.md) or [Gatsby](./../starters/gatsby-medusa-starter.md).
- Learn how to install the [Medusa Admin](../admin/quickstart.md).

50
docs/content/usage.md Normal file
View File

@@ -0,0 +1,50 @@
# Collected Usage Information
This document gives an overview of Medusas optional collected usage information, how it helps Medusa become a better platform, and how developers can opt out of this feature.
## Overview
At Medusa, we strive to provide the best experience for developers using our platform. For that reason, Medusa collects anonymous and non-sensitive data that provides a global understanding of how users are using Medusa on a live server.
## Purpose
As an open source solution, we work closely and constantly interact with our community to ensure that we provide the best experience for everyone using Medusa.
We are capable of getting a general understanding of how developers use Medusa and what general issues they run into through different means such as our Discord server, GitHub issues and discussions, and occasional one-on-one sessions.
However, although these methods can be insightful, theyre not enough to get a full and global understanding of how developers are using Medusa, especially in production.
Collecting this data allows us to understand certain details such as:
- What operating system do most Medusa developers use?
- What version of Medusa is widely used?
- What Node version is globally used? Should we focus our efforts on providing support for versions that we dont currently support?
## Collected Data
The following data is being collected on your server:
- Unique project ID generated with UUID.
- Unique machine ID generated with UUID.
- Operating system information including Node version or operating system platform used.
- The version of the Medusa server and Medusa CLI used.
:::info
Data is only collected when the server is run with the command `medusa start`.
:::
## How to Opt-Out
If you prefer to disable data collection, you can do it either by setting the following environment variable to true:
```bash
MEDUSA_DISABLE_TELEMETRY=true
```
Or, you can run the following command in the root of your Medusa server project to disable it:
```bash
medusa telemetry --disable
```

View File

@@ -0,0 +1,286 @@
# Configure your Server
In this document, youll learn what configurations you can add to your Medusa server and how to add them.
## Prerequisites
This document assumes you already followed along with the [“Set up your development environment” documentation](../tutorial/0-set-up-your-development-environment.mdx) and have installed a Medusa server.
## Medusa Configurations File
The configurations for your Medusa server are in `medusa-config.js`. This includes database, Redis, and plugin configurations, among other configurations.
Some of the configurations mentioned in this document are already defined in `medusa-config.js` with default values. Its important that you know what these configurations are used for and how to set them.
## Environment Variables
In your configurations, youll often use environment variables. For example, when using API keys or setting your database URL.
By default, Medusa loads environment variables from the systems environment variables. Any different method you prefer to use or other location youd prefer to load environment variables from you need to manually implement.
:::info
This change in how environment variables are loaded was introduced in version 1.3.0. You can learn more in the [upgrade guide for version 1.3.0](../advanced/backend/upgrade-guides/1-3-0.md).
:::
### Load from .env
A common way to use environment variables during development or in production is using `.env` files.
To load environment variables from a `.env` file, add the following at the top of `medusa-config.js`:
```jsx
const dotenv = require('dotenv')
let ENV_FILE_NAME = '';
switch (process.env.NODE_ENV) {
case 'production':
ENV_FILE_NAME = '.env.production';
break;
case 'staging':
ENV_FILE_NAME = '.env.staging';
break;
case 'test':
ENV_FILE_NAME = '.env.test';
break;
case 'development':
default:
ENV_FILE_NAME = '.env';
break;
}
try {
dotenv.config({ path: process.cwd() + '/' + ENV_FILE_NAME });
} catch (e) {
//handle error
}
```
This code snippet uses the [dotenv](https://www.npmjs.com/package/dotenv) library to load environment variables from a local file. The file chosen to be loaded will be loaded based on the current environment.
:::note
`dotenv` should be available to use in your Medusa server project without the need to install it. However, if its not available you can install it with the following command:
```npm2yarn
npm install dotenv --save
```
:::
## Database Configuration
Medusa supports 2 database types: SQLite and PostgreSQL.
:::tip
You can use SQLite for development purposes, however, its recommended to use PostgreSQL.
:::
### SQLite Configurations
For SQLite you mainly need 2 configurations:
```jsx
module.exports = {
projectConfig: {
//...other configurations
database_type: "sqlite",
database_database: "./medusa-db.sql",
},
};
```
Where `database_type` is `sqlite` and `database_database` is the location you want the SQLite database to be created in.
### PostgreSQL Configurations
For PostgreSQL you mainly need 2 configurations:
```jsx
module.exports = {
projectConfig: {
//...other configurations
database_type: "postgres",
database_url: DATABASE_URL,
},
};
```
Where `database_type` is `postgres` and `DATABASE_URL` is the URL connection string to your PostgreSQL database. You can check out how to format it in [PostgreSQLs documentation](https://www.postgresql.org/docs/current/libpq-connect.html).
### Common Configuration
As Medusa internally uses [Typeorm](https://typeorm.io/) to connect to the database, the following configurations are also available:
1. `database_logging`: enable or disable logging.
2. `database_extra`: extra options that you can pass to the underlying database driver.
These configurations are not required and can be omitted.
```jsx
module.exports = {
projectConfig: {
//...other configurations
database_logging: true,
database_extra: {}
},
};
```
## Redis
Medusa uses Redis to handle the event queue, among other usages. You need to set Redis URL in the configurations:
```jsx
module.exports = {
projectConfig: {
//...other configurations
redis_url: REDIS_URL
},
};
```
Where `REDIS_URL` is the URL used to connect to Redis. The format of the connection string is `redis[s]://[[username][:password]@][host][:port][/db-number]`.
:::tip
By default, the Redis connection string should be `redis://localhost:6379` unless you made any changes to the default configurations during the installation.
:::
If you omit this configuration, events will not be emitted and subscribers will not work.
:::info
You can learn more about Subscribers and events in the [Subscriber documentation](../advanced/backend/subscribers/create-subscriber.md).
:::
## JSON Web Token (JWT) Secret
Medusa uses JWT to handle user authentication. To set the JWT secret:
```jsx
module.exports = {
projectConfig: {
//...other configurations
jwt_secret: "very secure string",
},
};
```
Where `jwt_secret` is the secret used to create the tokens. The more secure it is the better.
:::caution
In a development environment, if this option is not set the default secret is “supersecret”. However, in production, if this option is not set an error will be thrown and your server will crash.
:::
## Cookie Secret
This configuration is used to sign the session ID cookie. To set the cookie secret:
```jsx
module.exports = {
projectConfig: {
//...other configurations
cookie_secret: "very secure string",
},
};
```
Where `cookie_secret` is the secret used to create the tokens. The more secure it is the better.
:::caution
In a development environment, if this option is not set the default secret is “supersecret”. However, in production, if this option is not set an error will be thrown and your server will crash.
:::
## Admin CORS
Medusa uses Cross-Origin Resource Sharing (CORS) to only allow specific origins to access the server. To make sure your Admin dashboard can access the Medusa servers admin endpoints, set this configuration:
```jsx
module.exports = {
projectConfig: {
//...other configurations
admin_cors: ADMIN_CORS,
},
};
```
Where `ADMIN_CORS` is the URL of your admin dashboard. By default, its `http://localhost:7000,http://localhost:7001`.
## Storefront CORS
Medusa uses CORS to only allow specific origins to access the server. To make sure your Storefront dashboard can access the Medusa server, set this configuration:
```jsx
module.exports = {
projectConfig: {
//...other configurations
store_cors: STORE_CORS,
},
};
```
Where `STORE_CORS` is the URL of your storefront. By default, its `http://localhost:8000`.
## Plugins
On your Medusa server, you can use Plugins to add custom features or integrate third-party services. For example, installing a plugin to use Stripe as a payment provider.
:::info
You can learn more about plugins in the [Plugins Overview documentation](../advanced/backend/plugins/overview.md).
:::
Aside from installing the plugin with NPM, you need to pass the plugin you installed into the `plugins` array defined in `medusa-config.js`. This array is then exported along with other configurations youve added:
```jsx
module.exports = {
projectConfig: {
//previous configurations mentioned...
},
plugins,
};
```
### Add a Plugin Without Configuration
To add a plugin that doesnt need any configurations, you can simply add its name to the `plugins` array:
```jsx
const plugins = [
//other plugins...
`medusa-my-plugin`,
];
```
### Add a Plugin With Configuration
To add a plugin with configurations, you need to add an object to the `plugins` array with the plugins name and configurations:
```jsx
const plugins = [
//other plugins...
{
resolve: `medusa-my-plugin`,
options: {
apiKey: `test`
}
}
];
```
## Whats Next 🚀
- Check out our [Next.js](../starters/nextjs-medusa-starter.md) and [Gatsby](../starters/gatsby-medusa-starter.md) starter storefronts.
- Install the [Medusa admin](../admin/quickstart.md).
- Learn about [deploying the Medusa server on Heroku](../how-to/deploying-on-heroku.md).

View File

@@ -486,7 +486,8 @@ describe("/store/carts", () => {
regions: ["test-region"],
}
let discountCart, discount
let discountCart
let discount
beforeEach(async () => {
try {
discount = await simpleDiscountFactory(
@@ -718,15 +719,17 @@ describe("/store/carts", () => {
)
.catch((err) => console.log(err))
expect(response.data.cart.items).toEqual([
expect.objectContaining({
cart_id: "test-cart-3",
unit_price: 8000,
variant_id: "test-variant-sale-cg",
quantity: 3,
adjustments: [],
}),
])
expect(response.data.cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
cart_id: "test-cart-3",
unit_price: 8000,
variant_id: "test-variant-sale-cg",
quantity: 3,
adjustments: [],
}),
])
)
})
it("updates line item of a cart containing a total fixed discount", async () => {
@@ -1363,7 +1366,14 @@ describe("/store/carts", () => {
.catch((error) => console.log(error))
expect(response.status).toEqual(200)
expect(response.data.cart.items[0].unit_price).toEqual(500)
expect(response.data.cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
variant_id: "test-variant-sale-cg",
unit_price: 500,
}),
])
)
})
it("updates prices when cart region id is updated", async () => {
@@ -1479,11 +1489,38 @@ describe("/store/carts", () => {
const getRes = await api.post(`/store/carts/test-cart-2/complete-cart`)
expect(getRes.status).toEqual(200)
expect(getRes.data.type).toEqual("order")
const variantRes = await api.get("/store/variants/test-variant")
expect(variantRes.data.variant.inventory_quantity).toEqual(0)
})
it("calculates correct payment totals on cart completion taking into account line item adjustments", async () => {
const api = useApi()
await api.post("/store/carts/test-cart-3", {
discounts: [{ code: "CREATED" }],
})
const createdOrder = await api.post(
`/store/carts/test-cart-3/complete-cart`
)
expect(createdOrder.data.type).toEqual("order")
expect(createdOrder.data.data.discount_total).toEqual(10000)
expect(createdOrder.data.data.subtotal).toEqual(16000)
expect(createdOrder.data.data.total).toEqual(6000)
expect(createdOrder.data.data.payments).toEqual(
expect.arrayContaining([
expect.objectContaining({
amount: 6000,
}),
])
)
expect(createdOrder.status).toEqual(200)
})
it("returns early, if cart is already completed", async () => {
const manager = dbConnection.manager
const api = useApi()
@@ -1739,7 +1776,9 @@ describe("/store/carts", () => {
.catch((err) => console.log(err))
// Ensure that the discount is only applied to the standard item
const itemId = cartWithGiftcard.data.cart.items.find(item => !item.is_giftcard).id
const itemId = cartWithGiftcard.data.cart.items.find(
(item) => !item.is_giftcard
).id
expect(cartWithGiftcard.data.cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({

View File

@@ -50,6 +50,7 @@ module.exports = async (connection, data = {}) => {
const r = manager.create(Region, {
id: "test-region",
name: "Test Region",
payment_providers: [{ id: "test-pay" }],
currency_code: "usd",
tax_rate: 0,
})
@@ -819,6 +820,23 @@ module.exports = async (connection, data = {}) => {
completed_at: null,
items: [],
})
await manager.save(cart3)
const ps = manager.create(PaymentSession, {
id: "test-cart-session",
cart_id: "test-cart-3",
provider_id: "test-pay",
is_selected: true,
data: {},
status: "authorized",
})
await manager.save(ps)
cart3.payment_sessions = [ps]
cart3.payment_session = ps
await manager.save(cart3)
await manager.insert(ShippingMethod, {
@@ -842,7 +860,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(li2)
const cart4 = manager.create(Cart, {
id: "test-cart-3",
id: "test-cart-4",
email: "some-customer@email.com",
shipping_address: {
id: "test-shipping-address",

View File

@@ -8,10 +8,10 @@ import {
} from "class-validator"
import { defaultStoreCartFields, defaultStoreCartRelations } from "."
import { CartService } from "../../../../services"
import { AddressPayload } from "../../../../types/common"
import { CartUpdateProps } from "../../../../types/cart"
import { IsType } from "../../../../utils/validators/is-type"
import { AddressPayload } from "../../../../types/common"
import { validator } from "../../../../utils/validator"
import { IsType } from "../../../../utils/validators/is-type"
/**
* @oas [post] /store/carts/{id}
@@ -90,7 +90,7 @@ export default async (req, res) => {
// Update the cart
const { shipping_address, billing_address, ...rest } = validated
const cartDataToUpdate: CartUpdateProps = { ...rest };
const cartDataToUpdate: CartUpdateProps = { ...rest }
if (typeof shipping_address === "string") {
cartDataToUpdate.shipping_address_id = shipping_address
} else {

View File

@@ -1,8 +1,8 @@
import LineItemAdjustmentService from "../line-item-adjustment"
import { MockManager, MockRepository, IdMap } from "medusa-test-utils"
import { EventBusServiceMock } from "../__mocks__/event-bus"
import { DiscountServiceMock } from "../__mocks__/discount"
import { MockManager, MockRepository } from "medusa-test-utils"
import { In } from "typeorm"
import LineItemAdjustmentService from "../line-item-adjustment"
import { DiscountServiceMock } from "../__mocks__/discount"
import { EventBusServiceMock } from "../__mocks__/event-bus"
describe("LineItemAdjustmentService", () => {
describe("list", () => {
@@ -101,7 +101,7 @@ describe("LineItemAdjustmentService", () => {
}
const lineItemAdjustmentRepo = MockRepository({
create: (f) => Promise.resolve(lineItemAdjustment),
create: (f) => lineItemAdjustment,
save: (f) => Promise.resolve(lineItemAdjustment),
})
@@ -270,11 +270,11 @@ describe("LineItemAdjustmentService", () => {
it("calls createAdjustmentForLineItem once when given a line item", () => {
const cart = {
id: "cart1",
discounts: ["disc-1"],
items: [{ id: "li-1" }],
},
lineItem = { id: "li-1" }
id: "cart1",
discounts: ["disc-1"],
items: [{ id: "li-1" }],
}
const lineItem = { id: "li-1" }
lineItemAdjustmentService.createAdjustments(cart, lineItem)
expect(

View File

@@ -3,6 +3,7 @@ import { MedusaError, Validator } from "medusa-core-utils"
import { DeepPartial, EntityManager, In } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import { IPriceSelectionStrategy } from "../interfaces/price-selection-strategy"
import { DiscountRuleType } from "../models"
import { Address } from "../models/address"
import { Cart } from "../models/cart"
import { CustomShippingOption } from "../models/custom-shipping-option"
@@ -1229,7 +1230,7 @@ class CartService extends TransactionBaseService<CartService> {
const freshCart = await this.retrieve(cart.id, {
select: ["total"],
relations: ["payment_sessions"],
relations: ["payment_sessions", "items", "items.adjustments"],
})
if (session.status === "authorized") {

View File

@@ -1,14 +1,14 @@
import { EntityManager } from "typeorm"
import { BaseService } from "medusa-interfaces"
import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import { EntityManager } from "typeorm"
import { Cart } from "../models/cart"
import { LineItem } from "../models/line-item"
import { LineItemAdjustment } from "../models/line-item-adjustment"
import { ProductVariant } from "../models/product-variant"
import { LineItemAdjustmentRepository } from "../repositories/line-item-adjustment"
import { FindConfig } from "../types/common"
import { LineItemAdjustment } from "../models/line-item-adjustment"
import { FilterableLineItemAdjustmentProps } from "../types/line-item-adjustment"
import { LineItem } from "../models/line-item"
import { Cart } from "../models/cart"
import DiscountService from "./discount"
import { ProductVariant } from "../models/product-variant"
type LineItemAdjustmentServiceProps = {
manager: EntityManager
@@ -96,7 +96,7 @@ class LineItemAdjustmentService extends BaseService {
const lineItemAdjustmentRepo: LineItemAdjustmentRepository =
manager.getCustomRepository(this.lineItemAdjustmentRepo_)
const lineItemAdjustment = await lineItemAdjustmentRepo.create(data)
const lineItemAdjustment = lineItemAdjustmentRepo.create(data)
return await lineItemAdjustmentRepo.save(lineItemAdjustment)
})

View File

@@ -23,6 +23,12 @@ module.exports = {
systemvars: true, // Set to true if you would rather load all system variables as well (useful for CI purposes)
},
],
[
"docusaurus-plugin-segment",
{
apiKey: process.env.SEGMENT_API_KEY || "temp"
}
]
],
themeConfig: {
colorMode: {

View File

@@ -23,6 +23,7 @@
"@svgr/webpack": "6.2.1",
"algoliasearch-helper": "^3.8.2",
"clsx": "^1.1.1",
"docusaurus-plugin-segment": "^1.0.3",
"docusaurus2-dotenv": "^1.4.0",
"file-loader": "^6.2.0",
"lodash": "^4.17.21",

View File

@@ -26,6 +26,10 @@ module.exports = {
id: "quickstart/quick-start",
label: "Quickstart Guide",
},
{
type: "doc",
id: "usage",
},
{
type: "category",
collapsed: false,
@@ -36,6 +40,11 @@ module.exports = {
id: "tutorial/set-up-your-development-environment",
label: "Set Up your Development Environment"
},
{
type: "doc",
id: "usage/configurations",
label: "Configure your Server"
},
{
type: "category",
collapsed: true,
@@ -112,12 +121,12 @@ module.exports = {
{
type: "doc",
id: "advanced/backend/endpoints/add-storefront",
label: "Add Endpoint for Storefront"
label: "Create Endpoint for Storefront"
},
{
type: "doc",
id: "advanced/backend/endpoints/add-admin",
label: "Add Endpoint for Admin"
label: "Create Endpoint for Admin"
},
]
},
@@ -129,7 +138,7 @@ module.exports = {
{
type: "category",
label: 'Subscribers',
collapsed: false,
collapsed: true,
items: [
{
type: "doc",
@@ -143,6 +152,11 @@ module.exports = {
},
]
},
{
type: "doc",
id: "advanced/backend/entities",
label: "Entities"
},
{
type: "category",
label: 'Shipping',
@@ -156,7 +170,7 @@ module.exports = {
{
type: "doc",
id: "advanced/backend/shipping/add-fulfillment-provider",
label: "Add Fulfillment Provider"
label: "Create a Fulfillment Provider"
}
]
},
@@ -173,20 +187,41 @@ module.exports = {
{
type: "doc",
id: "advanced/backend/payment/how-to-create-payment-provider",
},
{
type: "doc",
id: "advanced/backend/payment/frontend-payment-flow-in-checkout",
label: "Create a Payment Provider"
},
]
},
{
type: "doc",
id: "how-to/notification-api",
type: "category",
label: "Notification",
collapsed: true,
items: [
{
type: "doc",
id: "advanced/backend/notification/overview"
},
{
type: "doc",
id: "advanced/backend/notification/how-to-create-notification-provider",
label: "Create a Notification Provider"
}
]
},
{
type: "doc",
id: "guides/plugins",
type: "category",
label: "Plugins",
collapsed: true,
items: [
{
type: "doc",
id: "advanced/backend/plugins/overview",
label: "Overview"
},
{
type: "doc",
id: "advanced/backend/plugins/create",
}
]
},
{
type: "doc",
@@ -201,6 +236,10 @@ module.exports = {
type: "category",
label: 'Upgrade Guides',
collapsed: true,
link: {
type: 'doc',
id: 'advanced/backend/upgrade-guides/index'
},
items: [
{
type: "doc",
@@ -210,6 +249,17 @@ module.exports = {
]
},
]
},
{
type: "category",
label: "Storefront",
collapsed: true,
items: [
{
type: "doc",
id: "advanced/storefront/how-to-implement-checkout-flow",
},
]
}
]
},
@@ -387,6 +437,11 @@ module.exports = {
},
],
},
{
type: "doc",
id: "contribution-guidelines",
label: "Contribution Guidelines",
},
],
servicesSidebar: [
{

View File

@@ -259,7 +259,7 @@ html:not([data-theme="dark"]) footer {
/* Cards */
html:not([data-theme=dark]) .card {
border: 1px solid #1f1f1f;
border: 1px solid #cbcbcb;
border-radius: 8px;
}

View File

@@ -1798,6 +1798,25 @@
resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b"
integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==
"@ndhoule/each@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@ndhoule/each/-/each-2.0.1.tgz#bbed372a603e0713a3193c706a73ddebc5b426a9"
integrity sha1-u+03KmA+BxOjGTxwanPd68W0Jqk=
dependencies:
"@ndhoule/keys" "^2.0.0"
"@ndhoule/keys@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@ndhoule/keys/-/keys-2.0.0.tgz#3d64ae677c65a261747bf3a457c62eb292a4e0ce"
integrity sha1-PWSuZ3xlomF0e/OkV8YuspKk4M4=
"@ndhoule/map@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@ndhoule/map/-/map-2.0.1.tgz#f5ca0a47424ea67f46e2a6d499b9e9bc886aefa8"
integrity sha1-9coKR0JOpn9G4qbUmbnpvIhq76g=
dependencies:
"@ndhoule/each" "^2.0.1"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -1824,6 +1843,13 @@
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
"@segment/snippet@^4.13.2":
version "4.15.3"
resolved "https://registry.yarnpkg.com/@segment/snippet/-/snippet-4.15.3.tgz#ac829ec4570b249f559756293f4736e434885de7"
integrity sha512-75kVTYaQGYMkwVjJvCLLOlzxV8jCDxvKG68U88joo/rBx95SIXETcjUmIXF6A7SFRCgz83B+zrZbo+JYsmHkig==
dependencies:
"@ndhoule/map" "^2.0.1"
"@sideway/address@^4.1.3":
version "4.1.3"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27"
@@ -3628,6 +3654,13 @@ dns-txt@^2.0.2:
dependencies:
buffer-indexof "^1.0.0"
docusaurus-plugin-segment@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/docusaurus-plugin-segment/-/docusaurus-plugin-segment-1.0.3.tgz#d32ec8ace8625837dee650f853bc89ae2221206a"
integrity sha512-9DqebTx9TqjujCnB22qEeCm8NGJUAH7VAKLAa20/CyfSSrs+khTQI0FmzEALtiCqKNO1D3GWm3VvE4gqbuGqnw==
dependencies:
"@segment/snippet" "^4.13.2"
docusaurus2-dotenv@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/docusaurus2-dotenv/-/docusaurus2-dotenv-1.4.0.tgz#9ab900e29de9081f9f1f28f7224ff63760385641"

View File

@@ -66,12 +66,20 @@ module.exports = {
allowList: ["ALGOLIA_API_KEY"],
},
},
{
resolve: `gatsby-plugin-segment-js`,
options: {
prodKey: process.env.SEGMENT_API_KEY,
devKey: process.env.SEGMENT_API_KEY_DEV,
trackPage: true,
}
},
{
resolve: `gatsby-plugin-sitemap`,
options: {
output: '/api/sitemap'
}
}
},
// `gatsby-plugin-preact`,
// {
// resolve: `gatsby-source-openapi-aggregate`,

View File

@@ -29,6 +29,7 @@
"gatsby-plugin-preact": "^5.9.0",
"gatsby-plugin-react-helmet": "^3.3.12",
"gatsby-plugin-sitemap": "^5.15.0",
"gatsby-plugin-segment-js": "^3.7.1",
"gatsby-plugin-theme-ui": "^0.10.1",
"gatsby-remark-autolink-headers": "^4.6.0",
"gatsby-source-filesystem": "^3.9.0",

View File

@@ -5452,6 +5452,11 @@ gatsby-plugin-sitemap@^5.15.0:
minimatch "^3.1.2"
sitemap "^7.0.0"
gatsby-plugin-segment-js@^3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-segment-js/-/gatsby-plugin-segment-js-3.7.1.tgz#3432d59be0e45ee82f36a42cff060af220a0dd68"
integrity sha512-zAsjRrF77Kih7YRUJVp84thnODbzjDE44H6ePtdQfLVyPWiJj3IpS/WAwyRg9Cu3+FciFAT8WEjfJ2I02V0sUw==
gatsby-plugin-theme-ui@^0.10.1:
version "0.10.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-theme-ui/-/gatsby-plugin-theme-ui-0.10.1.tgz#d1ac7f4f1c4bf187694110a8670f0bb3ff470ad2"