JS

Medusa

What is Medusa?

Medusa is an ecommerce platform with a built-in framework for customization that allows you to build custom commerce applications without reinventing core commerce logic.


Installation

Reference URL

Run the following command and replace <NAME> and <DATABASE_URL> with your store name:

$ npx create-medusa-app@latest <NAME> --with-nextjs-starter --db-url "<DATABASE_URL>" --no-migrations

If you don't need any database connectivity in the beginning, you can add --skip-db, for example:

$ npx create-medusa-app@latest <NAME> --skip-db

Medusa CLI

$ npx medusa user -e admin@medusajs.com -p supersecret

Database related CLI:

$ medusa db:setup                      #Create the database, run migrations and sync links
$ medusa db:create                     #Create the database used by your application
$ medusa db:migrate                    #Migrate the database by executing pending migrations
$ medusa db:migrate:scripts            #Run all migration scripts
$ medusa db:rollback [modules...]      #Rollback last batch of executed migrations for a given module
$ medusa db:generate [modules...]      #Generate migrations for a given module
$ medusa plugin:db:generate            #Generate migrations for modules in a plugin
$ medusa db:sync-links                 #Sync database schema with the links defined by your application and Medusa core
$ medusa plugin:build                  #Build plugin source for publishing to a package registry
$ medusa plugin:develop                #Start plugin development process in watch mode. Changes will be re-published to the local

Deployment

Railway

Backend

  1. Click on Create on right top corner
  2. Choose GitHub Repo
  3. Select or authorize the Medusa Backend repository from your own GitHub account
  4. Open the Variables tab by clicking on the newly created service
  5. Deploy the new service
NODE_ENV="production"
PORT="9000"
ADMIN_CORS="https://${{RAILWAY_PUBLIC_DOMAIN}},https://${{RAILWAY_PRIVATE_DOMAIN}}"
AUTH_CORS="https://${{RAILWAY_PUBLIC_DOMAIN}},https://${{RAILWAY_PRIVATE_DOMAIN}}"
COOKIE_SECRET=""
DATABASE_URL="postgresql://<DB_OWNER>:<DB_PASSWORD>@<NEON_INSTANCE>.<REGION>.aws.neon.tech/<DB_NAME>?sslmode=require"
JWT_SECRET=""
MEILISEARCH_HOST="https://${{MeiliSearch.MEILI_PUBLIC_URL}}"
MEILISEARCH_MASTER_KEY="${{MeiliSearch.MEILI_MASTER_KEY}}"
MINIO_ACCESS_KEY="${{Bucket.MINIO_ROOT_USER}}"
MINIO_ENDPOINT="${{Bucket.MINIO_PUBLIC_HOST}}"
MINIO_SECRET_KEY="${{Bucket.MINIO_ROOT_PASSWORD}}"
RAILWAY_HEALTHCHECK_TIMEOUT_SEC="720"
RAILWAY_PUBLIC_DOMAIN_VALUE="https://${{RAILWAY_PUBLIC_DOMAIN}}"
REDIS_URL="${{Redis.REDIS_URL}}?family=0"

S3_ACCESS_KEY_ID=""
S3_BUCKET=""
S3_ENDPOINT="https://s3.<REGION>.amazonaws.com"
S3_FILE_URL="https://<BUCKET_NAME>.s3.<REGION>.amazonaws.com"
S3_REGION="<REGION>"
S3_SECRET_ACCESS_KEY=""

SENDGRID_API_KEY=""
SENDGRID_FROM_EMAIL=""

FOURPX_API_KEY="fecfabd3-aff1-46ef-a37c-9cf125c57b39"
FOURPX_API_SECRET="71862644-c4ac-4934-95c7-71deb5601c4e"

SFEXPRESS_API_CLIENTID=""
SFEXPRESS_MODE="sandbox"
SFEXPRESS_SECRET_PRODUCTION=""
SFEXPRESS_SECRET_SANDBOX=""
STORE_CORS=""
STRIPE_API_KEY="pk_...V"
STRIPE_WEBHOOK_SECRET="whsec_..."

Storefront

  1. Click on Create on right top corner
  2. Choose GitHub Repo
  3. Select or authorize the Medusa Storefront repository from your own GitHub account
  4. Open the Variables tab by clicking on the newly created service
  5. Deploy the new service
PORT=8000
MEDUSA_BACKEND_URL=https://${{Backend.RAILWAY_PUBLIC_DOMAIN}}
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_*****************
NEXT_PUBLIC_BASE_URL=https://${{RAILWAY_PUBLIC_DOMAIN}}
NEXT_PUBLIC_DEFAULT_REGION=us
NEXT_PUBLIC_STRIPE_KEY=
REVALIDATE_SECRET=supersecret
S3_FILE_URL=<bucket_name>.s3.<region>.amazonaws.com
  1. Open the Settings tab by clicking on the newly created service
  2. Click on **Custom Domain ** under Public Networking, select the port in the dropdown menu or type `8000``` which was defined in the Variables.

Modules

Essential Modules Config

import {loadEnv, defineConfig, Modules} from '@medusajs/framework/utils'

loadEnv(process.env.NODE_ENV || 'development', process.cwd())

module.exports = defineConfig({
  projectConfig: {
    databaseUrl: process.env.DATABASE_URL,
    redisUrl: process.env.REDIS_URL,
    databaseLogging: false,
    http: {
      storeCors: process.env.STORE_CORS!,
      adminCors: process.env.ADMIN_CORS!,
      authCors: process.env.AUTH_CORS!,
      jwtSecret: process.env.JWT_SECRET || "supersecret",
      cookieSecret: process.env.COOKIE_SECRET || "supersecret",
    }
  },
  modules: [
    {
      key: Modules.CACHE,
      resolve: "@medusajs/cache-redis",
      options: {
        redisUrl: process.env.REDIS_URL,
        ttl: 30, // Time to live in seconds
      },
    },
    {
      key: Modules.EVENT_BUS,
      resolve: "@medusajs/event-bus-redis",
      options: {
        redisUrl: process.env.REDIS_URL,
      },
    },
    {
      key: Modules.WORKFLOW_ENGINE,
      resolve: '@medusajs/workflow-engine-redis',
      options: {
        redis: {
          url: process.env.REDIS_URL,
        },
      },
    },
    {
      key: Modules.FILE,
      resolve: "@medusajs/medusa/file",
      options: {
        providers: [
          {
            resolve: "@medusajs/medusa/file-s3",
            id: "s3",
            options: {
              file_url: process.env.S3_FILE_URL,
              access_key_id: process.env.S3_ACCESS_KEY_ID,
              secret_access_key: process.env.S3_SECRET_ACCESS_KEY,
              region: process.env.S3_REGION,
              bucket: process.env.S3_BUCKET,
              endpoint: process.env.S3_ENDPOINT,
            },
          },
        ],
      },
      ...(process.env.SENDGRID_API_KEY && process.env.SENDGRID_FROM_EMAIL ? [{
        key: Modules.NOTIFICATION,
        resolve: '@medusajs/notification',
        options: {
          providers: [
            ...(process.env.SENDGRID_API_KEY && process.env.SENDGRID_FROM_EMAIL ? [{
              resolve: '@medusajs/notification-sendgrid',
              id: 'sendgrid',
              options: {
                channels: ['email'],
                api_key: process.env.SENDGRID_API_KEY,
                from: process.env.SENDGRID_FROM_EMAIL,
              }
            }] : []),
          ]
        }
      }] : []),
      ...(process.env.STRIPE_API_KEY && process.env.STRIPE_WEBHOOK_SECRET ? [{
        key: Modules.PAYMENT,
        resolve: "@medusajs/medusa/payment",
        options: {
          providers: [
            {
              resolve: "@medusajs/medusa/payment-stripe",
              id: "stripe",
              options: {
                apiKey: process.env.STRIPE_API_KEY,
              },
            },
          ],
        },
      }] : [])
    },
  ],
})

Redis

Reference URL

import {Modules} from "@medusajs/framework/utils"
// ...
module.exports = defineConfig({
  // ...
  modules: [
    {
      resolve: "@medusajs/medusa/cache-redis",
      options: {
        redisUrl: process.env.CACHE_REDIS_URL,
      },
    },
  ],
})

AWS S3 Storage

Reference URL

  • Create AWS user with AmazonS3FullAccess permissions.
  • Create AWS IAM user access key ID and secret access key.
  • Create S3 bucket with the "Public Access setting" enabled:
  1. On your bucket's dashboard, click on the Permissions tab.
  2. Click on the Edit button of the Block public access (bucket settings) section.
  3. In the form that opens, don't toggle any checkboxes and click the "Save changes" button.
  4. Confirm saving the changes by entering confirm in the pop-up that shows.
  5. Back on the Permissions page, scroll to the Object Ownership section and click the Edit button.
  6. In the form that opens:
  • Choose the "ACLs enabled" card.
  • Click on the "Save changes" button.
  1. Back on the Permissions page, scroll to the "Access Control List (ACL)" section and click on the Edit button.
  2. In the form that opens, enable the Read permission for "Everyone (public access)".
  3. Check the "I understand the effects of these changes on my objects and buckets." checkbox.
  4. Click on the "Save changes" button.

Stripe Payment

Stripe Setup Prerequisites

Register or login your Stripe account. Retrieve the API key and create webhook.

Install Stripe CLI:

$ brew install stripe/stripe-cli/stripe
$ stripe login

There is a pairing code showing and a Stripe link. Open that link and verify the pairing code.

Stripe Webhook

For production applications, you must set up webhooks in Stripe that inform Medusa of changes and updates to payments.

Webhook URL

Medusa has a {server_url}/hooks/payment/{provider_id} API route that you can use to register webhooks in Stripe, where:

  • {server_url} is the URL to your deployed Medusa application in server mode.

  • {provider_id} is the ID of the provider as explained in the Stripe Payment Provider IDs section, without the pp_ prefix.

  • The Stripe Module Provider supports the following payment types, and the webhook endpoint URL is different for each:

Stripe Payment TypeWebhook Endpoint URL
Basic Stripe Payment{server_url}/hooks/payment/stripe_stripe
Bancontact Payments{server_url}/hooks/payment/stripe-bancontact_stripe
BLIK Payments{server_url}/hooks/payment/stripe-blik_stripe
giropay Payments{server_url}/hooks/payment/stripe-giropay_stripe
iDEAL Payments{server_url}/hooks/payment/stripe-ideal_stripe
Przelewy24 Payments{server_url}/hooks/payment/stripe-przelewy24_stripe
PromptPay Payments{server_url}/hooks/payment/stripe-promptpay_stripe

Webhook Events

When you set up the webhook in Stripe, choose the following events to listen to:

  • payment_intent.amount_capturable_updated
  • payment_intent.succeeded
  • payment_intent.payment_failed

Backend

  1. Add it to the array of providers passed to the Payment Module in medusa-config.ts:
module.exports = defineConfig({
  // ...
  modules: [
    {
      resolve: "@medusajs/medusa/payment",
      options: {
        providers: [
          {
            resolve: "@medusajs/medusa/payment-stripe",
            id: "stripe",
            options: {
              apiKey: process.env.STRIPE_API_KEY,
              webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
              capture: true,
              automatic_payment_methods: true,
              payment_description: "Stripe is highly secured"
            },
          },
        ],
      },
    },
  ],
})
  1. Environment Variables

Add these to your .env file:

Login your Stripe account and open the Developer Workbench Overview page. Copy the Secret key from the API keys and a Signing secret from the Webhook tab.

STRIPE_API_KEY=sk_test_your_stripe_secret_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret
  1. Add Payment Provider to Region

In your Medusa admin, go to Settings > Regions and add "stripe" as a payment provider to your regions.

Storefront Stripe Implementation

Prerequisites

Steps

  1. Install Stripe SDK In your storefront, use the following command to install Stripe's JS and React SDKs:
$ pnpm add @stripe/react-stripe-js @stripe/stripe-js
  1. Add Stripe Environment Variables Next, add an environment variable holding your Stripe publishable API key. For example:
NEXT_PUBLIC_STRIPE_PK=pk_test_51Kj...

Test Payment

To test Stripe payments, use the following test credit card details:

  • Card Number: 4242 4242 4242 4242
  • Expiry Date: Any future date
  • CVC: Any 3 digits

Troubleshooting

Module @medusajs/cache-redis doesn't have a serviceName. Please provide a 'key' for the module or check the service joiner config.

  1. Open the medsua-config.ts file in Medusa backend repository
import {Modules} from "@medusajs/framework/utils"
//...
modules: [
  {
    key: Modules.CACHE,
    resolve: "@medusajs/cache-redis",
    options: {
      redisUrl: process.env.REDIS_URL,
      ttl: 30, // Time to live in seconds
    },
  },
//...

Make sure the modules used have the key

Previous
Electron