Describe the Bug
The ecommerce plugin's confirmOrder endpoint decrements stock with a direct payload.db.updateOne using a Mongo $inc:
await payload.db.updateOne({
id,
collection: variantsSlug, // or productsSlug
data: {
inventory: {
$inc: item.quantity * -1,
},
},
})
Source: https://github.com/payloadcms/payload/blob/v3.84.1/packages/plugin-ecommerce/src/endpoints/confirmOrder.ts#L182-L210
When the products / variants collection has drafts enabled (versions.drafts: true), Payload stores the document in two places:
- the main
products collection (latest published doc)
- the
_products_versions collection (all snapshots, with latest: true marking the row that the admin list view reads through queryDrafts)
The payload.db.updateOne call only writes to the main collection. The _versions row marked latest: true keeps its pre-decrement inventory, so the admin list view (and any find({ draft: true }) read) continues to show the old stock value after order confirmation. In practice: a customer purchases a product, the order is created and paid, but the admin "Products" list keeps showing the pre-purchase inventory.
Suggested fix: when the target collection has drafts enabled, mirror the $inc onto the latest: true versions doc as well (e.g. via payload.db.updateVersion).
Link to the code that reproduces this issue
https://github.com/jhb-dev/payload-ecommerce-decrement-versions-stale
Reproduction Steps
- Clone the reproduction repository and run the development server
- The
onInit seed (src/seed.ts) creates a Product with inventory: 10, then calls the same payload.db.updateOne({ inventory: { $inc: -1 } }) shape used by the plugin's confirmOrder
- The seed reads back both the main collection doc and the
_versions doc with latest: true, and logs them
- Expected: both reads return
inventory: 9
- Actual: main collection returns
9, but _versions (and find({ draft: true }), which is what the admin list view uses) still returns 10
Server log output:
[11:36:54] INFO: --- Reproduction: stock $inc leaves _versions stale ---
[11:36:55] INFO: Created product 69fb0bb628ecf760cde3fa29 with inventory=10 (published)
[11:36:55] INFO: Called payload.db.updateOne({ inventory: { $inc: -1 } })
[11:36:55] INFO: Main collection inventory: 9
[11:36:55] INFO: _versions latest inventory: 10
[11:36:55] INFO: find({ draft: true }) inventory: 10 (this is what the admin list view shows)
[11:36:55] ERROR: BUG REPRODUCED: main collection decremented to 9, but _versions still shows 10. The admin list view will show 10.
Which area(s) are affected?
plugin: ecommerce
Environment Info
```
Binaries:
Node: 24.3.0
npm: 11.4.2
Yarn: 1.22.22
pnpm: 10.33.0
Relevant Packages:
payload: 3.84.1
next: 16.2.3
@payloadcms/db-mongodb: 3.84.1
@payloadcms/graphql: 3.84.1
@payloadcms/next/utilities: 3.84.1
@payloadcms/richtext-lexical: 3.84.1
@payloadcms/translations: 3.84.1
@payloadcms/ui/shared: 3.84.1
react: 19.2.4
react-dom: 19.2.4
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 24.6.0
```
Describe the Bug
The ecommerce plugin's
confirmOrderendpoint decrements stock with a directpayload.db.updateOneusing a Mongo$inc:Source: https://github.com/payloadcms/payload/blob/v3.84.1/packages/plugin-ecommerce/src/endpoints/confirmOrder.ts#L182-L210
When the products / variants collection has drafts enabled (
versions.drafts: true), Payload stores the document in two places:productscollection (latest published doc)_products_versionscollection (all snapshots, withlatest: truemarking the row that the admin list view reads throughqueryDrafts)The
payload.db.updateOnecall only writes to the main collection. The_versionsrow markedlatest: truekeeps its pre-decrement inventory, so the admin list view (and anyfind({ draft: true })read) continues to show the old stock value after order confirmation. In practice: a customer purchases a product, the order is created and paid, but the admin "Products" list keeps showing the pre-purchase inventory.Suggested fix: when the target collection has drafts enabled, mirror the
$inconto thelatest: trueversions doc as well (e.g. viapayload.db.updateVersion).Link to the code that reproduces this issue
https://github.com/jhb-dev/payload-ecommerce-decrement-versions-stale
Reproduction Steps
onInitseed (src/seed.ts) creates aProductwithinventory: 10, then calls the samepayload.db.updateOne({ inventory: { $inc: -1 } })shape used by the plugin'sconfirmOrder_versionsdoc withlatest: true, and logs theminventory: 99, but_versions(andfind({ draft: true }), which is what the admin list view uses) still returns10Server log output:
Which area(s) are affected?
plugin: ecommerce
Environment Info
```
Binaries:
Node: 24.3.0
npm: 11.4.2
Yarn: 1.22.22
pnpm: 10.33.0
Relevant Packages:
payload: 3.84.1
next: 16.2.3
@payloadcms/db-mongodb: 3.84.1
@payloadcms/graphql: 3.84.1
@payloadcms/next/utilities: 3.84.1
@payloadcms/richtext-lexical: 3.84.1
@payloadcms/translations: 3.84.1
@payloadcms/ui/shared: 3.84.1
react: 19.2.4
react-dom: 19.2.4
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 24.6.0
```