@aglyn/aglyn 1.0.0-beta.231 → 1.0.0-beta.233
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/package.json +11 -11
- package/src/lib/app-utils/account-acquisition.d.ts +15 -2
- package/src/lib/app-utils/account-acquisition.js +20 -1
- package/src/lib/app-utils/account-acquisition.js.map +1 -1
- package/src/lib/app-utils/admin-audit-index.d.ts +3 -1
- package/src/lib/app-utils/admin-audit-index.js +9 -1
- package/src/lib/app-utils/admin-audit-index.js.map +1 -1
- package/src/lib/app-utils/analytics-summary.d.ts +95 -0
- package/src/lib/app-utils/analytics-summary.js +113 -0
- package/src/lib/app-utils/analytics-summary.js.map +1 -0
- package/src/lib/app-utils/artifact-list-keys.js +12 -0
- package/src/lib/app-utils/artifact-list-keys.js.map +1 -1
- package/src/lib/app-utils/artifact-list-queries.d.ts +51 -0
- package/src/lib/app-utils/artifact-list-queries.js +321 -0
- package/src/lib/app-utils/artifact-list-queries.js.map +1 -0
- package/src/lib/app-utils/business-profile.d.ts +171 -0
- package/src/lib/app-utils/business-profile.js +277 -0
- package/src/lib/app-utils/business-profile.js.map +1 -0
- package/src/lib/app-utils/child-contract.d.ts +4 -1
- package/src/lib/app-utils/child-contract.js +5 -2
- package/src/lib/app-utils/child-contract.js.map +1 -1
- package/src/lib/app-utils/console-routes.d.ts +5 -0
- package/src/lib/app-utils/console-routes.js +1 -0
- package/src/lib/app-utils/console-routes.js.map +1 -1
- package/src/lib/app-utils/content-schema-type.d.ts +9 -0
- package/src/lib/app-utils/content-schema-type.js +4 -0
- package/src/lib/app-utils/content-schema-type.js.map +1 -1
- package/src/lib/app-utils/crm.d.ts +30 -2
- package/src/lib/app-utils/crm.js +77 -15
- package/src/lib/app-utils/crm.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +58 -10
- package/src/lib/app-utils/docs-help.generated.js +118 -4
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.js +320 -19
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/entry-list-declaration.d.ts +33 -0
- package/src/lib/app-utils/entry-list-declaration.js +172 -0
- package/src/lib/app-utils/entry-list-declaration.js.map +1 -0
- package/src/lib/app-utils/first-version-seed.d.ts +19 -0
- package/src/lib/app-utils/first-version-seed.js +53 -0
- package/src/lib/app-utils/first-version-seed.js.map +1 -0
- package/src/lib/app-utils/local-business.d.ts +8 -0
- package/src/lib/app-utils/local-business.js +4 -0
- package/src/lib/app-utils/local-business.js.map +1 -1
- package/src/lib/app-utils/lockdown.js +1 -1
- package/src/lib/app-utils/lockdown.js.map +1 -1
- package/src/lib/app-utils/message-search.js +5 -2
- package/src/lib/app-utils/message-search.js.map +1 -1
- package/src/lib/app-utils/name-search.js +15 -3
- package/src/lib/app-utils/name-search.js.map +1 -1
- package/src/lib/app-utils/page-markdown.js +3 -0
- package/src/lib/app-utils/page-markdown.js.map +1 -1
- package/src/lib/app-utils/screen-analytics-aggregate.d.ts +70 -0
- package/src/lib/app-utils/screen-analytics-aggregate.js +82 -0
- package/src/lib/app-utils/screen-analytics-aggregate.js.map +1 -0
- package/src/lib/app-utils/screen-kind.d.ts +25 -0
- package/src/lib/app-utils/screen-kind.js +25 -0
- package/src/lib/app-utils/screen-kind.js.map +1 -0
- package/src/lib/app-utils/screen-route.d.ts +1 -5
- package/src/lib/app-utils/screen-route.js +2 -4
- package/src/lib/app-utils/screen-route.js.map +1 -1
- package/src/lib/app-utils/site-journey-steps.d.ts +38 -0
- package/src/lib/app-utils/site-journey-steps.js +54 -0
- package/src/lib/app-utils/site-journey-steps.js.map +1 -0
- package/src/lib/app-utils/site-journey.d.ts +2 -22
- package/src/lib/app-utils/site-journey.js +2 -32
- package/src/lib/app-utils/site-journey.js.map +1 -1
- package/src/lib/plugin-manager/feature-plugins.d.ts +19 -0
- package/src/lib/plugin-manager/feature-plugins.js +8 -0
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js +124 -2
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/plugin-channel-orders.d.ts +262 -0
- package/src/lib/plugin-manager/plugin-channel-orders.js +32 -0
- package/src/lib/plugin-manager/plugin-channel-orders.js.map +1 -0
- package/src/lib/plugin-manager/plugin-checkout-credits.d.ts +288 -0
- package/src/lib/plugin-manager/plugin-checkout-credits.js +196 -0
- package/src/lib/plugin-manager/plugin-checkout-credits.js.map +1 -0
- package/src/lib/plugin-manager/plugin-product-catalog.d.ts +5 -0
- package/src/lib/plugin-manager/plugin-product-catalog.js.map +1 -1
- package/src/lib/plugin-manager/plugin-product-writer.d.ts +166 -0
- package/src/lib/plugin-manager/plugin-product-writer.js +31 -0
- package/src/lib/plugin-manager/plugin-product-writer.js.map +1 -0
- package/src/lib/plugin-manager/plugin-shipping-rates.d.ts +10 -0
- package/src/lib/plugin-manager/plugin-shipping-rates.js.map +1 -1
- package/src/lib/plugin-manager/plugin-theme-presets.d.ts +40 -0
- package/src/lib/plugin-manager/plugin-theme-presets.js +44 -0
- package/src/lib/plugin-manager/plugin-theme-presets.js.map +1 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Orders a store took somewhere else (AGL-3638): a marketplace, a social
|
|
19
|
+
* shop, any outside channel that sells the store's goods and is paid there.
|
|
20
|
+
*
|
|
21
|
+
* Such an order must become a REAL order of the store — numbered with the
|
|
22
|
+
* rest, shown in the same list, shipped from the same dialog, announced to
|
|
23
|
+
* the same subscribers — and its units must leave the same shelf every other
|
|
24
|
+
* channel sells from, in the same write, or two channels sell the last unit
|
|
25
|
+
* twice. The plugin that brings the order in must not write the seller's
|
|
26
|
+
* orders or products itself, so the seller registers
|
|
27
|
+
* {@link PluginChannelOrders} and records each order under its own rules: the
|
|
28
|
+
* order number, the stock decrement and its ledger row, the oversell alert,
|
|
29
|
+
* the order events.
|
|
30
|
+
*
|
|
31
|
+
* ## Once per outside order
|
|
32
|
+
*
|
|
33
|
+
* An import is keyed by `channel.id` and `externalOrderId`. The same pair a
|
|
34
|
+
* second time — a retried run, a page read twice — answers `already` with
|
|
35
|
+
* the record the first import made, and nothing moves.
|
|
36
|
+
*
|
|
37
|
+
* ## The channel was paid, not the store
|
|
38
|
+
*
|
|
39
|
+
* The buyer paid the channel, and the channel pays the merchant out. No
|
|
40
|
+
* money moves through the seller for the order; its refunds and returns
|
|
41
|
+
* happen on the channel. What the channel charged the merchant for the sale
|
|
42
|
+
* (`fees`) is RECORDED on the order for the merchant's books and never
|
|
43
|
+
* charged or collected by anyone here.
|
|
44
|
+
*
|
|
45
|
+
* Money is integer minor units (cents for USD) in `currency`. A seller may
|
|
46
|
+
* refuse an order in a currency its store does not sell in.
|
|
47
|
+
*
|
|
48
|
+
* Import this module by its own subpath
|
|
49
|
+
* (`@aglyn/aglyn/plugin-manager/plugin-channel-orders`); it is not in the
|
|
50
|
+
* barrel.
|
|
51
|
+
*/
|
|
52
|
+
/** One line of an outside order. */
|
|
53
|
+
export interface PluginChannelOrderLine {
|
|
54
|
+
/** The channel's id for the line, which a shipment back to it names. */
|
|
55
|
+
externalLineId: string;
|
|
56
|
+
/** The seller SKU the channel sold it under; `null` when it gave none. */
|
|
57
|
+
sku: string | null;
|
|
58
|
+
/**
|
|
59
|
+
* The product and configuration, when the importer already knows them
|
|
60
|
+
* (it listed them from `core.product-catalog`). Otherwise the seller
|
|
61
|
+
* matches `sku`.
|
|
62
|
+
*/
|
|
63
|
+
productId?: string | null;
|
|
64
|
+
variantId?: string | null;
|
|
65
|
+
/** The channel's name for the item, kept on the order. */
|
|
66
|
+
name: string;
|
|
67
|
+
quantity: number;
|
|
68
|
+
/** Per unit, before tax. */
|
|
69
|
+
unitPriceCents: number;
|
|
70
|
+
}
|
|
71
|
+
/** The address the order ships to. */
|
|
72
|
+
export interface PluginChannelOrderAddress {
|
|
73
|
+
name?: string | null;
|
|
74
|
+
line1?: string | null;
|
|
75
|
+
line2?: string | null;
|
|
76
|
+
city?: string | null;
|
|
77
|
+
state?: string | null;
|
|
78
|
+
postalCode?: string | null;
|
|
79
|
+
/** ISO 3166-1 alpha-2, upper case. */
|
|
80
|
+
country?: string | null;
|
|
81
|
+
phone?: string | null;
|
|
82
|
+
}
|
|
83
|
+
/** A channel's charge on a sale, recorded for the merchant and never collected here. */
|
|
84
|
+
export interface PluginChannelOrderFee {
|
|
85
|
+
label: string;
|
|
86
|
+
amountCents: number;
|
|
87
|
+
}
|
|
88
|
+
export interface PluginChannelOrder {
|
|
89
|
+
hostId: string;
|
|
90
|
+
/** Stable id and display name, as the importer names its channel. */
|
|
91
|
+
channel: {
|
|
92
|
+
id: string;
|
|
93
|
+
label: string;
|
|
94
|
+
};
|
|
95
|
+
externalOrderId: string;
|
|
96
|
+
/** What the merchant sees on the channel. */
|
|
97
|
+
externalRef: string;
|
|
98
|
+
placedAtMs: number;
|
|
99
|
+
/** ISO 4217. */
|
|
100
|
+
currency: string;
|
|
101
|
+
lines: PluginChannelOrderLine[];
|
|
102
|
+
shippingCents: number;
|
|
103
|
+
/** Tax the channel collected; the channel, not the store, remits it. */
|
|
104
|
+
taxCents: number;
|
|
105
|
+
discountCents: number;
|
|
106
|
+
/** What the buyer paid the channel in all. */
|
|
107
|
+
totalCents: number;
|
|
108
|
+
/** The channel's charges on the sale; `null` while the channel has not said. */
|
|
109
|
+
fees: PluginChannelOrderFee[] | null;
|
|
110
|
+
customerName: string | null;
|
|
111
|
+
shippingAddress: PluginChannelOrderAddress | null;
|
|
112
|
+
/** Placed in the channel's sandbox: recorded, but never counted as revenue. */
|
|
113
|
+
testMode: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* How the goods leave the store (AGL-3644): `ship`, the default, by a
|
|
116
|
+
* carrier to `shippingAddress`; `courier`, handed over the counter to the
|
|
117
|
+
* channel's own courier (a delivery app), so nothing is shipped from here
|
|
118
|
+
* and the seller says so to the merchant.
|
|
119
|
+
*/
|
|
120
|
+
handoff?: 'ship' | 'courier';
|
|
121
|
+
}
|
|
122
|
+
/** A line the store's stock could not cover in full: the channel sold more than the shelf held. */
|
|
123
|
+
export interface PluginChannelOrderShortfall {
|
|
124
|
+
lineIndex: number;
|
|
125
|
+
sku: string | null;
|
|
126
|
+
/** Units sold beyond what the shelf held. */
|
|
127
|
+
short: number;
|
|
128
|
+
}
|
|
129
|
+
export type PluginChannelOrderOutcome = {
|
|
130
|
+
outcome: 'created';
|
|
131
|
+
recordId: string;
|
|
132
|
+
/** What a person calls it: `#1042`. */
|
|
133
|
+
displayRef: string;
|
|
134
|
+
/** Which order line each channel line became. */
|
|
135
|
+
lines: Array<{
|
|
136
|
+
lineIndex: number;
|
|
137
|
+
externalLineId: string;
|
|
138
|
+
}>;
|
|
139
|
+
/** Lines whose units the shelf could not cover. The order is recorded all the same. */
|
|
140
|
+
shortfalls: PluginChannelOrderShortfall[];
|
|
141
|
+
/** Lines matching no product, recorded by name with no stock moved. */
|
|
142
|
+
unmatched: number[];
|
|
143
|
+
} | {
|
|
144
|
+
outcome: 'already';
|
|
145
|
+
recordId: string;
|
|
146
|
+
displayRef: string;
|
|
147
|
+
lines: Array<{
|
|
148
|
+
lineIndex: number;
|
|
149
|
+
externalLineId: string;
|
|
150
|
+
}>;
|
|
151
|
+
}
|
|
152
|
+
/** The seller would not record it; `reason` says why, for the merchant. */
|
|
153
|
+
| {
|
|
154
|
+
outcome: 'refused';
|
|
155
|
+
reason: string;
|
|
156
|
+
};
|
|
157
|
+
export interface PluginChannelOrderCancel {
|
|
158
|
+
hostId: string;
|
|
159
|
+
recordId: string;
|
|
160
|
+
/** Shown on the order's timeline. */
|
|
161
|
+
reason: string;
|
|
162
|
+
}
|
|
163
|
+
export type PluginChannelOrderCancelOutcome =
|
|
164
|
+
/** Cancelled, with this many units back on the shelf. */
|
|
165
|
+
{
|
|
166
|
+
outcome: 'cancelled';
|
|
167
|
+
restockedUnits: number;
|
|
168
|
+
} | {
|
|
169
|
+
outcome: 'already';
|
|
170
|
+
}
|
|
171
|
+
/** The seller's rules refused (it has shipped, say); `status` is the seller's word for where it stands. */
|
|
172
|
+
| {
|
|
173
|
+
outcome: 'not_cancellable';
|
|
174
|
+
status: string;
|
|
175
|
+
} | {
|
|
176
|
+
outcome: 'no_such_record';
|
|
177
|
+
};
|
|
178
|
+
/** The channel's courier took the whole order (AGL-3644): it is fulfilled, with nothing shipped from here. */
|
|
179
|
+
export interface PluginChannelOrderHandoff {
|
|
180
|
+
hostId: string;
|
|
181
|
+
recordId: string;
|
|
182
|
+
/** Shown on the order's timeline, e.g. `Picked up by the DoorDash courier`. */
|
|
183
|
+
note: string;
|
|
184
|
+
}
|
|
185
|
+
export type PluginChannelOrderHandoffOutcome = {
|
|
186
|
+
outcome: 'completed';
|
|
187
|
+
} | {
|
|
188
|
+
outcome: 'already';
|
|
189
|
+
}
|
|
190
|
+
/** The seller's rules refused (it was cancelled, say); `status` is the seller's word for where it stands. */
|
|
191
|
+
| {
|
|
192
|
+
outcome: 'not_completable';
|
|
193
|
+
status: string;
|
|
194
|
+
} | {
|
|
195
|
+
outcome: 'no_such_record';
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Money the channel gave back to the buyer of an order it sent (AGL-3644):
|
|
199
|
+
* a refund, or the channel's adjustment of the order — an item removed, a
|
|
200
|
+
* quantity lowered. No money moves here: the channel refunded its buyer and
|
|
201
|
+
* takes it from the merchant's payout, so the seller RECORDS it on the order.
|
|
202
|
+
*
|
|
203
|
+
* `restock` names the units that never left the store (an item taken off the
|
|
204
|
+
* order before it was handed over); the seller puts back at most what the
|
|
205
|
+
* order's own sale took. Empty for a refund of goods already gone.
|
|
206
|
+
*/
|
|
207
|
+
export interface PluginChannelOrderRefund {
|
|
208
|
+
hostId: string;
|
|
209
|
+
recordId: string;
|
|
210
|
+
/** The channel's id for this refund or adjustment: the same id a second time records nothing. */
|
|
211
|
+
refundId: string;
|
|
212
|
+
/** Minor units in the order's currency; `0` for an adjustment that only moves stock. */
|
|
213
|
+
amountCents: number;
|
|
214
|
+
/** Shown on the order's timeline. */
|
|
215
|
+
reason: string;
|
|
216
|
+
restock: Array<{
|
|
217
|
+
lineIndex: number;
|
|
218
|
+
quantity: number;
|
|
219
|
+
}>;
|
|
220
|
+
}
|
|
221
|
+
export type PluginChannelOrderRefundOutcome =
|
|
222
|
+
/** Recorded: `refundedCents` is the order's refunded total now, `restockedUnits` what went back on the shelf. */
|
|
223
|
+
{
|
|
224
|
+
outcome: 'recorded';
|
|
225
|
+
refundedCents: number;
|
|
226
|
+
restockedUnits: number;
|
|
227
|
+
} | {
|
|
228
|
+
outcome: 'already';
|
|
229
|
+
}
|
|
230
|
+
/** The seller would not record it; `reason` says why, for the merchant. */
|
|
231
|
+
| {
|
|
232
|
+
outcome: 'refused';
|
|
233
|
+
reason: string;
|
|
234
|
+
} | {
|
|
235
|
+
outcome: 'no_such_record';
|
|
236
|
+
};
|
|
237
|
+
export interface PluginChannelOrders {
|
|
238
|
+
/** Records an outside order once, with its units off the shelf in the same write. */
|
|
239
|
+
importOrder(order: PluginChannelOrder): Promise<PluginChannelOrderOutcome>;
|
|
240
|
+
/** The channel canceled an order it had sent: cancel it here and put its units back. */
|
|
241
|
+
cancelOrder(request: PluginChannelOrderCancel): Promise<PluginChannelOrderCancelOutcome>;
|
|
242
|
+
/** The channel's charges on a sale, once it says what they were. */
|
|
243
|
+
recordFees(request: {
|
|
244
|
+
hostId: string;
|
|
245
|
+
recordId: string;
|
|
246
|
+
fees: PluginChannelOrderFee[];
|
|
247
|
+
}): Promise<'recorded' | 'no_such_record'>;
|
|
248
|
+
/**
|
|
249
|
+
* The channel's courier took the order (AGL-3644). Optional: a seller that
|
|
250
|
+
* cannot record a hand-off leaves it out, and the importer leaves the order
|
|
251
|
+
* for the merchant to fulfill.
|
|
252
|
+
*/
|
|
253
|
+
completeOrder?(request: PluginChannelOrderHandoff): Promise<PluginChannelOrderHandoffOutcome>;
|
|
254
|
+
/** The channel refunded or adjusted an order it sent (AGL-3644). Optional, as `completeOrder`. */
|
|
255
|
+
recordRefund?(request: PluginChannelOrderRefund): Promise<PluginChannelOrderRefundOutcome>;
|
|
256
|
+
}
|
|
257
|
+
/** Registers the seller. A second plugin is refused naming both. */
|
|
258
|
+
export declare function registerPluginChannelOrders(orders: PluginChannelOrders, options?: {
|
|
259
|
+
pluginId?: string;
|
|
260
|
+
}): void;
|
|
261
|
+
/** The seller, or `null` when no plugin registered one. */
|
|
262
|
+
export declare function pluginChannelOrders(): PluginChannelOrders | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { definePluginServiceContract, registerPluginService, resolvePluginServices } from "./plugin-services.js";
|
|
18
|
+
const PLUGIN_CHANNEL_ORDERS = definePluginServiceContract('core.channel-orders', {
|
|
19
|
+
multiple: false
|
|
20
|
+
});
|
|
21
|
+
/** Registers the seller. A second plugin is refused naming both. */ export function registerPluginChannelOrders(orders, options) {
|
|
22
|
+
registerPluginService(PLUGIN_CHANNEL_ORDERS, orders, _extends({}, (options == null ? void 0 : options.pluginId) ? {
|
|
23
|
+
pluginId: options.pluginId
|
|
24
|
+
} : {}));
|
|
25
|
+
}
|
|
26
|
+
/** The seller, or `null` when no plugin registered one. */ export function pluginChannelOrders() {
|
|
27
|
+
var _ref;
|
|
28
|
+
var _resolvePluginServices_;
|
|
29
|
+
return (_ref = (_resolvePluginServices_ = resolvePluginServices(PLUGIN_CHANNEL_ORDERS)[0]) == null ? void 0 : _resolvePluginServices_.impl) != null ? _ref : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=plugin-channel-orders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-channel-orders.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * Orders a store took somewhere else (AGL-3638): a marketplace, a social\n * shop, any outside channel that sells the store's goods and is paid there.\n *\n * Such an order must become a REAL order of the store — numbered with the\n * rest, shown in the same list, shipped from the same dialog, announced to\n * the same subscribers — and its units must leave the same shelf every other\n * channel sells from, in the same write, or two channels sell the last unit\n * twice. The plugin that brings the order in must not write the seller's\n * orders or products itself, so the seller registers\n * {@link PluginChannelOrders} and records each order under its own rules: the\n * order number, the stock decrement and its ledger row, the oversell alert,\n * the order events.\n *\n * ## Once per outside order\n *\n * An import is keyed by `channel.id` and `externalOrderId`. The same pair a\n * second time — a retried run, a page read twice — answers `already` with\n * the record the first import made, and nothing moves.\n *\n * ## The channel was paid, not the store\n *\n * The buyer paid the channel, and the channel pays the merchant out. No\n * money moves through the seller for the order; its refunds and returns\n * happen on the channel. What the channel charged the merchant for the sale\n * (`fees`) is RECORDED on the order for the merchant's books and never\n * charged or collected by anyone here.\n *\n * Money is integer minor units (cents for USD) in `currency`. A seller may\n * refuse an order in a currency its store does not sell in.\n *\n * Import this module by its own subpath\n * (`@aglyn/aglyn/plugin-manager/plugin-channel-orders`); it is not in the\n * barrel.\n */\n\n/** One line of an outside order. */\nexport interface PluginChannelOrderLine {\n /** The channel's id for the line, which a shipment back to it names. */\n externalLineId: string\n /** The seller SKU the channel sold it under; `null` when it gave none. */\n sku: string | null\n /**\n * The product and configuration, when the importer already knows them\n * (it listed them from `core.product-catalog`). Otherwise the seller\n * matches `sku`.\n */\n productId?: string | null\n variantId?: string | null\n /** The channel's name for the item, kept on the order. */\n name: string\n quantity: number\n /** Per unit, before tax. */\n unitPriceCents: number\n}\n\n/** The address the order ships to. */\nexport interface PluginChannelOrderAddress {\n name?: string | null\n line1?: string | null\n line2?: string | null\n city?: string | null\n state?: string | null\n postalCode?: string | null\n /** ISO 3166-1 alpha-2, upper case. */\n country?: string | null\n phone?: string | null\n}\n\n/** A channel's charge on a sale, recorded for the merchant and never collected here. */\nexport interface PluginChannelOrderFee {\n label: string\n amountCents: number\n}\n\nexport interface PluginChannelOrder {\n hostId: string\n /** Stable id and display name, as the importer names its channel. */\n channel: { id: string; label: string }\n externalOrderId: string\n /** What the merchant sees on the channel. */\n externalRef: string\n placedAtMs: number\n /** ISO 4217. */\n currency: string\n lines: PluginChannelOrderLine[]\n shippingCents: number\n /** Tax the channel collected; the channel, not the store, remits it. */\n taxCents: number\n discountCents: number\n /** What the buyer paid the channel in all. */\n totalCents: number\n /** The channel's charges on the sale; `null` while the channel has not said. */\n fees: PluginChannelOrderFee[] | null\n customerName: string | null\n shippingAddress: PluginChannelOrderAddress | null\n /** Placed in the channel's sandbox: recorded, but never counted as revenue. */\n testMode: boolean\n /**\n * How the goods leave the store (AGL-3644): `ship`, the default, by a\n * carrier to `shippingAddress`; `courier`, handed over the counter to the\n * channel's own courier (a delivery app), so nothing is shipped from here\n * and the seller says so to the merchant.\n */\n handoff?: 'ship' | 'courier'\n}\n\n/** A line the store's stock could not cover in full: the channel sold more than the shelf held. */\nexport interface PluginChannelOrderShortfall {\n lineIndex: number\n sku: string | null\n /** Units sold beyond what the shelf held. */\n short: number\n}\n\nexport type PluginChannelOrderOutcome =\n | {\n outcome: 'created'\n recordId: string\n /** What a person calls it: `#1042`. */\n displayRef: string\n /** Which order line each channel line became. */\n lines: Array<{ lineIndex: number; externalLineId: string }>\n /** Lines whose units the shelf could not cover. The order is recorded all the same. */\n shortfalls: PluginChannelOrderShortfall[]\n /** Lines matching no product, recorded by name with no stock moved. */\n unmatched: number[]\n }\n | {\n outcome: 'already'\n recordId: string\n displayRef: string\n lines: Array<{ lineIndex: number; externalLineId: string }>\n }\n /** The seller would not record it; `reason` says why, for the merchant. */\n | { outcome: 'refused'; reason: string }\n\nexport interface PluginChannelOrderCancel {\n hostId: string\n recordId: string\n /** Shown on the order's timeline. */\n reason: string\n}\n\nexport type PluginChannelOrderCancelOutcome =\n /** Cancelled, with this many units back on the shelf. */\n | { outcome: 'cancelled'; restockedUnits: number }\n | { outcome: 'already' }\n /** The seller's rules refused (it has shipped, say); `status` is the seller's word for where it stands. */\n | { outcome: 'not_cancellable'; status: string }\n | { outcome: 'no_such_record' }\n\n/** The channel's courier took the whole order (AGL-3644): it is fulfilled, with nothing shipped from here. */\nexport interface PluginChannelOrderHandoff {\n hostId: string\n recordId: string\n /** Shown on the order's timeline, e.g. `Picked up by the DoorDash courier`. */\n note: string\n}\n\nexport type PluginChannelOrderHandoffOutcome =\n | { outcome: 'completed' }\n | { outcome: 'already' }\n /** The seller's rules refused (it was cancelled, say); `status` is the seller's word for where it stands. */\n | { outcome: 'not_completable'; status: string }\n | { outcome: 'no_such_record' }\n\n/**\n * Money the channel gave back to the buyer of an order it sent (AGL-3644):\n * a refund, or the channel's adjustment of the order — an item removed, a\n * quantity lowered. No money moves here: the channel refunded its buyer and\n * takes it from the merchant's payout, so the seller RECORDS it on the order.\n *\n * `restock` names the units that never left the store (an item taken off the\n * order before it was handed over); the seller puts back at most what the\n * order's own sale took. Empty for a refund of goods already gone.\n */\nexport interface PluginChannelOrderRefund {\n hostId: string\n recordId: string\n /** The channel's id for this refund or adjustment: the same id a second time records nothing. */\n refundId: string\n /** Minor units in the order's currency; `0` for an adjustment that only moves stock. */\n amountCents: number\n /** Shown on the order's timeline. */\n reason: string\n restock: Array<{ lineIndex: number; quantity: number }>\n}\n\nexport type PluginChannelOrderRefundOutcome =\n /** Recorded: `refundedCents` is the order's refunded total now, `restockedUnits` what went back on the shelf. */\n | { outcome: 'recorded'; refundedCents: number; restockedUnits: number }\n | { outcome: 'already' }\n /** The seller would not record it; `reason` says why, for the merchant. */\n | { outcome: 'refused'; reason: string }\n | { outcome: 'no_such_record' }\n\nexport interface PluginChannelOrders {\n /** Records an outside order once, with its units off the shelf in the same write. */\n importOrder(order: PluginChannelOrder): Promise<PluginChannelOrderOutcome>\n /** The channel canceled an order it had sent: cancel it here and put its units back. */\n cancelOrder(request: PluginChannelOrderCancel): Promise<PluginChannelOrderCancelOutcome>\n /** The channel's charges on a sale, once it says what they were. */\n recordFees(request: {\n hostId: string\n recordId: string\n fees: PluginChannelOrderFee[]\n }): Promise<'recorded' | 'no_such_record'>\n /**\n * The channel's courier took the order (AGL-3644). Optional: a seller that\n * cannot record a hand-off leaves it out, and the importer leaves the order\n * for the merchant to fulfill.\n */\n completeOrder?(request: PluginChannelOrderHandoff): Promise<PluginChannelOrderHandoffOutcome>\n /** The channel refunded or adjusted an order it sent (AGL-3644). Optional, as `completeOrder`. */\n recordRefund?(request: PluginChannelOrderRefund): Promise<PluginChannelOrderRefundOutcome>\n}\n\nconst PLUGIN_CHANNEL_ORDERS = definePluginServiceContract<PluginChannelOrders>('core.channel-orders', {\n multiple: false,\n})\n\n/** Registers the seller. A second plugin is refused naming both. */\nexport function registerPluginChannelOrders(orders: PluginChannelOrders, options?: { pluginId?: string }): void {\n registerPluginService(PLUGIN_CHANNEL_ORDERS, orders, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\n/** The seller, or `null` when no plugin registered one. */\nexport function pluginChannelOrders(): PluginChannelOrders | null {\n return resolvePluginServices(PLUGIN_CHANNEL_ORDERS)[0]?.impl ?? null\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_CHANNEL_ORDERS","multiple","registerPluginChannelOrders","orders","options","pluginId","pluginChannelOrders","impl"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AA4N1B,MAAMC,wBAAwBH,4BAAiD,uBAAuB;IACpGI,UAAU;AACZ;AAEA,kEAAkE,GAClE,OAAO,SAASC,4BAA4BC,MAA2B,EAAEC,OAA+B;IACtGN,sBAAsBE,uBAAuBG,QAAQ,aAC/CC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAEA,yDAAyD,GACzD,OAAO,SAASC;;QACPP;IAAP,gBAAOA,0BAAAA,sBAAsBC,sBAAsB,CAAC,EAAE,qBAA/CD,wBAAiDQ,IAAI,mBAAI;AAClE"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Money another plugin honors at checkout and at the register (AGL-3640).
|
|
19
|
+
*
|
|
20
|
+
* A rewards balance, store credit and a friend's referral credit are all the
|
|
21
|
+
* same thing to a seller: a code the buyer brings that pays part of the sale
|
|
22
|
+
* from an account the SELLER does not keep. The plugin that keeps the account
|
|
23
|
+
* registers a provider here; the seller asks it, by code, how much the account
|
|
24
|
+
* can give, reserves that much while an online buyer pays, and takes it in the
|
|
25
|
+
* same transaction that writes the sale. The seller never reads the provider's
|
|
26
|
+
* documents and the provider never reads the seller's: the sale records which
|
|
27
|
+
* provider paid what, under the provider's own opaque `reference`, and the
|
|
28
|
+
* seller's order events carry that onward.
|
|
29
|
+
*
|
|
30
|
+
* ## Money
|
|
31
|
+
*
|
|
32
|
+
* Integer cents in the sale's currency, always. A provider answers what it
|
|
33
|
+
* CAN give and the seller never takes more than that.
|
|
34
|
+
*
|
|
35
|
+
* - Online, the seller {@link PluginCheckoutCreditProvider.hold | holds}
|
|
36
|
+
* before the payment processor is asked, so a shortfall is a refusal and
|
|
37
|
+
* not an apology. The hold is keyed by the checkout attempt, so a retry
|
|
38
|
+
* re-places the same hold rather than a second one, and it lapses on its
|
|
39
|
+
* own if the checkout is abandoned and nobody releases it.
|
|
40
|
+
* - When the sale is written, the seller {@link PluginCheckoutCreditProvider.stage | stages}
|
|
41
|
+
* the account INSIDE its own transaction: the provider reads (and only
|
|
42
|
+
* reads) what it needs, and the stage's `debit` writes the redemption in
|
|
43
|
+
* the same commit as the sale. A sale that is not written takes nothing;
|
|
44
|
+
* a redemption is never written without its sale.
|
|
45
|
+
* - A refund gives back through {@link PluginCheckoutCreditProvider.restore | restore},
|
|
46
|
+
* idempotent per key, so a retried refund restores once.
|
|
47
|
+
*
|
|
48
|
+
* ## Nobody home
|
|
49
|
+
*
|
|
50
|
+
* With no provider registered, or none offered on a site, the seller shows no
|
|
51
|
+
* field and sells exactly as it did before. Every reader here that a seller
|
|
52
|
+
* calls on a sale path is safe to call with nothing registered.
|
|
53
|
+
*
|
|
54
|
+
* Import this module by its own subpath
|
|
55
|
+
* (`@aglyn/aglyn/plugin-manager/plugin-checkout-credits`); it is not in the
|
|
56
|
+
* barrel.
|
|
57
|
+
*/
|
|
58
|
+
/** Where the sale happens. */
|
|
59
|
+
export type CheckoutCreditChannel = 'online' | 'pos';
|
|
60
|
+
/**
|
|
61
|
+
* A server transaction as a provider uses it: the Admin SDK's shape, typed
|
|
62
|
+
* structurally so the platform names no database SDK. Every `get` happens
|
|
63
|
+
* before any write, as the database requires.
|
|
64
|
+
*/
|
|
65
|
+
export interface CheckoutCreditTransaction {
|
|
66
|
+
get(ref: any): Promise<any>;
|
|
67
|
+
set(ref: any, data: any, options?: any): unknown;
|
|
68
|
+
update(ref: any, data: any): unknown;
|
|
69
|
+
create(ref: any, data: any): unknown;
|
|
70
|
+
}
|
|
71
|
+
/** A provider's refusal, in words the buyer or the cashier can read. */
|
|
72
|
+
export interface CheckoutCreditRefusal {
|
|
73
|
+
ok: false;
|
|
74
|
+
/** 400 for a bad code, 404 for an unknown one, 409 for an empty or busy one. */
|
|
75
|
+
status: number;
|
|
76
|
+
error: string;
|
|
77
|
+
}
|
|
78
|
+
/** The account a code names. */
|
|
79
|
+
export interface CheckoutCreditAccount {
|
|
80
|
+
ok: true;
|
|
81
|
+
/**
|
|
82
|
+
* The provider's own handle on the account, stored on the sale and handed
|
|
83
|
+
* back to it later. Never shown to a buyer and never a bearer secret.
|
|
84
|
+
* {@link CHECKOUT_CREDIT_REFERENCE} holds its shape.
|
|
85
|
+
*/
|
|
86
|
+
reference: string;
|
|
87
|
+
/** What the buyer and the receipt call it: `Rewards`, `Referral credit`. */
|
|
88
|
+
label: string;
|
|
89
|
+
/** The last characters of the code, for the register and the receipt. */
|
|
90
|
+
last4: string;
|
|
91
|
+
/** What the account could give right now, integer cents. */
|
|
92
|
+
availableCents: number;
|
|
93
|
+
/** One line for the cashier: `1,250 points and $5.00 store credit`. */
|
|
94
|
+
detail?: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The account as the seller's transaction read it. The provider builds it in
|
|
98
|
+
* {@link PluginCheckoutCreditProvider.stage}, which reads; its methods only
|
|
99
|
+
* WRITE, through the same transaction, so the redemption commits with the sale
|
|
100
|
+
* or not at all.
|
|
101
|
+
*/
|
|
102
|
+
export interface CheckoutCreditStage {
|
|
103
|
+
/**
|
|
104
|
+
* What the account may give now, honoring every live hold except the one
|
|
105
|
+
* the stage was opened for (that one is this sale's own).
|
|
106
|
+
*/
|
|
107
|
+
availableCents: number;
|
|
108
|
+
/**
|
|
109
|
+
* Takes up to `cents` and returns what was taken: never more than
|
|
110
|
+
* `availableCents` plus this sale's own hold. `key` names this redemption
|
|
111
|
+
* (the attempt, or the register payment) and is what a void reverses.
|
|
112
|
+
*/
|
|
113
|
+
debit(input: {
|
|
114
|
+
cents: number;
|
|
115
|
+
key: string;
|
|
116
|
+
orderId: string;
|
|
117
|
+
channel: CheckoutCreditChannel;
|
|
118
|
+
}): number;
|
|
119
|
+
/** Gives back exactly what the redemption named by `key` took: a voided register payment. */
|
|
120
|
+
reverse(input: {
|
|
121
|
+
key: string;
|
|
122
|
+
orderId: string;
|
|
123
|
+
}): number;
|
|
124
|
+
}
|
|
125
|
+
export interface CheckoutCreditHoldRequest {
|
|
126
|
+
hostId: string;
|
|
127
|
+
reference: string;
|
|
128
|
+
/** The checkout attempt: the same key re-places the same hold. */
|
|
129
|
+
holdKey: string;
|
|
130
|
+
/** The most the sale can take: what is left to pay on the goods. */
|
|
131
|
+
maxCents: number;
|
|
132
|
+
/** ISO-4217, lower case. */
|
|
133
|
+
currency: string;
|
|
134
|
+
customerEmail: string | null;
|
|
135
|
+
nowMs: number;
|
|
136
|
+
}
|
|
137
|
+
export interface PluginCheckoutCreditProvider {
|
|
138
|
+
/** Stable within the plugin: lower-case words and dashes. */
|
|
139
|
+
key: string;
|
|
140
|
+
/** What the field and the register's tender button say: `Rewards`. */
|
|
141
|
+
label: string;
|
|
142
|
+
/** Whether a code is one this provider issues. Syntax only: no reads. */
|
|
143
|
+
recognizes(code: string): boolean;
|
|
144
|
+
/** Whether the site offers it at all: plugin on, program on, plan carries it. */
|
|
145
|
+
offered(input: {
|
|
146
|
+
hostId: string;
|
|
147
|
+
channel: CheckoutCreditChannel;
|
|
148
|
+
}): Promise<boolean>;
|
|
149
|
+
/**
|
|
150
|
+
* A buyer's code — or, at the register only, a `reference` a staff
|
|
151
|
+
* {@link lookup} returned — to the account it names. `staff` is true only
|
|
152
|
+
* when the seller has authenticated a staff member for this site; a
|
|
153
|
+
* provider honors a bare `reference` only then.
|
|
154
|
+
*/
|
|
155
|
+
resolve(input: {
|
|
156
|
+
hostId: string;
|
|
157
|
+
code?: string;
|
|
158
|
+
reference?: string;
|
|
159
|
+
channel: CheckoutCreditChannel;
|
|
160
|
+
customerEmail: string | null;
|
|
161
|
+
staff: boolean;
|
|
162
|
+
}): Promise<CheckoutCreditAccount | CheckoutCreditRefusal>;
|
|
163
|
+
/** Reserves up to `maxCents` for one online checkout attempt. */
|
|
164
|
+
hold(input: CheckoutCreditHoldRequest): Promise<{
|
|
165
|
+
ok: true;
|
|
166
|
+
cents: number;
|
|
167
|
+
} | CheckoutCreditRefusal>;
|
|
168
|
+
/** Lets a hold go: an abandoned or refused checkout. Never throws. */
|
|
169
|
+
release(input: {
|
|
170
|
+
hostId: string;
|
|
171
|
+
reference: string;
|
|
172
|
+
holdKey: string;
|
|
173
|
+
}): Promise<void>;
|
|
174
|
+
/**
|
|
175
|
+
* Reads the account inside the seller's transaction. `null` when it no
|
|
176
|
+
* longer exists. `holdKey` is this sale's own online hold, when it has one.
|
|
177
|
+
*/
|
|
178
|
+
stage(input: {
|
|
179
|
+
transaction: CheckoutCreditTransaction;
|
|
180
|
+
hostId: string;
|
|
181
|
+
reference: string;
|
|
182
|
+
orderId: string;
|
|
183
|
+
nowMs: number;
|
|
184
|
+
holdKey?: string;
|
|
185
|
+
}): Promise<CheckoutCreditStage | null>;
|
|
186
|
+
/**
|
|
187
|
+
* Gives back up to `cents` of what the sale took from the account, for a
|
|
188
|
+
* refund. Idempotent per `key`; returns the cents given back (0 for a key
|
|
189
|
+
* already restored, or an account that is gone).
|
|
190
|
+
*/
|
|
191
|
+
restore(input: {
|
|
192
|
+
hostId: string;
|
|
193
|
+
reference: string;
|
|
194
|
+
orderId: string;
|
|
195
|
+
cents: number;
|
|
196
|
+
key: string;
|
|
197
|
+
}): Promise<number>;
|
|
198
|
+
/** Staff search at the register by email or code. Optional. */
|
|
199
|
+
lookup?(input: {
|
|
200
|
+
hostId: string;
|
|
201
|
+
query: string;
|
|
202
|
+
}): Promise<CheckoutCreditAccount[]>;
|
|
203
|
+
}
|
|
204
|
+
/** A provider as the seller resolves it. */
|
|
205
|
+
export interface ResolvedCheckoutCreditProvider {
|
|
206
|
+
/** `{pluginId}.{key}`: what a sale records. */
|
|
207
|
+
providerId: string;
|
|
208
|
+
pluginId: string;
|
|
209
|
+
provider: PluginCheckoutCreditProvider;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* What a sale records of one redemption, on the sale and in the events the
|
|
213
|
+
* seller raises about it, so the provider learns what was taken from the
|
|
214
|
+
* seller's own facts. `appliedAs` says how the seller counted it: online it
|
|
215
|
+
* comes off the goods like a discount, at the register it is one of the
|
|
216
|
+
* sale's payments.
|
|
217
|
+
*/
|
|
218
|
+
export interface PluginCheckoutCreditSold {
|
|
219
|
+
providerId: string;
|
|
220
|
+
pluginId: string;
|
|
221
|
+
key: string;
|
|
222
|
+
reference: string;
|
|
223
|
+
label: string;
|
|
224
|
+
last4: string;
|
|
225
|
+
amountCents: number;
|
|
226
|
+
appliedAs: 'discount' | 'tender';
|
|
227
|
+
}
|
|
228
|
+
/** The shape of a provider's `reference`: short, and safe in a document path segment. */
|
|
229
|
+
export declare const CHECKOUT_CREDIT_REFERENCE: RegExp;
|
|
230
|
+
/** Joins the providers. A plugin re-registering a key replaces its own. */
|
|
231
|
+
export declare function registerPluginCheckoutCredit(provider: PluginCheckoutCreditProvider, options?: {
|
|
232
|
+
pluginId?: string;
|
|
233
|
+
}): void;
|
|
234
|
+
/** Whether any plugin registered a provider: a seller with none skips the field. */
|
|
235
|
+
export declare function hasPluginCheckoutCredits(): boolean;
|
|
236
|
+
/**
|
|
237
|
+
* A code as typed — any case, spaces and dashes — in the one form providers
|
|
238
|
+
* see: upper case, letters, digits and single dashes, at most 40 characters.
|
|
239
|
+
* Empty when nothing usable was typed.
|
|
240
|
+
*/
|
|
241
|
+
export declare function normalizeCheckoutCreditCode(value: unknown): string;
|
|
242
|
+
/** The provider that issues a code, or `null`. The first to recognize it answers. */
|
|
243
|
+
export declare function checkoutCreditProviderForCode(code: string): ResolvedCheckoutCreditProvider | null;
|
|
244
|
+
/** One provider by the id a sale recorded, or `null` when it is gone. */
|
|
245
|
+
export declare function checkoutCreditProvider(providerId: string): ResolvedCheckoutCreditProvider | null;
|
|
246
|
+
/**
|
|
247
|
+
* The providers a site offers on a channel, for the cart's field and the
|
|
248
|
+
* register's tender. Never throws: a provider that fails is not offered.
|
|
249
|
+
*/
|
|
250
|
+
export declare function offeredCheckoutCredits(input: {
|
|
251
|
+
hostId: string;
|
|
252
|
+
channel: CheckoutCreditChannel;
|
|
253
|
+
}): Promise<Array<{
|
|
254
|
+
providerId: string;
|
|
255
|
+
label: string;
|
|
256
|
+
lookup: boolean;
|
|
257
|
+
}>>;
|
|
258
|
+
/** An account answer held to the contract, or a refusal in its place. */
|
|
259
|
+
export declare function normalizeCheckoutCreditAccount(answer: CheckoutCreditAccount | CheckoutCreditRefusal | null | undefined): CheckoutCreditAccount | CheckoutCreditRefusal;
|
|
260
|
+
/** Cents a provider says it held or took, as whole non-negative cents no larger than `ceiling`. */
|
|
261
|
+
export declare function boundedCreditCents(value: unknown, ceiling: number): number;
|
|
262
|
+
/** The metadata key an online redemption rides under on the payment processor. */
|
|
263
|
+
export declare const CHECKOUT_CREDIT_METADATA_KEY = "credit0";
|
|
264
|
+
/** What an online checkout carries to its webhook: the hold to settle. */
|
|
265
|
+
export interface CheckoutCreditHeld {
|
|
266
|
+
providerId: string;
|
|
267
|
+
reference: string;
|
|
268
|
+
holdKey: string;
|
|
269
|
+
amountCents: number;
|
|
270
|
+
label: string;
|
|
271
|
+
last4: string;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* The hold an online checkout placed, packed for a payment processor's
|
|
275
|
+
* metadata: one key holding `[providerId, reference, holdKey, cents, label, last4]`
|
|
276
|
+
* as JSON, inside a 500-character value. Never the code itself: metadata is
|
|
277
|
+
* readable on the merchant's dashboard, and a code is a bearer secret.
|
|
278
|
+
*/
|
|
279
|
+
export declare function encodeCheckoutCreditMetadata(held: CheckoutCreditHeld): Record<string, string>;
|
|
280
|
+
/** Reads {@link encodeCheckoutCreditMetadata} back. Anything unreadable is `null`, never guessed. */
|
|
281
|
+
export declare function decodeCheckoutCreditMetadata(metadata: Record<string, unknown> | null | undefined): CheckoutCreditHeld | null;
|
|
282
|
+
/** The plugin id and key of a provider id. */
|
|
283
|
+
export declare function splitCheckoutCreditProviderId(providerId: string): {
|
|
284
|
+
pluginId: string;
|
|
285
|
+
key: string;
|
|
286
|
+
} | null;
|
|
287
|
+
/** A recorded redemption, as a seller stores it and its events carry it. */
|
|
288
|
+
export declare function checkoutCreditSold(input: Omit<PluginCheckoutCreditSold, 'pluginId' | 'key'>): PluginCheckoutCreditSold | null;
|