Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

Payload Commerce Kit

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-ecommerce 3.88 or newer. Every package declares this in its own peerDependencies and was verified against Payload 3.88.0.

Status

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.

What was measured

Measured on 19 August 2026 against the published packages on the npm registry and against the contents of @payloadcms/plugin-ecommerce@3.88.0.

What the official plugin already covers

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.

The findings that shaped this list

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.

The state of the ecosystem

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.

Release cadence, and why it decides the design

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.

The packages

All names verified as available on the npm registry on 19 August 2026.

Tier 1, a store cannot trade without these

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

Tier 2, the store loses money without these

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

Tier 3, valuable but not blocking

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

Notes on scope

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.

Design rules taken from the measurement

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.

Order of work, as it was planned

  1. 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.
  2. payload-fulfillment with payload-order-emails, the most serious functional gap. At present a customer pays and receives nothing.
  3. payload-stock-reservation, raised in priority: the measurement showed the variant stock path needs the same protection, not only the race window.
  4. The rest by tier.

Settled at the outset

  • Distribution. One public repository per package, Poseidonas/<package-name>, the same shape as the WordPress family. Plain semver with a vX.Y.Z tag 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 both payload and @payloadcms/plugin-ecommerce.
  • Build. TypeScript strict, tsup, ESM with type declarations. Published tarball carries dist, README.md and LICENSE only.

Payload Commerce Kit

Μια οικογένεια μικρών 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.

Επίπεδο 1, χωρίς αυτά δεν ανοίγει κατάστημα

Πακέτο Τι προσθέτει Μέγεθος Έκδοση
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

Επίπεδο 2, χωρίς αυτά το κατάστημα χάνει χρήματα

Πακέτο Τι προσθέτει Μέγεθος Έκδοση
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

Επίπεδο 3, χρήσιμα αλλά όχι ανασταλτικά

Πακέτο Τι προσθέτει Μέγεθος Έκδοση
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. Ένα κατάστημα πρέπει να μπορεί να υιοθετήσει ένα πακέτο χωρίς να υιοθετήσει τα υπόλοιπα.

Μέτρηση πριν από κάθε ένα. Τα τρία ευρήματα παραπάνω άλλαξαν αυτή τη λίστα ενώ γραφόταν. Κάθε πακέτο περνάει από την ίδια διαδικασία πριν από την πρώτη του γραμμή κώδικα.

Σειρά εργασίας, όπως σχεδιάστηκε

  1. payload-order-numbers, το μικρότερο του συνόλου, με τη μεγαλύτερη αναλογία αξίας προς κόπο, και θεμελιώνει το build, το peer range, τη διάταξη δοκιμών και τη μορφή του README που θα ξαναχρησιμοποιήσουν τα υπόλοιπα έντεκα.
  2. payload-fulfillment μαζί με payload-order-emails, το σοβαρότερο λειτουργικό κενό. Αυτή τη στιγμή ο πελάτης πληρώνει και δεν λαμβάνει τίποτα.
  3. payload-stock-reservation, ανεβασμένο σε προτεραιότητα: η μέτρηση έδειξε ότι η διαδρομή αποθέματος παραλλαγών χρειάζεται την ίδια προστασία, όχι μόνο το παράθυρο ανταγωνισμού.
  4. Τα υπόλοιπα κατά επίπεδο.

Αποφασισμένα από την αρχή

  • Διανομή. Ένα δημόσιο αποθετήριο ανά πακέτο, 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.

About

A family of small, single-purpose npm packages for Payload CMS ecommerce: the measured gaps, the design rules and the state of each package.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors