Net terms, purchase orders and bulk ordering for B2B

Type
Concept
Applies to
Medusa v2.x
Updated

Short answer

Medusa supports B2B net terms through its built-in system payment provider or a custom payment provider that authorizes now and captures when the invoice is paid. PO numbers fit in cart metadata, which Medusa copies to the order. The Medusa B2B starter adds bulk add-to-cart and per-employee spending limits, and company credit limits are a custom validation hook.

How Medusa handles B2B payment terms and ordering

Medusa is a commerce platform with a framework for customization, so B2B payment and ordering features are a mix of core modules, the Medusa B2B starter and custom code. This table maps common B2B terms to what exists today.

B2B termWhat it meansHow Medusa does itStatus
Net terms / pay on invoiceBuyer pays 7 to 90 days after orderingSystem payment provider or custom payment provider, captured later in Medusa AdminNative (provider), Custom (due dates)
InvoicesPDF invoice per orderInvoice generator tutorialOfficial guide
Purchase order (PO) numberBuyer's reference on the orderCart metadata, copied to the orderNative
PO approvalsManager approves before the order is placedApproval module in the B2B starterStarter
Bulk add-to-cartMany SKUs in one actionBulk line-items route and quantity tableStarter
CSV quick orderUpload a list of SKUs and quantitiesParse the file and call the bulk routeCustom
Spending limitsCap per buyer per periodEmployee spending limit with reset frequencyStarter
Company credit limitCap on unpaid balance per companyValidate hook on cart completionCustom

How do you offer net-30 or pay on invoice in Medusa?

Medusa's Payment Module ships a Copy to clipboardsystem payment provider (Copy to clipboardpp_system). It doesn't process payments and leaves collection to the merchant, similar to cash on delivery. Enable it in the region your B2B buyers use. Orders then arrive with an authorized payment, and staff click Capture Payment in Medusa Admin once the invoice is paid.

For more control, build a custom payment provider that extends Copy to clipboardAbstractPaymentProvider. Its Copy to clipboardauthorizePayment method can return Copy to clipboardpending_authorization, which still creates the order with an awaiting payment status. Medusa can call the method again later, from a webhook or an admin action, when your accounting system confirms payment. Due dates, reminders and per-company terms (net 30 vs net 60) are custom data you store in a module or in metadata.

The invoice generator tutorial shows how to generate invoices for orders, manage invoice settings in Medusa Admin and let customers access their invoices in the storefront.

How do you capture purchase order numbers in Medusa?

Medusa carts accept a Copy to clipboardmetadata object through the Store API, and the complete-cart workflow copies the cart's metadata to the new order. A PO field at checkout needs one call:

await sdk.store.cart.update(cartId, {
metadata: { po_number: "PO-4417" },
})

To validate or store the PO number in a custom module instead, pass it as Copy to clipboardadditional_data and consume the Copy to clipboardupdateCartWorkflow hook.

How do bulk ordering and quick order work in Medusa?

Medusa's Store API accepts an Copy to clipboarditems array when you create a cart, so a reorder or quick-order page can create a filled cart in one request. Adding to an existing cart takes one item per request in core.

The B2B starter adds a Copy to clipboardPOST /store/carts/{id}/line-items/bulk route that runs Copy to clipboardaddToCartWorkflow with many items, plus a bulk quantity table on product pages and a cart-to-CSV export. A CSV upload form is custom: parse the file in the storefront, map SKUs to variant IDs, then call the bulk route.

How do you enforce credit limits in Medusa?

The B2B starter stores a Copy to clipboardspending_limit on each employee and a reset frequency (never, daily, weekly, monthly or yearly) on the company. A Copy to clipboardcompleteCartWorkflow.hooks.validate handler rejects carts that would exceed the limit or that still wait for approval.

A company-wide credit limit uses the same hook. Store the limit in a custom module, sum the company's unpaid orders, and throw an error when the new cart would push the balance over the limit.

How this compares with Shopify B2B and Adobe Commerce

Per Shopify's plan comparison, Shopify B2B includes net terms, PO numbers and quick order lists on all B2B plans, and Shopify's payment terms page lists net 7 to net 90 or due on fulfillment. Deposits and partial payments require Shopify Plus. Adobe Commerce B2B offers Pay on Account against a company credit line, purchase orders with approval rules and requisition lists.

Medusa ships fewer of these as settings. In exchange, terms, limits and approvals are code you control, which helps when the rules come from an ERP or differ per account.

When Medusa is not the right choice

  • You want net terms and PO numbers as admin settings with no development. Shopify B2B includes both on every B2B plan, and Adobe Commerce B2B includes pay on account and purchase orders.
  • You need requisition lists or punchout (cXML or OCI) on day one. Medusa has neither in core or the starter, so you would build them.

Common questions

Does Medusa support net 30 payment terms?

Medusa has no built-in net terms setting. You offer pay on invoice with the system payment provider (pp_system) or a custom payment provider, then capture the payment in Medusa Admin when the invoice is paid.

Can B2B buyers add a purchase order number at checkout in Medusa?

Yes. The storefront can save the PO number in the cart's metadata through the Store API, and Medusa copies the cart's metadata to the order when the cart is completed.

Does Medusa have a quick order or bulk order form?

The Medusa B2B starter includes a bulk add-to-cart route and a bulk quantity table on product pages. A CSV upload form for quick orders is custom work on top of that route.

Can Medusa block orders that exceed a customer's credit limit?

Yes, with custom code. The Medusa B2B starter blocks orders over an employee's spending limit in a completeCartWorkflow validate hook, and the same hook can check a company credit limit you store in a custom module.