A family of small, single-purpose npm packages for Payload CMS and Next.js, each one closing a measured gap between the official @payloadcms/plugin-ecommerce and what a real store needs in order to trade.
This repository is the index of the series: the measurement that shaped the list, the design rules every package follows, and the state of each one. Each package lives in its own repository and on the npm registry, and can be adopted on its own.
Compatibility — Payload 3.88 or newer, with
@payloadcms/plugin-ecommerce3.88 or newer. Every package declares this in its ownpeerDependenciesand was verified against Payload 3.88.0.
All of them are published. Each lives in its own repository under
Poseidonas and on the npm registry,
and can be adopted on its own.
| Published | the fifteen below, plus payload-barcodes and payload-ai-manager, between 19 and 21 August 2026 |
| Peer range | payload and @payloadcms/plugin-ecommerce at >=3.88 <4, declared by every one of them |
| Licence | MIT throughout |
| Author | George Vasiliades - https://github.com/Poseidonas |
| Baseline | @payloadcms/plugin-ecommerce 3.88.0, Next.js App Router |
Everything from here down was written before any of them existed, and is left as it was. It is the record of why the list has this shape, not a description of where it stands.
Measured on 19 August 2026 against the published packages on the npm registry and against the contents of @payloadcms/plugin-ecommerce@3.88.0.
| Area | Present |
|---|---|
| Collections | products, variants, carts, orders, addresses, transactions |
| Cart operations | add, update, remove, clear, merge, as REST endpoints |
| Payments | Stripe adapter with initiatePayment, confirmOrder, webhooks |
| Inventory | validated before payment, decremented after payment |
| Guest carts | yes |
| Frontend | React provider with cart hooks |
| Translations | 20 languages |
The plugin is competent at what it does. The gaps below are not defects in it; they are the parts of a storefront it does not claim to solve.
All of these were verified by running them against a live Payload 3.88 install with the official plugin, not by reading code alone. Two earlier readings of the source turned out to be wrong and were corrected by that run; they are noted below.
Variant stock and price are never validated. In initiatePayment the variant check sits inside a branch that requires the item to have no variant:
201 if (item.product && !item.variant) {
253 if (item.variant) {
310 }
311 }
The inner condition can never be true. Verified against a live install: a product with inventory 2 and no variant is refused with HTTP 400 and cause.code: OutOfStock, while the same quantity of a variant with inventory 2 passes validation completely and reaches the payment provider. The server log shows the out of stock error for the first and no inventory error at all for the second. confirmOrder then decrements variant inventory correctly, so the stock goes negative. productsValidation is also destructured in confirmOrderHandler but never called there.
Stock is validated but never held. Availability is checked in initiatePayment and the decrement happens in confirmOrder. Between those two calls nothing is reserved, so two customers can both pass validation holding the last unit.
Validation errors reach the client empty. The handler returns message: error where error is an Error instance, and JSON.stringify of an Error is {}. A refused checkout returns HTTP 400 with {"message": {}}. The machine readable reason survives in cause.code, the human readable one does not.
An order has no billing address. Confirmed from the generated types of a live install. The order carries items, shippingAddress, customer, customerEmail, transactions, status, amount and currency. Carts carry no address at all. There is no order number, no notes, no shipping cost, no tax, no discount and no tracking. The billing address is not on the order either, it lives on the transaction.
Cart abandonment is computed, not stored. The cart status field is declared virtual: true with an afterRead hook, so active, purchased and abandoned are derived on every read and written nowhere. statusBeforeRead.js is the whole rule: purchasedAt set means purchased, created less than 7 * 24 * 60 * 60 * 1000 ago means active, otherwise abandoned. Three consequences: the threshold is hard coded, it counts from createdAt rather than last activity so a cart made eight days ago and updated this morning already reads abandoned, and the value cannot be queried at all because Payload rejects a filter on a virtual field.
Correction, order status. An earlier reading of fields/statusField.js suggested that order status was payment status. That field belongs to transactions. Orders have their own status of processing, completed, cancelled, refunded, defaulting to processing. The fulfillment gap is therefore narrower than first stated: the states exist, but there is no state before payment, no on-hold, no failed, no shipped between processing and completed, no transition history, no notes and no tracking.
| Gap | Third party packages found |
|---|---|
| Tax and VAT | none |
| Shipping rates | none |
| Order emails | none |
| Invoices | none |
| Abandoned cart | none |
| Reports | none |
| Reviews | one, unmaintained since 2023 |
| Coupons | one, last published March 2026 |
Of 28 third party Payload packages surveyed, 14 have not been published since February 2026.
| Measure | Value |
|---|---|
Total payload versions |
1905 |
| Stable releases in the last 12 months | 54, one every 6.8 days |
| Minor bumps in the last 12 months | 39, from 3.50 to 3.88 |
| Peer range in official plugins | exact, "payload": "3.88.0" |
The official plugins pin an exact version because the whole monorepo publishes together. No surviving third party package does this. Every one that is still maintained declares a range, and every one that declared no peer at all is dead.
All names verified as available on the npm registry on 19 August 2026.
| Package | What it adds | WooCommerce equivalent | Size | Version |
|---|---|---|---|---|
payload-order-numbers |
Human readable sequential order numbers with prefix and padding, unique under concurrency | order number | Small | 1.0.1 |
payload-order-emails |
Transactional email on order events, templates, customer and admin recipients | WC_Email |
Medium | 1.0.1 |
payload-fulfillment |
A state before payment and an on hold state, a shipped state between processing and completed, transition history, internal notes, tracking number and carrier | order statuses and notes | Medium | 1.0.1 |
payload-tax-eu |
VAT by country, standard and reduced rates, B2B reverse charge, VIES validation, OSS reporting | tax classes and rates | Large | 1.0.0 |
payload-shipping-rates |
Zones by country and postcode, flat rate, free over threshold, weight and price bands, local pickup | WC_Shipping_Zones |
Large | 1.0.0 |
| Package | What it adds | WooCommerce equivalent | Size | Version |
|---|---|---|---|---|
payload-stock-reservation |
Validates variant stock at all, which the official plugin never does, and holds stock from checkout start until payment settles or expires | reserved stock table | Medium | 1.0.0 |
payload-coupons |
Percentage and fixed discounts, cart and line scope, usage limits, expiry, product and category exclusions | coupons | Large | 1.0.1 |
payload-refunds |
Partial and full refunds, per line item, optional restock, refund records against the original payment | refunds | Medium | 1.0.1 |
payload-invoices |
Sequential legal invoices, PDF generation, credit notes, company and VAT details | third party extension | Medium | 1.0.1 |
payload-abandoned-cart |
Marks carts abandoned on a schedule, recovery email sequence, recovery link and attribution | third party extension | Medium | 1.0.1 |
| Package | What it adds | WooCommerce equivalent | Size | Version |
|---|---|---|---|---|
payload-reviews |
Product reviews and ratings, verified purchase flag, moderation, aggregate rating | product reviews | Medium | 1.0.1 |
payload-downloads |
Digital products, expiring signed links, download limits, per order access | downloadable products | Medium | 1.0.1 |
payload-shipping-classes |
Weight, dimensions and shipping class on products and variants, consumed by payload-shipping-rates |
shipping classes | Small | 1.0.0 |
payload-stock-alerts |
Low stock threshold, admin notification, backorder handling | stock settings | Small | 1.0.0 |
payload-sales-reports |
Sales by period, product and customer, inside the admin panel | analytics | Medium | 1.0.1 |
Fifteen packages are not fifteen projects.
payload-order-emails and payload-fulfillment share a boundary, because the emails are sent by the status transitions. They are listed separately because a store may want the transitions without the mail, but they will be built together.
payload-shipping-classes exists only to feed payload-shipping-rates. It ships separately so that a store using flat rates does not have to carry it, but it is one piece of work.
Realistically the list is eleven to twelve pieces of work.
Declare a peer range, never an exact pin. Exact pinning works for the official monorepo because it publishes everything on the same day. For an independent package it would mean breaking every 6.8 days.
Stay on the stable surface. Collections, fields, hooks, endpoints and utilities survive minor bumps. Admin components do not, and that is where the abandoned third party packages died. Fourteen of the fifteen packages here need no admin component at all. payload-sales-reports does, and carries the corresponding risk.
Override, never replace. Each package extends the collections the official plugin already defines, in the same way the official plugins extend the base config. A store must be able to adopt one package without adopting the rest.
Measure before building each one. The three findings above changed this list while it was being written. Every package gets the same treatment before its first line of code.
payload-order-numbers, smallest of the set, highest value against effort, and it establishes the build, the peer range, the test setup and the README shape that the other eleven reuse.payload-fulfillmentwithpayload-order-emails, the most serious functional gap. At present a customer pays and receives nothing.payload-stock-reservation, raised in priority: the measurement showed the variant stock path needs the same protection, not only the race window.- The rest by tier.
- Distribution. One public repository per package,
Poseidonas/<package-name>, the same shape as the WordPress family. Plain semver with avX.Y.Ztag per repo, MIT throughout. This document lives in an umbrella repository,Poseidonas/payload-commerce-kit, which carries the plan and links to each package. - Names. Unscoped
payload-*as listed above. - Peer range.
">=3.88 <4"on every package, for bothpayloadand@payloadcms/plugin-ecommerce. - Build. TypeScript strict, tsup, ESM with type declarations. Published tarball carries
dist,README.mdandLICENSEonly.
Μια οικογένεια μικρών npm πακέτων ενός σκοπού για το Payload CMS και το Next.js. Κάθε ένα καλύπτει ένα μετρημένο κενό ανάμεσα στο επίσημο @payloadcms/plugin-ecommerce και σε αυτό που χρειάζεται ένα πραγματικό κατάστημα για να λειτουργήσει.
Και τα δεκαπέντε είναι δημοσιευμένα στο npm, μαζί με τα payload-barcodes και payload-ai-manager,
από τις 19 έως τις 21 Αυγούστου 2026. Καθένα ζει σε δικό του αποθετήριο και εγκαθίσταται μόνο του.
Το κείμενο που ακολουθεί γράφτηκε πριν υπάρξει οποιοδήποτε από αυτά και μένει όπως ήταν. Είναι η καταγραφή του γιατί η λίστα έχει αυτό το σχήμα, όχι περιγραφή του πού βρίσκεται.
Μετρήθηκε στις 19 Αυγούστου 2026, πάνω στα δημοσιευμένα πακέτα του μητρώου npm και στο περιεχόμενο του @payloadcms/plugin-ecommerce@3.88.0.
Όλα επαληθεύτηκαν εκτελώντας τα πάνω σε ζωντανή εγκατάσταση Payload 3.88 με το επίσημο πρόσθετο, όχι διαβάζοντας μόνο τον κώδικα. Δύο προηγούμενες αναγνώσεις της πηγής αποδείχθηκαν λανθασμένες και διορθώθηκαν από αυτή την εκτέλεση. Σημειώνονται παρακάτω.
Το απόθεμα και η τιμή των παραλλαγών δεν ελέγχονται ποτέ. Στο initiatePayment ο έλεγχος παραλλαγής βρίσκεται μέσα σε κλάδο που απαιτεί το αντικείμενο να μην έχει παραλλαγή:
201 if (item.product && !item.variant) {
253 if (item.variant) {
310 }
311 }
Η εσωτερική συνθήκη δεν μπορεί ποτέ να είναι αληθής. Επαληθεύτηκε σε ζωντανή εγκατάσταση: προϊόν με απόθεμα 2 και χωρίς παραλλαγή απορρίπτεται με HTTP 400 και cause.code: OutOfStock, ενώ η ίδια ποσότητα παραλλαγής με απόθεμα 2 περνά πλήρως την επικύρωση και φτάνει στον πάροχο πληρωμών. Το αρχείο καταγραφής του διακομιστή δείχνει το σφάλμα αποθέματος για το πρώτο και κανένα απολύτως σφάλμα αποθέματος για το δεύτερο. Στη συνέχεια το confirmOrder μειώνει κανονικά το απόθεμα της παραλλαγής, οπότε αυτό γίνεται αρνητικό. Το productsValidation επίσης αποδομείται στο confirmOrderHandler αλλά δεν καλείται ποτέ εκεί.
Το απόθεμα ελέγχεται αλλά δεν δεσμεύεται. Η διαθεσιμότητα ελέγχεται στο initiatePayment και η μείωση γίνεται στο confirmOrder. Ανάμεσα στις δύο κλήσεις δεν κρατιέται τίποτα, οπότε δύο πελάτες μπορούν να περάσουν και οι δύο τον έλεγχο κρατώντας το τελευταίο τεμάχιο.
Τα σφάλματα επικύρωσης φτάνουν άδεια στον πελάτη. Ο χειριστής επιστρέφει message: error όπου το error είναι στιγμιότυπο Error, και το JSON.stringify ενός Error δίνει {}. Μια απορριφθείσα παραγγελία επιστρέφει HTTP 400 με {"message": {}}. Ο μηχανικά αναγνώσιμος λόγος επιβιώνει στο cause.code, ο ανθρώπινα αναγνώσιμος όχι.
Η παραγγελία δεν έχει διεύθυνση τιμολόγησης. Επιβεβαιώθηκε από τους παραγόμενους τύπους ζωντανής εγκατάστασης. Η παραγγελία φέρει items, shippingAddress, customer, customerEmail, transactions, status, amount και currency. Τα καλάθια δεν φέρουν καμία διεύθυνση. Δεν υπάρχει αριθμός παραγγελίας, σημειώσεις, κόστος αποστολής, φόρος, έκπτωση ούτε αριθμός αποστολής. Η διεύθυνση τιμολόγησης δεν βρίσκεται ούτε αυτή στην παραγγελία, ζει στη συναλλαγή.
Η εγκατάλειψη καλαθιού υπολογίζεται, δεν αποθηκεύεται. Το πεδίο status του καλαθιού δηλώνεται virtual: true με hook afterRead, οπότε τα active, purchased και abandoned παράγονται σε κάθε ανάγνωση και δεν γράφονται πουθενά. Το statusBeforeRead.js είναι ολόκληρος ο κανόνας: αν υπάρχει purchasedAt είναι purchased, αν δημιουργήθηκε πριν από λιγότερο από 7 * 24 * 60 * 60 * 1000 είναι active, αλλιώς abandoned. Τρεις συνέπειες: το όριο είναι σταθερό στον κώδικα, μετρά από το createdAt και όχι από την τελευταία δραστηριότητα, και η τιμή δεν μπορεί να αναζητηθεί καθόλου επειδή το Payload απορρίπτει φίλτρο σε virtual πεδίο.
Διόρθωση, κατάσταση παραγγελίας. Προηγούμενη ανάγνωση του fields/statusField.js υποδείκνυε ότι η κατάσταση παραγγελίας είναι κατάσταση πληρωμής. Το πεδίο εκείνο ανήκει στα transactions. Οι παραγγελίες έχουν δική τους κατάσταση: processing, completed, cancelled, refunded, με προεπιλογή processing. Το κενό στην εκτέλεση παραγγελιών είναι επομένως στενότερο από ό,τι δηλώθηκε αρχικά: οι καταστάσεις υπάρχουν, αλλά δεν υπάρχει κατάσταση πριν την πληρωμή, ούτε on-hold, ούτε failed, ούτε shipped ανάμεσα στο processing και το completed, ούτε ιστορικό μεταβάσεων, ούτε σημειώσεις, ούτε αριθμός αποστολής.
Από τα 28 third party πακέτα Payload που εξετάστηκαν, τα 14 δεν έχουν δημοσιευτεί από τον Φεβρουάριο του 2026. Για τον φόρο, τα μεταφορικά, τα email παραγγελίας, τα τιμολόγια, το εγκαταλελειμμένο καλάθι και τις αναφορές δεν βρέθηκε κανένα πακέτο.
| Μέτρηση | Τιμή |
|---|---|
Συνολικές εκδόσεις payload |
1905 |
| Σταθερές εκδόσεις σε 12 μήνες | 54, μία κάθε 6,8 ημέρες |
| Minor bumps σε 12 μήνες | 39, από 3.50 έως 3.88 |
| Peer range στα επίσημα πρόσθετα | ακριβές, "payload": "3.88.0" |
Τα επίσημα πρόσθετα κλειδώνουν σε ακριβή έκδοση επειδή ολόκληρο το monorepo δημοσιεύεται μαζί. Κανένα third party πακέτο που επιβιώνει δεν το κάνει αυτό. Όσα συντηρούνται δηλώνουν εύρος, και όσα δεν δήλωσαν peer καθόλου είναι νεκρά.
Όλα τα ονόματα επιβεβαιώθηκαν ως διαθέσιμα στο μητρώο npm στις 19 Αυγούστου 2026.
| Πακέτο | Τι προσθέτει | Μέγεθος | Έκδοση |
|---|---|---|---|
payload-order-numbers |
Αναγνώσιμοι αύξοντες αριθμοί παραγγελίας, με πρόθεμα και συμπλήρωση, μοναδικοί υπό ταυτοχρονία | Μικρό | 1.0.1 |
payload-order-emails |
Email σε γεγονότα παραγγελίας, πρότυπα, παραλήπτες πελάτη και διαχειριστή | Μεσαίο | 1.0.1 |
payload-fulfillment |
Κατάσταση πριν την πληρωμή και κατάσταση αναμονής, κατάσταση απεσταλμένου ανάμεσα στο processing και το completed, ιστορικό μεταβάσεων, εσωτερικές σημειώσεις, αριθμός αποστολής και μεταφορέας | Μεσαίο | 1.0.1 |
payload-tax-eu |
ΦΠΑ ανά χώρα, κανονικοί και μειωμένοι συντελεστές, αντίστροφη χρέωση B2B, έλεγχος VIES, αναφορά OSS | Μεγάλο | 1.0.0 |
payload-shipping-rates |
Ζώνες ανά χώρα και ταχυδρομικό κώδικα, σταθερή χρέωση, δωρεάν πάνω από όριο, κλίμακες βάρους και αξίας, παραλαβή από κατάστημα | Μεγάλο | 1.0.0 |
| Πακέτο | Τι προσθέτει | Μέγεθος | Έκδοση |
|---|---|---|---|
payload-stock-reservation |
Έλεγχος αποθέματος παραλλαγών, τον οποίο το επίσημο πρόσθετο δεν κάνει καθόλου, και δέσμευση αποθέματος από την έναρξη του checkout μέχρι την εκκαθάριση ή τη λήξη | Μεσαίο | 1.0.0 |
payload-coupons |
Ποσοστιαίες και σταθερές εκπτώσεις, εμβέλεια καλαθιού και γραμμής, όρια χρήσης, λήξη, εξαιρέσεις προϊόντων και κατηγοριών | Μεγάλο | 1.0.1 |
payload-refunds |
Μερικές και πλήρεις επιστροφές, ανά γραμμή, προαιρετική επαναφορά αποθέματος, εγγραφές επιστροφής στην αρχική πληρωμή | Μεσαίο | 1.0.1 |
payload-invoices |
Αύξουσα σειρά νόμιμων τιμολογίων, παραγωγή PDF, πιστωτικά, στοιχεία εταιρείας και ΑΦΜ | Μεσαίο | 1.0.1 |
payload-abandoned-cart |
Χαρακτηρισμός καλαθιών ως εγκαταλελειμμένων, ακολουθία email ανάκτησης, σύνδεσμος επιστροφής και απόδοση | Μεσαίο | 1.0.1 |
| Πακέτο | Τι προσθέτει | Μέγεθος | Έκδοση |
|---|---|---|---|
payload-reviews |
Κριτικές και βαθμολογίες, σήμανση επιβεβαιωμένης αγοράς, έλεγχος, συγκεντρωτική βαθμολογία | Μεσαίο | 1.0.1 |
payload-downloads |
Ψηφιακά προϊόντα, υπογεγραμμένοι σύνδεσμοι με λήξη, όρια λήψεων, πρόσβαση ανά παραγγελία | Μεσαίο | 1.0.1 |
payload-shipping-classes |
Βάρος, διαστάσεις και κλάση αποστολής σε προϊόντα και παραλλαγές, τροφοδοτεί το payload-shipping-rates |
Μικρό | 1.0.0 |
payload-stock-alerts |
Όριο χαμηλού αποθέματος, ειδοποίηση διαχειριστή, χειρισμός προπαραγγελιών | Μικρό | 1.0.0 |
payload-sales-reports |
Πωλήσεις ανά περίοδο, προϊόν και πελάτη, μέσα στον πίνακα διαχείρισης | Μεσαίο | 1.0.1 |
Δεκαπέντε πακέτα δεν είναι δεκαπέντε έργα.
Το payload-order-emails και το payload-fulfillment μοιράζονται όριο, επειδή τα email στέλνονται από τις μεταβάσεις κατάστασης. Αναφέρονται ξεχωριστά επειδή ένα κατάστημα μπορεί να θέλει τις μεταβάσεις χωρίς τα μηνύματα, αλλά θα χτιστούν μαζί.
Το payload-shipping-classes υπάρχει μόνο για να τροφοδοτεί το payload-shipping-rates. Κυκλοφορεί χωριστά ώστε ένα κατάστημα με σταθερή χρέωση να μην το κουβαλάει, αλλά είναι ένα κομμάτι δουλειάς.
Ρεαλιστικά η λίστα είναι έντεκα με δώδεκα κομμάτια δουλειάς.
Δήλωση εύρους peer, ποτέ ακριβής έκδοση. Το ακρίβες κλείδωμα δουλεύει για το επίσημο monorepo επειδή δημοσιεύει τα πάντα την ίδια μέρα. Για ανεξάρτητο πακέτο θα σήμαινε σπάσιμο κάθε 6,8 ημέρες.
Παραμονή στη σταθερή επιφάνεια. Collections, fields, hooks, endpoints και utilities επιβιώνουν στα minor bumps. Τα admin components όχι, και εκεί ακριβώς πέθαναν τα εγκαταλελειμμένα third party πακέτα. Δεκατέσσερα από τα δεκαπέντε πακέτα εδώ δεν χρειάζονται κανένα admin component. Το payload-sales-reports χρειάζεται, και φέρει το αντίστοιχο ρίσκο.
Επέκταση, ποτέ αντικατάσταση. Κάθε πακέτο επεκτείνει τις collections που ορίζει ήδη το επίσημο πρόσθετο, με τον ίδιο τρόπο που τα επίσημα πρόσθετα επεκτείνουν το βασικό config. Ένα κατάστημα πρέπει να μπορεί να υιοθετήσει ένα πακέτο χωρίς να υιοθετήσει τα υπόλοιπα.
Μέτρηση πριν από κάθε ένα. Τα τρία ευρήματα παραπάνω άλλαξαν αυτή τη λίστα ενώ γραφόταν. Κάθε πακέτο περνάει από την ίδια διαδικασία πριν από την πρώτη του γραμμή κώδικα.
payload-order-numbers, το μικρότερο του συνόλου, με τη μεγαλύτερη αναλογία αξίας προς κόπο, και θεμελιώνει το build, το peer range, τη διάταξη δοκιμών και τη μορφή του README που θα ξαναχρησιμοποιήσουν τα υπόλοιπα έντεκα.payload-fulfillmentμαζί μεpayload-order-emails, το σοβαρότερο λειτουργικό κενό. Αυτή τη στιγμή ο πελάτης πληρώνει και δεν λαμβάνει τίποτα.payload-stock-reservation, ανεβασμένο σε προτεραιότητα: η μέτρηση έδειξε ότι η διαδρομή αποθέματος παραλλαγών χρειάζεται την ίδια προστασία, όχι μόνο το παράθυρο ανταγωνισμού.- Τα υπόλοιπα κατά επίπεδο.
- Διανομή. Ένα δημόσιο αποθετήριο ανά πακέτο,
Poseidonas/<όνομα-πακέτου>, με τη μορφή της οικογένειας WordPress. Απλό semver με ετικέταvX.Y.Zανά αποθετήριο, MIT παντού. Το παρόν κείμενο ζει σε αποθετήριο-ομπρέλα,Poseidonas/payload-commerce-kit, που φέρει το πλάνο και τους συνδέσμους προς κάθε πακέτο. - Ονόματα. Χωρίς scope,
payload-*όπως στη λίστα παραπάνω. - Εύρος peer.
">=3.88 <4"σε κάθε πακέτο, γιαpayloadκαι@payloadcms/plugin-ecommerce. - Build. TypeScript strict, tsup, ESM με δηλώσεις τύπων. Το δημοσιευμένο tarball φέρει μόνο
dist,README.mdκαιLICENSE.