npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shadowmkj/plugin-ecommerce

v3.85.5

Published

Ecommerce plugin for Payload

Readme

@shadowmkj/plugin-ecommerce

A full-featured, modular E-commerce plugin for Payload CMS 3. Effortlessly manage multi-currency products, variants, carts, addresses, transactions, orders, and payment integrations within Payload CMS.


⚡ Features

  • 🛍️ Complete E-Commerce Infrastructure: Auto-generated collections for Products, Product Variants, Variant Types, Variant Options, Carts, Addresses, Transactions, and Orders.
  • 💳 Plug-and-Play Payment Adapters:
    • Stripe: Built-in support for PaymentIntents and Webhooks.
    • Razorpay: Full order creation, payment capture confirmation, and HMAC SHA256 webhook validation.
    • Cash on Delivery (COD): Complete offline/cash-on-delivery workflow.
  • 💱 Multi-Currency Support: Configurable default and supported currencies (USD, EUR, GBP, INR, etc.) with automatic price formatting and admin input controls.
  • ⚛️ React Client SDK & Hooks: Modular client-side context provider (EcommerceProvider) and focused hooks (useCart, useCurrency, useAddresses, usePayments).
  • 🎨 Custom Admin Components: Includes PriceInput, FormattedInput, PriceCell, and VariantOptionsSelector for Payload Admin UI.
  • 🌐 Built-in i18n: Multilingual support with built-in translations across 30+ languages.
  • 🔒 Granular Access Control: Customizable access functions per collection for guest and authenticated user workflows.

📦 Installation

# Using pnpm
pnpm add @shadowmkj/plugin-ecommerce

# Using npm
npm install @shadowmkj/plugin-ecommerce

# Using yarn
yarn add @shadowmkj/plugin-ecommerce

🚀 Quick Start

Add ecommercePlugin to your payload.config.ts:

import { buildConfig } from 'payload'
import { ecommercePlugin } from '@shadowmkj/plugin-ecommerce'
import { stripeAdapter } from '@shadowmkj/plugin-ecommerce/payments/stripe'
import { razorpayAdapter } from '@shadowmkj/plugin-ecommerce/payments/razorpay'
import { codAdapter } from '@shadowmkj/plugin-ecommerce/payments/cod'

export default buildConfig({
  // ... your Payload config
  plugins: [
    ecommercePlugin({
      currencies: {
        defaultCurrency: 'USD',
        supportedCurrencies: [
          { code: 'USD', symbol: '$', label: 'US Dollar', decimals: 2 },
          { code: 'EUR', symbol: '€', label: 'Euro', decimals: 2 },
          { code: 'INR', symbol: '₹', label: 'Indian Rupee', decimals: 2 },
        ],
      },
      products: {
        variants: true,
      },
      payments: {
        paymentMethods: [
          stripeAdapter({
            secretKey: process.env.STRIPE_SECRET_KEY!,
          }),
          razorpayAdapter({
            publishableKey: process.env.RAZORPAY_KEY_ID!,
            secretKey: process.env.RAZORPAY_KEY_SECRET!,
          }),
          codAdapter({}),
        ],
      },
    }),
  ],
})

⚙️ Configuration Options

The ecommercePlugin function accepts an EcommercePluginConfig object with the following properties:

| Option | Type | Default | Description | |--------|------|---------|-------------| | currencies | CurrenciesConfig | { defaultCurrency: 'USD', supportedCurrencies: [USD] } | Configures allowed currencies, symbols, and decimal precision. | | products | boolean \| ProductsConfig | { variants: true } | Enables product management and optional variant options/types collections. | | payments | PaymentsConfig | { paymentMethods: [] } | List of backend payment adapters (stripeAdapter, razorpayAdapter, codAdapter). | | addresses | boolean \| AddressesConfig | true | Enables customer address management collection and supported country definitions. | | carts | boolean \| CartsConfig | true | Configures shopping cart collection options and item matching logic. | | orders | boolean \| OrdersConfig | true | Configures order fulfillment and transaction history collections. | | access | AccessConfig | Default access rules | Override collection-level access permissions. |


🔌 Payment Adapters

1. Stripe Adapter

import { stripeAdapter, stripeAdapterClient } from '@shadowmkj/plugin-ecommerce/payments/stripe'

// Server-side (Payload Plugin Config)
stripeAdapter({
  secretKey: process.env.STRIPE_SECRET_KEY!,
})

// Client-side (React Provider)
stripeAdapterClient()

2. Razorpay Adapter

import { razorpayAdapter, razorpayAdapterClient } from '@shadowmkj/plugin-ecommerce/payments/razorpay'

// Server-side (Payload Plugin Config)
razorpayAdapter({
  publishableKey: process.env.RAZORPAY_KEY_ID!,
  secretKey: process.env.RAZORPAY_KEY_SECRET!,
})

// Client-side (React Provider)
razorpayAdapterClient({
  publishableKey: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID!,
})

3. Cash on Delivery (COD) Adapter

import { codAdapter, codAdapterClient } from '@shadowmkj/plugin-ecommerce/payments/cod'

// Server-side
codAdapter({})

// Client-side
codAdapterClient({})

⚛️ Client SDK (React Provider & Hooks)

Wrap your frontend application with EcommerceProvider to access stateful cart management, currency switching, address creation, and checkout initiation.

'use client'
import React from 'react'
import { EcommerceProvider, useCart, useCurrency, usePayments } from '@shadowmkj/plugin-ecommerce/client/react'
import { stripeAdapterClient } from '@shadowmkj/plugin-ecommerce/payments/stripe'
import { razorpayAdapterClient } from '@shadowmkj/plugin-ecommerce/payments/razorpay'

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <EcommerceProvider
      paymentMethods={[
        stripeAdapterClient(),
        razorpayAdapterClient({ publishableKey: 'rzp_test_xxx' }),
      ]}
    >
      {children}
    </EcommerceProvider>
  )
}

function CartSummary() {
  const { cart, addItem, removeItem, clearCart } = useCart()
  const { currency, setCurrency } = useCurrency()
  const { initiatePayment } = usePayments()

  return (
    <div>
      <h2>Shopping Cart ({cart?.items?.length || 0} items)</h2>
      <button onClick={() => setCurrency('EUR')}>Switch to EUR</button>
      <button onClick={() => initiatePayment({ paymentMethod: 'stripe' })}>
        Checkout
      </button>
    </div>
  )
}

Exported Hooks

  • useEcommerce(): Access full context including user, cart, config, and payment methods.
  • useCart(): Methods to addItem, removeItem, incrementItem, decrementItem, and clearCart.
  • useCurrency(): Access active currency context (code, symbol, decimals) and setCurrency.
  • useAddresses(): Create and update user billing/shipping addresses.
  • usePayments(): initiatePayment and confirmOrder flow handlers.

📚 Tutorial: Setting Up & Using Multi-Currency

@shadowmkj/plugin-ecommerce provides end-to-end multi-currency support out of the box, including dynamic schema generation, administrative input controls, real-time cart subtotal recalculation, and client-side React hooks.

Follow this step-by-step tutorial to configure and use multi-currency in your project.


Step 1: Define Currencies in payload.config.ts

Configure your default currency and all supported currencies in your Payload CMS plugin configuration:

import { buildConfig } from 'payload'
import { ecommercePlugin } from '@shadowmkj/plugin-ecommerce'

export default buildConfig({
  plugins: [
    ecommercePlugin({
      currencies: {
        defaultCurrency: 'USD',
        supportedCurrencies: [
          { code: 'USD', symbol: '$', label: 'US Dollar', decimals: 2 },
          { code: 'EUR', symbol: '€', label: 'Euro', decimals: 2 },
          { code: 'INR', symbol: '₹', label: 'Indian Rupee', decimals: 2 },
        ],
      },
    }),
  ],
})

What happens under the hood: For every currency specified in supportedCurrencies, the plugin automatically generates dedicated price input fields (priceInUSD, priceInEUR, priceInINR) on the products and variants collections in Payload Admin.


Step 2: Wrap Your Application with EcommerceProvider

In your Next.js / React application root layout, wrap your components with EcommerceProvider:

'use client'
import React from 'react'
import { EcommerceProvider } from '@shadowmkj/plugin-ecommerce/client/react'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <EcommerceProvider>
      {children}
    </EcommerceProvider>
  )
}

Step 3: Build a Currency Switcher Component

Use the useCurrency() hook to display a currency dropdown selector:

'use client'
import React from 'react'
import { useCurrency } from '@shadowmkj/plugin-ecommerce/client/react'

export function CurrencySelector() {
  const { currency, setCurrency, supportedCurrencies } = useCurrency()

  return (
    <select
      value={currency.code}
      onChange={(e) => setCurrency(e.target.value)}
      aria-label="Select Currency"
    >
      {supportedCurrencies.map((c) => (
        <option key={c.code} value={c.code}>
          {c.label} ({c.symbol})
        </option>
      ))}
    </select>
  )
}

Step 4: Display Dynamic Product Prices

Create a price component that automatically resolves the active currency field on product or variant objects:

'use client'
import React from 'react'
import { useCurrency } from '@shadowmkj/plugin-ecommerce/client/react'

export function ProductPrice({ product }: { product: { [key: string]: any } }) {
  const { currency } = useCurrency()

  // Dynamically resolve the price field key (e.g. 'priceInUSD', 'priceInEUR')
  const priceField = `priceIn${currency.code}`
  const rawPrice = product[priceField] ?? 0

  return (
    <span className="product-price">
      {currency.symbol}
      {rawPrice.toFixed(currency.decimals)}
    </span>
  )
}

Step 5: Add Items to Cart & Handle Automatic Currency Syncing

Use useCart() alongside useCurrency() to interact with the shopping cart. When a user switches currency, the plugin automatically updates the cart on the backend and recalculates the subtotal in the active currency:

'use client'
import React from 'react'
import { useCart, useCurrency } from '@shadowmkj/plugin-ecommerce/client/react'

export function ProductCard({ product }: { product: any }) {
  const { addItem } = useCart()
  const { currency } = useCurrency()

  const priceField = `priceIn${currency.code}`
  const price = product[priceField] ?? 0

  return (
    <div className="product-card">
      <h3>{product.title}</h3>
      <p>Price: {currency.symbol}{price.toFixed(currency.decimals)}</p>
      <button onClick={() => addItem(product.id, 1)}>
        Add to Cart
      </button>
    </div>
  )
}

Note on Automatic Subtotal Recalculation: When setCurrency('EUR') is called:

  1. A PATCH request updates cart.currency to 'EUR' on the backend.
  2. The backend beforeChangeCart hook fetches prices (priceInEUR) for all cart items and updates cart.subtotal.
  3. The React provider refetches the cart with EUR price populates, seamlessly updating client state.

🖥️ Development & Scripts

# Build TypeScript declarations and SWC bundle
pnpm build

# Run unit tests with Vitest
npx vitest

# Run linter
pnpm lint

📜 License

Distributed under the MIT License.

Developed by shadowmkj