Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Major documentation reorganization completed:
- ✅ Charge Credits (`docs/integrate/patterns/charge-credits.mdx`)
- ✅ Subscription Access (`docs/integrate/patterns/subscription-access.mdx`)
- ✅ Dynamic Pricing (`docs/integrate/patterns/dynamic-pricing.mdx`)
- ✅ Orders: Goods & Services Checkout (`docs/integrate/patterns/orders.mdx`) — Phase 1 contract, marked not yet GA
- ✅ Orders: Goods & Services Checkout (`docs/integrate/patterns/orders.mdx`) — Phase 1 contract

#### Platform Integrations (Symlinks to existing content)
- ✅ x402 Protocol (`docs/integrate/platforms/x402-protocol.mdx`)
Expand Down
10 changes: 3 additions & 7 deletions integrate/patterns/orders.mdx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
---
title: "Orders: Goods & Services Checkout"
description: "Charge a buyer an arbitrary cart total by card in the browser with a single server-to-server call. No plan registration, no buyer Nevermined account, no delegation."

Check warning on line 3 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L3

Did you really mean 'Nevermined'?
icon: "cart-shopping"
---

<Warning>
**Not yet generally available.** Orders are being rolled out to organization accounts and are not enabled in production yet. This guide documents the Phase 1 contract so you can plan your integration. Watch this page for availability.
</Warning>

An **Order** is a first-class, off-plan charge for an arbitrary amount: the price is whatever your cart adds up to on this request. You call one endpoint from your server, state the amount, and get back a `clientSecret` the buyer's browser confirms with their card. Nothing needs to exist in advance: no plan, no buyer Nevermined account, no delegation.

Check warning on line 7 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L7

Did you really mean 'Nevermined'?

Orders live alongside plans; they don't replace them. Use a plan for a reusable, catalog-listed service. Use an Order when you need to charge one buyer one specific total and move on.

<Note>
An Order is not the same thing as ordering a plan. `orderPlan()` / `order_plan()` buys credits on an existing plan for a Nevermined account. An Order has no plan and no account behind it.

Check warning on line 12 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L12

Did you really mean 'Nevermined'?
</Note>

## When to use an Order
Expand All @@ -21,15 +17,15 @@
| | Plan | Order |
|---|---|---|
| Price | Fixed at registration | Arbitrary, set per request |
| Buyer | Agent or human; may need a Nevermined account or delegation | A human in a browser, **no** Nevermined account |

Check warning on line 20 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L20

Did you really mean 'Nevermined'?

Check warning on line 20 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L20

Did you really mean 'Nevermined'?
| Fits | A service or subscription sold repeatedly | "Charge this cart for $3,437.95 and forget it" |
| Currency | USD, EUR, USDC | USD only (Phase 1) |

## Before you start

- **An active organization account.** Orders are available to organizations only. A personal API key is refused with `BCK.ORDER.0003`. See [Organizations](/solutions/organizations/overview).
- **A Nevermined API key** [scoped to that organization](/solutions/organizations/workspaces-and-members). The key identifies you as the Merchant of Record for every Order it creates.

Check warning on line 27 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L27

Did you really mean 'Nevermined'?
- **A validated Stripe Connect account.** Nevermined looks for one on your organization first, then on the API key owner's profile. Order revenue is paid out to it, the same way [fiat plan payments](/integrate/patterns/fiat-payments#revenue-routing) are. Without one, `POST /api/v1/orders` fails with `BCK.ORDER.0004`. See the [Payments FAQ](/products/payments/faq) for seller onboarding.

Check warning on line 28 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L28

Did you really mean 'Nevermined'?

Set your key once:

Expand All @@ -41,34 +37,34 @@

## How it works

![Diagram: a merchant creates an order server-side with its API key; Nevermined and Stripe run the checkout in three steps — Create returns a clientSecret, the buyer Confirms the card payment in the hosted checkout, and a Stripe webhook Settles the order as paid — then the paid result returns to the merchant with payout to its Connect account. The buyer needs no Nevermined account.](/images/orders-flow.png)

Check warning on line 40 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L40

Did you really mean 'Nevermined'?

Check warning on line 40 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L40

Did you really mean 'clientSecret'?

Check warning on line 40 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L40

Did you really mean 'Nevermined'?

Your server sets the price. The buyer's browser confirms the payment. A Stripe webhook tells Nevermined the money moved. Nothing buyer-facing can change the amount.

Check warning on line 42 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L42

Did you really mean 'Nevermined'?

<Steps>
<Step title="Create the Order from your server">
Call `POST /api/v1/orders` with your API key and the amount in USD cents. Nevermined creates a Stripe PaymentIntent for that exact amount and returns an unguessable `orderId` and a `clientSecret`. No money moves yet.

Check warning on line 46 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L46

Did you really mean 'Nevermined'?
</Step>

<Step title="Hand the buyer to the Nevermined checkout">
Pass the buyer only the `orderId`, by redirect or by embedding the Nevermined-hosted checkout. The checkout reads the Order through `GET /api/v1/orders/{id}` and renders the amount and the card form. You don't build a card form or handle card data, and the buyer never signs in to Nevermined.

Check warning on line 50 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L50

Did you really mean 'Nevermined'?
</Step>

<Step title="The buyer confirms in the browser">
Stripe Elements confirms the payment client-side against the `clientSecret`, running 3D Secure if the issuer requires it. Nevermined only ever created the PaymentIntent; it never initiates the charge itself.

Check warning on line 54 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L54

Did you really mean 'Nevermined'?
</Step>

<Step title="The Order becomes paid">
Stripe notifies Nevermined by webhook and the Order's status flips to `paid`. Read the Order from your server and confirm the status before you fulfill.

Check warning on line 58 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L58

Did you really mean 'Nevermined'?
</Step>
</Steps>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ NICE — Splitting the banner's two jobs and keeping the audience gate is the right call

Worth naming because it's the part that could easily have gone wrong. The removed <Warning> was doing two different jobs: a temporal hedge ("not enabled in production yet") and an audience gate ("being rolled out to organization accounts"). Only the first is now false.

The second survives exactly where it belongs — Prerequisites, :26, untouched by this PR:

An active organization account. Orders are available to organizations only. A personal API key is refused with BCK.ORDER.0003.

And that matches the implementation rather than merely sounding right. apps/api/src/orders/order-env.ts says so in its header: "Eligibility to create orders is gated on active-organization membership (#3313), not an env var; these knobs are the per-merchant caps…" — so there is no global on/off flag whose state could contradict the page; the restriction is per-caller and is documented as such.

The sweep is also complete: not yet generally available / not-yet-GA / pre-GA / not enabled in production / rolled out to organization return zero matches repo-wide after this change, .md/.mdx/.json alike. And dropping "While Orders is pre-GA" from the tutorial sentences while keeping the local-stack fact is the safer direction — a wrong reason would have been worse than none.

Anchored at line 64; the finding refers to integrate/patterns/orders.mdx:63, which is outside this PR's diff hunks.

🤖 pr-review-toolkit · direct-verification · high


<Note>
The hosted checkout is the buyer-facing half of Orders and ships with the same rollout as the API, together with SDK helpers for `POST /api/v1/orders`. You don't need your own Stripe Elements integration: the hosted checkout confirms the `clientSecret` for you. Your side is the server-to-server REST contract on this page.
The hosted checkout is the buyer-facing half of Orders. You don't need your own Stripe Elements integration: the hosted checkout confirms the `clientSecret` for you. Your side is the server-to-server REST contract on this page.
Comment thread
aaitor marked this conversation as resolved.
</Note>

<Tip>
Prefer to read working code? The [Fiat Checkout Chat tutorial](https://github.com/nevermined-io/tutorials/tree/main/fiat-checkout-chat) is a minimal Next.js chat UI plus a thin merchant backend that implements this exact flow — create an order, hand off the `orderId`, confirm by card in the hosted checkout, and read the paid status. While Orders is pre-GA it runs against a local Nevermined stack; see the tutorial's prerequisites.
Prefer to read working code? The [Fiat Checkout Chat tutorial](https://github.com/nevermined-io/tutorials/tree/main/fiat-checkout-chat) is a minimal Next.js chat UI plus a thin merchant backend that implements this exact flow — create an order, hand off the `orderId`, confirm by card in the hosted checkout, and read the paid status. It runs against a local Nevermined stack see the tutorial's prerequisites.

Check warning on line 67 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L67

Did you really mean 'Nevermined'?
Comment thread
aaitor marked this conversation as resolved.
</Tip>

## Create an Order
Expand All @@ -87,7 +83,7 @@
| `paymentProvider` | string | No | Defaults to `"stripe"`, the only provider in Phase 1. |
| `captureMode` | string | No | Defaults to `"automatic"`, the only mode in Phase 1. |

There is deliberately no field for a Stripe account, fee, or transfer destination. Nevermined resolves your Connect account server-side from the API key on every request, so a buyer-facing bug can never redirect a payout.

Check warning on line 86 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L86

Did you really mean 'Nevermined'?

<Tabs>
<Tab title="curl">
Expand Down Expand Up @@ -225,17 +221,17 @@

An Order is payable for 24 hours by default. After `expiresAt` the read stops returning `clientSecret` and the buyer can no longer pay it. Create a new Order instead.

## Retries and idempotency

Check warning on line 224 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L224

Did you really mean 'idempotency'?

Network timeouts happen. Send an `idempotencyKey` (your own order id works well) and a retried `POST /api/v1/orders` with the same key returns the same `orderId`, and the same `clientSecret` while the Order is still payable, rather than creating a second charge. Behind it, Nevermined also pins the Stripe PaymentIntent to the Order, so a retry can never mint two PaymentIntents for one cart.

Check warning on line 226 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L226

Did you really mean 'Nevermined'?

If you reuse a key with a different **amount or currency**, the call is refused with `409` `BCK.ORDER.0007`. Reusing it with the same amount and currency returns the original Order unchanged: the `description`, `buyerRef`, `lineItems` and `metadata` of the retried request are ignored, not merged. Generate a fresh key for a genuinely new Order.

## Refunds and disputes

In Phase 1 there is no refund endpoint. Orders are charged on Nevermined's Stripe platform account and paid out to your Connect account, so a refund is issued on the Stripe side by Nevermined rather than from your own Stripe Dashboard. Contact Nevermined to refund an Order. Once Stripe reports the refund by webhook, the Order moves to `refunded` or `partially_refunded` and `amountRefundedMinor` is updated.

Check warning on line 232 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L232

Did you really mean 'Nevermined's'?

Check warning on line 232 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L232

Did you really mean 'Nevermined'?

Check warning on line 232 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L232

Did you really mean 'Nevermined'?

Chargebacks are recorded, not managed. When a buyer disputes a charge, the Order moves to `disputed`. If the dispute is won, the Order returns to `paid`; if it is lost, it stays `disputed`. Evidence submission through Nevermined is a later phase; in Phase 1, work with the Nevermined team on any dispute.

Check warning on line 234 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L234

Did you really mean 'Nevermined'?

Check warning on line 234 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L234

Did you really mean 'Nevermined'?

## Error codes

Expand All @@ -245,9 +241,9 @@
|---|---|---|---|---|
| `BCK.ORDER.0001` | 400 | No | Invalid request: amount out of range, a currency other than `usd`, or an unsupported `paymentProvider` or `captureMode`. | Fix the body. |
| `BCK.ORDER.0002` | 404 | No | No Order exists with that id. | Check the id you passed to `GET /api/v1/orders/{id}`. |
| `BCK.ORDER.0003` | 403 | No | The API key doesn't belong to an active organization, or the amount exceeds your per-order cap. | Use an organization API key. If the cap is the blocker, contact Nevermined. |

Check warning on line 244 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L244

Did you really mean 'Nevermined'?
| `BCK.ORDER.0004` | 500 | No | No validated Stripe Connect account able to receive payments was found on your organization or on the API key owner's profile. | Complete Stripe Connect onboarding. |
| `BCK.ORDER.0005` | 500 | No | Stripe could not create the PaymentIntent. The Order is marked `failed`; no money moved. | Create a new Order. If it persists, contact Nevermined. |

Check warning on line 246 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L246

Did you really mean 'Nevermined'?
| `BCK.ORDER.0007` | 409 | No | The `idempotencyKey` was reused with a different amount or currency. | Send a new key for a new Order. |
| `BCK.ORDER.0010` | 429 | Yes | You created Orders faster than your velocity limit allows. | Back off and retry. |

Expand All @@ -273,15 +269,15 @@
| Provider | Stripe, via your organization's Connect account |
| Capture | Automatic on confirmation |
| Payable window | 24 hours by default |
| Fees | The Nevermined fee is set on the PaymentIntent at creation and retained when Stripe pays out. Your Connect account receives the remainder. See [Fees and Settlement](/integrate/patterns/fiat-payments#fees-and-settlement). |

Check warning on line 272 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L272

Did you really mean 'Nevermined'?

Server-side checkout by agents, a refund API, and dispute handling through Nevermined are later phases and are not available today.

Check warning on line 274 in integrate/patterns/orders.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

integrate/patterns/orders.mdx#L274

Did you really mean 'Nevermined'?

## Next steps

<CardGroup cols={2}>
<Card title="Fiat Checkout Chat tutorial" icon="github" href="https://github.com/nevermined-io/tutorials/tree/main/fiat-checkout-chat">
A worked Next.js + merchant-backend example of the Orders flow. Runs on a local stack while Orders is pre-GA.
A worked Next.js + merchant-backend example of the Orders flow. Runs against a local Nevermined stack; see its prerequisites.
</Card>
<Card title="Fiat Payments" icon="credit-card" href="/integrate/patterns/fiat-payments">
Card plans, delegations, revenue routing, and fees for the rest of the fiat rails.
Expand Down