@doany-ai/sdk 0.2.9-alpha.0 → 0.3.0-alpha.0
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.
- package/dist/client.js +51 -7
- package/dist/client.types.d.ts +55 -5
- package/dist/index.d.ts +8 -4
- package/dist/modules/auth.js +6 -0
- package/dist/modules/auth.types.d.ts +30 -0
- package/dist/modules/catalog.d.ts +3 -2
- package/dist/modules/catalog.js +53 -20
- package/dist/modules/catalog.types.d.ts +118 -18
- package/dist/modules/contacts.d.ts +9 -0
- package/dist/modules/contacts.js +42 -0
- package/dist/modules/contacts.types.d.ts +112 -0
- package/dist/modules/contacts.types.js +1 -0
- package/dist/modules/events.d.ts +9 -0
- package/dist/modules/events.js +15 -0
- package/dist/modules/events.types.d.ts +42 -0
- package/dist/modules/events.types.js +1 -0
- package/dist/modules/order-access.d.ts +20 -0
- package/dist/modules/order-access.js +84 -0
- package/dist/modules/orders.d.ts +6 -2
- package/dist/modules/orders.js +125 -159
- package/dist/modules/orders.types.d.ts +121 -31
- package/dist/modules/payments.d.ts +2 -1
- package/dist/modules/payments.js +28 -1
- package/dist/modules/payments.types.d.ts +146 -0
- package/dist/modules/project.d.ts +31 -0
- package/dist/modules/project.js +52 -0
- package/dist/modules/project.types.d.ts +58 -0
- package/dist/modules/project.types.js +1 -0
- package/dist/modules/users.d.ts +7 -13
- package/dist/modules/users.js +24 -11
- package/dist/modules/users.types.d.ts +62 -0
- package/dist/modules/users.types.js +1 -0
- package/package.json +1 -1
package/dist/modules/orders.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { actorOf, nameOf, orderAccessHeaders, rememberOrderToken, session } from "./order-access.js";
|
|
2
|
+
import { queryOf, seg } from "./project.js";
|
|
1
3
|
// Shared by every client the page creates: two clients placing the same order
|
|
2
4
|
// at once are still one attempt, and neither may release a key the other is
|
|
3
5
|
// still waiting on. Entries are named by app, actor and request.
|
|
@@ -23,40 +25,6 @@ function newKey() {
|
|
|
23
25
|
* that ordering the same things again later is a new order.
|
|
24
26
|
*/
|
|
25
27
|
const ATTEMPT_TTL_MS = 15 * 60 * 1000;
|
|
26
|
-
/** cyrb53: a short, stable name for a payload, so the payload itself (the buyer's
|
|
27
|
-
* details) is not what gets written as a storage key. A collision would only
|
|
28
|
-
* reuse a key for a different payload, which the backend refuses with 409. */
|
|
29
|
-
function nameOf(text) {
|
|
30
|
-
let h1 = 0xdeadbeef;
|
|
31
|
-
let h2 = 0x41c6ce57;
|
|
32
|
-
for (let i = 0; i < text.length; i++) {
|
|
33
|
-
const ch = text.charCodeAt(i);
|
|
34
|
-
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
35
|
-
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
36
|
-
}
|
|
37
|
-
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
38
|
-
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
39
|
-
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36);
|
|
40
|
-
}
|
|
41
|
-
/** Who the backend will take this request to be from: it keeps idempotency
|
|
42
|
-
* keys per signed-in account and per guest, so an unresolved attempt belongs
|
|
43
|
-
* to whoever made it and nobody else may reuse or clear it. */
|
|
44
|
-
function actorOf(axios) {
|
|
45
|
-
var _a, _b, _c;
|
|
46
|
-
const header = (_c = (_b = (_a = axios.defaults) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b.common) === null || _c === void 0 ? void 0 : _c["Authorization"];
|
|
47
|
-
if (typeof header !== "string" || !header.startsWith("Bearer "))
|
|
48
|
-
return "guest";
|
|
49
|
-
try {
|
|
50
|
-
const payload = header.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
51
|
-
const sub = JSON.parse(atob(payload)).sub;
|
|
52
|
-
if (typeof sub === "string" && sub)
|
|
53
|
-
return `user:${sub}`;
|
|
54
|
-
}
|
|
55
|
-
catch (_d) {
|
|
56
|
-
/* not a JWT we can read */
|
|
57
|
-
}
|
|
58
|
-
return `token:${nameOf(header)}`;
|
|
59
|
-
}
|
|
60
28
|
/**
|
|
61
29
|
* The request exactly as it will be sent, in one canonical form: keys sorted,
|
|
62
30
|
* `quantity` filled in with its default, `variant_id` lower-cased.
|
|
@@ -104,7 +72,7 @@ function tidyBuyer(buyer) {
|
|
|
104
72
|
}
|
|
105
73
|
return out;
|
|
106
74
|
}
|
|
107
|
-
function wireOf(params) {
|
|
75
|
+
function wireOf(params, appId) {
|
|
108
76
|
const items = Array.isArray(params === null || params === void 0 ? void 0 : params.items)
|
|
109
77
|
? params.items.map((item) => item && typeof item === "object"
|
|
110
78
|
? {
|
|
@@ -116,15 +84,9 @@ function wireOf(params) {
|
|
|
116
84
|
}
|
|
117
85
|
: item)
|
|
118
86
|
: params === null || params === void 0 ? void 0 : params.items;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
return typeof window !== "undefined" ? window.sessionStorage : null;
|
|
124
|
-
}
|
|
125
|
-
catch (_a) {
|
|
126
|
-
return null; // storage can be blocked (private mode, sandboxed iframe)
|
|
127
|
-
}
|
|
87
|
+
// The site the order is placed on, unless the page names another (or none).
|
|
88
|
+
const app_id = (params === null || params === void 0 ? void 0 : params.app_id) === undefined ? appId : params.app_id;
|
|
89
|
+
return canonical({ ...params, app_id, items, buyer: tidyBuyer(params === null || params === void 0 ? void 0 : params.buyer) });
|
|
128
90
|
}
|
|
129
91
|
/**
|
|
130
92
|
* Creates the orders module for the Doany SDK.
|
|
@@ -138,12 +100,17 @@ function session() {
|
|
|
138
100
|
* the same order — also after a page reload, for 15 minutes. Once the
|
|
139
101
|
* server has answered, the next identical order is a new one.
|
|
140
102
|
* - **A guest can reopen their order.** The order's access token is kept for
|
|
141
|
-
* the browser session and sent with `get` / `cancel` for that order
|
|
103
|
+
* the browser session and sent with `get` / `cancel` for that order, and
|
|
104
|
+
* with `payments.checkout` / `payments.getForOrder` (order-access.ts).
|
|
142
105
|
*
|
|
143
106
|
* @internal
|
|
144
107
|
*/
|
|
145
|
-
export function createOrdersModule(axios, appId
|
|
146
|
-
|
|
108
|
+
export function createOrdersModule(axios, appId, project, {
|
|
109
|
+
// Off for the service role: a backend function's runtime serves many
|
|
110
|
+
// invocations under the same app key, and state shared between them would
|
|
111
|
+
// hand one invocation another's order, or swallow an order as a duplicate.
|
|
112
|
+
rememberAttempts = true, } = {}) {
|
|
113
|
+
const orders = (suffix = "") => project.path(`/orders${suffix}`);
|
|
147
114
|
// Keys of orders whose outcome is unknown. In memory, and mirrored to
|
|
148
115
|
// sessionStorage so that reloading the page after a failed request — the
|
|
149
116
|
// usual way people recover — still retries with the same key.
|
|
@@ -194,137 +161,136 @@ export function createOrdersModule(axios, appId) {
|
|
|
194
161
|
}
|
|
195
162
|
},
|
|
196
163
|
};
|
|
197
|
-
|
|
198
|
-
// actor and looked up under the current one — plus `guest`, so an order placed
|
|
199
|
-
// before signing in can still be opened after. What it never does is follow
|
|
200
|
-
// the tab to another account, or stay usable once its account signed out.
|
|
201
|
-
const tokenKey = (actor, orderId) => { var _a, _b; return `doany_order_token_${appId}_${nameOf(`${(_b = (_a = axios.defaults) === null || _a === void 0 ? void 0 : _a.baseURL) !== null && _b !== void 0 ? _b : ""}|${actor}`)}_${orderId}`; };
|
|
202
|
-
function remember(actor, orderId, token) {
|
|
203
|
-
var _a;
|
|
204
|
-
if (!token)
|
|
205
|
-
return;
|
|
206
|
-
try {
|
|
207
|
-
(_a = session()) === null || _a === void 0 ? void 0 : _a.setItem(tokenKey(actor, orderId), token);
|
|
208
|
-
}
|
|
209
|
-
catch (_b) {
|
|
210
|
-
/* best effort */
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
function accessHeaders(orderId, options) {
|
|
214
|
-
var _a, _b;
|
|
215
|
-
let token = options === null || options === void 0 ? void 0 : options.accessToken;
|
|
216
|
-
if (!token) {
|
|
217
|
-
try {
|
|
218
|
-
const store = session();
|
|
219
|
-
token =
|
|
220
|
-
(_b = (_a = store === null || store === void 0 ? void 0 : store.getItem(tokenKey(actorOf(axios), orderId))) !== null && _a !== void 0 ? _a : store === null || store === void 0 ? void 0 : store.getItem(tokenKey("guest", orderId))) !== null && _b !== void 0 ? _b : undefined;
|
|
221
|
-
}
|
|
222
|
-
catch (_c) {
|
|
223
|
-
token = undefined;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return token ? { "X-Doany-Access-Token": token } : {};
|
|
227
|
-
}
|
|
164
|
+
const accessHeaders = (orderId, accessToken) => orderAccessHeaders(axios, appId, orderId, accessToken);
|
|
228
165
|
return {
|
|
229
166
|
create(params, options) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const wire = wireOf(params);
|
|
236
|
-
const fingerprint = JSON.stringify([(_b = (_a = axios.defaults) === null || _a === void 0 ? void 0 : _a.baseURL) !== null && _b !== void 0 ? _b : "", appId, actor, wire]);
|
|
237
|
-
const flight = `${(_c = options === null || options === void 0 ? void 0 : options.idempotencyKey) !== null && _c !== void 0 ? _c : ""}\u0000${fingerprint}`;
|
|
238
|
-
// A second identical call while the first is still out is the same
|
|
239
|
-
// attempt (a double click): it gets the first one's answer, not a
|
|
240
|
-
// request of its own.
|
|
241
|
-
const running = inFlight.get(flight);
|
|
242
|
-
if (running)
|
|
243
|
-
return running;
|
|
244
|
-
const key = (_e = (_d = options === null || options === void 0 ? void 0 : options.idempotencyKey) !== null && _d !== void 0 ? _d : undecided.get(fingerprint)) !== null && _e !== void 0 ? _e : newKey();
|
|
245
|
-
// Written on every attempt, not only the first: the window runs from the
|
|
246
|
-
// LAST time this key went out, or a retry late in it would leave the next
|
|
247
|
-
// one without the key of an order that may just have been placed.
|
|
248
|
-
if (!(options === null || options === void 0 ? void 0 : options.idempotencyKey))
|
|
249
|
-
undecided.set(fingerprint, key);
|
|
250
|
-
// Only the attempt that put a key there may take it away: a page-keyed
|
|
251
|
-
// attempt for the same things must not release the automatic one's key.
|
|
252
|
-
const settle = () => {
|
|
253
|
-
if (!(options === null || options === void 0 ? void 0 : options.idempotencyKey) && undecided.get(fingerprint) === key) {
|
|
254
|
-
undecided.delete(fingerprint);
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
const attempt = (async () => {
|
|
258
|
-
var _a;
|
|
259
|
-
try {
|
|
167
|
+
if (!rememberAttempts) {
|
|
168
|
+
// One call, one key: the caller's, else a new one. A function that
|
|
169
|
+
// retries passes its own key.
|
|
170
|
+
return (async () => {
|
|
171
|
+
var _a, _b;
|
|
260
172
|
const data = (await axios.request({
|
|
261
173
|
method: "POST",
|
|
262
|
-
url:
|
|
263
|
-
data:
|
|
264
|
-
headers: { "Idempotency-Key":
|
|
174
|
+
url: await orders(),
|
|
175
|
+
data: wireOf(params, appId),
|
|
176
|
+
headers: { "Idempotency-Key": (_a = options === null || options === void 0 ? void 0 : options.idempotencyKey) !== null && _a !== void 0 ? _a : newKey() },
|
|
265
177
|
}));
|
|
266
|
-
|
|
267
|
-
// deployment) decides nothing: the key stays for the retry.
|
|
268
|
-
if (typeof ((_a = data === null || data === void 0 ? void 0 : data.order) === null || _a === void 0 ? void 0 : _a.id) !== "string") {
|
|
178
|
+
if (typeof ((_b = data === null || data === void 0 ? void 0 : data.order) === null || _b === void 0 ? void 0 : _b.id) !== "string") {
|
|
269
179
|
throw new Error("The order service answered without an order");
|
|
270
180
|
}
|
|
271
|
-
settle();
|
|
272
|
-
remember(actor, data.order.id, data.access_token);
|
|
273
181
|
return data;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
182
|
+
})();
|
|
183
|
+
}
|
|
184
|
+
return (async () => {
|
|
185
|
+
var _a, _b, _c, _d, _e;
|
|
186
|
+
// The actor is read after the path, with nothing awaited between it and
|
|
187
|
+
// the request: the request goes out as this actor, and its token must be
|
|
188
|
+
// kept for this actor even if the page signs in as someone else before
|
|
189
|
+
// the answer arrives. (Read before the path, a sign-in during the first
|
|
190
|
+
// public-settings read would file the order under the previous account.)
|
|
191
|
+
const url = await orders();
|
|
192
|
+
const actor = actorOf(axios);
|
|
193
|
+
const wire = wireOf(params, appId);
|
|
194
|
+
const fingerprint = JSON.stringify([(_b = (_a = axios.defaults) === null || _a === void 0 ? void 0 : _a.baseURL) !== null && _b !== void 0 ? _b : "", appId, actor, wire]);
|
|
195
|
+
const flight = `${(_c = options === null || options === void 0 ? void 0 : options.idempotencyKey) !== null && _c !== void 0 ? _c : ""}\u0000${fingerprint}`;
|
|
196
|
+
// A second identical call while the first is still out is the same
|
|
197
|
+
// attempt (a double click): it gets the first one's answer, not a
|
|
198
|
+
// request of its own.
|
|
199
|
+
const running = inFlight.get(flight);
|
|
200
|
+
if (running)
|
|
201
|
+
return running;
|
|
202
|
+
const key = (_e = (_d = options === null || options === void 0 ? void 0 : options.idempotencyKey) !== null && _d !== void 0 ? _d : undecided.get(fingerprint)) !== null && _e !== void 0 ? _e : newKey();
|
|
203
|
+
// Written on every attempt, not only the first: the window runs from the
|
|
204
|
+
// LAST time this key went out, or a retry late in it would leave the next
|
|
205
|
+
// one without the key of an order that may just have been placed.
|
|
206
|
+
if (!(options === null || options === void 0 ? void 0 : options.idempotencyKey))
|
|
207
|
+
undecided.set(fingerprint, key);
|
|
208
|
+
// Only the attempt that put a key there may take it away: a page-keyed
|
|
209
|
+
// attempt for the same things must not release the automatic one's key.
|
|
210
|
+
const settle = () => {
|
|
211
|
+
if (!(options === null || options === void 0 ? void 0 : options.idempotencyKey) && undecided.get(fingerprint) === key) {
|
|
212
|
+
undecided.delete(fingerprint);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const attempt = (async () => {
|
|
216
|
+
var _a;
|
|
217
|
+
try {
|
|
218
|
+
const data = (await axios.request({
|
|
219
|
+
method: "POST",
|
|
220
|
+
url,
|
|
221
|
+
data: wire,
|
|
222
|
+
headers: { "Idempotency-Key": key },
|
|
223
|
+
}));
|
|
224
|
+
// A 2xx that does not carry an order (a proxy's page, a mismatched
|
|
225
|
+
// deployment) decides nothing: the key stays for the retry.
|
|
226
|
+
if (typeof ((_a = data === null || data === void 0 ? void 0 : data.order) === null || _a === void 0 ? void 0 : _a.id) !== "string") {
|
|
227
|
+
throw new Error("The order service answered without an order");
|
|
228
|
+
}
|
|
286
229
|
settle();
|
|
230
|
+
rememberOrderToken(axios, appId, actor, data.order.id, data.access_token);
|
|
231
|
+
return data;
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
// A 4xx is a decision (refused, invalid) — except 408 and 429, and
|
|
235
|
+
// 401, which the rest of the client also treats as "try again"
|
|
236
|
+
// (client.ts): a proxy can time out AFTER passing the request on.
|
|
237
|
+
// Everything else may have placed the order, so the same key goes
|
|
238
|
+
// out again next time; keeping a key too long costs nothing.
|
|
239
|
+
const status = error === null || error === void 0 ? void 0 : error.status;
|
|
240
|
+
if (typeof status === "number" &&
|
|
241
|
+
status >= 400 &&
|
|
242
|
+
status < 500 &&
|
|
243
|
+
![401, 408, 429].includes(status)) {
|
|
244
|
+
settle();
|
|
245
|
+
}
|
|
246
|
+
else if (!(options === null || options === void 0 ? void 0 : options.idempotencyKey)) {
|
|
247
|
+
// Still undecided: the window runs from now, however long the
|
|
248
|
+
// request itself was out.
|
|
249
|
+
undecided.set(fingerprint, key);
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
287
252
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
// request itself was out.
|
|
291
|
-
undecided.set(fingerprint, key);
|
|
253
|
+
finally {
|
|
254
|
+
inFlight.delete(flight);
|
|
292
255
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
inFlight.delete(flight);
|
|
297
|
-
}
|
|
256
|
+
})();
|
|
257
|
+
inFlight.set(flight, attempt);
|
|
258
|
+
return attempt;
|
|
298
259
|
})();
|
|
299
|
-
inFlight.set(flight, attempt);
|
|
300
|
-
return attempt;
|
|
301
260
|
},
|
|
302
261
|
async list(params = {}) {
|
|
303
|
-
|
|
304
|
-
if (params.status)
|
|
305
|
-
query.status = params.status;
|
|
306
|
-
if (params.limit)
|
|
307
|
-
query.limit = params.limit;
|
|
308
|
-
if (params.skip)
|
|
309
|
-
query.skip = params.skip;
|
|
310
|
-
if (params.sort)
|
|
311
|
-
query.sort = params.sort;
|
|
312
|
-
const data = await axios.get(baseURL, { params: query });
|
|
313
|
-
return data;
|
|
262
|
+
return (await axios.get(await orders(), { params: queryOf(params) }));
|
|
314
263
|
},
|
|
315
264
|
async get(orderId, options) {
|
|
316
|
-
|
|
317
|
-
headers: accessHeaders(orderId, options),
|
|
318
|
-
});
|
|
319
|
-
|
|
265
|
+
return (await axios.get(await orders(`/${seg(orderId)}`), {
|
|
266
|
+
headers: accessHeaders(orderId, options === null || options === void 0 ? void 0 : options.accessToken),
|
|
267
|
+
}));
|
|
268
|
+
},
|
|
269
|
+
async update(orderId, params) {
|
|
270
|
+
return (await axios.patch(await orders(`/${seg(orderId)}`), params));
|
|
320
271
|
},
|
|
321
272
|
async cancel(orderId, options) {
|
|
322
|
-
const
|
|
273
|
+
const version = options === null || options === void 0 ? void 0 : options.version;
|
|
274
|
+
return (await axios.request({
|
|
275
|
+
method: "POST",
|
|
276
|
+
url: await orders(`/${seg(orderId)}/cancel`),
|
|
277
|
+
// The version is optional here: a customer's cancel sends none.
|
|
278
|
+
data: version === undefined ? undefined : { version },
|
|
279
|
+
headers: accessHeaders(orderId, options === null || options === void 0 ? void 0 : options.accessToken),
|
|
280
|
+
}));
|
|
281
|
+
},
|
|
282
|
+
async setFulfillment(orderId, { status, version }) {
|
|
283
|
+
return (await axios.put(await orders(`/${seg(orderId)}/fulfillment`), {
|
|
284
|
+
status,
|
|
285
|
+
version,
|
|
286
|
+
}));
|
|
287
|
+
},
|
|
288
|
+
async complete(orderId, { version }) {
|
|
289
|
+
return (await axios.request({
|
|
323
290
|
method: "POST",
|
|
324
|
-
url:
|
|
325
|
-
|
|
326
|
-
});
|
|
327
|
-
return data;
|
|
291
|
+
url: await orders(`/${seg(orderId)}/complete`),
|
|
292
|
+
data: { version },
|
|
293
|
+
}));
|
|
328
294
|
},
|
|
329
295
|
};
|
|
330
296
|
}
|
|
@@ -1,57 +1,104 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { Address, Page, PageParams, VersionInput } from "./project.types";
|
|
2
|
+
/**
|
|
3
|
+
* - `pending`: placed on the site, waiting for its payment; expires after an hour unpaid.
|
|
4
|
+
* - `placed`: paid, free, or entered by the business. Has an order number.
|
|
5
|
+
* - `completed`: the business marked it done. Cannot be canceled any more.
|
|
6
|
+
* - `canceled`, `expired`.
|
|
7
|
+
*/
|
|
8
|
+
export type OrderStatus = "pending" | "placed" | "completed" | "canceled" | "expired";
|
|
9
|
+
export type OrderChannel = "online" | "staff";
|
|
10
|
+
/** Delivery is marked for the whole order. */
|
|
11
|
+
export type FulfillmentStatus = "not_required" | "unfulfilled" | "fulfilled";
|
|
3
12
|
/** A line of an order. Names, price and duration are as they were when it was placed. */
|
|
4
13
|
export interface OrderItem {
|
|
5
14
|
id: string;
|
|
6
|
-
variant_id: string;
|
|
15
|
+
variant_id: string | null;
|
|
7
16
|
item_name: string;
|
|
8
|
-
variant_name: string;
|
|
17
|
+
variant_name: string | null;
|
|
9
18
|
duration_minutes: number | null;
|
|
10
19
|
unit_amount: number;
|
|
11
20
|
quantity: number;
|
|
12
21
|
line_amount: number;
|
|
13
|
-
fulfillment_mode: "manual" | "calendar";
|
|
14
|
-
fulfillment_status: FulfillmentStatus | null;
|
|
15
|
-
fulfilled_at: string | null;
|
|
16
22
|
}
|
|
17
23
|
export interface OrderBuyer {
|
|
18
24
|
name: string;
|
|
19
25
|
email: string | null;
|
|
20
26
|
phone: string | null;
|
|
21
27
|
}
|
|
28
|
+
/** An address, plus who receives it (empty: the buyer). */
|
|
29
|
+
export interface ShippingAddress extends Address {
|
|
30
|
+
name: string | null;
|
|
31
|
+
phone: string | null;
|
|
32
|
+
}
|
|
22
33
|
export interface Order {
|
|
23
34
|
id: string;
|
|
24
|
-
/**
|
|
25
|
-
order_number:
|
|
35
|
+
/** Consecutive per business and environment, given when the order is placed. Show it as `#12`. */
|
|
36
|
+
order_number: number | null;
|
|
26
37
|
status: OrderStatus;
|
|
38
|
+
channel: OrderChannel;
|
|
39
|
+
/** When a `pending` order stops waiting for its payment. */
|
|
40
|
+
expires_at: string | null;
|
|
41
|
+
fulfillment_status: FulfillmentStatus;
|
|
27
42
|
currency: string;
|
|
28
43
|
/** In the currency's smallest unit (cents). */
|
|
29
44
|
total_amount: number;
|
|
45
|
+
/** A test (preview) order or a real one. */
|
|
46
|
+
livemode: boolean;
|
|
47
|
+
/** The site it was placed on. */
|
|
48
|
+
app_id: string | null;
|
|
49
|
+
contact_id: string | null;
|
|
50
|
+
/** The account that placed it. */
|
|
51
|
+
user_id: string | null;
|
|
30
52
|
buyer: OrderBuyer;
|
|
53
|
+
shipping_address: ShippingAddress | null;
|
|
54
|
+
customer_note: string | null;
|
|
55
|
+
/** Only the site's admin and service callers see it. */
|
|
56
|
+
internal_note?: string | null;
|
|
57
|
+
/** In the order they were bought in. */
|
|
31
58
|
items: OrderItem[];
|
|
32
59
|
payment: {
|
|
33
|
-
/** `false` for a free order, which is `
|
|
60
|
+
/** `false` for a free order, which is `placed` as soon as it is created. */
|
|
34
61
|
required: boolean;
|
|
35
62
|
amount_received: number;
|
|
36
63
|
amount_refunded: number;
|
|
37
64
|
};
|
|
65
|
+
/** Only the site's admin and service callers see it. */
|
|
66
|
+
created_by?: "customer" | "owner" | "system";
|
|
67
|
+
created_by_user_id?: string | null;
|
|
68
|
+
access?: {
|
|
69
|
+
issued: boolean;
|
|
70
|
+
revoked: boolean;
|
|
71
|
+
};
|
|
72
|
+
version: number;
|
|
38
73
|
created_at: string;
|
|
74
|
+
updated_at: string;
|
|
75
|
+
placed_at: string | null;
|
|
39
76
|
completed_at: string | null;
|
|
40
77
|
canceled_at: string | null;
|
|
41
78
|
}
|
|
42
79
|
export interface OrderSummary {
|
|
43
80
|
id: string;
|
|
44
|
-
order_number:
|
|
81
|
+
order_number: number | null;
|
|
45
82
|
status: OrderStatus;
|
|
83
|
+
channel: OrderChannel;
|
|
84
|
+
fulfillment_status: FulfillmentStatus;
|
|
46
85
|
currency: string;
|
|
47
86
|
total_amount: number;
|
|
87
|
+
livemode: boolean;
|
|
88
|
+
app_id: string | null;
|
|
89
|
+
contact_id: string | null;
|
|
90
|
+
user_id: string | null;
|
|
48
91
|
buyer: {
|
|
49
92
|
name: string;
|
|
50
93
|
};
|
|
51
94
|
created_at: string;
|
|
52
95
|
}
|
|
53
96
|
export interface CreateOrderParams {
|
|
54
|
-
/**
|
|
97
|
+
/** `online` (default): a customer on the site. `staff`: the business entering one. */
|
|
98
|
+
channel?: OrderChannel;
|
|
99
|
+
/** The site it is placed on. Defaults to this client's app. */
|
|
100
|
+
app_id?: string | null;
|
|
101
|
+
/** 1–100 variants, each once. Prices are not sent: the order takes them from the catalog. */
|
|
55
102
|
items: Array<{
|
|
56
103
|
variant_id: string;
|
|
57
104
|
quantity?: number;
|
|
@@ -62,6 +109,16 @@ export interface CreateOrderParams {
|
|
|
62
109
|
email?: string;
|
|
63
110
|
phone?: string;
|
|
64
111
|
};
|
|
112
|
+
/** Required when something in it is shipped (`kind: "physical"`): at least `line` and `country`. */
|
|
113
|
+
shipping_address?: Partial<ShippingAddress>;
|
|
114
|
+
/** Up to 1000 characters. */
|
|
115
|
+
customer_note?: string;
|
|
116
|
+
/** `staff` only, and required there: a real order or a test. */
|
|
117
|
+
livemode?: boolean;
|
|
118
|
+
/** `staff` only: the contact it is for, instead of matching the buyer. */
|
|
119
|
+
contact_id?: string;
|
|
120
|
+
/** `staff` only. */
|
|
121
|
+
internal_note?: string;
|
|
65
122
|
}
|
|
66
123
|
export interface CreateOrderOptions {
|
|
67
124
|
/**
|
|
@@ -75,34 +132,54 @@ export interface CreateOrderResult {
|
|
|
75
132
|
order: Order;
|
|
76
133
|
/**
|
|
77
134
|
* Lets someone who is not signed in open this order again. The SDK keeps it
|
|
78
|
-
* for the rest of the browser session, so `orders.get
|
|
79
|
-
* work without passing it; put it in a link (after `#`)
|
|
80
|
-
* from elsewhere.
|
|
135
|
+
* for the rest of the browser session, so `orders.get`, `orders.cancel` and
|
|
136
|
+
* the payments calls work without passing it; put it in a link (after `#`)
|
|
137
|
+
* to reach the order from elsewhere.
|
|
81
138
|
*/
|
|
82
139
|
access_token?: string;
|
|
83
|
-
/** `false` when the order is free and already
|
|
140
|
+
/** `false` when the order is free and already placed. */
|
|
84
141
|
payment_required: boolean;
|
|
85
142
|
}
|
|
86
|
-
export interface OrderListParams {
|
|
143
|
+
export interface OrderListParams extends PageParams {
|
|
87
144
|
status?: OrderStatus;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
145
|
+
channel?: OrderChannel;
|
|
146
|
+
/** Orders placed on this site. */
|
|
147
|
+
app_id?: string;
|
|
148
|
+
/** The contact's orders, and those of contacts merged into it. */
|
|
149
|
+
contact_id?: string;
|
|
150
|
+
user_id?: string;
|
|
151
|
+
/** Order number, buyer name or email. */
|
|
152
|
+
q?: string;
|
|
153
|
+
created_from?: string;
|
|
154
|
+
created_to?: string;
|
|
92
155
|
}
|
|
93
156
|
export interface OrderAccessOptions {
|
|
94
157
|
/** The order's `access_token`, when it came from a link rather than from this browser session. */
|
|
95
158
|
accessToken?: string;
|
|
96
159
|
}
|
|
160
|
+
export interface UpdateOrderParams extends VersionInput {
|
|
161
|
+
/** Not once the order is delivered, completed or closed. */
|
|
162
|
+
shipping_address?: Partial<ShippingAddress> | null;
|
|
163
|
+
internal_note?: string | null;
|
|
164
|
+
}
|
|
97
165
|
/**
|
|
98
|
-
* Orders
|
|
166
|
+
* Orders.
|
|
167
|
+
*
|
|
168
|
+
* Who sees an order: the account that placed it, accounts with the records
|
|
169
|
+
* of its contact, anyone holding its access token, the site's admin and
|
|
170
|
+
* service callers. Anyone else gets 404.
|
|
99
171
|
*/
|
|
100
172
|
export interface OrdersModule {
|
|
101
173
|
/**
|
|
102
174
|
* Places an order for catalog variants. Works signed in or not.
|
|
103
175
|
*
|
|
104
|
-
* Rejects with
|
|
105
|
-
* when something in it cannot be bought,
|
|
176
|
+
* Rejects with 409 `NOT_PURCHASABLE` (`details.variant_id`, `details.reason`)
|
|
177
|
+
* when something in it cannot be bought, 409 `MIXED_CURRENCY`, or 429
|
|
178
|
+
* `RATE_LIMITED`.
|
|
179
|
+
*
|
|
180
|
+
* An `online` order takes its environment (preview or published) from the
|
|
181
|
+
* page's `Origin`, which a backend function does not send: with
|
|
182
|
+
* `asServiceRole`, enter a `staff` order and say `livemode` instead.
|
|
106
183
|
*
|
|
107
184
|
* @example
|
|
108
185
|
* ```typescript
|
|
@@ -113,13 +190,26 @@ export interface OrdersModule {
|
|
|
113
190
|
* ```
|
|
114
191
|
*/
|
|
115
192
|
create(params: CreateOrderParams, options?: CreateOrderOptions): Promise<CreateOrderResult>;
|
|
116
|
-
/** The
|
|
117
|
-
list(params?: OrderListParams): Promise<OrderSummary
|
|
193
|
+
/** The orders the caller may see, newest first. Rejects with 401 when nobody is signed in. */
|
|
194
|
+
list(params?: OrderListParams): Promise<Page<OrderSummary>>;
|
|
195
|
+
/** One order. */
|
|
196
|
+
get(orderId: string, options?: OrderAccessOptions): Promise<Order>;
|
|
197
|
+
/** Changes the shipping address or the internal note. The site's admin and service callers only. */
|
|
198
|
+
update(orderId: string, params: UpdateOrderParams): Promise<Order>;
|
|
118
199
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
200
|
+
* Cancels a `pending` or `placed` order (a customer: only one not paid yet).
|
|
201
|
+
* Canceling is not refunding. Rejects with 409 `INVALID_STATE` once the
|
|
202
|
+
* order is completed or expired, 409 `PAYMENT_UNRESOLVED` while a payment
|
|
203
|
+
* is still being confirmed.
|
|
121
204
|
*/
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
|
|
205
|
+
cancel(orderId: string, options?: OrderAccessOptions & Partial<VersionInput>): Promise<Order>;
|
|
206
|
+
/** Marks the whole order delivered, or takes that back. `placed` orders only. */
|
|
207
|
+
setFulfillment(orderId: string, params: VersionInput & {
|
|
208
|
+
status: "fulfilled" | "unfulfilled";
|
|
209
|
+
}): Promise<Order>;
|
|
210
|
+
/**
|
|
211
|
+
* Says the order is done. Nothing may still be owed (409 `INVALID_STATE`,
|
|
212
|
+
* `details.amount_due`); delivery is not required.
|
|
213
|
+
*/
|
|
214
|
+
complete(orderId: string, params: VersionInput): Promise<Order>;
|
|
125
215
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AxiosInstance } from "axios";
|
|
2
2
|
import { PaymentsModule, ProductsModule } from "./payments.types";
|
|
3
|
+
import { ProjectScope } from "./project.js";
|
|
3
4
|
/**
|
|
4
5
|
* The product reads.
|
|
5
6
|
*
|
|
@@ -21,4 +22,4 @@ export declare function createProductsModule(axios: AxiosInstance, appId: string
|
|
|
21
22
|
*
|
|
22
23
|
* @internal
|
|
23
24
|
*/
|
|
24
|
-
export declare function createPaymentsModule(axios: AxiosInstance, appId: string): PaymentsModule;
|
|
25
|
+
export declare function createPaymentsModule(axios: AxiosInstance, appId: string, project: ProjectScope): PaymentsModule;
|
package/dist/modules/payments.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { orderAccessHeaders } from "./order-access.js";
|
|
2
|
+
import { queryOf, seg } from "./project.js";
|
|
1
3
|
/**
|
|
2
4
|
* The product reads.
|
|
3
5
|
*
|
|
@@ -47,7 +49,7 @@ export function createProductsModule(axios, appId) {
|
|
|
47
49
|
*
|
|
48
50
|
* @internal
|
|
49
51
|
*/
|
|
50
|
-
export function createPaymentsModule(axios, appId) {
|
|
52
|
+
export function createPaymentsModule(axios, appId, project) {
|
|
51
53
|
async function createCheckoutSession(params) {
|
|
52
54
|
const data = await axios.request({
|
|
53
55
|
method: "POST",
|
|
@@ -84,5 +86,30 @@ export function createPaymentsModule(axios, appId) {
|
|
|
84
86
|
});
|
|
85
87
|
return data;
|
|
86
88
|
},
|
|
89
|
+
// Paying for an order: /projects/{project_id}/orders/{order_id}/...
|
|
90
|
+
// The order's access token rides along, as for orders.get.
|
|
91
|
+
async checkout(orderId, params = {}, options) {
|
|
92
|
+
const data = await axios.request({
|
|
93
|
+
method: "POST",
|
|
94
|
+
url: await project.path(`/orders/${seg(orderId)}/checkout`),
|
|
95
|
+
data: params,
|
|
96
|
+
headers: orderAccessHeaders(axios, appId, orderId, options === null || options === void 0 ? void 0 : options.accessToken),
|
|
97
|
+
});
|
|
98
|
+
return data;
|
|
99
|
+
},
|
|
100
|
+
async getForOrder(orderId, options) {
|
|
101
|
+
const data = await axios.get(await project.path(`/orders/${seg(orderId)}/payments`), {
|
|
102
|
+
headers: orderAccessHeaders(axios, appId, orderId, options === null || options === void 0 ? void 0 : options.accessToken),
|
|
103
|
+
});
|
|
104
|
+
return data;
|
|
105
|
+
},
|
|
106
|
+
async list(params = {}) {
|
|
107
|
+
const data = await axios.get(await project.path("/payments"), { params: queryOf(params) });
|
|
108
|
+
return data;
|
|
109
|
+
},
|
|
110
|
+
async get(paymentId) {
|
|
111
|
+
const data = await axios.get(await project.path(`/payments/${seg(paymentId)}`));
|
|
112
|
+
return data;
|
|
113
|
+
},
|
|
87
114
|
};
|
|
88
115
|
}
|