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
5 changes: 3 additions & 2 deletions docs/payments.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ norbix payments integration save --provider Montonio --name "Montonio sandbox" -
| `norbix payments order create --buyer <usr_…> --reference <ref> --key <key> --line <sku:qty:cents[:title]>... [--meta k=v]... [--currency EUR] [--price-version v1] [--file <json>]` | create an order for one buyer | `POST /orders` |
| `norbix payments order checkout <id> --integration <id> --return-url <url> [--method <m>] [--country <cc>] [--provider <code>]` | start or reuse the checkout; answers the payment URL | `POST /orders/{Id}/checkout` |

The price is yours: Norbix adds up the lines (whole cents, EUR) and never
reads a price from the buyer. `--key` is the idempotency key: the same key with
The price is yours: Norbix adds up the lines (whole cents, EUR — at least 1
cent per unit) and never reads a price from the buyer. Every line needs a title;
`--line` without one uses the sku. `--key` is the idempotency key: the same key with
the same body returns the same order, with another body it is refused (409).
Creating an order needs a key whose role has "Payments — full access" (or
Administrator); a buyer can read and check out only their own orders.
Expand Down
3 changes: 2 additions & 1 deletion src/commands/payments/order/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export default class PaymentsOrderCreate extends BaseCommand {
static description = `Create a payment order for one buyer

The price is yours: give each line as --line sku:quantity:unitCents[:title]
(repeat for several); Norbix adds up the total in cents. --key is the
(repeat for several; without a title the sku is used as the title); Norbix
adds up the total in cents. --key is the
idempotency key (8 characters or more): the same key with the same body
returns the same order, the same key with another body is refused. Needs a
key whose role may create orders (Payments — full access, or Administrator).
Expand Down
7 changes: 4 additions & 3 deletions src/lib/payments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ export const PAYMENT_PROVIDERS = [

/**
* One order line from `--line sku:quantity:unitCents[:title]` — e.g. `star-day:21:30`.
* The server requires a title on every line: without one, the sku is the title.
* A title with spaces goes in --file instead.
*/
export function parseOrderLine(raw: string): {sku: string; quantity: number; unitAmountMinor: number; title?: string} {
const [sku, quantity, unit, ...title] = raw.split(':')
const q = Number(quantity)
const u = Number(unit)
if (!sku || !Number.isInteger(q) || q < 1 || !Number.isInteger(u) || u < 0) {
throw new Error(`--line "${raw}" must be sku:quantity:unitCents[:title], e.g. star-day:21:30 (quantity 1 or more, cents 0 or more)`)
if (!sku || !Number.isInteger(q) || q < 1 || !Number.isInteger(u) || u < 1) {
throw new Error(`--line "${raw}" must be sku:quantity:unitCents[:title], e.g. star-day:21:30 (quantity 1 or more, cents 1 or more)`)
}
return title.length > 0 ? {sku, quantity: q, unitAmountMinor: u, title: title.join(':')} : {sku, quantity: q, unitAmountMinor: u}
return {sku, quantity: q, unitAmountMinor: u, title: title.length > 0 ? title.join(':') : sku}
}

/** `--meta key=value` (repeatable) → a string map. */
Expand Down
8 changes: 7 additions & 1 deletion tests/payments-routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ describe('payments order create', () => {
currency: 'EUR',
priceVersion: 'v1',
lines: [
{sku: 'star-day', quantity: 21, unitAmountMinor: 30},
{sku: 'star-day', quantity: 21, unitAmountMinor: 30, title: 'star-day'},
{sku: 'highlight-day', quantity: 7, unitAmountMinor: 30, title: 'Highlight'},
],
metadata: {adId: 'ad_1042'},
Expand All @@ -335,6 +335,12 @@ describe('payments order create', () => {
})
})

it('refuses a 0-cent line (the server needs 1 cent or more)', async () => {
const {error} = await runCommand(['payments', 'order', 'create', '--line', 'star-day:21:0', ...globalArgs])
expect(error?.message).toMatch(/cents 1 or more/)
expect(calls).toHaveLength(0)
})

it('refuses a malformed --line before sending anything', async () => {
const {error} = await runCommand(['payments', 'order', 'create', '--line', 'star-day:zero:30', ...globalArgs])
expect(error?.message).toMatch(/must be sku:quantity:unitCents/)
Expand Down
Loading