diff --git a/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/.gitignore b/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/.gitignore new file mode 100644 index 0000000000..13b6b556eb --- /dev/null +++ b/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/.gitignore @@ -0,0 +1,2 @@ +dist +.yarn \ No newline at end of file diff --git a/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/README.md b/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/README.md index db7fa1e8db..4ac6242c06 100644 --- a/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/README.md +++ b/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/README.md @@ -33,3 +33,11 @@ frontmatterData: { pagesPattern: "internal\\.", }, ``` + +## Building the Plugin + +Before using any command that makes use of this plugin, make sure to run the `build` command: + +```bash +yarn build +``` diff --git a/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/dist/index.js b/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/dist/index.js deleted file mode 100644 index 10bbf68223..0000000000 --- a/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/dist/index.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.load = void 0; -const typedoc_1 = require("typedoc"); -function load(app) { - app.options.addDeclaration({ - name: "frontmatterData", - help: "An object of key-value pairs to be added to frontmatter", - type: typedoc_1.ParameterType.Mixed, - defaultValue: {}, - validate: (value) => { - if (typeof value === 'string') { - //decode it with JSON to check if it's an object - value = JSON.parse(value); - } - if (!(typeof value === 'object' && - !Array.isArray(value) && - value !== null)) { - throw new Error("Value should be an object"); - } - } - }); - app.options.addDeclaration({ - name: "pagesPattern", - help: "A string of pages pattern. The pattern will be tested using RegExp to determine whether the frontmatterData will be added or not.", - type: typedoc_1.ParameterType.String, - defaultValue: "" - }); - app.renderer.on(typedoc_1.PageEvent.END, (page) => { - const patternStr = app.options.getValue("pagesPattern"); - const pattern = new RegExp(patternStr); - let frontmatterData = app.options.getValue("frontmatterData"); - if (typeof frontmatterData === 'string') { - frontmatterData = JSON.parse(frontmatterData); - } - const frontmatterDataEntries = Object.entries(frontmatterData); - if (!frontmatterDataEntries.length || !pattern.test(page.filename)) { - return; - } - let frontmatterStr = `---\n`; - for (const [key, value] of frontmatterDataEntries) { - frontmatterStr += `${key}: ${value}\n`; - } - frontmatterStr += `---\n\n`; - page.contents = frontmatterStr + page.contents; - }); -} -exports.load = load; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/dist/index.js.map b/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/dist/index.js.map deleted file mode 100644 index b99a5fae84..0000000000 --- a/docs-util/typedoc-plugins/typedoc-frontmatter-plugin/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAAgE;AAEhE,SAAgB,IAAI,CAAC,GAAgB;IACjC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC;QACvB,IAAI,EAAE,iBAAiB;QACvB,IAAI,EAAE,yDAAyD;QAC/D,IAAI,EAAE,uBAAa,CAAC,KAAK;QACzB,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,CAAC,KAAU,EAAE,EAAE;YACvB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;gBAC7B,gDAAgD;gBAChD,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;aAC3B;YAED,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;gBAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACrB,KAAK,KAAK,IAAI,CAAC,EAAE;gBACf,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;aAC7C;QACH,CAAC;KACJ,CAAC,CAAC;IAEH,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC;QACzB,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,mIAAmI;QACzI,IAAI,EAAE,uBAAa,CAAC,MAAM;QAC1B,YAAY,EAAE,EAAE;KACjB,CAAC,CAAA;IAEF,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,mBAAS,CAAC,GAAG,EAAE,CAAC,IAAe,EAAE,EAAE;QAC/C,MAAM,UAAU,GAAiB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;QACrE,MAAM,OAAO,GAAW,IAAI,MAAM,CAAC,UAAU,CAAC,CAAA;QAC9C,IAAI,eAAe,GAAiB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAA;QAC3E,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE;YACvC,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;SAC/C;QACD,MAAM,sBAAsB,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;QAE9D,IAAI,CAAC,sBAAsB,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;YAClE,OAAO;SACR;QAED,IAAI,cAAc,GAAG,OAAO,CAAA;QAE5B,KAAI,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,sBAAsB,EAAE;YAChD,cAAc,IAAI,GAAG,GAAG,KAAK,KAAK,IAAI,CAAA;SACvC;QAED,cAAc,IAAI,SAAS,CAAA;QAE3B,IAAI,CAAC,QAAQ,GAAG,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAA;IAClD,CAAC,CAAC,CAAC;AACP,CAAC;AAlDD,oBAkDC"} \ No newline at end of file diff --git a/docs/content/advanced/backend/subscribers/create-subscriber.md b/docs/content/advanced/backend/subscribers/create-subscriber.md index 5dcb25fa17..7a63a2151d 100644 --- a/docs/content/advanced/backend/subscribers/create-subscriber.md +++ b/docs/content/advanced/backend/subscribers/create-subscriber.md @@ -54,7 +54,20 @@ constructor({ productService, eventBusService }) { } ``` -You can then use `this.productService` anywhere in your subscriber’s methods. +You can then use `this.productService` anywhere in your subscriber’s methods. For example: + +```ts +handleOrder = async (data) => { + //... + const product = this.productService.list() +} +``` + +:::note + +When using attributes defined in the subscriber, such as the `productService` in the example above, you must use an arrow function to declare the method. Otherwise, the attribute will be undefined when used. + +::: ## What’s Next diff --git a/docs/content/advanced/backend/subscribers/events-list.md b/docs/content/advanced/backend/subscribers/events-list.md index 883fc0d91f..02953731bc 100644 --- a/docs/content/advanced/backend/subscribers/events-list.md +++ b/docs/content/advanced/backend/subscribers/events-list.md @@ -2681,6 +2681,55 @@ Object of the following format: +## Token Events + +This section holds all events related to tokens. + + + + + + + + + + + + + + + + + +
+Event Name + +Description + +Event Data Payload +
+ +`order-update-token.created` + + + +Triggered when a customer requests to claim an order and a token is created. + + + +Object of the following format: + +```js noReport noCopy +{ + old_email, //string email of order + new_customer_id, //string ID of customer + orders, //array of string IDs of orders + token, //string token used for verification +} +``` + +
+ ## User Events This section holds all events related to users. diff --git a/docs/content/advanced/ecommerce/handle-order-claim-event.md b/docs/content/advanced/ecommerce/handle-order-claim-event.md new file mode 100644 index 0000000000..5b5d08a750 --- /dev/null +++ b/docs/content/advanced/ecommerce/handle-order-claim-event.md @@ -0,0 +1,149 @@ +# How to Handle Order Claim Event + +In this document, you’ll learn how to handle the order claim event and send a confirmation email when the event is triggered. + +## Overview + +When a guest customer places an order, the order is not associated with a customer. It is associated with an email address. + +After the customer registers, later on, they can claim that order by providing the order’s ID. + +When the customer requests to claim the order, the event `order-update-token.created` is triggered on the Medusa server. This event should be used to send the customer a confirmation email. + +### What You’ll Learn + +In this document, you’ll learn how to handle the `order-update-token.created` event on the server to send the customer a confirmation email. + +## Prerequisites + +### Medusa Components + +It's assumed that you already have a Medusa server installed and set up. If not, you can follow the [quickstart guide](../../quickstart/quick-start.md) to get started. + +### Redis + +Redis is required for batch jobs to work. Make sure you [install Redis](../../tutorial/0-set-up-your-development-environment.mdx#redis) and [configure it with your Medusa server](../../usage/configurations.md#redis). + +### Notification Provider + +To send an email or another type of notification method, you must have a notification provider installed or configured. + +This document has an example using the [SendGrid](../../add-plugins/sendgrid.mdx) plugin. + +## Step 1: Create a Subscriber + +To subscribe to and handle an event, you must create a subscriber. + +:::tip + +You can learn more about subscribers in the [Subscribers](../backend/subscribers/overview.md) documentation. + +::: + +Create the file `src/subscribers/claim-order.ts` with the following content: + +```ts title=src/subscribers/claim-order.ts +import { EventBusService } from "@medusajs/medusa"; + +type InjectedDependencies = { + eventBusService: EventBusService, +} + +class ClaimOrderSubscriber { + constructor({ eventBusService }: InjectedDependencies) { + + } +} + +export default ClaimOrderSubscriber; +``` + +If you want to add any other dependencies, you can add them to the `InjectedDependencies` type. + +:::tip + +You can learn more about dependency injection in [this documentation](../backend/dependency-container/index.md). + +::: + +## Step 2: Subscribe to the Event + +In the subscriber you created, add the following in the `constructor`: + +```ts title=src/subscribers/claim-order.ts +constructor({ eventBusService }: InjectedDependencies) { + eventBusService.subscribe("order-update-token.created", this.handleRequestClaimOrder); +} +``` + +You use the `eventBusService` to subscribe to the `order-update-token.created` event. You pass the method `handleRequestClaimOrder` as a handler to that event. You’ll create this method in the next step. + +## Step 3: Create Event Handler + +In the subscriber, add a new method `handleRequestClaimOrder`: + +```ts title=src/subscribers/claim-order.ts +class ClaimOrderSubscriber { + //... + + handleRequestClaimOrder = async (data) { + //TODO: handle event + } +} + +export default ClaimOrderSubscriber; +``` + +The `handleRequestClaimOrder` event receives a `data` object as a parameter. This object holds the following properties: + +1. `old_email`: The email associated with the orders. +2. `new_customer_id`: The ID of the customer claiming the orders. +3. `orders`: An array of the order IDs that the customer is requesting to claim. +4. `token`: A verification token. This token is used to later verify the claim request and associate the order with the customer. + +In this method, you should typically send an email to the customer’s old email. In the email, you should link to a page in your storefront and pass the `token` as a parameter. + +The page would then send a request to the server to verify that the `token` is valid and associate the order with the customer. You can read more about how to implement this in your storefront in [this documentation](../storefront/implement-claim-order.mdx). + +## Example: Using SendGrid + +For example, you can implement this subscriber to send emails using SendGrid: + +```ts title=src/subscribers/claim-order.ts +import { EventBusService } from "@medusajs/medusa"; + +type InjectedDependencies = { + eventBusService: EventBusService, + sendgridService: any +} + +class ClaimOrderSubscriber { + protected sendGridService: any; + + constructor({ eventBusService, sendgridService }: InjectedDependencies) { + this.sendGridService = sendgridService; + eventBusService.subscribe("order-update-token.created", this.handleRequestClaimOrder); + } + + + handleRequestClaimOrder = async (data) => { + this.sendGridService.sendEmail({ + templateId: 'order-claim-confirmation', + from: 'hello@medusajs.com', + to: data.old_email, + data: { + link: `http://example-storefront.com/confirm-order-claim/${data.token}`, + //other data... + } + }) + } +} + +export default ClaimOrderSubscriber; +``` + +Notice how the `token` is passed to the storefront link as a parameter. + +## See Also + +- Learn [how to implement claim-order flow in your storefront](../storefront/implement-claim-order.mdx). \ No newline at end of file diff --git a/docs/content/advanced/storefront/implement-claim-order.mdx b/docs/content/advanced/storefront/implement-claim-order.mdx new file mode 100644 index 0000000000..0c223d556c --- /dev/null +++ b/docs/content/advanced/storefront/implement-claim-order.mdx @@ -0,0 +1,163 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# How to Implement Claim Order Flow + +In this document, you’ll learn how to implement the claim order flow in a storefront to allow customers to claim their orders. + +## Flow Overview + +When a guest customer places an order, their order is not associated with any customer. The order is only associated with an email that the guest customer provides during checkout. + +This email must be an email that isn’t used with an existing account. Also, this email cannot be used later on to create a new account. It can, however, be used to create another order as a guest customer. + +After this customer registers with a different email and logs in, they can claim their order by providing the order’s ID. An email will then be sent to the email address associated with the order. + +The email should contain a link to a page in the storefront, and the link should have a token as a parameter. This token will be used for verification. + +The customer must then click the link in the email they received. If the token is valid, the order will be associated with the customer. + +![Claim Order Flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1671040165/Medusa%20Docs/Diagrams/Claim_Order_Flow_ybf2ok.jpg) + +### What You’ll Learn + +In this document, you’ll learn how to implement two parts of this flow: + +1. Allow customers to claim their orders. +2. Verify a claim to an order. + +## Prerequisites + +### Medusa Components + +It's assumed that you already have a Medusa server installed and set up. If not, you can follow the [quickstart guide](../../quickstart/quick-start.md) to get started. + +It is also assumed you already have a storefront set up. It can be a custom storefront or one of Medusa’s storefronts. If you don’t have a storefront set up, you can install either the [Next.js](../../starters/nextjs-medusa-starter.md) or [Gatsby](../../starters/gatsby-medusa-starter.md) storefronts. + +### JS Client + +This guide includes code snippets to send requests to your Medusa server using Medusa’s JS Client and JavaScript’s Fetch API. + +If you follow the JS Client code blocks, it’s assumed you already have [Medusa’s JS Client installed](../../js-client/overview.md) and have [created an instance of the client](../../js-client/overview.md#configuration). + +### Handle Order Claim Request Event + +When the customer requests to claim the order, an event will be triggered. You should subscribe to this event to send a confirmation email to the customer when the event is triggered. + +You can learn how to implement this flow in [this documentation](../ecommerce/handle-order-claim-event.md). + +### Previous Steps + +It is assumed you already have an order placed by a guest customer. You can refer to the [Cart](../../guides/carts-in-medusa.mdx) and [Checkout](./how-to-implement-checkout-flow.mdx) implementation documentation to learn how to implement them in your storefront. + +In addition, it is assumed you already have a logged-in customer before performing the steps in this document. You can refer to the [API reference](/api/store/#tag/Auth/operation/PostAuth) for more details on that. + +## Request to Claim an Order + +When the customer wants to claim an order, they must supply its ID. + +To allow the customer to claim an order, send a request to the Claim an Order endpoint: + + + + +```tsx +medusa.orders.claimOrders({ + order_ids: [ + order_id, + ], +}) +.then(() => { + // successful +}) +.catch(() => { + // an error occurred +}); +``` + + + + +```tsx +fetch(`/store/orders/batch/customer/token`, { + method: 'POST', + credentials: 'include', + body: JSON.stringify({ + order_ids: [ + order_id, + ], + }), + headers: { + 'Content-Type': 'application/json' + } +}) +.then(() => { + //successful +}) +.catch(() => { + //display an error to the customer +}); +``` + + + + +This request accepts as a body parameter the array `order_ids`. Each item in the array is the ID of an order that the customer wants to claim. You can pass more than one ID. + +If the customer’s request has been processed successfully, the request returns a response with a `200` status code. + +The customer at this point will receive an email with a link to verify their claim on the order. + +## Manually Verify a Claim to an Order + +The link in the email that the customer receives should be a page in your storefront that accepts a `token` query parameter. + +Then, you send a request to the Verify Claim Order endpoint: + + + + +```tsx +medusa.orders.confirmRequest({ + token +}) +.then(() => { + // successful +}) +.catch(() => { + // an error occurred +}); +``` + + + + +```tsx +fetch(`/store/orders/customer/confirm`, { + method: 'POST', + credentials: 'include', + body: JSON.stringify({ + token + }), + headers: { + 'Content-Type': 'application/json' + } +}) +.then(() => { + //successful +}) +.catch(() => { + //display an error to the customer +}); +``` + + + + +This request accepts as a body parameter the string `token`. This would be the token passed as a parameter to your storefront page through the link in the email. + +If the verification is successful, the order will now be associated with the customer and the customer will be able to see it among their orders. + +## See Also + +- Learn [how to send a confirmation email to claim an order](../ecommerce/handle-order-claim-event.md). diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index 24f20876c9..317114cc31 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -225,6 +225,11 @@ module.exports = { id: "advanced/storefront/use-discounts-in-checkout", label: "Use Discounts in Checkout" }, + { + type: "doc", + id: "advanced/backend/taxes/manual-calculation", + label: "Calculate Taxes Manually" + }, { type: "doc", id: "advanced/storefront/how-to-implement-checkout-flow", @@ -232,8 +237,8 @@ module.exports = { }, { type: "doc", - id: "advanced/backend/taxes/manual-calculation", - label: "Calculate Taxes Manually" + id: "advanced/storefront/implement-claim-order", + label: "Implement Claim Order" }, { type: "doc", @@ -347,6 +352,11 @@ module.exports = { id: "advanced/backend/price-selection-strategy/override", label: "Override Price Selection" }, + { + type: "doc", + id: "advanced/ecommerce/handle-order-claim-event", + label: "Handle Order Claim Event" + }, ] }, {