Skip to content

Commit

Permalink
docs: updates to digital product recipe (#9165)
Browse files Browse the repository at this point in the history
- Create workflow for fulfilling digital products that sends a notification + create a shipment to mark the fulfillment as fulfilled / shipped.
- Update the subscriber handling the `digital_product_order.created` event to use the workflow
- Add section on using the local notification module provider for testing purposes.
  • Loading branch information
shahednasser committed Sep 20, 2024
1 parent 2f3d15d commit a4b5d66
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1817,79 +1817,80 @@ In a later step, you’ll add an API route to allow customers to view and downlo

---

## Step 12: Handle the Digital Product Order Event
## Step 12: Fulfill Digital Order Workflow

In this step, you'll create a subscriber that listens to the `digital_product_order.created` event and sends a customer an email with the digital products they purchased.
In this step, you'll create a workflow that fulfills a digital order by sending a notification to the customer. Later, you'll execute this workflow in a subscriber.

Create the file `digital-product/src/subscribers/handle-digital-order.ts` with the following content:
The workflow has the following steps:

export const subscriberHighlight = [
["20", "notificationModuleService", "Resolve the Notification Module's service to use it later to send a notification."],
["22", "fileModuleService", "Resolve the File Module's service to use it later to retrieve a media's URL."],
["26", "query", "Run the query to retrieve the digital product order."]
]
1. Retrieve the digital product order's details. For this, you'll use the `useRemoteQueryStep` imported from `@medusajs/core-flows`.
2. Send a notification to the customer with the digital products to download.

```ts title="digital-product/src/subscribers/handle-digital-order.ts" highlights={subscriberHighlight} collapsibleLines="1-14" expandMoreLabel="Show Imports"
import type {
SubscriberArgs,
SubscriberConfig,
} from "@medusajs/medusa"
import {
INotificationModuleService,
IFileModuleService,
} from "@medusajs/types"
import {
Modules,
ContainerRegistrationKeys,
} from "@medusajs/utils"
import { MediaType } from "../modules/digital-product/types"
So, you only need to implement the second step.

async function digitalProductOrderCreatedHandler({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const query = container.resolve(ContainerRegistrationKeys.QUERY)
const notificationModuleService: INotificationModuleService = container
.resolve(Modules.NOTIFICATION)
const fileModuleService: IFileModuleService = container.resolve(
Modules.FILE
)
### Add Types

const { data: [digitalProductOrder] } = await query.graph({
entity: "digital_product_order",
fields: [
"*",
"products.*",
"products.medias.*",
"order.*",
],
filters: {
id: data.id,
},
})
Before creating the step, add to `src/modules/digital-product/types/index.ts` the following:

```ts
import { OrderDTO } from "@medusajs/types"

// ...

// TODO format and send notification
export type DigitalProductOrderData = {
id: string
status: OrderStatus
products?: DigitalProductData[]
order?: OrderDTO
}
```
export default digitalProductOrderCreatedHandler
This adds a type for a digital product order, which you'll use next.
export const config: SubscriberConfig = {
event: "digital_product_order.created",
### Create sendDigitalOrderNotificationStep
To create the step, create the file `src/workflows/fulfill-digital-order/steps/send-digital-order-notification.ts` with the following content:
```ts title="src/workflows/fulfill-digital-order/steps/send-digital-order-notification.ts" collapsibleLines="1-11" expandMoreLabel="Show Imports"
import {
createStep,
StepResponse
} from "@medusajs/workflows-sdk"
import {
INotificationModuleService,
IFileModuleService
} from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { DigitalProductOrderData, MediaType } from "../../../modules/digital-product/types"

type SendDigitalOrderNotificationStepInput = {
digital_product_order: DigitalProductOrderData
}

export const sendDigitalOrderNotificationStep = createStep(
"send-digital-order-notification",
async ({
digital_product_order: digitalProductOrder
}: SendDigitalOrderNotificationStepInput,
{ container }) => {
const notificationModuleService: INotificationModuleService = container
.resolve(ModuleRegistrationName.NOTIFICATION)
const fileModuleService: IFileModuleService = container.resolve(
ModuleRegistrationName.FILE
)

// TODO assemble notification
}
)
```

This adds a subscriber that listens to the `digital_product_order.created` event. For now, it just resolves dependencies and retrieves the digital product order.
This creates the `sendDigitalOrderNotificationStep` step that receives a digital product order as an input.

Next, replace the `TODO` with the following:
In the step, so far you resolve the main services of the Notification and File Modules.

export const subscriber2Highlights = [
["1", "notificationData", "Format the data to be sent as a notification payload."],
["10", "retrieveFile", "Retrieve the media's URL using the File Module's service."],
["22", "createNotifications", "Send the notification to the customer."],
["24", `"digital-order-template"`, "Replace with a real template ID."]
]
Replace the `TODO` with the following:

```ts highlights={subscriber2Highlights}
```ts title="src/workflows/fulfill-digital-order/steps/send-digital-order-notification.ts"
const notificationData = await Promise.all(
digitalProductOrder.products.map(async (product) => {
const medias = []
Expand All @@ -1906,44 +1907,169 @@ const notificationData = await Promise.all(

return {
name: product.name,
medias,
medias
}
})
)

await notificationModuleService.createNotifications({
// TODO send notification
```

In this snippet, you put together the data to send in the notification. You loop over the digital products in the order and retrieve the URL of their main files using the File Module.

Finally, replace the new `TODO` with the following:

```ts title="src/workflows/fulfill-digital-order/steps/send-digital-order-notification.ts"
const notification = await notificationModuleService.createNotifications({
to: digitalProductOrder.order.email,
template: "digital-order-template",
channel: "email",
data: {
products: notificationData,
},
products: notificationData
}
})

return new StepResponse(notification)
```

First, you format the data payload to send in the notification by retrieving the URLs of the purchased products' main medias. You use the File Module's service to retrieve the media URLs.
You use the `createNotifications` method of the Notification Module's main service to send an email using the installed provider.

Then, you use the Notification Module's service to send the notification as an email.
### Create Workflow

<Note title="Tip">
Create the workflow in the file `src/workflows/fulfill-digital-order/index.ts`:

Replace the `digital-order-template` with a real template ID from your third-party notification service.
export const fulfillWorkflowHighlights = [
["17", "useRemoteQueryStep", "Retrieve the digital product order's details."],
["33", "sendDigitalOrderNotificationStep", "Send a notification to the customer."]
]

</Note>
```ts title="src/workflows/fulfill-digital-order/index.ts" highlights={fulfillWorkflowHighlights} collapsibleLines="1-10" expandMoreLabel="Show Imports"
import {
createWorkflow,
WorkflowResponse
} from "@medusajs/workflows-sdk"
import {
useRemoteQueryStep
} from "@medusajs/core-flows"
import { sendDigitalOrderNotificationStep } from "./steps/send-digital-order-notification"

### Test Subscriber Out
type FulfillDigitalOrderWorkflowInput = {
id: string
}

To test out the subscriber, place an order with digital products. This triggers the `digital_product_order.created` event which executes the subscriber.
export const fulfillDigitalOrderWorkflow = createWorkflow(
"fulfill-digital-order",
({ id }: FulfillDigitalOrderWorkflowInput) => {
const digitalProductOrder = useRemoteQueryStep({
entry_point: "digital_product_order",
fields: [
"*",
"products.*",
"products.medias.*",
"order.*"
],
variables: {
filters: {
id,
},
},
list: false,
throw_if_key_not_found: true
})

<Note title="Tip">
sendDigitalOrderNotificationStep({
digital_product_order: digitalProductOrder
})

Check out the [integrations page](../../../../integrations/page.mdx) to find notification and file modules.
return new WorkflowResponse(
digitalProductOrder
)
}
)
```

</Note>
In the workflow, you:

1. Retrieve the digital product order's details using the `useRemoteQueryStep` imported from `@medusajs/core-flows`.
2. Send a notification to the customer with the digital product download links using the `sendDigitalOrderNotificationStep`.

### Configure Notification Module Provider

In the `sendDigitalOrderNotificationStep`, you use a notification provider configured for the `email` channel to send the notification.

Check out the [Integrations page](../../../../integrations/page.mdx) to find Notification Module Providers.

For testing purposes, add to `medusa-config.js` the following to use the Local Notification Module Provider:

```js title="medusa-config.js"
module.exports = defineConfig({
// ...
modules: {
// ...
[Modules.NOTIFICATION]: {
resolve: "@medusajs/notification",
options: {
providers: [
{
resolve: "@medusajs/notification-local",
id: "local",
options: {
name: "Local Notification Provider",
channels: ["email"],
},
},
],
},
},
}
})

```

---

## Step 13: Handle the Digital Product Order Event

In this step, you'll create a subscriber that listens to the `digital_product_order.created` event and executes the workflow from the above step.

Create the file `src/subscribers/handle-digital-order.ts` with the following content:

```ts title="src/subscribers/handle-digital-order.ts" collapsibleLines="1-8" expandMoreLabel="Show Imports"
import type {
SubscriberArgs,
SubscriberConfig,
} from "@medusajs/medusa"
import {
fulfillDigitalOrderWorkflow
} from "../workflows/fulfill-digital-order"

async function digitalProductOrderCreatedHandler({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
await fulfillDigitalOrderWorkflow(container).run({
input: {
id: data.id
}
})
}

export default digitalProductOrderCreatedHandler

export const config: SubscriberConfig = {
event: "digital_product_order.created",
}
```

This adds a subscriber that listens to the `digital_product_order.created` event. It executes the `fulfillDigitalOrderWorkflow` to send the customer an email and mark the order's fulfillment as fulfilled.

### Test Subscriber Out

To test out the subscriber, place an order with digital products. This triggers the `digital_product_order.created` event which executes the subscriber.

---

## Step 13: Create Store API Routes
## Step 14: Create Store API Routes

In this step, you’ll create three store API routes:

Expand Down Expand Up @@ -2172,7 +2298,7 @@ You’ll test out these API routes in the next step.

---

## Step 14: Customize Next.js Starter
## Step 15: Customize Next.js Starter

In this section, you’ll customize the [Next.js Starter storefront](../../../../nextjs-starter/page.mdx) to:

Expand Down
2 changes: 1 addition & 1 deletion www/apps/resources/generated/edit-dates.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export const generatedEditDates = {
"app/nextjs-starter/page.mdx": "2024-07-01T10:21:19+03:00",
"app/recipes/b2b/page.mdx": "2024-08-29T09:23:12.736Z",
"app/recipes/commerce-automation/page.mdx": "2024-08-05T07:24:27+00:00",
"app/recipes/digital-products/examples/standard/page.mdx": "2024-09-11T10:50:14.310Z",
"app/recipes/digital-products/examples/standard/page.mdx": "2024-09-17T14:30:02.190Z",
"app/recipes/digital-products/page.mdx": "2024-08-02T13:02:06+00:00",
"app/recipes/ecommerce/page.mdx": "2024-06-09T15:18:43+02:00",
"app/recipes/integrate-ecommerce-stack/page.mdx": "2024-08-05T07:24:27+00:00",
Expand Down

0 comments on commit a4b5d66

Please sign in to comment.