Native-feeling digital products, protected downloads, streaming, entitlements, and software licenses for Medusa v2.
adds a native Medusa v2 domain for digital releases, protected files, durable purchase entitlements, download and stream grants, and software licenses.
Medusa remains the source of truth for products, variants, prices, carts, orders, customers, payment, tax, promotions, and fulfillment. The plugin links digital configuration to those records instead of creating a second commerce stack.
Email layout, branding, provider credentials, and transport remain owned by the host application's Medusa Notification Module. The plugin supplies durable delivery events, template data, retry state, and copy-ready provider/template examples.
See the Medusa Admin guide for the complete merchant workflow and the storefront guide for authenticated, per-order, guest, download, and license integration.
The plugin domain is organized around:
The package does not provide DRM, a hosted storefront, a replacement checkout, or a payment provider. Public previews are separate from protected master assets.
The plugin is built and host-tested against Medusa 2.18 and runs source CI on Node.js 20 and 22. Test the exact Medusa/plugin combination before production upgrades.
Install from npm in the Medusa backend:
npm install @makecrypto/medusa-plugin-digital-downloadsFor reproducible deployments, commit your lockfile or pin an approved version. Verified tarballs, checksums, CycloneDX SBOMs, and build-provenance attestations are available from GitHub Releases. Verify the selected channel and package integrity during every deployment; see RELEASE.md.
Register it in . This minimal local-storage example uses a persistent directory outside any publicly served tree:
1import { defineConfig } from "@medusajs/framework/utils"2
3export default defineConfig({4 plugins: [5 {6 resolve: "@makecrypto/medusa-plugin-digital-downloads",7 options: {8 encryptionKey: process.env.DIGITAL_DOWNLOADS_ENCRYPTION_KEY,9 tokenSecret: process.env.DIGITAL_DOWNLOADS_TOKEN_SECRET,10 storage: {11 defaultProvider: "local",12 local: {13 rootPath: process.env.DIGITAL_DOWNLOADS_LOCAL_ROOT,14 signingSecret:15 process.env.DIGITAL_DOWNLOADS_LOCAL_SIGNING_SECRET,16 },17 },18 },19 },20 ],21})Generate independent secrets and store them in the deployment's secret manager:
1openssl rand -hex 32 # encryption key2openssl rand -hex 32 # token secret3openssl rand -hex 32 # local descriptor signing secretThe encryption key must be exactly 64 hexadecimal characters representing 32 random bytes. Generate it independently from the token secret; the plugin rejects configurations where both values are identical. Token and local signing secrets remain arbitrary UTF-8 values containing at least 32 bytes.
Keep stable. Existing encrypted license keys cannot be recovered after it is lost or replaced without a supported re-encryption migration.
Apply the packaged module migrations from the Medusa application:
npx medusa db:migrateThen restart the Medusa API and worker processes. See docs/CONFIGURATION.md for complete local and S3 examples and docs/MIGRATIONS.md before a production upgrade.
Choose S3 as the default provider and supply a private bucket:
1{2 resolve: "@makecrypto/medusa-plugin-digital-downloads",3 options: {4 encryptionKey: process.env.DIGITAL_DOWNLOADS_ENCRYPTION_KEY,5 tokenSecret: process.env.DIGITAL_DOWNLOADS_TOKEN_SECRET,6 storage: {7 defaultProvider: "s3",8 s3: {9 bucket: process.env.DIGITAL_DOWNLOADS_S3_BUCKET!,10 region: process.env.DIGITAL_DOWNLOADS_S3_REGION || "us-east-1",11 endpoint: process.env.DIGITAL_DOWNLOADS_S3_ENDPOINT,12 accessKeyId: process.env.DIGITAL_DOWNLOADS_S3_ACCESS_KEY_ID!,13 secretAccessKey:14 process.env.DIGITAL_DOWNLOADS_S3_SECRET_ACCESS_KEY!,15 sessionToken: process.env.DIGITAL_DOWNLOADS_S3_SESSION_TOKEN,16 prefix: process.env.DIGITAL_DOWNLOADS_S3_PREFIX || "digital-downloads",17 forcePathStyle:18 process.env.DIGITAL_DOWNLOADS_S3_FORCE_PATH_STYLE === "true",19 },20 },21 },22}The bucket must not be public. Restrict credentials to the configured bucket and prefix. Use HTTPS; insecure endpoints are intended only for deliberate local MinIO-style development.
After the plugin and migrations are installed, its Medusa Admin extensions are the merchant entry point. The intended flow is:
Every digital product, including a license-only product, requires a current published release before fulfillment. Download, stream, and mixed releases must contain the ready deliverables required by their delivery type; license-only releases may publish with zero assets when an enabled generated or pooled license policy supplies the deliverable. Publishing with is intentionally unsupported until explicit entitlement update-policy semantics ship; use the default value.
The exact Admin surfaces available in the installed build are documented in docs/ADMIN_GUIDE.md.
Each digital configuration chooses when fulfillment becomes eligible:
The placed, completed, and captured subscribers converge on the same durable fulfillment operation. A five-minute reconciliation job retries pending, failed, or stale leased work. Order cancellation, payment refund, and chargeback/dispute subscribers apply the configured revocation policy. When the host configures an email provider and the documented templates, delivery notifications use a durable outbox and a separate two-minute retry job. Email failure never rolls back ownership.
Order issuance and order-wide revocation share one order lock. A selected refund/revocation commits every entitlement state change, capability/license invalidation, and its notification outbox rows in one transaction. Reissuing an expired entitlement renews its recorded purchase term by default (or uses an explicit replacement deadline) so the restored state is usable. The hourly expiry job also scans a separately bounded terminal batch and atomically repairs missing expiry/revocation outboxes from legacy or interrupted lifecycle writes. It rechecks status and deadlines under the entitlement lock.
The optional zero-cost Digital delivery Fulfillment Module provider can expose digital delivery in Medusa's native fulfillment UI. It does not issue entitlements itself and does not replace the event subscribers or cart completion. Registration and refund-policy details are in docs/CONFIGURATION.md.
The optional storefront export provides a Fetch transport, a Medusa SDK adapter, TypeScript response types, hooks, and accessible React primitives:
1import {2 createDigitalDownloadsFetchClient,3} from "@makecrypto/medusa-plugin-digital-downloads/storefront"4
5const digitalDownloads = createDigitalDownloadsFetchClient({6 baseUrl: process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!,7 publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,8 authToken: async () => getCustomerToken(),9 credentials: "include",10})See docs/STOREFRONT.md for customer/guest authentication, React examples, grant handling, and security guidance. See docs/API.md for the backend contract.
Use for the authenticated account-wide purchase library and inside Medusa's native customer order-detail view. Both surface protected files and buyer-controlled license reveal without replacing the host's account, checkout, or order-history implementation.
React customer resources require a changing, non-secret so cached state cannot cross a login/account boundary. Guest flows send ; order-email matching is enabled by default, so storefronts should prompt for the order email and send it as on access/grants and on guest license reveal. The guest purchase capability lifetime is configured separately from short-lived content grants and defaults to about 30 days. The built-in browser opener is a convenience for safe content up to 64 MiB; use the streaming BFF pattern for larger or seekable assets.
Digital Downloads is payment-provider agnostic. It works alongside the separate payment provider because both use normal Medusa payment/order lifecycle events:
Do not pass MakePay API or webhook secrets to Digital Downloads. The two packages have no secret-sharing or direct runtime dependency.
Report vulnerabilities privately using SECURITY.md.
1npm ci2npm run lint3npm run typecheck4npm run test:unit5npm run build6npm run pack:dry-runPostgreSQL integration commands and local plugin-development instructions are in CONTRIBUTING.md.
MIT. See LICENSE.