@vaam-apps/vpay-sdk 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +732 -0
  3. package/dist/auth.d.ts +180 -0
  4. package/dist/auth.js +297 -0
  5. package/dist/client.d.ts +75 -0
  6. package/dist/client.js +79 -0
  7. package/dist/errors.d.ts +91 -0
  8. package/dist/errors.js +112 -0
  9. package/dist/form.d.ts +47 -0
  10. package/dist/form.js +127 -0
  11. package/dist/http.d.ts +33 -0
  12. package/dist/http.js +115 -0
  13. package/dist/index.d.ts +15 -0
  14. package/dist/index.js +14 -0
  15. package/dist/resources/account-holders.d.ts +36 -0
  16. package/dist/resources/account-holders.js +46 -0
  17. package/dist/resources/balance.d.ts +8 -0
  18. package/dist/resources/balance.js +10 -0
  19. package/dist/resources/checkout-sessions.d.ts +55 -0
  20. package/dist/resources/checkout-sessions.js +127 -0
  21. package/dist/resources/customers.d.ts +97 -0
  22. package/dist/resources/customers.js +157 -0
  23. package/dist/resources/events.d.ts +8 -0
  24. package/dist/resources/events.js +10 -0
  25. package/dist/resources/invoices.d.ts +193 -0
  26. package/dist/resources/invoices.js +275 -0
  27. package/dist/resources/payment-intents.d.ts +12 -0
  28. package/dist/resources/payment-intents.js +44 -0
  29. package/dist/resources/refunds.d.ts +92 -0
  30. package/dist/resources/refunds.js +106 -0
  31. package/dist/stripe-auth.d.ts +177 -0
  32. package/dist/stripe-auth.js +192 -0
  33. package/dist/types.d.ts +1268 -0
  34. package/dist/types.js +48 -0
  35. package/dist/validate.d.ts +16 -0
  36. package/dist/validate.js +20 -0
  37. package/dist/version.d.ts +12 -0
  38. package/dist/version.js +12 -0
  39. package/dist/webhooks.d.ts +22 -0
  40. package/dist/webhooks.js +94 -0
  41. package/package.json +63 -0
  42. package/scripts/mint-assertion.mjs +73 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * `/v1/checkout/sessions` — the four merchant operations Step 9's wire
3
+ * contract defines, and the one thing this file does that no other resource
4
+ * does: it redacts the object's `client_secret` from `util.inspect`.
5
+ *
6
+ * ADR-0015's matrix records, as a dated gap, that `PaymentIntent` is a plain
7
+ * interface here and so `console.log(intent)` prints a live payer
8
+ * credential, while the Rust SDK's hand-written `Debug` redacts it. That gap
9
+ * is not repeated for a **new** object: every `CheckoutSession` this SDK
10
+ * hands back carries a custom inspect representation that prints
11
+ * `[N chars redacted]` in place of the secret, which is exactly what
12
+ * `vpay_sdk::CheckoutSession`'s `impl Debug` prints.
13
+ *
14
+ * `JSON.stringify` is deliberately **not** redacted, and that asymmetry is
15
+ * the same one Rust has (`Debug` redacts, `Serialize` does not): an embedded
16
+ * integration must serialise the secret to get it to the browser at all, so
17
+ * a redacting `toJSON` would break the one thing the field exists for. The
18
+ * hazard being closed here is the accidental one — a session that reaches a
19
+ * log line because something logged the whole object.
20
+ */
21
+ import type { HttpClient } from "../http.js";
22
+ import type { CheckoutSession, CreateCheckoutSessionParams, List, ListCheckoutSessionsParams, RequestOptions } from "../types.js";
23
+ /** `client.checkout.sessions` — see {@link CheckoutResource}. */
24
+ export declare class CheckoutSessionsResource {
25
+ #private;
26
+ constructor(http: HttpClient);
27
+ /**
28
+ * `POST /v1/checkout/sessions`. Answers the session **with**
29
+ * `client_secret`, and with `url` when `ui_mode` is `hosted`.
30
+ */
31
+ create(params: CreateCheckoutSessionParams, options?: RequestOptions): Promise<CheckoutSession>;
32
+ /** `GET /v1/checkout/sessions/{id}`. Answers the session with `client_secret`. */
33
+ retrieve(id: string): Promise<CheckoutSession>;
34
+ /** `GET /v1/checkout/sessions`. List items never carry `client_secret`. */
35
+ list(params?: ListCheckoutSessionsParams): Promise<List<CheckoutSession>>;
36
+ /**
37
+ * `POST /v1/checkout/sessions/{id}/expire`. `open` → `expired`; a session
38
+ * whose intent already has a live charge is refused `409`.
39
+ */
40
+ expire(id: string, options?: RequestOptions): Promise<CheckoutSession>;
41
+ }
42
+ /**
43
+ * `client.checkout` — a namespace, not a resource with operations of its
44
+ * own.
45
+ *
46
+ * It exists so the call reads `client.checkout.sessions.create(…)`, which is
47
+ * the path the wire contract uses (`/v1/checkout/sessions`) and the shape
48
+ * `vpay_sdk`'s `client.checkout().sessions()` mirrors. A flat
49
+ * `client.checkoutSessions` would have been shorter and would have made the
50
+ * two SDKs read differently for the same route.
51
+ */
52
+ export declare class CheckoutResource {
53
+ readonly sessions: CheckoutSessionsResource;
54
+ constructor(http: HttpClient);
55
+ }
@@ -0,0 +1,127 @@
1
+ const INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
2
+ /**
3
+ * The `url` of a hosted session with its fragment redacted.
4
+ *
5
+ * D6 puts the session's `client_secret` in the hosted page's URL fragment
6
+ * (`https://checkout.example/c/cs_1#cs_1_secret_…`), so redacting
7
+ * `client_secret` alone and printing `url` verbatim would leak exactly the
8
+ * value the redaction exists to hide — measured, not assumed: the first
9
+ * version of this file did that, and the test that now pins it failed.
10
+ */
11
+ function redactUrlFragment(url) {
12
+ const hash = url.indexOf("#");
13
+ if (hash === -1) {
14
+ return url;
15
+ }
16
+ const fragment = url.slice(hash + 1);
17
+ return `${url.slice(0, hash + 1)}[${fragment.length} chars redacted]`;
18
+ }
19
+ /**
20
+ * Attaches a redacting `util.inspect` representation to one decoded
21
+ * session, in place.
22
+ *
23
+ * Non-enumerable, so the property is invisible to `Object.keys`,
24
+ * `JSON.stringify`, spread and deep-equality — the object a merchant
25
+ * receives is still, in every observable way, the decoded response body.
26
+ * The representation itself is built from a **spread copy**, which does not
27
+ * carry the symbol, so `inspect` inside it cannot recurse.
28
+ */
29
+ function withRedactedInspect(session) {
30
+ if (typeof session !== "object" || session === null) {
31
+ return session;
32
+ }
33
+ Object.defineProperty(session, INSPECT_CUSTOM, {
34
+ value: function redactedInspect(depth, options, inspect) {
35
+ const secret = this.client_secret;
36
+ const copy = { ...this };
37
+ if (typeof secret === "string") {
38
+ copy["client_secret"] = `[${secret.length} chars redacted]`;
39
+ }
40
+ const url = copy["url"];
41
+ if (typeof url === "string") {
42
+ copy["url"] = redactUrlFragment(url);
43
+ }
44
+ // `inspect` is handed in by `node:util` itself, so this works
45
+ // whatever options the caller passed (colours, depth, breakLength)
46
+ // without this file importing `node:util` at all.
47
+ return `CheckoutSession ${inspect(copy, options)}`;
48
+ },
49
+ enumerable: false,
50
+ writable: false,
51
+ configurable: true,
52
+ });
53
+ return session;
54
+ }
55
+ /** `client.checkout.sessions` — see {@link CheckoutResource}. */
56
+ export class CheckoutSessionsResource {
57
+ #http;
58
+ constructor(http) {
59
+ this.#http = http;
60
+ }
61
+ /**
62
+ * `POST /v1/checkout/sessions`. Answers the session **with**
63
+ * `client_secret`, and with `url` when `ui_mode` is `hosted`.
64
+ */
65
+ async create(params, options) {
66
+ // Field order is the wire order the Rust SDK pins byte for byte
67
+ // (`sdks/rust/tests/resources.rs`); an unset field is omitted from the
68
+ // body entirely, because `success_url=` and no `success_url` are
69
+ // different requests and only the second means "not applicable".
70
+ const body = {
71
+ payment_intent: params.payment_intent,
72
+ };
73
+ if (params.ui_mode !== undefined) {
74
+ body["ui_mode"] = params.ui_mode;
75
+ }
76
+ if (params.success_url !== undefined) {
77
+ body["success_url"] = params.success_url;
78
+ }
79
+ if (params.cancel_url !== undefined) {
80
+ body["cancel_url"] = params.cancel_url;
81
+ }
82
+ if (params.return_url !== undefined) {
83
+ body["return_url"] = params.return_url;
84
+ }
85
+ if (params.customer !== undefined) {
86
+ body["customer"] = params.customer;
87
+ }
88
+ return withRedactedInspect(await this.#http.request("POST", "/checkout/sessions", body, options));
89
+ }
90
+ /** `GET /v1/checkout/sessions/{id}`. Answers the session with `client_secret`. */
91
+ async retrieve(id) {
92
+ return withRedactedInspect(await this.#http.request("GET", `/checkout/sessions/${encodeURIComponent(id)}`));
93
+ }
94
+ /** `GET /v1/checkout/sessions`. List items never carry `client_secret`. */
95
+ async list(params) {
96
+ const page = await this.#http.request("GET", "/checkout/sessions", params);
97
+ if (Array.isArray(page?.data)) {
98
+ for (const session of page.data) {
99
+ withRedactedInspect(session);
100
+ }
101
+ }
102
+ return page;
103
+ }
104
+ /**
105
+ * `POST /v1/checkout/sessions/{id}/expire`. `open` → `expired`; a session
106
+ * whose intent already has a live charge is refused `409`.
107
+ */
108
+ async expire(id, options) {
109
+ return withRedactedInspect(await this.#http.request("POST", `/checkout/sessions/${encodeURIComponent(id)}/expire`, {}, options));
110
+ }
111
+ }
112
+ /**
113
+ * `client.checkout` — a namespace, not a resource with operations of its
114
+ * own.
115
+ *
116
+ * It exists so the call reads `client.checkout.sessions.create(…)`, which is
117
+ * the path the wire contract uses (`/v1/checkout/sessions`) and the shape
118
+ * `vpay_sdk`'s `client.checkout().sessions()` mirrors. A flat
119
+ * `client.checkoutSessions` would have been shorter and would have made the
120
+ * two SDKs read differently for the same route.
121
+ */
122
+ export class CheckoutResource {
123
+ sessions;
124
+ constructor(http) {
125
+ this.sessions = new CheckoutSessionsResource(http);
126
+ }
127
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * `/v1/customers` — the five merchant operations on the Customer object
3
+ * (S4a), and the one thing this file does that no other resource does: it
4
+ * distinguishes *clear this field* from *leave it alone* — for four fields
5
+ * now, `address` included. `address` is also the one field on this API that
6
+ * is WIDER than Stripe's: it carries the GPS point beside the six formal
7
+ * components (the maintainer, 2026-09-11), and `Address` in `types.ts` says
8
+ * why — including why the coordinate is a whole count of microdegrees and
9
+ * never a decimal degree.
10
+ *
11
+ * # The three states, and why `update` looks the way it does
12
+ *
13
+ * `undefined` (or an absent key) means "leave it alone"; a string means "set
14
+ * it"; **`null` means "clear it"**, and this file sends that as `name=`,
15
+ * which is what the wire spells "clear". Collapsing `null` and `undefined` —
16
+ * the obvious simplification, and what a `params.name !== undefined` guard
17
+ * alone would do — makes a payer's email unclearable through the API that
18
+ * documents how to clear it. `sdks/rust`'s `UpdateCustomerParams` uses
19
+ * `Option<Option<String>>` for exactly this and the server carries the same
20
+ * three states in `vpay_db::CustomerPatch`.
21
+ *
22
+ * # No redacting `util.inspect`, unlike `checkout-sessions.ts`
23
+ *
24
+ * That file redacts because a `CheckoutSession` carries a *credential*:
25
+ * printing one is a compromise, and the merchant who logged it can do nothing
26
+ * about it afterwards. A `Customer` carries a payer's own name, email and
27
+ * phone number — data the merchant collected, already holds and is
28
+ * responsible for. Redacting it here would hide their own data from them
29
+ * while doing nothing about the copy in their database.
30
+ *
31
+ * The place that judgement is made *for* them is the server: vpay's
32
+ * `CustomerRow` redacts all three in its `Debug`, because vpay's logs are not
33
+ * the merchant's. `docs/flows/customers.md` records the asymmetry so it reads
34
+ * as a decision rather than an oversight, and `sdks/rust`'s `Customer`
35
+ * derives `Debug` for the same reason.
36
+ */
37
+ import type { HttpClient } from "../http.js";
38
+ import type { CreateCustomerParams, Customer, DeletedCustomer, List, ListCustomersParams, RequestOptions, UpdateCustomerParams } from "../types.js";
39
+ /** `client.customers` — see {@link CustomersResource}. */
40
+ export declare class CustomersResource {
41
+ #private;
42
+ constructor(http: HttpClient);
43
+ /**
44
+ * `POST /v1/customers`.
45
+ *
46
+ * Field order is the wire order the Rust SDK pins byte for byte
47
+ * (`sdks/rust/tests/resources.rs`); an unset field is omitted from the body
48
+ * entirely, because `name=` and no `name` are different requests and only
49
+ * the second means "not applicable".
50
+ */
51
+ create(params: CreateCustomerParams, options?: RequestOptions): Promise<Customer>;
52
+ /** `GET /v1/customers/{id}`. */
53
+ retrieve(id: string): Promise<Customer>;
54
+ /**
55
+ * `POST /v1/customers/{id}` — the update.
56
+ *
57
+ * A `POST` and not a `PUT`/`PATCH`, because that is what the API is:
58
+ * Stripe has neither verb, and a merchant's existing client sends a `POST`.
59
+ *
60
+ * A patch that would clear the customer's **last** identifier is refused
61
+ * `400` naming `name` — a customer must always have one of `name`, `email`
62
+ * and `phone`.
63
+ */
64
+ update(id: string, params: UpdateCustomerParams, options?: RequestOptions): Promise<Customer>;
65
+ /** `GET /v1/customers`. */
66
+ list(params?: ListCustomersParams): Promise<List<Customer>>;
67
+ /**
68
+ * `DELETE /v1/customers/{id}` — an **erasure**, in one of two shapes.
69
+ *
70
+ * `del` and not `delete`: `delete` is a reserved word in older JavaScript
71
+ * object literals, which is why Stripe's own SDKs spell it this way, and
72
+ * `sdks/rust`'s `CustomersResource::del` matches so a merchant who read one
73
+ * SDK's docs can use the other.
74
+ *
75
+ * A customer with **no payment history** is removed outright: a subsequent
76
+ * {@link retrieve} answers the same 404 as an id that never existed.
77
+ *
78
+ * A customer any PaymentIntent, Checkout Session or Invoice references
79
+ * cannot be removed — vpay keeps a payment attached to the payer it was
80
+ * taken from — so it is **anonymised** instead. The row and the payment
81
+ * record stay; every identifier on the customer becomes `[redacted]`, and
82
+ * so does every copy vpay kept elsewhere. A later {@link retrieve} answers
83
+ * `200` with {@link Customer.deleted} `true`, so your own stored `cus_…`
84
+ * keeps resolving. `metadata` is untouched: that is your data, not the
85
+ * payer's.
86
+ *
87
+ * Either way this answers `{deleted: true}`, and a second call is a no-op
88
+ * rather than a second erasure. Until 2026-09-10 the second case answered
89
+ * `409` instead, with advice — clear `name`, `email` and `phone` — that the
90
+ * server's own one-of rule refuses.
91
+ *
92
+ * Carries an `Idempotency-Key` like every other write on this API, which is
93
+ * what makes a retried delete answer the original `{deleted: true}` rather
94
+ * than a `404`.
95
+ */
96
+ del(id: string, options?: RequestOptions): Promise<DeletedCustomer>;
97
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * One patch field, in the wire's own three-state encoding.
3
+ *
4
+ * Returns `undefined` for "the request did not mention this", which the
5
+ * caller drops from the body entirely, and the empty string for "clear it".
6
+ */
7
+ function patch(value) {
8
+ if (value === undefined) {
9
+ return undefined;
10
+ }
11
+ return value === null ? "" : value;
12
+ }
13
+ /**
14
+ * The eight address components as a nested body value, with every unset one
15
+ * omitted entirely.
16
+ *
17
+ * Omitted and not sent empty, because on this API those are different
18
+ * requests everywhere else — and because the server replaces the address
19
+ * whole, so an omitted component is cleared either way and `address[city]=`
20
+ * would only add a pair saying the same thing.
21
+ *
22
+ * The two coordinate keys go through the same loop as the six formal ones and
23
+ * are typed `number` rather than `string`: the form encoder renders a number
24
+ * as a decimal integer with no separators and refuses a non-finite one
25
+ * (`form.ts`), so there is no path from this function to a body carrying a
26
+ * float — which is the whole of why the wire field is named for its unit.
27
+ */
28
+ function addressBody(address) {
29
+ const body = {};
30
+ for (const key of [
31
+ "line1",
32
+ "line2",
33
+ "city",
34
+ "state",
35
+ "postal_code",
36
+ "country",
37
+ "latitude_microdeg",
38
+ "longitude_microdeg",
39
+ ]) {
40
+ const value = address[key];
41
+ if (value !== undefined) {
42
+ body[key] = value;
43
+ }
44
+ }
45
+ return body;
46
+ }
47
+ /** `client.customers` — see {@link CustomersResource}. */
48
+ export class CustomersResource {
49
+ #http;
50
+ constructor(http) {
51
+ this.#http = http;
52
+ }
53
+ /**
54
+ * `POST /v1/customers`.
55
+ *
56
+ * Field order is the wire order the Rust SDK pins byte for byte
57
+ * (`sdks/rust/tests/resources.rs`); an unset field is omitted from the body
58
+ * entirely, because `name=` and no `name` are different requests and only
59
+ * the second means "not applicable".
60
+ */
61
+ async create(params, options) {
62
+ const body = {};
63
+ if (params.name !== undefined) {
64
+ body["name"] = params.name;
65
+ }
66
+ if (params.email !== undefined) {
67
+ body["email"] = params.email;
68
+ }
69
+ if (params.phone !== undefined) {
70
+ body["phone"] = params.phone;
71
+ }
72
+ if (params.address !== undefined) {
73
+ body["address"] = addressBody(params.address);
74
+ }
75
+ if (params.metadata !== undefined) {
76
+ body["metadata"] = params.metadata;
77
+ }
78
+ return this.#http.request("POST", "/customers", body, options);
79
+ }
80
+ /** `GET /v1/customers/{id}`. */
81
+ async retrieve(id) {
82
+ return this.#http.request("GET", `/customers/${encodeURIComponent(id)}`);
83
+ }
84
+ /**
85
+ * `POST /v1/customers/{id}` — the update.
86
+ *
87
+ * A `POST` and not a `PUT`/`PATCH`, because that is what the API is:
88
+ * Stripe has neither verb, and a merchant's existing client sends a `POST`.
89
+ *
90
+ * A patch that would clear the customer's **last** identifier is refused
91
+ * `400` naming `name` — a customer must always have one of `name`, `email`
92
+ * and `phone`.
93
+ */
94
+ async update(id, params, options) {
95
+ const body = {};
96
+ const name = patch(params.name);
97
+ if (name !== undefined) {
98
+ body["name"] = name;
99
+ }
100
+ const email = patch(params.email);
101
+ if (email !== undefined) {
102
+ body["email"] = email;
103
+ }
104
+ const phone = patch(params.phone);
105
+ if (phone !== undefined) {
106
+ body["phone"] = phone;
107
+ }
108
+ if (params.address !== undefined) {
109
+ // `address=` — the wire's own "remove this", the same spelling `name=`
110
+ // uses. NOT eight empty components: the server reads a *scalar* `address`
111
+ // as the clear, and one spelling means a merchant reading the body sees
112
+ // what they asked for.
113
+ body["address"] =
114
+ params.address === null ? "" : addressBody(params.address);
115
+ }
116
+ if (params.metadata !== undefined) {
117
+ body["metadata"] = params.metadata;
118
+ }
119
+ return this.#http.request("POST", `/customers/${encodeURIComponent(id)}`, body, options);
120
+ }
121
+ /** `GET /v1/customers`. */
122
+ async list(params) {
123
+ return this.#http.request("GET", "/customers", params);
124
+ }
125
+ /**
126
+ * `DELETE /v1/customers/{id}` — an **erasure**, in one of two shapes.
127
+ *
128
+ * `del` and not `delete`: `delete` is a reserved word in older JavaScript
129
+ * object literals, which is why Stripe's own SDKs spell it this way, and
130
+ * `sdks/rust`'s `CustomersResource::del` matches so a merchant who read one
131
+ * SDK's docs can use the other.
132
+ *
133
+ * A customer with **no payment history** is removed outright: a subsequent
134
+ * {@link retrieve} answers the same 404 as an id that never existed.
135
+ *
136
+ * A customer any PaymentIntent, Checkout Session or Invoice references
137
+ * cannot be removed — vpay keeps a payment attached to the payer it was
138
+ * taken from — so it is **anonymised** instead. The row and the payment
139
+ * record stay; every identifier on the customer becomes `[redacted]`, and
140
+ * so does every copy vpay kept elsewhere. A later {@link retrieve} answers
141
+ * `200` with {@link Customer.deleted} `true`, so your own stored `cus_…`
142
+ * keeps resolving. `metadata` is untouched: that is your data, not the
143
+ * payer's.
144
+ *
145
+ * Either way this answers `{deleted: true}`, and a second call is a no-op
146
+ * rather than a second erasure. Until 2026-09-10 the second case answered
147
+ * `409` instead, with advice — clear `name`, `email` and `phone` — that the
148
+ * server's own one-of rule refuses.
149
+ *
150
+ * Carries an `Idempotency-Key` like every other write on this API, which is
151
+ * what makes a retried delete answer the original `{deleted: true}` rather
152
+ * than a `404`.
153
+ */
154
+ async del(id, options) {
155
+ return this.#http.request("DELETE", `/customers/${encodeURIComponent(id)}`, undefined, options);
156
+ }
157
+ }
@@ -0,0 +1,8 @@
1
+ import type { HttpClient } from "../http.js";
2
+ import type { Event, List, ListEventsParams } from "../types.js";
3
+ /** `/v1/events` — docs/flows/merchant-auth.md, "Resources". */
4
+ export declare class EventsResource {
5
+ #private;
6
+ constructor(http: HttpClient);
7
+ list(params?: ListEventsParams): Promise<List<Event>>;
8
+ }
@@ -0,0 +1,10 @@
1
+ /** `/v1/events` — docs/flows/merchant-auth.md, "Resources". */
2
+ export class EventsResource {
3
+ #http;
4
+ constructor(http) {
5
+ this.#http = http;
6
+ }
7
+ async list(params) {
8
+ return this.#http.request("GET", "/events", params);
9
+ }
10
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * `/v1/invoices` and `/v1/invoice_items` — the thirteen merchant operations
3
+ * on the Invoice object and its lines (S4b).
4
+ *
5
+ * # The one thing to know before calling any of them
6
+ *
7
+ * **The state machine is the server's `WHERE` clause, not a check this SDK
8
+ * makes.** A transition that does not apply comes back a `409` naming the
9
+ * invoice's current status, and this SDK does not try to predict which —
10
+ * `draft → open → paid | void | uncollectible`, and every method below says
11
+ * which statuses it accepts. Reading the invoice first to decide whether to
12
+ * call is a race; call, and read the refusal.
13
+ *
14
+ * # Two resources, one file
15
+ *
16
+ * A line has no life of its own: it belongs to exactly one invoice, its
17
+ * currency and tenant are copied off that invoice, and every write to it is
18
+ * refused once the invoice is issued. Splitting the two would put that guard
19
+ * in a file that does not mention the thing it guards. `sdks/rust` keeps them
20
+ * in one module for the same reason.
21
+ */
22
+ import type { HttpClient } from "../http.js";
23
+ import type { CreateInvoiceItemParams, CreateInvoiceParams, DeletedInvoice, DeletedInvoiceItem, Invoice, InvoiceLine, List, ListInvoicesParams, PayInvoiceParams, RequestOptions, UpdateInvoiceItemParams, UpdateInvoiceParams } from "../types.js";
24
+ /** `client.invoices` — see the module doc. */
25
+ export declare class InvoicesResource {
26
+ #private;
27
+ constructor(http: HttpClient);
28
+ /**
29
+ * `POST /v1/invoices` — creates a **draft**, with no number and zero
30
+ * amounts.
31
+ *
32
+ * Field order is the wire order `sdks/rust/tests/resources.rs` pins byte
33
+ * for byte; an unset field is omitted from the body entirely, because
34
+ * `description=` and no `description` are different requests and only the
35
+ * second means "not applicable".
36
+ *
37
+ * A `400` naming `customer` when it is absent, unknown, or another
38
+ * merchant's — one sentence for all three, so the parameter is not an
39
+ * oracle; and a `400` naming `currency` for one this deployment does not
40
+ * settle.
41
+ */
42
+ create(params: CreateInvoiceParams, options?: RequestOptions): Promise<Invoice>;
43
+ /**
44
+ * `GET /v1/invoices/{id}` — the invoice **with its lines**.
45
+ *
46
+ * This is the read a webhook handler falls back to: an `invoice.*` event
47
+ * body carries an empty `lines.data`, and this response always carries the
48
+ * lines.
49
+ *
50
+ * Another merchant's `in_…` is the same `404`, byte for byte, as one that
51
+ * never existed.
52
+ */
53
+ retrieve(id: string): Promise<Invoice>;
54
+ /**
55
+ * `POST /v1/invoices/{id}` — patches a **draft**.
56
+ *
57
+ * A `POST` and not a `PATCH`, for `customers.update`'s reason, even though
58
+ * the server mounts both verbs on the one handler: `POST` is what a
59
+ * merchant's existing Stripe client sends, so it is the one that has to
60
+ * work.
61
+ *
62
+ * A `409` naming the status once the invoice is no longer a draft — an
63
+ * issued document's terms are what was sent to the payer. Void it and issue
64
+ * a new one.
65
+ */
66
+ update(id: string, params: UpdateInvoiceParams, options?: RequestOptions): Promise<Invoice>;
67
+ /** `GET /v1/invoices` — newest first. */
68
+ list(params?: ListInvoicesParams): Promise<List<Invoice>>;
69
+ /**
70
+ * `DELETE /v1/invoices/{id}` — **draft only**, and its lines go with it.
71
+ *
72
+ * An issued invoice is {@link InvoicesResource.void}ed, never deleted: a
73
+ * voided draft would be a non-draft row with no number, which the database
74
+ * refuses outright, and a document that vanished is a hole an accountant
75
+ * reads as a destroyed one.
76
+ *
77
+ * `del` and not `delete`, for `customers.del`'s reason. Carries an
78
+ * `Idempotency-Key` like every other write, which is what makes a retried
79
+ * delete answer the original `{deleted: true}` rather than a `404`.
80
+ */
81
+ del(id: string, options?: RequestOptions): Promise<DeletedInvoice>;
82
+ /**
83
+ * `POST /v1/invoices/{id}/finalize` — issues the document. **Draft only.**
84
+ *
85
+ * The money transition: the invoice takes the next number out of this
86
+ * merchant's own sequence under a row lock, its lines freeze, its amounts
87
+ * are computed once, and it moves to `"open"` — all in one transaction,
88
+ * with `invoice.finalized` inside it. Numbers are consecutive and have no
89
+ * holes, unlike Stripe's.
90
+ *
91
+ * A `400` naming `invoice` when it has no lines, or when its total is past
92
+ * `Number.MAX_SAFE_INTEGER` minor units; a `409` when it is not a draft.
93
+ */
94
+ finalize(id: string, options?: RequestOptions): Promise<Invoice>;
95
+ /**
96
+ * `POST /v1/invoices/{id}/void` — cancels an issued document. **Open
97
+ * only.**
98
+ *
99
+ * The invoice **keeps its number**. Terminal.
100
+ *
101
+ * A `409` when it is not open, and a `409` while a payment intent is
102
+ * attached and not yet canceled — cancel that intent first.
103
+ */
104
+ void(id: string, options?: RequestOptions): Promise<Invoice>;
105
+ /**
106
+ * `POST /v1/invoices/{id}/mark_uncollectible` — writes an issued document
107
+ * off. **Open only.**
108
+ *
109
+ * Still owed, never expected. Terminal, and it **emits no event**: vpay
110
+ * does not write `invoice.marked_uncollectible`, so nothing arrives on a
111
+ * webhook and a merchant reconciling write-offs reads them with
112
+ * `list({ status: "uncollectible" })`.
113
+ *
114
+ * # `markUncollectible` here, `mark_uncollectible` in `sdks/rust`
115
+ *
116
+ * The only capability the two SDKs spell differently, and the difference is
117
+ * each language's own casing rather than a divergence: `sdks/rust` may not
118
+ * spell `markUncollectible` (`non_snake_case` is a rustc lint, not a
119
+ * preference) and this package should not spell `mark_uncollectible` —
120
+ * Stripe's own Node SDK, which merchants arrive from, says
121
+ * `markUncollectible`, and the other spelling would be the one snake_case
122
+ * method in a camelCase package.
123
+ *
124
+ * [ADR-0015](../../../docs/adr/0015-sdk-parity.md) decision 1 is that
125
+ * parity is **per capability, not per method name**, and its alternatives
126
+ * reject method-name parity in as many words. Until 2026-09-08
127
+ * `verify-sdk-parity` could not express that — it keyed a row on one
128
+ * spelling — and this method was named `mark_uncollectible` to satisfy the
129
+ * gate. It is `markUncollectible` now because the gate learned the
130
+ * distinction instead: `PARITY_COLUMN_SPELLINGS` in `.xtask/src/main.rs`
131
+ * carries the one entry, and an entry that exempts nothing fails the build.
132
+ */
133
+ markUncollectible(id: string, options?: RequestOptions): Promise<Invoice>;
134
+ /**
135
+ * `POST /v1/invoices/{id}/pay` — mints a payment intent and a hosted
136
+ * checkout for what is left. **Open only.**
137
+ *
138
+ * **This does not charge anybody.** Stripe's `pay` charges a stored payment
139
+ * method; vpay has none, so this answers the invoice with
140
+ * {@link Invoice.payment_intent} and {@link Invoice.hosted_invoice_url}
141
+ * set — a page to send the payer to. The invoice stays `"open"` until the
142
+ * settlement moves it, which is when `invoice.paid` is emitted.
143
+ *
144
+ * One payment at a time: calling this again while the attached intent is
145
+ * live is a `409`, and cancelling that intent is the way back. Two
146
+ * concurrent calls attach exactly one intent.
147
+ *
148
+ * A `500 checkout_not_configured` when this deployment serves no checkout
149
+ * page.
150
+ */
151
+ pay(id: string, params: PayInvoiceParams, options?: RequestOptions): Promise<Invoice>;
152
+ }
153
+ /**
154
+ * `client.invoiceItems` — the four operations on `/v1/invoice_items`: the
155
+ * lines of a **draft** invoice.
156
+ *
157
+ * There is deliberately no `list`: an invoice's lines are read off the
158
+ * invoice ({@link Invoice.lines}, expanded on every render), and a
159
+ * merchant-wide list of lines across invoices answers no question anybody
160
+ * asked. The server mounts no collection `GET` either, so this is not an
161
+ * omission here.
162
+ *
163
+ * Every write requires the parent to still be a `"draft"`;
164
+ * {@link InvoiceItemsResource.retrieve} does not.
165
+ */
166
+ export declare class InvoiceItemsResource {
167
+ #private;
168
+ constructor(http: HttpClient);
169
+ /**
170
+ * `POST /v1/invoice_items` — adds a line to a draft, and the invoice's
171
+ * `amount_due` moves with it.
172
+ *
173
+ * Throws `TypeError` before any request when `unit_amount` is not a
174
+ * non-negative safe integer; a `400` naming `invoice` when it is not one of
175
+ * your drafts.
176
+ */
177
+ create(params: CreateInvoiceItemParams, options?: RequestOptions): Promise<InvoiceLine>;
178
+ /** `GET /v1/invoice_items/{id}` — readable whatever the parent's status. */
179
+ retrieve(id: string): Promise<InvoiceLine>;
180
+ /**
181
+ * `POST /v1/invoice_items/{id}` — **draft parent only**, with `amount`
182
+ * recomputed.
183
+ *
184
+ * Throws `TypeError` before any request for a `unit_amount` outside the
185
+ * bound; a `409` once the parent is no longer a draft.
186
+ */
187
+ update(id: string, params: UpdateInvoiceItemParams, options?: RequestOptions): Promise<InvoiceLine>;
188
+ /**
189
+ * `DELETE /v1/invoice_items/{id}` — **draft parent only**, and the invoice
190
+ * re-totals.
191
+ */
192
+ del(id: string, options?: RequestOptions): Promise<DeletedInvoiceItem>;
193
+ }