From 0502e8bf78280a06c5b8959f384cd84ae59a9fde Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Thu, 12 Jun 2025 16:32:45 +0300 Subject: [PATCH] docs: added slack integration guide (#12696) * docs: added slack integration guide * generate --- www/apps/book/public/llms-full.txt | 634 ++++++++++++++- .../analytics/local/page.mdx | 2 +- .../analytics/posthog/page.mdx | 2 +- .../app/integrations/guides/segment/page.mdx | 4 +- .../app/integrations/guides/slack/page.mdx | 735 ++++++++++++++++++ www/apps/resources/generated/edit-dates.mjs | 3 +- www/apps/resources/generated/files-map.mjs | 4 + ...nerated-infrastructure-modules-sidebar.mjs | 8 + .../generated-integrations-sidebar.mjs | 8 + www/apps/resources/sidebars/integrations.mjs | 5 + www/packages/tags/src/tags/notification.ts | 4 + www/packages/tags/src/tags/product.ts | 4 + www/packages/tags/src/tags/server.ts | 4 + www/packages/tags/src/tags/tutorial.ts | 4 + 14 files changed, 1412 insertions(+), 9 deletions(-) create mode 100644 www/apps/resources/app/integrations/guides/slack/page.mdx diff --git a/www/apps/book/public/llms-full.txt b/www/apps/book/public/llms-full.txt index 050fd32f0b..aa7a3c4ea9 100644 --- a/www/apps/book/public/llms-full.txt +++ b/www/apps/book/public/llms-full.txt @@ -31119,7 +31119,7 @@ module.exports = defineConfig({ { resolve: "@medusajs/analytics-local", id: "local", - } + }, ], }, }, @@ -31431,7 +31431,7 @@ module.exports = defineConfig({ posthogEventsKey: process.env.POSTHOG_EVENTS_API_KEY, posthogHost: process.env.POSTHOG_HOST, }, - } + }, ], }, }, @@ -61840,14 +61840,14 @@ class SegmentAnalyticsProviderService extends AbstractAnalyticsProviderService { userId: data.actor_id, anonymousId, traits, - context: data.properties + context: data.properties, }) } else { this.client.identify({ userId: data.actor_id, anonymousId, traits, - context: data.properties + context: data.properties, }) } } @@ -63601,6 +63601,632 @@ If you're new to Medusa, check out the [main documentation](https://docs.medusaj To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md). +# Integrate Slack (Notification) with Medusa + +In this tutorial, you'll learn how to integrate Slack with Medusa to receive notifications about created orders. + +When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. Medusa's architecture facilitates integrating third-party services to customize Medusa's infrastructure for your business needs. + +Medusa's [Notification Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/notification/index.html.md) allows you to customize Medusa's infrastructure to send notifications using the third-party provider that fits your business needs, such as [Slack](https://slack.com/). + +In this tutorial, you'll integrate Slack with Medusa to receive notifications about created orders. + +## Summary + +By following this tutorial, you'll learn how to: + +- Install and set up Medusa. +- Integrate Slack with Medusa. +- Handle Medusa's `order.placed` event to send notifications to Slack. + +You can follow this tutorial whether you're new to Medusa or an advanced Medusa developer. + +![Diagram showcasing the flow of the integration between Medusa and Slack](https://res.cloudinary.com/dza7lstvk/image/upload/v1748943447/slack-integration-overview_vkdijx.jpg) + +[Example Repository](https://github.com/medusajs/examples/tree/main/slack-integration): Find the full code of the guide in this repository. + +*** + +## Step 1: Install a Medusa Application + +### Prerequisites + +- [Node.js v20+](https://nodejs.org/en/download) +- [Git CLI tool](https://git-scm.com/downloads) +- [PostgreSQL](https://www.postgresql.org/download/) + +Start by installing the Medusa application on your machine with the following command: + +```bash +npx create-medusa-app@latest +``` + +First, you'll be asked for the project's name. Then, when prompted about installing the [Next.js Starter Storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md), choose "Yes." + +Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name and the Next.js Starter Storefront in a separate directory named `{project-name}-storefront`. + +The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Learn more in [Medusa's Architecture documentation](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md). + +Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterwards, you can log in with the new user and explore the dashboard. + +Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/create-medusa-app-errors/index.html.md) for help. + +*** + +## Step 2: Create Slack Module Provider + +To integrate third-party services into Medusa, you create a custom [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md). A module is a reusable package with functionalities related to a single feature or domain. + +Medusa's [Notification Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/notification/index.html.md) provides an interface to send notifications in your Medusa application. It delegates the actual sending of notifications to the underlying provider, such as Slack. + +In this step, you'll integrate Slack as a Notification Module Provider. Later, you'll use it to send a notification when an order is created. + +Refer to the [Modules](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) documentation to learn more about modules in Medusa. + +### a. Install Axios + +To send requests to Slack, you'll use the [Axios](https://axios-http.com/) library. So, run the following command to install it in your Medusa application: + +```bash npm2yarn +npm install axios +``` + +You'll use Axios in the module's service. + +### b. Create Module Directory + +A module is created under the `src/modules` directory of your Medusa application. So, create the directory `src/modules/slack`. + +### c. Create Slack Module's Service + +A module has a service that contains its logic. For Notification Module Providers, the service implements the logic to send notifications with a third-party service. + +To create the service of the Slack Notification Module Provider, create the file `src/modules/slack/service.ts` with the following content: + +```ts title="src/modules/slack/service.ts" highlights={serviceHighlights} +import { + AbstractNotificationProviderService, +} from "@medusajs/framework/utils" + +type Options = { + webhook_url: string + admin_url: string +} + +type InjectedDependencies = {} + +class SlackNotificationProviderService extends AbstractNotificationProviderService { + static identifier = "slack" + protected options: Options + + constructor(container: InjectedDependencies, options: Options) { + super() + this.options = options + } +} +``` + +A Notification Module Provider's service must extend the `AbstractNotificationProviderService` class. You'll implement its methods in a bit. + +The service must also have an `identifier` static property, which is a unique identifier for the module. This identifier is used when registering the module in the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md). + +The service's constructor receives two parameters: + +- `container`: The [module's container](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md) that contains Framework resources available to the module. You don't need to access any resources for this provider. +- `options`: Options that are passed to the module provider when it's registered in Medusa's configurations. You define the following options: + - `webhook_url`: The Slack webhook URL to send notifications to. + - `admin_url`: The URL of the Medusa Admin dashboard, which you'll use to add links in the notifications. + +You'll learn how to set these options when you [add the module provider to Medusa's configurations](#g-add-module-provider-to-medusas-configurations). + +In the constructor, you set the `options` property to the passed options. + +In the next sections, you'll implement the methods of the `AbstractNotificationProviderService` class. + +Refer to the [Create Notification Module Provider](https://docs.medusajs.com/references/notification-provider-module/index.html.md) guide for detailed information about the methods. + +### d. Implement validateOptions Method + +The `validateOptions` method is used to validate the options passed to the module provider. If the method throws an error, the Medusa application won't start. + +So, add the `validateOptions` method to the `SlackNotificationProviderService` class: + +```ts title="src/modules/slack/service.ts" +// other imports... +import { + MedusaError, +} from "@medusajs/framework/utils" + +class SlackNotificationProviderService extends AbstractNotificationProviderService { + // ... + static validateOptions(options: Record): void | never { + if (!options.webhook_url) { + throw new MedusaError( + MedusaError.Types.INVALID_ARGUMENT, + "Webhook URL is required" + ) + } + if (!options.admin_url) { + throw new MedusaError( + MedusaError.Types.INVALID_ARGUMENT, + "Admin URL is required" + ) + } + } +} +``` + +The `validateOptions` method receives the options passed to the module provider as a parameter. + +In the method, you throw an error if any of the options are not set. + +### e. Implement send Method + +When the Medusa application needs to send a notification through a channel (such as Slack), it calls the `send` method of the channel's module provider. + +You'll first add helper methods that you'll use in the `send` method. Then, you'll implement the `send` method itself. + +#### getDisplayAmount Method + +The first method you'll add is a method to format amounts for displaying them in notifications. So, add the `getDisplayAmount` method to the `SlackNotificationProviderService` class: + +```ts title="src/modules/slack/service.ts" +class SlackNotificationProviderService extends AbstractNotificationProviderService { + // ... + private async getDisplayAmount(amount: number, currencyCode: string) { + return Intl.NumberFormat("en-US", { + style: "currency", + currency: currencyCode, + }).format(amount) + } +} +``` + +The `getDisplayAmount` method receives an amount and a currency code and returns the formatted amount as a string. + +#### sendOrderNotification Method + +Next, you'll add a method to format a Slack message for created orders. So, add the `sendOrderNotification` method to the `SlackNotificationProviderService` class: + +```ts title="src/modules/slack/service.ts" highlights={sendOrderNotificationHighlights} +// other imports... +import { + OrderDTO, + ProviderSendNotificationDTO, + ProviderSendNotificationResultsDTO, +} from "@medusajs/framework/types" +import axios from "axios" + +class SlackNotificationProviderService extends AbstractNotificationProviderService { + // ... + async sendOrderNotification(notification: ProviderSendNotificationDTO) { + const order = notification.data?.order as OrderDTO + if (!order) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + "Order not found" + ) + } + const shippingAddress = order.shipping_address + const blocks: Record[] = [ + { + type: "section", + text: { + type: "mrkdwn", + text: `Order *<${this.options.admin_url}/orders/${order.id}|#${order.display_id}>* has been created.`, + }, + }, + ] + + if (shippingAddress) { + blocks.push({ + type: "section", + text: { + type: "mrkdwn", + text: `*Customer*\n${shippingAddress.first_name} ${ + shippingAddress.last_name + }\n${order.email}\n*Destination*\n${ + shippingAddress.address_1 + }\n${ + shippingAddress.city + }, ${(shippingAddress.country_code as string).toUpperCase()}`, + }, + }) + } + + blocks.push( + { + type: "section", + text: { + type: "mrkdwn", + text: `*Subtotal*\t${await this.getDisplayAmount(order.subtotal as number, order.currency_code)}\n*Shipping*\t${ + await this.getDisplayAmount(order.shipping_total as number, order.currency_code) + }\n*Discount Total*\t${await this.getDisplayAmount( + order.discount_total as number, + order.currency_code + )}\n*Tax*\t${await this.getDisplayAmount(order.tax_total as number, order.currency_code)}\n*Total*\t${ + await this.getDisplayAmount(order.total as number, order.currency_code) + }`, + }, + }, + { + type: "divider", + } + ) + + await Promise.all( + order.items?.map(async (item) => { + const line: Record = { + type: "section", + text: { + type: "mrkdwn", + text: `*${item.title}*\n${item.quantity} x ${await this.getDisplayAmount( + item.unit_price as number, + order.currency_code + )}`, + }, + } + + if (item.thumbnail) { + const url = item.thumbnail + + line.accessory = { + type: "image", + alt_text: "Item", + image_url: url, + } + } + + blocks.push(line) + blocks.push({ + type: "divider", + }) + }) || [] + ) + + await axios.post(this.options.webhook_url, { + text: `Order ${order.display_id} was created`, + blocks, + }) + + return { + id: order.id, + } + } +} +``` + +The `sendOrderNotification` method receives a `ProviderSendNotificationDTO` object, which is the object passed to the `send` method that you'll implement next. The object has a `data` property that contains the order data. + +In the method, you format the Slack message to show the order's ID, customer information, shipping address, order items, and total amounts. You also add a link to the order's details page in the Medusa Admin dashboard. + +Finally, you send the formatted message to Slack using the `axios.post` method with the configured webhook URL. + +#### send Method + +You can now implement the `send` method, which is the method that Medusa calls to send notifications using the provider. Add the `send` method to the `SlackNotificationProviderService` class: + +```ts title="src/modules/slack/service.ts" +class SlackNotificationProviderService extends AbstractNotificationProviderService { + // ... + async send( + notification: ProviderSendNotificationDTO + ): Promise { + const { template } = notification + + switch (template) { + case "order-created": + return this.sendOrderNotification(notification) + default: + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Template ${template} not supported` + ) + } + } +} +``` + +The `send` method receives a `ProviderSendNotificationDTO` object, which contains the notification data and the template to use for sending the notification. + +The method receives other parameters, which you can find in the [Create Notification Module Provider](https://docs.medusajs.com/references/notification-provider-module#send/index.html.md) guide. + +In the method, you check the `template` property of the notification object. If it's `order-created`, you call the `sendOrderNotification` method to send the notification. Otherwise, you throw an error indicating that the template is not supported. + +### f. Export Module Definition + +You've now finished implementing the necessary methods for the Slack Notification Module Provider. + +The final piece to a module is its definition, which you export in an `index.ts` file at the module's root directory. This definition tells Medusa the module's details, including its service. + +To create the module's definition, create the file `src/modules/slack/index.ts` with the following content: + +```ts title="src/modules/slack/index.ts" +import SlackNotificationProvider from "./service" +import { + ModuleProvider, + Modules, +} from "@medusajs/framework/utils" + +export default ModuleProvider(Modules.NOTIFICATION, { + services: [SlackNotificationProvider], +}) +``` + +You use `ModuleProvider` from the Modules SDK to create the module provider's definition. It accepts two parameters: + +1. The name of the module that this provider belongs to, which is `Modules.NOTIFICATION` in this case. +2. An object with a required property `services` indicating the Module Provider's services. + +### g. Add Module Provider to Medusa's Configurations + +Once you finish building the module, add it to Medusa's configurations to start using it. + +In `medusa-config.ts`, add a `modules` property: + +```ts title="medusa-config.ts" highlights={configHighlights} +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "@medusajs/medusa/notification", + options: { + providers: [ + { + resolve: "./src/modules/slack", + id: "slack", + options: { + channels: ["slack"], + webhook_url: process.env.SLACK_WEBHOOK_URL, + admin_url: process.env.SLACK_ADMIN_URL, + }, + }, + ], + }, + }, + ], +}) +``` + +To pass a Notification Module Provider to the Notification Module, you add the `modules` property to the Medusa configuration and pass the Notification Module in its value. + +The Notification Module accepts a `providers` option, which is an array of Notification Module Providers to register. + +To register the Slack Notification Module Provider, you add an object to the `providers` array with the following properties: + +- `resolve`: The NPM package or path to the module provider. In this case, it's the path to the `src/modules/slack` directory. +- `id`: The ID of the module provider. The Notification Module Provider is then registered with the ID `np_{identifier}_{id}`, where: + - `{identifier}`: The identifier static property defined in the Module Provider's service, which is `slack` in this case. + - `{id}`: The ID set in this configuration, which is also `slack` in this case. +- `options`: The options to pass to the module provider. These are the options you defined in the `Options` interface of the module provider's service. + - You must also set a `channel` option that indicates the channel this provider is used to send notifications. + +### h. Set Options as Environment Variables + +Next, you'll set the options you passed to the Slack Notification Module Provider as environment variables. + +To set the webhook URL, you need to create a Slack application and configure a webhook URL. To do that: + +1. Go to your [Slack Apps](https://api.slack.com/apps) page. +2. Click the "Create New App" button. + +![Create New App button on Slack Apps page](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940543/Medusa%20Resources/CleanShot_2025-06-03_at_10.43.01_2x_zadmwn.png) + +3. In the pop-up, choose "From scratch". +4. Enter the app's name and select the workspace to install it in. +5. Once you're done, click the "Create App" button. + +![Create App pop-up](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940602/Medusa%20Resources/CleanShot_2025-06-03_at_10.43.52_2x_edqawv.png) + +6. In the app's settings, go to the "Incoming Webhooks" section. +7. Enable the "Activate Incoming Webhooks" toggle. + +![Enable Incoming Webhooks toggle](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940647/Medusa%20Resources/CleanShot_2025-06-03_at_10.44.07_2x_rykabg.png) + +8. In the "Webhook URLs for Your Workspace" section, click the "Add New Webhook" button. + +![Add New Webhook button](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940671/Medusa%20Resources/CleanShot_2025-06-03_at_10.44.33_2x_e7qk8o.png) + +9. Select the channel or conversation to send notifications to and click the "Allow" button. +10. Copy the generated webhook URL. + +![Copy the generated webhook URL](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940718/Medusa%20Resources/CleanShot_2025-06-03_at_10.45.51_2x_xet5u5.png) + +Then, in the `.env` file of your Medusa application, set the `SLACK_WEBHOOK_URL` and `SLACK_ADMIN_URL` environment variables: + +```shell title=".env" +SLACK_WEBHOOK_URL=https://hooks.slack.com/... +SLACK_ADMIN_URL=http://localhost:9000/app +``` + +In development, the Medusa Admin dashboard is running on `http://localhost:9000/app`, so you can use that URL for the `SLACK_ADMIN_URL` environment variable. + +You'll test out the integration when you handle the `order.placed` event in the next step. + +*** + +## Step 3: Handle the `order.placed` Event + +Now that you've integrated Slack with Medusa, you can send notifications to Slack at any point, including when an order is created. + +To send a notification to Slack when an order is created, you will: + +- Implement the order details retrieval and notification sending with Slack in a [workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). A workflow is a series of actions, called steps, that complete a task with rollback and retry mechanisms. +- Listen to the `order.placed` event in a [subscriber](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). A subscriber is an asynchronous function that listens to events to perform actions, such as execute a workflow, when the event is emitted. + +### a. Create the Workflow + +The workflow to send notifications to Slack will have the following steps: + +- [useQueryGraphStep](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve the order details. +- [sendNotificationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/sendNotificationsStep/index.html.md): Send a notification with a configured provider. + +Medusa provides both steps in its `@medusajs/medusa/core-flows` package. + +So, to create the workflow, create the file `src/workflows/order-placed-notification.ts` with the following content: + +```ts title="src/workflows/order-placed-notification.ts" highlights={orderPlacedNotificationWorkflowHighlights} +import { + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { sendNotificationsStep, useQueryGraphStep } from "@medusajs/medusa/core-flows" + +type WorkflowInput = { + id: string +} + +export const orderPlacedNotificationWorkflow = createWorkflow( + "order-placed-notification", + ({ id }: WorkflowInput) => { + // @ts-ignore + const { data: orders } = useQueryGraphStep({ + entity: "order", + fields: [ + "id", + "display_id", + "email", + "shipping_address.*", + "subtotal", + "shipping_total", + "currency_code", + "discount_total", + "tax_total", + "total", + "items.*", + "original_total", + ], + filters: { + id, + }, + }) + + sendNotificationsStep([{ + to: "slack-channel", // This will be configured in the Slack app + channel: "slack", + template: "order-created", + data: { + order: orders[0], + }, + }]) + } +) +``` + +You create a workflow using `createWorkflow` from the Workflows SDK. It accepts the workflow's unique name as a first parameter. + +It accepts as a second parameter a constructor function, which is the workflow's implementation. The function can accept input, which in this case is an object holding the ID of the order placed. + +In the workflow's constructor function, you: + +- Retrieve the order's details using the `useQueryGraphStep`. This step uses Medusa's [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md) tool to retrieve data across modules. You pass it the order ID to retrieve. +- Send a notification using the `sendNotificationsStep`. You pass the step an array of notifications to send, and each notification is an object with the following properties: + - `to`: The channel to send the notification to. Since you configure this in the Slack app, you just set it to `slack-channel`. + - `channel`: The channel to use for sending the notification. By setting it to `slack`, the Notification Module will use the Slack Notification Module Provider to send the notification. + - `template`: The template to use for sending the notification, which is `order-created` in this case. + - `data`: The data payload to pass with the notification, which contains the order details. + +You now have a workflow that retrieves the order details and sends a notification to Slack when an order is placed. + +Refer to the [Workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md) documentation to learn more about workflows and steps. + +### b. Create the Subscriber + +To execute the workflow when an order is placed, you need to create a subscriber that listens to the `order.placed` event and executes the workflow. + +To create the subscriber, create the file `src/subscribers/order-placed.ts` with the following content: + +```ts title="src/subscribers/order-placed.ts" +import { SubscriberArgs, SubscriberConfig } from "@medusajs/framework" +import { + orderPlacedNotificationWorkflow, +} from "../workflows/order-placed-notification" + +export default async function orderPlacedHandler({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + await orderPlacedNotificationWorkflow(container) + .run({ + input: data, + }) +} + +export const config: SubscriberConfig = { + event: "order.placed", +} +``` + +A subscriber file must export: + +1. An asynchronous function, which is the subscriber that is executed when the event is emitted. +2. A configuration object that holds the name of the event that the subscriber listens to, which is `order.placed` in this case. + +The subscriber function receives an object as a parameter that has the following properties: + +- `event`: An object that holds the event's data payload. The payload of the `order.placed` event is the ID of the order placed. +- `container`: The [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md) to access the Framework and commerce tools. + +In the subscriber function, you execute the `orderPlacedNotificationWorkflow` by invoking it, passing the Medusa container as a parameter. Then, you chain a `run` method, passing it the order ID from the event's data payload as input. + +Refer to the [Events and Subscribers](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md) documentation to learn more about creating subscribers. + +### Test it Out + +You'll now test out the integration by placing an order using the [Next.js Starter Storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md). + +The Next.js Starter Storefront was installed in a separate directory from Medusa. The directory's name is `{your-project}-storefront`. + +So, if your Medusa application's directory is `medusa-slack`, you can find the storefront by going back to the parent directory and changing to the `medusa-slack-storefront` directory: + +```bash +cd ../medusa-slack-storefront # change based on your project name +``` + +First, start the Medusa application by running the following command in the Medusa application's directory: + +```bash npm2yarn badgeLabel="Medusa Application" badgeColor="green" +npm run dev +``` + +Then, start the Next.js Starter Storefront by running the following command in the storefront's directory: + +```bash npm2yarn badgeLabel="Next.js Starter Storefront" badgeColor="blue" +npm run dev +``` + +Next, open the Next.js Starter Storefront in your browser at `http://localhost:8000`. Add a product to the cart, proceed to checkout, and place an order. + +Afterwards, you'll receive a notification in the Slack channel you configured in the Slack app. The notification will contain the order details. + +You can also click on the order's ID in the notification to open the order's details page in the Medusa Admin dashboard. + +![Slack Notification Example](https://res.cloudinary.com/dza7lstvk/image/upload/v1748942706/Medusa%20Resources/CleanShot_2025-06-03_at_11.24.16_2x_umecps.png) + +*** + +## Next Steps + +You've now integrated Slack with Medusa to receive notifications about created orders. You can expand on this integration to send other types of notifications, such as order updates. Refer to the [Events Reference](https://docs.medusajs.com/references/events/index.html.md) for a list of events you can listen to in your subscribers. + +You can also customize the notifications to include more information or change the format of the messages. Refer to [Slack's documentation](https://api.slack.com/reference/surfaces/formatting) for more information on how to format messages in Slack. + +If you're new to Medusa, check out the [main documentation](https://docs.medusajs.com/docs/learn/index.html.md), where you'll get a more in-depth understanding of all the concepts you've used in this guide and more. + +To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md). + +### Troubleshooting + +If you encounter issues during your development, check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/index.html.md). + +### Getting Help + +If you encounter issues not covered in the troubleshooting guides: + +1. Visit the [Medusa GitHub repository](https://github.com/medusajs/medusa) to report issues or ask questions. +2. Join the [Medusa Discord community](https://discord.gg/medusajs) for real-time support from community members. +3. Contact the [sales team](https://medusajs.com/contact/) to get help from the Medusa team. + + # Integrations You can integrate any third-party service to Medusa, including storage services, notification systems, Content-Management Systems (CMS), etc… By integrating third-party services, you build flows and synchronize data around these integrations, making Medusa not only your commerce application, but a middleware layer between your data sources and operations. diff --git a/www/apps/resources/app/infrastructure-modules/analytics/local/page.mdx b/www/apps/resources/app/infrastructure-modules/analytics/local/page.mdx index a9b1222a7f..3377e00f25 100644 --- a/www/apps/resources/app/infrastructure-modules/analytics/local/page.mdx +++ b/www/apps/resources/app/infrastructure-modules/analytics/local/page.mdx @@ -37,7 +37,7 @@ module.exports = defineConfig({ { resolve: "@medusajs/analytics-local", id: "local", - } + }, ], }, }, diff --git a/www/apps/resources/app/infrastructure-modules/analytics/posthog/page.mdx b/www/apps/resources/app/infrastructure-modules/analytics/posthog/page.mdx index 2b856abf5b..79c812b3f9 100644 --- a/www/apps/resources/app/infrastructure-modules/analytics/posthog/page.mdx +++ b/www/apps/resources/app/infrastructure-modules/analytics/posthog/page.mdx @@ -58,7 +58,7 @@ module.exports = defineConfig({ posthogEventsKey: process.env.POSTHOG_EVENTS_API_KEY, posthogHost: process.env.POSTHOG_HOST, }, - } + }, ], }, }, diff --git a/www/apps/resources/app/integrations/guides/segment/page.mdx b/www/apps/resources/app/integrations/guides/segment/page.mdx index deb43f2ca5..9798a160df 100644 --- a/www/apps/resources/app/integrations/guides/segment/page.mdx +++ b/www/apps/resources/app/integrations/guides/segment/page.mdx @@ -205,14 +205,14 @@ class SegmentAnalyticsProviderService extends AbstractAnalyticsProviderService { userId: data.actor_id, anonymousId, traits, - context: data.properties + context: data.properties, }) } else { this.client.identify({ userId: data.actor_id, anonymousId, traits, - context: data.properties + context: data.properties, }) } } diff --git a/www/apps/resources/app/integrations/guides/slack/page.mdx b/www/apps/resources/app/integrations/guides/slack/page.mdx new file mode 100644 index 0000000000..94b5f5c26b --- /dev/null +++ b/www/apps/resources/app/integrations/guides/slack/page.mdx @@ -0,0 +1,735 @@ +--- +sidebar_label: "Integrate Slack" +tags: + - server + - tutorial + - notification +products: + - order +--- + +import { Card, Prerequisites, Details, WorkflowDiagram } from "docs-ui" +import { Github, PlaySolid } from "@medusajs/icons" + +export const metadata = { + title: `Integrate Slack (Notification) with Medusa`, +} + +# {metadata.title} + +In this tutorial, you'll learn how to integrate Slack with Medusa to receive notifications about created orders. + +When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. Medusa's architecture facilitates integrating third-party services to customize Medusa's infrastructure for your business needs. + +Medusa's [Notification Module](../../../infrastructure-modules/notification/page.mdx) allows you to customize Medusa's infrastructure to send notifications using the third-party provider that fits your business needs, such as [Slack](https://slack.com/). + +In this tutorial, you'll integrate Slack with Medusa to receive notifications about created orders. + +## Summary + +By following this tutorial, you'll learn how to: + +- Install and set up Medusa. +- Integrate Slack with Medusa. +- Handle Medusa's `order.placed` event to send notifications to Slack. + +You can follow this tutorial whether you're new to Medusa or an advanced Medusa developer. + +![Diagram showcasing the flow of the integration between Medusa and Slack](https://res.cloudinary.com/dza7lstvk/image/upload/v1748943447/slack-integration-overview_vkdijx.jpg) + + + +--- + +## Step 1: Install a Medusa Application + + + +Start by installing the Medusa application on your machine with the following command: + +```bash +npx create-medusa-app@latest +``` + +First, you'll be asked for the project's name. Then, when prompted about installing the [Next.js Starter Storefront](../../../nextjs-starter/page.mdx), choose "Yes." + +Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name and the Next.js Starter Storefront in a separate directory named `{project-name}-storefront`. + + + +The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](!docs!/learn/fundamentals/api-routes). Learn more in [Medusa's Architecture documentation](!docs!/learn/introduction/architecture). + + + +Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterwards, you can log in with the new user and explore the dashboard. + + + +Check out the [troubleshooting guides](../../../troubleshooting/create-medusa-app-errors/page.mdx) for help. + + + +--- + +## Step 2: Create Slack Module Provider + +To integrate third-party services into Medusa, you create a custom [module](!docs!/learn/fundamentals/modules). A module is a reusable package with functionalities related to a single feature or domain. + +Medusa's [Notification Module](../../../infrastructure-modules/notification/page.mdx) provides an interface to send notifications in your Medusa application. It delegates the actual sending of notifications to the underlying provider, such as Slack. + +In this step, you'll integrate Slack as a Notification Module Provider. Later, you'll use it to send a notification when an order is created. + + + +Refer to the [Modules](!docs!/learn/fundamentals/modules) documentation to learn more about modules in Medusa. + + + +### a. Install Axios + +To send requests to Slack, you'll use the [Axios](https://axios-http.com/) library. So, run the following command to install it in your Medusa application: + +```bash npm2yarn +npm install axios +``` + +You'll use Axios in the module's service. + +### b. Create Module Directory + +A module is created under the `src/modules` directory of your Medusa application. So, create the directory `src/modules/slack`. + +### c. Create Slack Module's Service + +A module has a service that contains its logic. For Notification Module Providers, the service implements the logic to send notifications with a third-party service. + +To create the service of the Slack Notification Module Provider, create the file `src/modules/slack/service.ts` with the following content: + +export const serviceHighlights = [ + ["6", "webhook_url", "The Slack webhook URL to send notifications to."], + ["7", "admin_url", "The URL of the Medusa Admin dashboard, used to add links in the notifications."], + ["13", "identifier", "The unique identifier for the module provider."], +] + +```ts title="src/modules/slack/service.ts" highlights={serviceHighlights} +import { + AbstractNotificationProviderService, +} from "@medusajs/framework/utils" + +type Options = { + webhook_url: string + admin_url: string +} + +type InjectedDependencies = {} + +class SlackNotificationProviderService extends AbstractNotificationProviderService { + static identifier = "slack" + protected options: Options + + constructor(container: InjectedDependencies, options: Options) { + super() + this.options = options + } +} +``` + +A Notification Module Provider's service must extend the `AbstractNotificationProviderService` class. You'll implement its methods in a bit. + +The service must also have an `identifier` static property, which is a unique identifier for the module. This identifier is used when registering the module in the [Medusa container](!docs!/learn/fundamentals/medusa-container). + +The service's constructor receives two parameters: + +- `container`: The [module's container](!docs!/learn/fundamentals/modules/container) that contains Framework resources available to the module. You don't need to access any resources for this provider. +- `options`: Options that are passed to the module provider when it's registered in Medusa's configurations. You define the following options: + - `webhook_url`: The Slack webhook URL to send notifications to. + - `admin_url`: The URL of the Medusa Admin dashboard, which you'll use to add links in the notifications. + + + +You'll learn how to set these options when you [add the module provider to Medusa's configurations](#g-add-module-provider-to-medusas-configurations). + + + +In the constructor, you set the `options` property to the passed options. + +In the next sections, you'll implement the methods of the `AbstractNotificationProviderService` class. + + + +Refer to the [Create Notification Module Provider](/references/notification-provider-module) guide for detailed information about the methods. + + + +### d. Implement validateOptions Method + +The `validateOptions` method is used to validate the options passed to the module provider. If the method throws an error, the Medusa application won't start. + +So, add the `validateOptions` method to the `SlackNotificationProviderService` class: + +```ts title="src/modules/slack/service.ts" +// other imports... +import { + MedusaError, +} from "@medusajs/framework/utils" + +class SlackNotificationProviderService extends AbstractNotificationProviderService { + // ... + static validateOptions(options: Record): void | never { + if (!options.webhook_url) { + throw new MedusaError( + MedusaError.Types.INVALID_ARGUMENT, + "Webhook URL is required" + ) + } + if (!options.admin_url) { + throw new MedusaError( + MedusaError.Types.INVALID_ARGUMENT, + "Admin URL is required" + ) + } + } +} +``` + +The `validateOptions` method receives the options passed to the module provider as a parameter. + +In the method, you throw an error if any of the options are not set. + +### e. Implement send Method + +When the Medusa application needs to send a notification through a channel (such as Slack), it calls the `send` method of the channel's module provider. + +You'll first add helper methods that you'll use in the `send` method. Then, you'll implement the `send` method itself. + +#### getDisplayAmount Method + +The first method you'll add is a method to format amounts for displaying them in notifications. So, add the `getDisplayAmount` method to the `SlackNotificationProviderService` class: + +```ts title="src/modules/slack/service.ts" +class SlackNotificationProviderService extends AbstractNotificationProviderService { + // ... + private async getDisplayAmount(amount: number, currencyCode: string) { + return Intl.NumberFormat("en-US", { + style: "currency", + currency: currencyCode, + }).format(amount) + } +} +``` + +The `getDisplayAmount` method receives an amount and a currency code and returns the formatted amount as a string. + +#### sendOrderNotification Method + +Next, you'll add a method to format a Slack message for created orders. So, add the `sendOrderNotification` method to the `SlackNotificationProviderService` class: + +export const sendOrderNotificationHighlights = [ + ["12", "order", "The order to send a notification for."], + ["20", "blocks", "Format the slack message as blocks."], + ["96", "axios", "Send the formatted message to Slack using Axios."], +] + +```ts title="src/modules/slack/service.ts" highlights={sendOrderNotificationHighlights} +// other imports... +import { + OrderDTO, + ProviderSendNotificationDTO, + ProviderSendNotificationResultsDTO, +} from "@medusajs/framework/types" +import axios from "axios" + +class SlackNotificationProviderService extends AbstractNotificationProviderService { + // ... + async sendOrderNotification(notification: ProviderSendNotificationDTO) { + const order = notification.data?.order as OrderDTO + if (!order) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + "Order not found" + ) + } + const shippingAddress = order.shipping_address + const blocks: Record[] = [ + { + type: "section", + text: { + type: "mrkdwn", + text: `Order *<${this.options.admin_url}/orders/${order.id}|#${order.display_id}>* has been created.`, + }, + }, + ] + + if (shippingAddress) { + blocks.push({ + type: "section", + text: { + type: "mrkdwn", + text: `*Customer*\n${shippingAddress.first_name} ${ + shippingAddress.last_name + }\n${order.email}\n*Destination*\n${ + shippingAddress.address_1 + }\n${ + shippingAddress.city + }, ${(shippingAddress.country_code as string).toUpperCase()}`, + }, + }) + } + + blocks.push( + { + type: "section", + text: { + type: "mrkdwn", + text: `*Subtotal*\t${await this.getDisplayAmount(order.subtotal as number, order.currency_code)}\n*Shipping*\t${ + await this.getDisplayAmount(order.shipping_total as number, order.currency_code) + }\n*Discount Total*\t${await this.getDisplayAmount( + order.discount_total as number, + order.currency_code + )}\n*Tax*\t${await this.getDisplayAmount(order.tax_total as number, order.currency_code)}\n*Total*\t${ + await this.getDisplayAmount(order.total as number, order.currency_code) + }`, + }, + }, + { + type: "divider", + } + ) + + await Promise.all( + order.items?.map(async (item) => { + const line: Record = { + type: "section", + text: { + type: "mrkdwn", + text: `*${item.title}*\n${item.quantity} x ${await this.getDisplayAmount( + item.unit_price as number, + order.currency_code + )}`, + }, + } + + if (item.thumbnail) { + const url = item.thumbnail + + line.accessory = { + type: "image", + alt_text: "Item", + image_url: url, + } + } + + blocks.push(line) + blocks.push({ + type: "divider", + }) + }) || [] + ) + + await axios.post(this.options.webhook_url, { + text: `Order ${order.display_id} was created`, + blocks, + }) + + return { + id: order.id, + } + } +} +``` + +The `sendOrderNotification` method receives a `ProviderSendNotificationDTO` object, which is the object passed to the `send` method that you'll implement next. The object has a `data` property that contains the order data. + +In the method, you format the Slack message to show the order's ID, customer information, shipping address, order items, and total amounts. You also add a link to the order's details page in the Medusa Admin dashboard. + +Finally, you send the formatted message to Slack using the `axios.post` method with the configured webhook URL. + +#### send Method + +You can now implement the `send` method, which is the method that Medusa calls to send notifications using the provider. Add the `send` method to the `SlackNotificationProviderService` class: + +```ts title="src/modules/slack/service.ts" +class SlackNotificationProviderService extends AbstractNotificationProviderService { + // ... + async send( + notification: ProviderSendNotificationDTO + ): Promise { + const { template } = notification + + switch (template) { + case "order-created": + return this.sendOrderNotification(notification) + default: + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Template ${template} not supported` + ) + } + } +} +``` + +The `send` method receives a `ProviderSendNotificationDTO` object, which contains the notification data and the template to use for sending the notification. + + + +The method receives other parameters, which you can find in the [Create Notification Module Provider](/references/notification-provider-module#send) guide. + + + +In the method, you check the `template` property of the notification object. If it's `order-created`, you call the `sendOrderNotification` method to send the notification. Otherwise, you throw an error indicating that the template is not supported. + +### f. Export Module Definition + +You've now finished implementing the necessary methods for the Slack Notification Module Provider. + +The final piece to a module is its definition, which you export in an `index.ts` file at the module's root directory. This definition tells Medusa the module's details, including its service. + +To create the module's definition, create the file `src/modules/slack/index.ts` with the following content: + +```ts title="src/modules/slack/index.ts" +import SlackNotificationProvider from "./service" +import { + ModuleProvider, + Modules, +} from "@medusajs/framework/utils" + +export default ModuleProvider(Modules.NOTIFICATION, { + services: [SlackNotificationProvider], +}) +``` + +You use `ModuleProvider` from the Modules SDK to create the module provider's definition. It accepts two parameters: + +1. The name of the module that this provider belongs to, which is `Modules.NOTIFICATION` in this case. +2. An object with a required property `services` indicating the Module Provider's services. + +### g. Add Module Provider to Medusa's Configurations + +Once you finish building the module, add it to Medusa's configurations to start using it. + +In `medusa-config.ts`, add a `modules` property: + +export const configHighlights = [ + ["10", "id", "The ID of the module provider, used to register it."], + ["12", "channels", "The channels that this provider is used to send notifications."], + ["13", "webhook_url", "The Slack webhook URL to send notifications to."], + ["14", "admin_url", "The URL of the Medusa Admin dashboard, used to add links in the notifications."] +] + +```ts title="medusa-config.ts" highlights={configHighlights} +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "@medusajs/medusa/notification", + options: { + providers: [ + { + resolve: "./src/modules/slack", + id: "slack", + options: { + channels: ["slack"], + webhook_url: process.env.SLACK_WEBHOOK_URL, + admin_url: process.env.SLACK_ADMIN_URL, + }, + }, + ], + }, + }, + ], +}) +``` + +To pass a Notification Module Provider to the Notification Module, you add the `modules` property to the Medusa configuration and pass the Notification Module in its value. + +The Notification Module accepts a `providers` option, which is an array of Notification Module Providers to register. + +To register the Slack Notification Module Provider, you add an object to the `providers` array with the following properties: + +- `resolve`: The NPM package or path to the module provider. In this case, it's the path to the `src/modules/slack` directory. +- `id`: The ID of the module provider. The Notification Module Provider is then registered with the ID `np_{identifier}_{id}`, where: + - `{identifier}`: The identifier static property defined in the Module Provider's service, which is `slack` in this case. + - `{id}`: The ID set in this configuration, which is also `slack` in this case. +- `options`: The options to pass to the module provider. These are the options you defined in the `Options` interface of the module provider's service. + - You must also set a `channel` option that indicates the channel this provider is used to send notifications. + +### h. Set Options as Environment Variables + +Next, you'll set the options you passed to the Slack Notification Module Provider as environment variables. + +To set the webhook URL, you need to create a Slack application and configure a webhook URL. To do that: + +1. Go to your [Slack Apps](https://api.slack.com/apps) page. +2. Click the "Create New App" button. + +![Create New App button on Slack Apps page](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940543/Medusa%20Resources/CleanShot_2025-06-03_at_10.43.01_2x_zadmwn.png) + +3. In the pop-up, choose "From scratch". +4. Enter the app's name and select the workspace to install it in. +5. Once you're done, click the "Create App" button. + +![Create App pop-up](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940602/Medusa%20Resources/CleanShot_2025-06-03_at_10.43.52_2x_edqawv.png) + +6. In the app's settings, go to the "Incoming Webhooks" section. +7. Enable the "Activate Incoming Webhooks" toggle. + +![Enable Incoming Webhooks toggle](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940647/Medusa%20Resources/CleanShot_2025-06-03_at_10.44.07_2x_rykabg.png) + +8. In the "Webhook URLs for Your Workspace" section, click the "Add New Webhook" button. + +![Add New Webhook button](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940671/Medusa%20Resources/CleanShot_2025-06-03_at_10.44.33_2x_e7qk8o.png) + +9. Select the channel or conversation to send notifications to and click the "Allow" button. +10. Copy the generated webhook URL. + +![Copy the generated webhook URL](https://res.cloudinary.com/dza7lstvk/image/upload/v1748940718/Medusa%20Resources/CleanShot_2025-06-03_at_10.45.51_2x_xet5u5.png) + +Then, in the `.env` file of your Medusa application, set the `SLACK_WEBHOOK_URL` and `SLACK_ADMIN_URL` environment variables: + +```shell title=".env" +SLACK_WEBHOOK_URL=https://hooks.slack.com/... +SLACK_ADMIN_URL=http://localhost:9000/app +``` + +In development, the Medusa Admin dashboard is running on `http://localhost:9000/app`, so you can use that URL for the `SLACK_ADMIN_URL` environment variable. + +You'll test out the integration when you handle the `order.placed` event in the next step. + +--- + +## Step 3: Handle the `order.placed` Event + +Now that you've integrated Slack with Medusa, you can send notifications to Slack at any point, including when an order is created. + +To send a notification to Slack when an order is created, you will: + +- Implement the order details retrieval and notification sending with Slack in a [workflow](!docs!/learn/fundamentals/workflows). A workflow is a series of actions, called steps, that complete a task with rollback and retry mechanisms. +- Listen to the `order.placed` event in a [subscriber](!docs!/learn/fundamentals/events-and-subscribers). A subscriber is an asynchronous function that listens to events to perform actions, such as execute a workflow, when the event is emitted. + +### a. Create the Workflow + +The workflow to send notifications to Slack will have the following steps: + + + +Medusa provides both steps in its `@medusajs/medusa/core-flows` package. + +So, to create the workflow, create the file `src/workflows/order-placed-notification.ts` with the following content: + +export const orderPlacedNotificationWorkflowHighlights = [ + ["14", "useQueryGraphStep", "Retrieve the order details using the Query Graph."], + ["35", "sendNotificationsStep", "Send a notification to Slack with the order details."] +] + +```ts title="src/workflows/order-placed-notification.ts" highlights={orderPlacedNotificationWorkflowHighlights} +import { + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { sendNotificationsStep, useQueryGraphStep } from "@medusajs/medusa/core-flows" + +type WorkflowInput = { + id: string +} + +export const orderPlacedNotificationWorkflow = createWorkflow( + "order-placed-notification", + ({ id }: WorkflowInput) => { + // @ts-ignore + const { data: orders } = useQueryGraphStep({ + entity: "order", + fields: [ + "id", + "display_id", + "email", + "shipping_address.*", + "subtotal", + "shipping_total", + "currency_code", + "discount_total", + "tax_total", + "total", + "items.*", + "original_total", + ], + filters: { + id, + }, + }) + + sendNotificationsStep([{ + to: "slack-channel", // This will be configured in the Slack app + channel: "slack", + template: "order-created", + data: { + order: orders[0], + }, + }]) + } +) +``` + +You create a workflow using `createWorkflow` from the Workflows SDK. It accepts the workflow's unique name as a first parameter. + +It accepts as a second parameter a constructor function, which is the workflow's implementation. The function can accept input, which in this case is an object holding the ID of the order placed. + +In the workflow's constructor function, you: + +- Retrieve the order's details using the `useQueryGraphStep`. This step uses Medusa's [Query](!docs!/learn/fundamentals/module-links/query) tool to retrieve data across modules. You pass it the order ID to retrieve. +- Send a notification using the `sendNotificationsStep`. You pass the step an array of notifications to send, and each notification is an object with the following properties: + - `to`: The channel to send the notification to. Since you configure this in the Slack app, you just set it to `slack-channel`. + - `channel`: The channel to use for sending the notification. By setting it to `slack`, the Notification Module will use the Slack Notification Module Provider to send the notification. + - `template`: The template to use for sending the notification, which is `order-created` in this case. + - `data`: The data payload to pass with the notification, which contains the order details. + +You now have a workflow that retrieves the order details and sends a notification to Slack when an order is placed. + + + +Refer to the [Workflows](!docs!/learn/fundamentals/workflows) documentation to learn more about workflows and steps. + + + +### b. Create the Subscriber + +To execute the workflow when an order is placed, you need to create a subscriber that listens to the `order.placed` event and executes the workflow. + +To create the subscriber, create the file `src/subscribers/order-placed.ts` with the following content: + +```ts title="src/subscribers/order-placed.ts" +import { SubscriberArgs, SubscriberConfig } from "@medusajs/framework" +import { + orderPlacedNotificationWorkflow, +} from "../workflows/order-placed-notification" + +export default async function orderPlacedHandler({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + await orderPlacedNotificationWorkflow(container) + .run({ + input: data, + }) +} + +export const config: SubscriberConfig = { + event: "order.placed", +} +``` + +A subscriber file must export: + +1. An asynchronous function, which is the subscriber that is executed when the event is emitted. +2. A configuration object that holds the name of the event that the subscriber listens to, which is `order.placed` in this case. + +The subscriber function receives an object as a parameter that has the following properties: + +- `event`: An object that holds the event's data payload. The payload of the `order.placed` event is the ID of the order placed. +- `container`: The [Medusa container](!docs!/learn/fundamentals/medusa-container) to access the Framework and commerce tools. + +In the subscriber function, you execute the `orderPlacedNotificationWorkflow` by invoking it, passing the Medusa container as a parameter. Then, you chain a `run` method, passing it the order ID from the event's data payload as input. + + + +Refer to the [Events and Subscribers](!docs!/learn/fundamentals/events-and-subscribers) documentation to learn more about creating subscribers. + + + +### Test it Out + +You'll now test out the integration by placing an order using the [Next.js Starter Storefront](../../../nextjs-starter/page.mdx). + + + +The Next.js Starter Storefront was installed in a separate directory from Medusa. The directory's name is `{your-project}-storefront`. + +So, if your Medusa application's directory is `medusa-slack`, you can find the storefront by going back to the parent directory and changing to the `medusa-slack-storefront` directory: + +```bash +cd ../medusa-slack-storefront # change based on your project name +``` + + + +First, start the Medusa application by running the following command in the Medusa application's directory: + +```bash npm2yarn badgeLabel="Medusa Application" badgeColor="green" +npm run dev +``` + +Then, start the Next.js Starter Storefront by running the following command in the storefront's directory: + +```bash npm2yarn badgeLabel="Next.js Starter Storefront" badgeColor="blue" +npm run dev +``` + +Next, open the Next.js Starter Storefront in your browser at `http://localhost:8000`. Add a product to the cart, proceed to checkout, and place an order. + +Afterwards, you'll receive a notification in the Slack channel you configured in the Slack app. The notification will contain the order details. + +You can also click on the order's ID in the notification to open the order's details page in the Medusa Admin dashboard. + +![Slack Notification Example](https://res.cloudinary.com/dza7lstvk/image/upload/v1748942706/Medusa%20Resources/CleanShot_2025-06-03_at_11.24.16_2x_umecps.png) + +--- + +## Next Steps + +You've now integrated Slack with Medusa to receive notifications about created orders. You can expand on this integration to send other types of notifications, such as order updates. Refer to the [Events Reference](/references/events) for a list of events you can listen to in your subscribers. + +You can also customize the notifications to include more information or change the format of the messages. Refer to [Slack's documentation](https://api.slack.com/reference/surfaces/formatting) for more information on how to format messages in Slack. + +If you're new to Medusa, check out the [main documentation](!docs!/learn), where you'll get a more in-depth understanding of all the concepts you've used in this guide and more. + +To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](../../../commerce-modules/page.mdx). + +### Troubleshooting + +If you encounter issues during your development, check out the [troubleshooting guides](../../../troubleshooting/page.mdx). + +### Getting Help + +If you encounter issues not covered in the troubleshooting guides: + +1. Visit the [Medusa GitHub repository](https://github.com/medusajs/medusa) to report issues or ask questions. +2. Join the [Medusa Discord community](https://discord.gg/medusajs) for real-time support from community members. +3. Contact the [sales team](https://medusajs.com/contact/) to get help from the Medusa team. diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs index 6281fbc2ea..784586a48f 100644 --- a/www/apps/resources/generated/edit-dates.mjs +++ b/www/apps/resources/generated/edit-dates.mjs @@ -6544,5 +6544,6 @@ export const generatedEditDates = { "references/js_sdk/admin/DraftOrder/methods/js_sdk.admin.DraftOrder.delete/page.mdx": "2025-06-05T19:05:54.961Z", "references/utils/Payment/variables/utils.Payment.PaymentEvents/page.mdx": "2025-06-05T19:05:53.491Z", "references/utils/types/utils.NormalizedRow/page.mdx": "2025-06-05T19:05:53.365Z", - "references/utils/utils.Payment/page.mdx": "2025-06-05T19:05:53.489Z" + "references/utils/utils.Payment/page.mdx": "2025-06-05T19:05:53.489Z", + "app/integrations/guides/slack/page.mdx": "2025-06-03T09:37:42.528Z" } \ No newline at end of file diff --git a/www/apps/resources/generated/files-map.mjs b/www/apps/resources/generated/files-map.mjs index 6a3599eeec..ef35e12656 100644 --- a/www/apps/resources/generated/files-map.mjs +++ b/www/apps/resources/generated/files-map.mjs @@ -891,6 +891,10 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/integrations/guides/shipstation/page.mdx", "pathname": "/integrations/guides/shipstation" }, + { + "filePath": "/www/apps/resources/app/integrations/guides/slack/page.mdx", + "pathname": "/integrations/guides/slack" + }, { "filePath": "/www/apps/resources/app/integrations/page.mdx", "pathname": "/integrations" diff --git a/www/apps/resources/generated/generated-infrastructure-modules-sidebar.mjs b/www/apps/resources/generated/generated-infrastructure-modules-sidebar.mjs index 8e85e98111..75dd4cb7c4 100644 --- a/www/apps/resources/generated/generated-infrastructure-modules-sidebar.mjs +++ b/www/apps/resources/generated/generated-infrastructure-modules-sidebar.mjs @@ -426,6 +426,14 @@ const generatedgeneratedInfrastructureModulesSidebarSidebar = { "title": "Integrate Resend", "children": [] }, + { + "loaded": true, + "isPathHref": true, + "type": "ref", + "title": "Integrate Slack", + "path": "https://docs.medusajs.com/resources/integrations/guides/slack", + "children": [] + }, { "loaded": true, "isPathHref": true, diff --git a/www/apps/resources/generated/generated-integrations-sidebar.mjs b/www/apps/resources/generated/generated-integrations-sidebar.mjs index c41a274f2b..4209009517 100644 --- a/www/apps/resources/generated/generated-integrations-sidebar.mjs +++ b/www/apps/resources/generated/generated-integrations-sidebar.mjs @@ -179,6 +179,14 @@ const generatedgeneratedIntegrationsSidebarSidebar = { "title": "SendGrid", "children": [] }, + { + "loaded": true, + "isPathHref": true, + "type": "link", + "path": "/integrations/guides/slack", + "title": "Slack", + "children": [] + }, { "loaded": true, "isPathHref": true, diff --git a/www/apps/resources/sidebars/integrations.mjs b/www/apps/resources/sidebars/integrations.mjs index f75c1bce40..301c91fbb8 100644 --- a/www/apps/resources/sidebars/integrations.mjs +++ b/www/apps/resources/sidebars/integrations.mjs @@ -122,6 +122,11 @@ export const integrationsSidebar = [ path: "/infrastructure-modules/notification/sendgrid", title: "SendGrid", }, + { + type: "link", + path: "/integrations/guides/slack", + title: "Slack", + }, { type: "ref", path: "/how-to-tutorials/tutorials/phone-auth#step-3-integrate-twilio-sms", diff --git a/www/packages/tags/src/tags/notification.ts b/www/packages/tags/src/tags/notification.ts index f5395cd037..103d1e93de 100644 --- a/www/packages/tags/src/tags/notification.ts +++ b/www/packages/tags/src/tags/notification.ts @@ -11,6 +11,10 @@ export const notification = [ "title": "Send Notification", "path": "https://docs.medusajs.com/resources/infrastructure-modules/notification/send-notification" }, + { + "title": "Integrate Slack", + "path": "https://docs.medusajs.com/resources/integrations/guides/slack" + }, { "title": "notifyOnFailureStep", "path": "https://docs.medusajs.com/resources/references/medusa-workflows/steps/notifyOnFailureStep" diff --git a/www/packages/tags/src/tags/product.ts b/www/packages/tags/src/tags/product.ts index 2f1dd0b509..24a039256e 100644 --- a/www/packages/tags/src/tags/product.ts +++ b/www/packages/tags/src/tags/product.ts @@ -71,6 +71,10 @@ export const product = [ "title": "Get Variant Inventory", "path": "https://docs.medusajs.com/resources/commerce-modules/product/guides/variant-inventory" }, + { + "title": "Implement Custom Line Item Pricing in Medusa", + "path": "https://docs.medusajs.com/resources/examples/guides/custom-item-price" + }, { "title": "Implement Product Reviews", "path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/product-reviews" diff --git a/www/packages/tags/src/tags/server.ts b/www/packages/tags/src/tags/server.ts index a3595fada5..f00521252a 100644 --- a/www/packages/tags/src/tags/server.ts +++ b/www/packages/tags/src/tags/server.ts @@ -91,6 +91,10 @@ export const server = [ "title": "Integrate Segment", "path": "https://docs.medusajs.com/resources/integrations/guides/segment" }, + { + "title": "Integrate Slack", + "path": "https://docs.medusajs.com/resources/integrations/guides/slack" + }, { "title": "Build Wishlist Plugin", "path": "https://docs.medusajs.com/resources/plugins/guides/wishlist" diff --git a/www/packages/tags/src/tags/tutorial.ts b/www/packages/tags/src/tags/tutorial.ts index 5f9fca3fc7..49100efcf8 100644 --- a/www/packages/tags/src/tags/tutorial.ts +++ b/www/packages/tags/src/tags/tutorial.ts @@ -55,6 +55,10 @@ export const tutorial = [ "title": "Integrate Segment", "path": "https://docs.medusajs.com/resources/integrations/guides/segment" }, + { + "title": "Integrate Slack", + "path": "https://docs.medusajs.com/resources/integrations/guides/slack" + }, { "title": "Build Wishlist Plugin", "path": "https://docs.medusajs.com/resources/plugins/guides/wishlist"