Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

How to Build a Digital Products Store with Medusa and Next.js

In this tutorial, you will learn how to build an e-book online store using Medusa and Next.js.Throughout the course of the article, we will:Let's get started.Medusa is a suite of tools and modules specifically designed for e-Commerce products. Using Medusa, you can build modularized commerce logic like carts, products, and order management. It also provide tools that help you orchestrate powerful ecommerce websites, POS applications, commerce-enabled products, and everything in between.Before you get started with the tutorial, you should have installed:Using the Next.js starter, you can create a new Medusa app by running the following command:After that, you can opt to create a user account for admin panel access. Then, set up the backend infrastructure following the Medusa Digital Products Recipe. Once the backend is set, create sample products through your Medusa admin interface. Make sure that these products include digital media files for previews and primary content. Also make sure to incorporate relevant product metadata values using key/value pairs linked to each product.If you’re using regular JavaScript, you can skip this step.Before we continue, let's make sure to add in the necessary TypeScript type definitions for digital products in the Next.js storefront.This code defines TypeScript types and interfaces for managing digital products and their associated media files in an e-commerce system. It introduces several crucial structures:Now, let's move forward by adding e-book previews to our product detail page. To do this, we'll get the media previews linked to the currently selected product variant. In the src/lib/data/index.ts file, we'll create a Function to get these previews based on the chosen variant.This function is responsible for fetching information related to a specific product variant. It does this by making an HTTP request to the /product-media endpoint. It takes one argument, variant, which is expected to be of type Variant. The request includes query parameters specifying the variant_ids and requests additional details about related "variants". The function awaits the response from the HTTP request and extracts the response body, which is assumed to be an array of product media objects. It then returns the first product media object from this array, presuming there is at least one such object. If an error occurs during the request, it catches the error and rethrows it.To give customers a glimpse of the e-book's content, we'll provide a preview PDF with the first few pages. To do this, we'll set up a Next API route to manage file downloads while keeping the file's location private. We'll also create a component for a straightforward "download free preview" button. If a product variant has preview media, it will be shown in the product-actions component.You can use the newly created DigitalProduct and DigitalProductVariant types to fix any TypeScript errors that you may encounter.The GET function is designed to handle incoming HTTP GET requests using the Next.js framework. It first extracts information from the request URL, specifically the filepath and filename, which are expected to be query parameters. It then attempts to fetch a PDF file from the specified filepath. If the PDF is successfully retrieved, it proceeds to convert the PDF content into a buffer.In case the PDF retrieval fails, for instance, if the file is not found, it returns a response with a "PDF not found" message and a 404 status code, indicating a not found error.If the PDF is successfully fetched, it defines response headers, specifying that the content type is "application/pdf" and setting the "Content-Disposition" header to control the behavior of file downloads. The Content-Disposition header is set to "attachment," and the filename parameter is used to suggest a filename for the downloaded PDF. The above component displays a preview of a product's media along with a button to download a free preview of that media. The component receives a prop named media, which is expected to be of type ProductMedia.Inside the component, there's a downloadPreview function that's called when a user clicks the "Download free preview" button. This function constructs a URL for downloading the preview using the window.location.href property. It combines the base URL from the environment variable NEXT_PUBLIC_BASE_URL with the "/api/download/preview" route and includes query parameters for the file path and file name, which are extracted from the media prop.This component is responsible for displaying product-related actions, such as adding a product to the cart, and showing product media preview if available. It leverages asynchronous operations to fetch the media data based on the provided variant, making it a dynamic and interactive component.Because product and shipping information differs between digital and physical products, we'll make changes to these sections on the product page as needed.I've added product details to the e-book using the product's metadata section in the Medusa admin. Since we're not using the standard attributes, we'll enhance the ProductInfoTab component to display any additional metadata we include.By default, metadata is structured as an object. To make it simpler to create our list of attributes, we'll change it into an array. In this case, we'll feature four attributes from the metadata, splitting them into two columns. If you want to show a different number of attributes, you can easily adjust the values within the slice() function as needed.Shipping information isn't relevant for digital products, so we'll change the content in this tab. You can make any necessary adjustments to the content within the ShippingInfoTab component in the same file to better match your store's requirements.The ProductTabs component is used for rendering a set of tabs. The component takes a product prop, and it uses the useMemo hook to create an array of tab objects. Each tab object consists of a label and a component to be displayed when that tab is active. In the above snippet, there are two tabs: "Product Information" and "E-book delivery." The "Product Information" tab displays information about the product using the ProductInfoTab component, which we defined earlier. The "E-book delivery" tab uses the ShippingInfoTab component to display information related to e-book delivery. Inside the ShippingInfoTab component, it provides details about the delivery process, mentioning instant delivery via email and the option to download from an account, as well as free e-book previews.Selling digital products doesn't require gathering customers' physical addresses. We only need their first name and email address to deliver the e-book, making the Checkout process simpler by removing unnecessary input fields. In this example, we'll keep only the first name, last name, country, and email fields, completely removing the billing address section. Keep in mind that your specific requirements may require different input fields.To start, we'll adjust the checkout types and context by removing any references to values that are no longer needed.In the above snippet, you define TypeScript types for address values and the overall form structure. The CheckoutContext is also created to serve as a context for sharing checkout-related data and functions with other components.The CheckoutProvider component manages cart data, customer information, form handling, and interactions with payment and shipping methods. It sets up various hooks and functions for these purposes.You also define an idempotency key which will be used for preventing duplicate requests during payment session creation.In this section of code, several variables and hooks are initialized to facilitate the management of a checkout process. We use the methods variable to manage the checkout form, with initial values populated by the mapFormValues function. The code also sets up mutation functions for updating the payment session and the cart (setPaymentSessionMutation and updateCart) and tracks their loading states. It retrieves available shipping options and regions using hooks, and it also handles cart resets and region selection. It also employs boolean states (editAddresses and sameAsBilling) to manage whether the user is currently editing addresses and whether the billing address matches the shipping address. These components collectively ensure smooth navigation and data management in the checkout process.In the code above, first the isLoading boolean is computed using the useMemo hook. It reflects whether any part of the checkout is in a loading state. This is determined by observing four loading flags: addingShippingMethod, settingPaymentSession, updatingCart, and completingCheckout. If any of these flags is true, the isLoading flag will also be true. This indicates that some part of the checkout is currently in progress.The readyToComplete boolean, also computed with useMemo, assesses whether the checkout is prepared for completion. To be deemed ready, several conditions must be met: there must be a valid cart object, an email address, a shipping address, a billing address, a payment session, and at least one shipping method selected. If all these conditions are satisfied, readyToComplete will be true, signaling that the checkout process is set to be finalized.Finally, the shippingMethods variable is computed using useMemo. It is an array of available shipping methods with associated information. It maps the shipping_options (if they exist) to an array of objects, each containing a value, label, and price. These objects represent the shipping options, their names, and prices, formatted using the formatAmount function. This data is used to display and select shipping methods during the checkout process.This code section orchestrates various aspects of an e-commerce checkout process. It manages form state, resets the form when the cart changes, and toggles address editing visibility. It handles the selection of shipping methods, the creation and initialization of payment sessions, and the choice of payment providers. And it ensures that shipping addresses, billing addresses, and email information are set appropriately, and validates the cart's region based on the shipping address. It also coordinates the completion of the checkout process, including payment processing and order confirmation. All of these functions and data are encapsulated within the CheckoutProvider component.The useCheckout hook is used to access the checkout context and form context, typically used in React components. It retrieves the CheckoutContext from the context of the application, and it also gets the form context of the checkout form, allowing components to access and utilize these contexts.The mapFormValues function is responsible for mapping and prioritizing information for the checkout form. It takes customer and cart data, along with the current country, and generates values for the checkout form fields. It prioritizes data in this order: 1) Cart information, 2) Customer information, and 3) Default values set to null if no information is available. This function helps populate the checkout form with the most relevant data, ensuring a smoother user experience during the checkout process.Now that the context is updated, we’ll remove the redundant input fields from the checkout form.In the last step, we'll modify the shipping-details component to show important information after the order is successfully placed. In this situation, we'll remove any extra details and add the buyer's email address for reference.There are various ways to get digital products to customers, like sending a download link by email, adding a download button on the order confirmation page, or giving access through their account.In all these situations, our main goal is to confirm that only those who've purchased the product can get it. To do this, I've set up the backend to create a special code (token) for each digital item in an order. We can use GET /store/:token to check the token and give the file to the user. But this method shows the file's web address to the user, which isn't great for preventing piracy. So we are going to make a Next API route at src/app/api/download/main/[token]/route.ts. This route will handle the token, acting as a middleman to provide the file to the user without revealing where it's stored.This code defines a serverless function for handling HTTP GET requests in a Next.js application. It retrieves a PDF file using a token provided in the URL parameters, fetching the file from an external source. The function ensures the token's validity and the availability of the PDF file. If the token is invalid, it returns a "401 Unauthorized" response. If the PDF is not found, it returns a "404 Not Found" response. When the PDF is successfully fetched, it constructs response headers, including the content type as "application/pdf" and a suggested filename for download, and returns the PDF file to the client as a downloadable attachment. This code is typically used to serve PDF files in response to specific GET requests.We can now link to this API route from the delivery email like this: {your_store_url}/api/download/main/{token}.You can add your own logic to invalidate tokens after a certain time or X number of downloads.Congratulations, you've made it! Don't forget to explore more Recipes for further ways to make the most of Medusa.Application Developer at Thoughtworks India If you read this far, thank the author to show them you care. Say Thanks Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started freeCodeCamp is a donor-supported tax-exempt 501(c)(3) charity organization (United States Federal Tax Identification Number: 82-0779546)Our mission: to help people learn to code for free. We accomplish this by creating thousands of videos, articles, and interactive coding lessons - all freely available to the public. We also have thousands of freeCodeCamp study groups around the world.Donations to freeCodeCamp go toward our education initiatives, and help pay for servers, services, and staff. You can make a tax-deductible donation here.



This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

How to Build a Digital Products Store with Medusa and Next.js

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×