WordPress and WooCommerce
how to invoice LMS course enrolments from WooCommerce automatically
why LMS enrolment and invoicing fall out of sync after checkout
Teams that sell online courses through WooCommerce plus LearnDash, Tutor LMS, or LifterLMS often split fulfilment across three desks. Checkout captures the payment. Someone in operations opens the order, copies the buyer email into the LMS roster, and assigns the course product that maps to a paid SKU. Separately, finance retypes the same buyer into Fakturownia, wFirma, Comarch Optima, or another invoicing stack because the shop receipt is not a fiscal invoice in their country. That double entry is where seats get granted without a document, or documents get issued to the wrong VAT ID while the student waits for access. The failure mode is not rare. Bundle SKUs that unlock two courses, corporate seats bought by a purchasing manager for five employees, and renewal orders on subscription products all break naive "one order line equals one enrolment" assumptions. Refunds and partial refunds make it worse: the LMS still shows active access while accounting has already issued a corrective invoice, or the opposite happens and the student loses access before finance closes the credit note. Support then spends the afternoon reconciling three systems that never shared a correlation ID. Manual spreadsheets hide the lag until a cohort start date. If fifty enrolments land on a Monday morning promo, operations cannot keep pace with paid orders, so students email "I paid, where is my course" while finance is still batching invoices on Friday. The business risk is chargebacks and chargeback-like disputes when the buyer can prove payment but cannot prove access was granted within a reasonable window. Automation is justified when enrolment volume regularly exceeds what one person can clear the same day without errors, and when invoice numbering must stay continuous for the tax office. Course creators also stumble when a free lead-magnet course sits next to paid cohorts in the same LMS. Staff accidentally reuse the paid product hook for the free SKU, which either invoices zero-value nonsense documents or enrols paid students into the wrong drip sequence. Another frequent gap is instructor-led cohorts with a fixed start date: students may pay weeks early, so access should be scheduled rather than opened immediately, while finance still needs the invoice on the payment date. That split between fiscal date and access date must be encoded explicitly or support invents one-off exceptions every launch. Localisation matters for VAT. A Polish buyer with a valid NIP expects a fiscal invoice shaped for the tax office, while an EU VAT-registered company may need reverse charge annotations. If the automation always emits a retail receipt style document, finance redoes the work by hand and loses trust in the queue. Capture tax class and buyer type at checkout, pass them through unchanged, and refuse to invent country rules inside the LMS plugin.
when a paid WooCommerce order should create an LMS enrolment invoice
Fire the workflow when a WooCommerce order reaches a paid status that your shop treats as final for digital goods: typically processing or completed, depending on whether you keep virtual orders in processing until fulfilment hooks finish. Also listen for subscription renewal payments if the course is sold as a WooCommerce Subscriptions product, and for manual admin status flips from on-hold to processing after a bank transfer clears. Do not enrol or invoice on pending or failed payments. For B2B checkout fields, wait until VAT ID validation has succeeded or been explicitly waived by a staff role, otherwise you will invoice a placeholder company name that finance must void later. Include partial captures from payment gateways only when your shop configuration treats the order as paid for digital delivery. Ignore order notes and customer-download permission toggles as triggers; those are too noisy. If you use a learning platform outside WordPress that still bills through WooCommerce, treat the external enrolment API as the LMS step but keep the same paid-status gate.
how to enrol the student and issue the invoice in one automation
- Map each paid order line to an LMS course or group ID using a maintained SKU table, including bundle parents that expand into multiple course IDs and seat quantities for corporate products.
- Create or update the student user in WordPress, attach the enrolment or group membership, and store the WooCommerce order ID plus line item ID on the LMS enrolment meta so refunds can reverse the same seat.
- Build the invoice payload from billing fields, VAT ID, and tax class; send it to Fakturownia, wFirma, Optima, or your accounting API; write the remote invoice number and PDF URL back to order meta.
- Email the student a single message that includes the course login link and the invoice attachment or secure download, using the same correlation ID in both LMS and accounting logs.
- On refund or subscription cancellation webhooks, revoke or schedule-end the enrolment according to policy, request a corrective invoice or credit note, and notify finance if the remote document API rejects the correction.
- Queue a nightly reconcile that lists paid digital orders missing either an enrolment meta key or an invoice number so operations can clear exceptions before the next cohort email.
- When access must wait for a cohort start date, write a scheduled Action Scheduler job that grants the LMS seat at the start timestamp while still issuing the invoice on payment.
Systems
- WooCommerce
- LearnDash / Tutor LMS
- Fakturownia / wFirma
- Comarch Optima
Tools
- Action Scheduler
- WooCommerce webhooks
- n8n
- REST APIs
data flow from WooCommerce payment to LMS seat and accounting document
WooCommerce remains the commercial source of truth for payment state. A webhook or Action Scheduler job posts the order ID into n8n or a custom PHP listener. The listener loads line items, resolves SKU-to-course mappings from a options table or Google Sheet mirrored into wp_options, and calls the LMS enrolment API or LearnDash group join functions inside WordPress when possible so capability checks stay local. Invoice creation usually leaves WordPress: REST to Fakturownia or wFirma for Polish sole traders and small companies, or a queued export into Comarch Optima / enova when the accounting team refuses cloud invoice SaaS. Prefer n8n when finance systems change often; prefer Action Scheduler plus a small plugin when you must keep secrets and enrolments inside the WP runtime. Every hop writes the same order_id and a generated fulfilment_id into logs so support can search one key across LMS, shop, and accounting. Persist a fulfilment state machine on the order: paid_received, enrolled, invoiced, notified, reconcile_ok. Expose those states to a simple admin list table so operations does not need n8n access. When Optima is involved, many teams write a staging table that the Optima integrator polls; do not block the student email on Optima's batch window. Instead mark invoiced_pending_erp and let finance complete numbering, while still showing the student a commercial confirmation.
what to do when enrolment succeeds but the invoice API fails
If the LMS enrolment call fails after payment, do not issue the invoice yet; retry with backoff, then open a Slack or email alert with the order link and leave the order in a custom "fulfilment-error" status so warehouse-style dashboards can see digital failures too. If enrolment succeeds and the invoice API returns 4xx because of a bad VAT ID, keep the seat, flag order meta invoice_error, and route to finance without blocking the student. If the invoice API succeeds but the PDF fetch fails, store the remote ID and retry PDF download separately. Dead-letter any job older than your SLA, and never auto-enrol on a second payment webhook for the same order_id without an idempotency key. Duplicate webhooks from the gateway are normal. Gate every side effect on an idempotency record keyed by order_id plus ability name (enrol vs invoice). If a student already has lifetime access to the course from a previous purchase, skip re-enrolment but still invoice the new commercial order if it is a separate paid SKU such as coaching add-on. Document that branch so support does not treat skipped enrolment as a bug.
when manual LMS invoicing is still cheaper than automation
Skip this build when you sell fewer than roughly twenty course seats a month, issue invoices by hand in under ten minutes each, and never run cohort launches that spike volume. Also skip when every sale is a custom corporate contract already invoiced in Optima before the LMS seat is granted by an account manager. Automation that fights a human-negotiated billing process creates duplicate documents and angry finance teams. Corporate learning departments that invoice via annual framework agreements in enova and only use WooCommerce as a catalogue front should not force per-seat shop invoices through this flow. Use a hand-off to finance instead of fake retail documents.
KPIs for LMS enrolment invoicing automation
- Median minutes from paid order to LMS enrolment meta written
- Share of paid digital orders with both enrolment ID and invoice number within 24 hours
- Count of fulfilment-error orders aged over your support SLA
- Refunds where access remained active more than one hour after credit note
Automation ROI calculator
Enter how often the process runs each month, minutes per run, and your loaded hourly cost. The result is a simulation, not a quote for our work.
Hours = runs × minutes / 60 × recovery %. Time value = hours × rate × realisation %. Avoided errors = runs × error % × cost per error × realisation %. Net = sum − upkeep. Payback = build / net, only when net > 0.
Hours recovered / month: -
Hours recovered / year: -
Time value / month: -
Avoided error cost / month: -
Net / month: -
Payback (months): -
First-year result: -
Frequently asked questions
Practical answers to apply the topic in real execution.
Should the invoice or the LMS enrolment happen first?
#How do bundles and corporate seats map to invoices?
#What about bank transfer orders that stay on-hold?
#Can the same automation handle free and paid courses?
#Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.
Let’s discuss