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

medusa-plugin-digital-products

v0.2.2

Published

Digital products, licensing, entitlement, activation, and secure download management for Medusa v2.

Downloads

51

Readme

Medusa Digital Products

CI npm license

An installable Medusa v2 plugin for digital products, software licensing, customer entitlements, device activations, and protected file delivery.

The plugin fills the domain gap between selling a non-shippable product and operating a production licensing system. It is built as a native Medusa plugin: module data models, rollback-capable workflows, authenticated Admin and Store APIs, a product-variant module link, subscribers, a scheduled expiry job, migrations, and an Admin extension.

Features

  • Digital products linked one-to-one with Medusa product variants.
  • Versioned assets backed by Medusa's configured File Module provider.
  • Perpetual, time-limited, subscription, and trial license policies.
  • A native Medusa Fulfillment Module provider for zero-cost digital delivery.
  • Automatic, idempotent fulfillment on order.placed: entitlements, quantity-aware license keys, protected download grants, email delivery, and delivered status.
  • Manual grants for support, promotions, migrations, and B2B use cases.
  • Automatic revocation when an order is canceled and explicit admin revocation for refunds or chargebacks.
  • Quantity-aware, one-time license-key issuance.
  • HMAC-hashed license keys and download tokens; plaintext credentials are never persisted.
  • Device activation limits, validation, reactivation, and deactivation.
  • Short-lived download grants with policy-level, per-asset download limits.
  • Immutable audit events for sensitive mutations.
  • Hourly expiry of entitlements, licenses, and download grants.
  • A read-only Admin overview at /app/digital-products.

Compatibility

  • Medusa >=2.17 <3
  • Node.js >=20
  • PostgreSQL (as required by Medusa)

Installation

npm install medusa-plugin-digital-products

Register the plugin in medusa-config.ts:

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

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

module.exports = defineConfig({
  // your existing projectConfig
  modules: [
    {
      resolve: "@medusajs/medusa/fulfillment",
      options: {
        providers: [
          {
            resolve: "medusa-plugin-digital-products/providers/digital-fulfillment",
            id: "digital-products",
          },
        ],
      },
    },
  ],
  plugins: [
    {
      resolve: "medusa-plugin-digital-products",
      options: {},
    },
  ],
})

Configure a secret of at least 32 characters. Treat this as production key material and keep it stable; changing it invalidates credential lookups.

DIGITAL_PRODUCTS_SECRET=replace-with-a-random-secret-at-least-32-characters
MEDUSA_BACKEND_URL=https://api.example.com
DIGITAL_PRODUCTS_ACCOUNT_URL=https://shop.example.com/account/digital-products
# Optional; defaults to seven days and is capped at thirty days.
DIGITAL_PRODUCTS_DELIVERY_TOKEN_TTL_SECONDS=604800

Run the plugin migrations from the host application:

npx medusa db:migrate

Use an S3-compatible File Module provider in production. Medusa's local provider is intended for development.

Configure an email-capable Notification Module provider that supports direct content. Raw license keys and download tokens are only placed in the transient provider payload, never in the plugin database, fulfillment metadata, audit log, or notification data record.

Fulfillment setup

After registering the provider, add fp_digital-products_digital-products to the stock location that sells digital products. Create a fixed-price shipping option from its digital-delivery service, price it at zero, and make it available in the relevant service zone. Digital carts must select that option during checkout. This is especially important for mixed carts: the plugin explicitly selects the digital shipping option for the digital line items and leaves physical items to their own provider.

Configure linked variants as non-physical (requires_shipping: false) and do not manage inventory unless you intentionally impose digital stock limits. The order must still contain the zero-priced digital shipping method so Medusa can associate its fulfillment with this provider.

Domain flow

  1. Create a license policy.
  2. Create a digital product linked to an existing product variant.
  3. Upload the binary with Medusa's File API, then attach its file_id as a digital asset.
  4. Publish the digital product by setting its status to active.
  5. When the linked variant is purchased, the order.placed subscriber runs one rollback-capable delivery workflow.
  6. The workflow grants entitlements, issues one key per purchased quantity, creates protected asset links, creates the Medusa fulfillment through this provider, marks it delivered, and emails the buyer.
  7. Authenticated customers can later issue any remaining keys or fresh short-lived download grants from their account.
  8. License clients activate and validate keys using a stable device identifier.

For non-shippable catalog configuration, set the Medusa product/variant inventory and shipping requirements according to the official selling-products guide.

Admin API

All Admin endpoints use Medusa's standard Admin authentication.

| Method | Route | Purpose | | --- | --- | --- | | GET, POST | /admin/digital-products | List or create digital products | | GET, POST, DELETE | /admin/digital-products/:id | Retrieve, update, or archive a digital product | | POST | /admin/digital-products/:id/assets | Attach a File Module asset | | GET, POST | /admin/digital-products/license-policies | List or create policies | | GET, POST | /admin/digital-products/entitlements | List or manually grant entitlements | | POST | /admin/digital-products/entitlements/:id/revoke | Revoke an entitlement | | POST | /admin/digital-products/entitlements/:id/licenses | Issue a license key once | | POST | /admin/digital-products/orders/:id/fulfill | Safely retry or manually trigger digital fulfillment |

Store API

Use the Medusa JS SDK so customer authentication and publishable API keys are included automatically.

| Method | Route | Purpose | | --- | --- | --- | | GET | /store/customers/me/digital-products | List the authenticated customer's entitlements | | POST | /store/customers/me/digital-products/:id/licenses | Issue a license key owned by the customer | | POST | /store/customers/me/digital-products/:id/downloads | Create a short-lived download token | | POST | /store/digital-products/downloads/redeem | Consume a token and return the provider URL to an SDK client | | POST | /store/digital-products/licenses/activate | Activate a license for a device | | POST | /store/digital-products/licenses/validate | Validate a license/device pair | | POST | /store/digital-products/licenses/deactivate | Release a device activation |

Example SDK request:

const result = await sdk.client.fetch(
  `/store/customers/me/digital-products/${entitlementId}/downloads`,
  {
    method: "POST",
    body: { asset_id: assetId },
  }
)

Emailed download links use GET /digital-products/downloads/redeem?token=.... This browser-facing route is outside Medusa's publishable-key-protected /store namespace because normal link navigation cannot attach SDK headers. The high-entropy, expiring, usage-limited token is the authorization credential; successful redemption redirects to the File Module provider URL.

Security model

  • License keys and download tokens are returned only at issuance and stored as HMAC-SHA-256 hashes.
  • Customer ownership checks run inside workflows, not route handlers.
  • Download grants expire after five minutes by default, are capped at one hour, and are one-use.
  • File URLs come from the configured Medusa File Module provider only after grant consumption.
  • Device identifiers are HMAC-hashed before persistence.
  • API responses use Cache-Control: no-store for redeemed links.
  • Never put raw license keys or download tokens in logs, metadata, analytics, or audit events.

Guest buyers receive their license keys and protected download links at the order email. Their entitlement remains email-bound until the host application associates or claims it for a customer account.

If delivery fails—for example because the email provider is unavailable—the workflow compensates the fulfillment, licenses, grants, and newly created entitlements. Fix the provider configuration and retry with POST /admin/digital-products/orders/:id/fulfill. Completed deliveries are detected from fulfillment metadata and are not duplicated.

Local development

npm install
npm run build
npm run lint
npm test
# Requires PostgreSQL and the DB_HOST, DB_PORT, DB_USERNAME, and DB_PASSWORD variables.
npm run test:integration:modules
npm run test:package
# Requires Docker; installs the packed plugin into an isolated Medusa host.
npm run test:acceptance

Publish to Medusa's local plugin registry:

npx medusa plugin:publish

Then add it to a Medusa application with npx medusa plugin:add medusa-plugin-digital-products, register it in medusa-config.ts, run npx medusa db:migrate, and start the host with npm run dev.

Publishing and Medusa listing

Build and inspect the package before publishing:

npm run release:check
npm run test:integration:modules
npm pack --dry-run

Releases are published through the GitHub release workflow with npm provenance. The Git tag must exactly match v<package-version>. See RELEASING.md for first-release and trusted-publishing setup.

This plugin targets the Other category in Medusa's npm-curated Integrations Directory. The live category includes reusable commerce-feature plugins as well as third-party services, so the package uses the current medusa-plugin-integration and medusa-plugin-other discovery metadata. See the listing strategy for the evidence, sync checklist, and escalation path. Directory inclusion remains Medusa's decision.

Support and security

Use GitHub issues for reproducible bugs and feature requests. Read SUPPORT.md before requesting help and report vulnerabilities privately as described in SECURITY.md.

License

MIT