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-invoice-sbl

v0.0.6

Published

A starter for Medusa plugins.

Readme

Medusa Invoice Download Plugin

A Medusa plugin that enables customers and admins to download invoices as PDF files for orders. Supports custom PDF templates and automatic email attachments.

Features

  • Storefront API: Authenticated customers can download invoices for their own orders
  • Admin API: Admins can download invoices for any order
  • Template-Based PDF Generation: Support for custom PDF templates via file paths or functions
  • Email Integration: Automatically attach invoices to order creation emails (configurable)
  • Admin UI: Download button widget in order details sidebar
  • Authentication: Proper authentication and authorization checks

Compatibility

This plugin is compatible with Medusa v2.4.0 and above.

Installation

  1. Install the plugin:
npm install medusa-invoice
# or
yarn add medusa-invoice
  1. Add the plugin to your medusa-config.js:
module.exports = {
  // ... other config
  plugins: [
    {
      resolve: "medusa-invoice",
      options: {
        // Optional: Custom template (file path or function)
        template: "./templates/invoice-template.js",
        // Optional: Attach invoice to order creation emails
        attachInvoiceToEmail: true,
        // Optional: Default template path if no template provided
        defaultTemplatePath: "./templates/default-invoice.js",
      },
    },
  ],
}
  1. Run migrations (if any):
npx medusa db:migrate

Configuration Options

template (optional)

The PDF template to use for invoice generation. Can be:

  • File path (string): Path to a template file that exports a function
  • Function: A function that accepts OrderDTO and returns Promise<Buffer>

Example template files are available in the templates/ directory:

  • templates/invoice-template.example.js - Full-featured template with styling, tables, and formatting
  • templates/invoice-template-simple.example.js - Minimal template for quick setup

Basic template structure:

const PDFDocument = require("pdfkit")

// Helper to convert BigNumber values
function toNumber(value) {
  if (typeof value === "number") return value
  if (typeof value === "string") return Number(value) || 0
  if (value && typeof value === "object" && "numeric" in value) {
    return value.numeric || 0
  }
  return 0
}

module.exports = async function generateInvoice(order) {
  const doc = new PDFDocument({ margin: 50 })
  const buffers = []
  
  doc.on("data", buffers.push.bind(buffers))
  
  // Custom PDF generation logic
  doc.fontSize(20).text("Invoice", { align: "center" })
  doc.moveDown()
  doc.text(`Order ID: ${order.id}`)
  doc.text(`Total: ${toNumber(order.total).toFixed(2)}`)
  // ... more content
  
  doc.end()
  
  return new Promise((resolve) => {
    doc.on("end", () => {
      resolve(Buffer.concat(buffers))
    })
  })
}

Important: Always use the toNumber() helper when working with order totals (subtotal, tax_total, shipping_total, total) as they may be BigNumber objects, strings, or numbers.

attachInvoiceToEmail (optional)

Boolean flag to enable/disable automatic invoice attachment to order creation emails. Default: false

defaultTemplatePath (optional)

Path to a default template file to use when no template is provided. If not specified, a basic default template is used.

API Endpoints

Storefront API

Download Invoice

Endpoint: GET /store/invoice/download/:order_id

Authentication: Required (customer)

Description: Download invoice PDF for a specific order. Only works for orders belonging to the authenticated customer.

Response: PDF file with Content-Type: application/pdf

Example:

curl -X GET \
  'http://localhost:9000/store/invoice/download/order_123' \
  -H 'Authorization: Bearer <customer_token>' \
  --output invoice.pdf

Admin API

Download Invoice

Endpoint: GET /admin/invoice/download/:order_id

Authentication: Required (admin)

Description: Download invoice PDF for any order.

Response: PDF file with Content-Type: application/pdf

Example:

curl -X GET \
  'http://localhost:9000/admin/invoice/download/order_123' \
  -H 'Authorization: Bearer <admin_token>' \
  --output invoice.pdf

Get Settings

Endpoint: GET /admin/invoice/settings

Authentication: Required (admin)

Description: Get plugin configuration settings.

Response:

{
  "settings": {
    "attachInvoiceToEmail": true,
    "hasTemplate": true,
    "hasDefaultTemplatePath": false
  }
}

Creating Custom Templates

Template Function Signature

Your template function must accept an OrderDTO and return a Promise<Buffer>:

import { OrderDTO } from "@medusajs/framework/types"

async function myTemplate(order: OrderDTO): Promise<Buffer> {
  // Generate PDF using PDFKit or any PDF library
  // Return PDF buffer
}

Using PDFKit

The plugin uses PDFKit internally. You can use it in your templates:

const PDFDocument = require("pdfkit")

module.exports = async function generateInvoice(order) {
  const doc = new PDFDocument({ margin: 50 })
  const buffers = []
  
  doc.on("data", buffers.push.bind(buffers))
  
  // Your custom PDF generation
  doc.fontSize(20).text("Invoice", { align: "center" })
  doc.moveDown()
  
  // Access order data
  doc.text(`Order ID: ${order.id}`)
  doc.text(`Email: ${order.email}`)
  doc.text(`Total: ${order.total}`)
  
  // Access order items
  if (order.items) {
    order.items.forEach((item) => {
      doc.text(`${item.title} - ${item.quantity}x ${item.unit_price}`)
    })
  }
  
  // Access addresses
  if (order.shipping_address) {
    doc.text(`Shipping: ${order.shipping_address.address_1}`)
  }
  
  doc.end()
  
  return new Promise((resolve) => {
    doc.on("end", () => {
      resolve(Buffer.concat(buffers))
    })
  })
}

OrderDTO Structure

The OrderDTO object contains:

  • id: Order ID
  • email: Customer email
  • total: Order total
  • subtotal: Order subtotal
  • tax_total: Tax total
  • shipping_total: Shipping total
  • items: Array of order items
  • shipping_address: Shipping address object
  • billing_address: Billing address object
  • created_at: Order creation date
  • And more...

Email Integration

When attachInvoiceToEmail is enabled, the plugin automatically generates an invoice PDF when an order is created and attaches it to the order creation email notification.

The subscriber listens to the order.placed event (checkout completion in some Medusa setups). The flow is:

  1. Checks if invoice attachment is enabled
  2. Generates the invoice PDF
  3. Attaches it to the email notification

Notes:

  • Ensure attachInvoiceToEmail is true in your plugin configuration.
  • The plugin sends its own email (with attachment) using the configured notification provider; this operates independently of other plugins' email templates.

Troubleshooting attachments:

  • Verify your notification provider supports attachments (some providers or custom adapters ignore attachments by default).

  • Enable debug logging and check server logs for these messages from the invoice subscriber:

    • [Invoice Subscriber] Payload summary - keys: ...; attachments=... (shows payload shape)
    • [Invoice Subscriber] ✅ Email sent successfully - Order: ...; attachment_sizes=[...] (shows attachment byte size)
  • If attachments do not appear, confirm your provider reads attachments, files, or attachmentsRaw from the payload. We now include several shapes for compatibility:

    • attachments (base64 content, encoding: "base64") - common provider shape
    • files (base64 content, minimal fields)
    • attachmentsRaw (Buffer) - for Nodemailer-like providers that expect Buffer
    • sgAttachments (SendGrid-style: base64 + disposition: "attachment")

    If your provider requires a different shape, either adapt your provider to accept one of these fields or tell me which provider you're using and I can add a small adapter to map our payload into the provider's expected format.

Admin UI

The plugin adds a download button widget to the order details page in the Medusa Admin. The widget appears in the sidebar and allows admins to download invoices with a single click.

Workflows

The plugin exports a workflow for programmatic invoice generation:

import { generateInvoiceWorkflow } from "medusa-invoice"

const { result } = await generateInvoiceWorkflow(container).run({
  input: {
    orderId: "order_123",
    customerId: "customer_456", // Optional, required for storefront
    template: customTemplate, // Optional
  },
})

const pdfBuffer = result.buffer

TypeScript Support

The plugin is written in TypeScript and exports types:

import type {
  InvoicePluginOptions,
  InvoiceTemplate,
  InvoiceGenerationResult,
} from "medusa-invoice"

Development

Building the Plugin

npm run build

Development Mode

npm run dev

Troubleshooting

Invoice Generation Fails

  • Check that the order exists and has required data
  • Verify template function returns a valid PDF buffer
  • Check server logs for detailed error messages

Email Attachment Not Working

  • Ensure attachInvoiceToEmail is set to true in config
  • Verify notification service is properly configured
  • Check that the order.placed event is being emitted

Template Not Loading

  • Verify template file path is correct (relative to project root or absolute)
  • Ensure template exports a function with correct signature
  • Check file permissions

License

MIT

Support

For issues and questions, please open an issue on GitHub.