@final-commerce/command-frame 0.1.43 → 0.1.46
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/README.md +34 -0
- package/dist/CommonTypes.d.ts +97 -35
- package/dist/CommonTypes.js +44 -0
- package/dist/actions/add-non-revenue-item/action.d.ts +2 -0
- package/dist/actions/add-non-revenue-item/action.js +4 -0
- package/dist/actions/add-non-revenue-item/mock.d.ts +2 -0
- package/dist/actions/add-non-revenue-item/mock.js +41 -0
- package/dist/actions/add-non-revenue-item/types.d.ts +36 -0
- package/dist/actions/add-non-revenue-item/types.js +1 -0
- package/dist/actions/add-product/mock.js +7 -5
- package/dist/actions/add-product/types.d.ts +2 -2
- package/dist/actions/edit-product/mock.js +3 -1
- package/dist/actions/extension-payment/action.d.ts +5 -0
- package/dist/actions/extension-payment/action.js +7 -0
- package/dist/actions/extension-payment/mock.d.ts +2 -0
- package/dist/actions/extension-payment/mock.js +11 -0
- package/dist/actions/extension-payment/types.d.ts +19 -0
- package/dist/actions/extension-payment/types.js +1 -0
- package/dist/actions/extension-refund/constants.d.ts +2 -0
- package/dist/actions/extension-refund/constants.js +2 -0
- package/dist/actions/extension-refund/extension-refund-listener.d.ts +11 -0
- package/dist/actions/extension-refund/extension-refund-listener.js +60 -0
- package/dist/actions/extension-refund/types.d.ts +32 -0
- package/dist/actions/extension-refund/types.js +5 -0
- package/dist/actions/get-current-company-custom-extensions/mock.js +2 -2
- package/dist/actions/get-custom-extensions/mock.js +2 -2
- package/dist/actions/redeem-payment/action.d.ts +5 -0
- package/dist/actions/redeem-payment/action.js +7 -0
- package/dist/actions/redeem-payment/mock.d.ts +2 -0
- package/dist/actions/redeem-payment/mock.js +4 -0
- package/dist/actions/redeem-payment/types.d.ts +12 -0
- package/dist/actions/redeem-payment/types.js +1 -0
- package/dist/actions/resume-parked-order/mock.js +3 -3
- package/dist/common-types/custom-extensions.d.ts +1 -1
- package/dist/demo/database.d.ts +1 -1
- package/dist/demo/database.js +68 -60
- package/dist/demo/mocks/custom-tables.js +3 -3
- package/dist/index.d.ts +9 -0
- package/dist/index.js +8 -0
- package/dist/projects/render/mocks.js +6 -0
- package/dist/projects/render/types.d.ts +4 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ The library provides three main capabilities:
|
|
|
13
13
|
| **Commands** | Call host functions from the iframe (e.g. get products, open cash drawer) | Request/response per call |
|
|
14
14
|
| **Pub/Sub** | Subscribe to real-time events from the host (e.g. cart changes, payments) | Page-scoped (while iframe is mounted) |
|
|
15
15
|
| **Hooks** | Register business-logic callbacks that persist across all pages | Session-scoped (survives page navigation) |
|
|
16
|
+
| **Host → iframe refunds** | Render asks the extension to reverse redeem / gift-card payments before completing a POS refund | Parent `postMessage` + `requestId` (see below) |
|
|
16
17
|
|
|
17
18
|
## Installation
|
|
18
19
|
|
|
@@ -109,6 +110,39 @@ hooks.register('cart', async (event, hostCommands) => {
|
|
|
109
110
|
hooks.unregister('my-extension:cart-log');
|
|
110
111
|
```
|
|
111
112
|
|
|
113
|
+
## Host-initiated extension refunds (redeem / gift card)
|
|
114
|
+
|
|
115
|
+
**Extensions that accept redeem / extension payments must implement a refund listener.** When staff refund an order paid with `paymentType: "redeem"`, Render (host) `postMessage`s into your iframe **before** it records the refund locally. If your app does not handle this message, redeem refunds will time out or fail.
|
|
116
|
+
|
|
117
|
+
### What you should do
|
|
118
|
+
|
|
119
|
+
1. **Recommended:** call **`installExtensionRefundListener`** once when your extension boots (e.g. next to your `RenderClient` setup). Pass an `async` handler that calls your provider (gift card API, wallet, etc.) and returns an **`ExtensionRefundResponse`** (`success`, optional `error`, optional `extensionTransactionId` for receipts / support).
|
|
120
|
+
2. The helper validates `event.source === window.parent`, parses params, and replies with the same **`PostMessageResponse`** envelope as the rest of Command Frame (`requestId`, `success`, `data` / `error`).
|
|
121
|
+
3. **Alternative:** implement a `window` `message` listener yourself using the same contract (action name: **`extensionRefundRequest`**, or import **`EXTENSION_REFUND_REQUEST_ACTION`** from this package).
|
|
122
|
+
|
|
123
|
+
Exported APIs: `installExtensionRefundListener`, `EXTENSION_REFUND_REQUEST_ACTION`, types **`ExtensionRefundParams`** / **`ExtensionRefundResponse`**.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import {
|
|
127
|
+
installExtensionRefundListener,
|
|
128
|
+
type ExtensionRefundParams,
|
|
129
|
+
type ExtensionRefundResponse
|
|
130
|
+
} from '@final-commerce/command-frame';
|
|
131
|
+
|
|
132
|
+
const unsubscribe = installExtensionRefundListener(async (params: ExtensionRefundParams): Promise<ExtensionRefundResponse> => {
|
|
133
|
+
// params.paymentType === "redeem", params.amount in major currency units, params.saleId, params.processor, etc.
|
|
134
|
+
const ok = await myGiftCardProvider.refund(params);
|
|
135
|
+
return ok
|
|
136
|
+
? { success: true, extensionTransactionId: ok.providerRefundId }
|
|
137
|
+
: { success: false, error: 'Refund declined' };
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// on teardown (optional)
|
|
141
|
+
// unsubscribe();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Full protocol, edge cases, and manual handling:** **[Extension refund documentation](./src/actions/extension-refund/README.md)**.
|
|
145
|
+
|
|
112
146
|
## Development & Testing
|
|
113
147
|
|
|
114
148
|
### Demo Mode / Mocking
|
package/dist/CommonTypes.d.ts
CHANGED
|
@@ -1,4 +1,47 @@
|
|
|
1
1
|
export * from "./common-types";
|
|
2
|
+
export declare enum CurrencyCode {
|
|
3
|
+
USD = "USD",
|
|
4
|
+
EUR = "EUR",
|
|
5
|
+
GBP = "GBP",
|
|
6
|
+
CAD = "CAD",
|
|
7
|
+
AUD = "AUD",
|
|
8
|
+
NZD = "NZD",
|
|
9
|
+
CHF = "CHF",
|
|
10
|
+
CNY = "CNY",
|
|
11
|
+
INR = "INR",
|
|
12
|
+
MXN = "MXN",
|
|
13
|
+
BRL = "BRL",
|
|
14
|
+
ZAR = "ZAR",
|
|
15
|
+
SGD = "SGD",
|
|
16
|
+
HKD = "HKD",
|
|
17
|
+
SEK = "SEK",
|
|
18
|
+
NOK = "NOK",
|
|
19
|
+
DKK = "DKK",
|
|
20
|
+
PLN = "PLN",
|
|
21
|
+
THB = "THB",
|
|
22
|
+
MYR = "MYR",
|
|
23
|
+
PHP = "PHP",
|
|
24
|
+
IDR = "IDR",
|
|
25
|
+
AED = "AED",
|
|
26
|
+
SAR = "SAR",
|
|
27
|
+
ILS = "ILS",
|
|
28
|
+
TRY = "TRY",
|
|
29
|
+
RUB = "RUB",
|
|
30
|
+
JPY = "JPY",
|
|
31
|
+
KRW = "KRW",
|
|
32
|
+
VND = "VND",
|
|
33
|
+
CLP = "CLP",
|
|
34
|
+
ISK = "ISK",
|
|
35
|
+
HUF = "HUF",
|
|
36
|
+
TWD = "TWD",
|
|
37
|
+
KWD = "KWD",
|
|
38
|
+
BHD = "BHD",
|
|
39
|
+
OMR = "OMR",
|
|
40
|
+
JOD = "JOD",
|
|
41
|
+
TND = "TND",
|
|
42
|
+
LYD = "LYD",
|
|
43
|
+
IQD = "IQD"
|
|
44
|
+
}
|
|
2
45
|
export declare enum CFProductType {
|
|
3
46
|
SIMPLE = "simple",
|
|
4
47
|
VARIABLE = "variable"
|
|
@@ -51,7 +94,7 @@ export interface CFTax {
|
|
|
51
94
|
id: string;
|
|
52
95
|
name: string;
|
|
53
96
|
percentage: number;
|
|
54
|
-
amount:
|
|
97
|
+
amount: number;
|
|
55
98
|
taxTableName: string;
|
|
56
99
|
taxTableId: string;
|
|
57
100
|
}
|
|
@@ -82,11 +125,11 @@ export interface CFCategory {
|
|
|
82
125
|
}
|
|
83
126
|
export interface CFProductVariant {
|
|
84
127
|
sku: string;
|
|
85
|
-
price:
|
|
86
|
-
salePrice:
|
|
128
|
+
price: number;
|
|
129
|
+
salePrice: number;
|
|
87
130
|
isOnSale: boolean;
|
|
88
131
|
barcode?: string;
|
|
89
|
-
costPrice?:
|
|
132
|
+
costPrice?: number;
|
|
90
133
|
manageStock: boolean;
|
|
91
134
|
externalId?: string;
|
|
92
135
|
inventory?: CFInventory[];
|
|
@@ -121,8 +164,10 @@ export interface CFProduct {
|
|
|
121
164
|
sku?: string;
|
|
122
165
|
productType: CFProductType;
|
|
123
166
|
variants: CFProductVariant[];
|
|
124
|
-
|
|
125
|
-
|
|
167
|
+
currency: CurrencyCode;
|
|
168
|
+
minorUnits: number;
|
|
169
|
+
minPrice?: number;
|
|
170
|
+
maxPrice?: number;
|
|
126
171
|
status?: string;
|
|
127
172
|
isDeleted?: boolean;
|
|
128
173
|
}
|
|
@@ -172,40 +217,42 @@ export interface CFActiveCustomer extends CFCustomer {
|
|
|
172
217
|
id?: string;
|
|
173
218
|
}
|
|
174
219
|
export interface CFTip {
|
|
175
|
-
amount:
|
|
220
|
+
amount: number;
|
|
176
221
|
percentage: number;
|
|
177
222
|
}
|
|
178
223
|
export interface CFSummary {
|
|
179
|
-
discountTotal:
|
|
180
|
-
shippingTotal?:
|
|
181
|
-
total:
|
|
182
|
-
totalTaxes:
|
|
183
|
-
subTotal:
|
|
224
|
+
discountTotal: number;
|
|
225
|
+
shippingTotal?: number | null;
|
|
226
|
+
total: number;
|
|
227
|
+
totalTaxes: number;
|
|
228
|
+
subTotal: number;
|
|
184
229
|
taxes: CFTax[];
|
|
185
230
|
tip?: CFTip | null;
|
|
186
231
|
isTaxInclusive: boolean;
|
|
232
|
+
/** Portion of order total that is non-revenue (e.g. gift card load), same string money format as `total` */
|
|
233
|
+
nonRevenueTotal?: string;
|
|
187
234
|
}
|
|
188
235
|
export interface CFCartDiscountItem {
|
|
189
236
|
label: string;
|
|
190
|
-
amount:
|
|
237
|
+
amount: number;
|
|
191
238
|
percentage: number;
|
|
192
239
|
}
|
|
193
240
|
export interface CFCartFeeItem {
|
|
194
241
|
id: string;
|
|
195
242
|
label: string;
|
|
196
|
-
amount:
|
|
243
|
+
amount: number;
|
|
197
244
|
percentage: number;
|
|
198
245
|
taxTableId?: string;
|
|
199
|
-
tax?:
|
|
246
|
+
tax?: number;
|
|
200
247
|
taxName: string;
|
|
201
248
|
}
|
|
202
249
|
export interface CFTipPayment {
|
|
203
|
-
amount:
|
|
250
|
+
amount: number;
|
|
204
251
|
tipTo: string;
|
|
205
252
|
percentage: number;
|
|
206
253
|
}
|
|
207
254
|
export interface CFRefundedTipPayment {
|
|
208
|
-
amount:
|
|
255
|
+
amount: number;
|
|
209
256
|
percentage: number;
|
|
210
257
|
transactionId: string;
|
|
211
258
|
tipTo: string;
|
|
@@ -213,11 +260,11 @@ export interface CFRefundedTipPayment {
|
|
|
213
260
|
export interface CFPaymentMethod {
|
|
214
261
|
transactionId: string;
|
|
215
262
|
paymentType: string;
|
|
216
|
-
amount:
|
|
263
|
+
amount: number;
|
|
217
264
|
timestamp: string;
|
|
218
265
|
processor: string;
|
|
219
266
|
saleId?: string;
|
|
220
|
-
change?:
|
|
267
|
+
change?: number | null;
|
|
221
268
|
tip?: CFTipPayment | null;
|
|
222
269
|
cashRounding?: number;
|
|
223
270
|
emv?: string | null;
|
|
@@ -233,13 +280,13 @@ export interface CFPosDataItem {
|
|
|
233
280
|
}
|
|
234
281
|
export interface CFDiscountDetail {
|
|
235
282
|
percentage: number;
|
|
236
|
-
amount:
|
|
283
|
+
amount: number;
|
|
237
284
|
label?: string;
|
|
238
285
|
}
|
|
239
286
|
export interface CFFeeDetail {
|
|
240
287
|
percentage: number;
|
|
241
|
-
amount:
|
|
242
|
-
tax:
|
|
288
|
+
amount: number;
|
|
289
|
+
tax: number;
|
|
243
290
|
taxTableId: string;
|
|
244
291
|
label?: string;
|
|
245
292
|
}
|
|
@@ -258,12 +305,12 @@ export interface CFLineItem {
|
|
|
258
305
|
internalId?: string;
|
|
259
306
|
name: string;
|
|
260
307
|
quantity: number;
|
|
261
|
-
price:
|
|
308
|
+
price: number;
|
|
262
309
|
taxes: CFTax[];
|
|
263
310
|
discount: CFDiscountLineItem;
|
|
264
311
|
fee: CFFeeLineItem;
|
|
265
|
-
totalTax:
|
|
266
|
-
total:
|
|
312
|
+
totalTax: number;
|
|
313
|
+
total: number;
|
|
267
314
|
metadata: CFMetadataItem[];
|
|
268
315
|
image: string;
|
|
269
316
|
sku: string;
|
|
@@ -277,11 +324,11 @@ export interface CFLineItem {
|
|
|
277
324
|
export interface CFCustomSale {
|
|
278
325
|
customSaleId: string;
|
|
279
326
|
name: string;
|
|
280
|
-
price:
|
|
327
|
+
price: number;
|
|
281
328
|
quantity: number;
|
|
282
329
|
applyTaxes: boolean;
|
|
283
|
-
total:
|
|
284
|
-
totalTax:
|
|
330
|
+
total: number;
|
|
331
|
+
totalTax: number;
|
|
285
332
|
taxes: CFTax[];
|
|
286
333
|
discount: {
|
|
287
334
|
cartDiscount: CFDiscountDetail;
|
|
@@ -298,11 +345,11 @@ export interface CFRefundedLineItem {
|
|
|
298
345
|
internalId?: string;
|
|
299
346
|
name: string;
|
|
300
347
|
quantity: number;
|
|
301
|
-
price:
|
|
348
|
+
price: number;
|
|
302
349
|
taxes: CFTax[];
|
|
303
350
|
discount: CFDiscountLineItem;
|
|
304
|
-
totalTax:
|
|
305
|
-
total:
|
|
351
|
+
totalTax: number;
|
|
352
|
+
total: number;
|
|
306
353
|
image: string;
|
|
307
354
|
sku: string;
|
|
308
355
|
note?: string;
|
|
@@ -321,12 +368,15 @@ export interface CFRefundItem {
|
|
|
321
368
|
timestamp: string | undefined;
|
|
322
369
|
summary?: CFSummary;
|
|
323
370
|
refundPayment: CFPaymentMethod[];
|
|
324
|
-
balance?:
|
|
371
|
+
balance?: number;
|
|
325
372
|
receiptId?: string;
|
|
326
|
-
currency
|
|
373
|
+
currency: CurrencyCode;
|
|
374
|
+
minorUnits: number;
|
|
327
375
|
}
|
|
328
376
|
export interface CFOrder {
|
|
329
377
|
_id: string;
|
|
378
|
+
currency: CurrencyCode;
|
|
379
|
+
minorUnits: number;
|
|
330
380
|
receiptId?: string;
|
|
331
381
|
companyId: string;
|
|
332
382
|
externalId: string | null;
|
|
@@ -348,8 +398,10 @@ export interface CFOrder {
|
|
|
348
398
|
shipping: CFAddress | null;
|
|
349
399
|
lineItems: CFLineItem[];
|
|
350
400
|
customSales: CFCustomSale[];
|
|
401
|
+
/** Gift card / liability purchase lines (not product revenue) */
|
|
402
|
+
nonRevenueItems?: CFNonRevenueItem[];
|
|
351
403
|
refund?: CFRefundItem[];
|
|
352
|
-
balance:
|
|
404
|
+
balance: number;
|
|
353
405
|
signature?: string | null;
|
|
354
406
|
}
|
|
355
407
|
export interface CFActiveUserRole {
|
|
@@ -420,7 +472,6 @@ export interface CFActiveOrder extends CFOrder {
|
|
|
420
472
|
outlet?: CFActiveOutlet;
|
|
421
473
|
isDeleted?: boolean;
|
|
422
474
|
newOrder?: boolean;
|
|
423
|
-
currency?: string;
|
|
424
475
|
station?: CFActiveStation;
|
|
425
476
|
}
|
|
426
477
|
export interface CFActiveCustomSales {
|
|
@@ -433,6 +484,15 @@ export interface CFActiveCustomSales {
|
|
|
433
484
|
discount?: any;
|
|
434
485
|
fee?: any;
|
|
435
486
|
}
|
|
487
|
+
/** Non-revenue cart line (e.g. gift card load) — aligned with Render NonRevenueItem */
|
|
488
|
+
export interface CFNonRevenueItem {
|
|
489
|
+
id: string;
|
|
490
|
+
amount: number | string;
|
|
491
|
+
label?: string;
|
|
492
|
+
metadata?: Record<string, unknown>;
|
|
493
|
+
applyTaxes?: boolean;
|
|
494
|
+
taxTableId?: string;
|
|
495
|
+
}
|
|
436
496
|
export interface CFActiveCart extends CFActiveEntity {
|
|
437
497
|
tax?: number;
|
|
438
498
|
total: number;
|
|
@@ -441,6 +501,8 @@ export interface CFActiveCart extends CFActiveEntity {
|
|
|
441
501
|
customFee?: CFCustomFee[];
|
|
442
502
|
products: CFActiveProduct[];
|
|
443
503
|
customSales?: CFActiveCustomSales[];
|
|
504
|
+
/** Gift card / liability lines — included in cart total */
|
|
505
|
+
nonRevenueItems?: CFNonRevenueItem[];
|
|
444
506
|
remainingBalance?: number;
|
|
445
507
|
amountToBeCharged: number;
|
|
446
508
|
customer?: Partial<CFCustomer | null> | null;
|
package/dist/CommonTypes.js
CHANGED
|
@@ -1,4 +1,48 @@
|
|
|
1
1
|
export * from "./common-types";
|
|
2
|
+
export var CurrencyCode;
|
|
3
|
+
(function (CurrencyCode) {
|
|
4
|
+
CurrencyCode["USD"] = "USD";
|
|
5
|
+
CurrencyCode["EUR"] = "EUR";
|
|
6
|
+
CurrencyCode["GBP"] = "GBP";
|
|
7
|
+
CurrencyCode["CAD"] = "CAD";
|
|
8
|
+
CurrencyCode["AUD"] = "AUD";
|
|
9
|
+
CurrencyCode["NZD"] = "NZD";
|
|
10
|
+
CurrencyCode["CHF"] = "CHF";
|
|
11
|
+
CurrencyCode["CNY"] = "CNY";
|
|
12
|
+
CurrencyCode["INR"] = "INR";
|
|
13
|
+
CurrencyCode["MXN"] = "MXN";
|
|
14
|
+
CurrencyCode["BRL"] = "BRL";
|
|
15
|
+
CurrencyCode["ZAR"] = "ZAR";
|
|
16
|
+
CurrencyCode["SGD"] = "SGD";
|
|
17
|
+
CurrencyCode["HKD"] = "HKD";
|
|
18
|
+
CurrencyCode["SEK"] = "SEK";
|
|
19
|
+
CurrencyCode["NOK"] = "NOK";
|
|
20
|
+
CurrencyCode["DKK"] = "DKK";
|
|
21
|
+
CurrencyCode["PLN"] = "PLN";
|
|
22
|
+
CurrencyCode["THB"] = "THB";
|
|
23
|
+
CurrencyCode["MYR"] = "MYR";
|
|
24
|
+
CurrencyCode["PHP"] = "PHP";
|
|
25
|
+
CurrencyCode["IDR"] = "IDR";
|
|
26
|
+
CurrencyCode["AED"] = "AED";
|
|
27
|
+
CurrencyCode["SAR"] = "SAR";
|
|
28
|
+
CurrencyCode["ILS"] = "ILS";
|
|
29
|
+
CurrencyCode["TRY"] = "TRY";
|
|
30
|
+
CurrencyCode["RUB"] = "RUB";
|
|
31
|
+
CurrencyCode["JPY"] = "JPY";
|
|
32
|
+
CurrencyCode["KRW"] = "KRW";
|
|
33
|
+
CurrencyCode["VND"] = "VND";
|
|
34
|
+
CurrencyCode["CLP"] = "CLP";
|
|
35
|
+
CurrencyCode["ISK"] = "ISK";
|
|
36
|
+
CurrencyCode["HUF"] = "HUF";
|
|
37
|
+
CurrencyCode["TWD"] = "TWD";
|
|
38
|
+
CurrencyCode["KWD"] = "KWD";
|
|
39
|
+
CurrencyCode["BHD"] = "BHD";
|
|
40
|
+
CurrencyCode["OMR"] = "OMR";
|
|
41
|
+
CurrencyCode["JOD"] = "JOD";
|
|
42
|
+
CurrencyCode["TND"] = "TND";
|
|
43
|
+
CurrencyCode["LYD"] = "LYD";
|
|
44
|
+
CurrencyCode["IQD"] = "IQD";
|
|
45
|
+
})(CurrencyCode || (CurrencyCode = {}));
|
|
2
46
|
// Enums
|
|
3
47
|
export var CFProductType;
|
|
4
48
|
(function (CFProductType) {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MOCK_CART } from "../../demo/database";
|
|
2
|
+
export const mockAddNonRevenueItem = async (params) => {
|
|
3
|
+
console.log("[Mock] addNonRevenueItem called", params);
|
|
4
|
+
const amount = Number(params.amount);
|
|
5
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
6
|
+
throw new Error("amount must be a positive number");
|
|
7
|
+
}
|
|
8
|
+
const lineId = `nr-mock-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
9
|
+
const refId = params.id;
|
|
10
|
+
const metadata = {
|
|
11
|
+
...(params.metadata ?? {}),
|
|
12
|
+
refId
|
|
13
|
+
};
|
|
14
|
+
const row = {
|
|
15
|
+
id: lineId,
|
|
16
|
+
amount,
|
|
17
|
+
applyTaxes: params.applyTaxes === true,
|
|
18
|
+
...(params.taxTableId ? { taxTableId: params.taxTableId } : {}),
|
|
19
|
+
...(params.label !== undefined ? { label: params.label } : {}),
|
|
20
|
+
metadata
|
|
21
|
+
};
|
|
22
|
+
if (!MOCK_CART.nonRevenueItems) {
|
|
23
|
+
MOCK_CART.nonRevenueItems = [];
|
|
24
|
+
}
|
|
25
|
+
MOCK_CART.nonRevenueItems.push(row);
|
|
26
|
+
MOCK_CART.total += amount;
|
|
27
|
+
MOCK_CART.amountToBeCharged = MOCK_CART.total;
|
|
28
|
+
MOCK_CART.remainingBalance = MOCK_CART.total;
|
|
29
|
+
MOCK_CART.subtotal = (MOCK_CART.subtotal ?? 0) + amount;
|
|
30
|
+
return {
|
|
31
|
+
success: true,
|
|
32
|
+
id: lineId,
|
|
33
|
+
refId,
|
|
34
|
+
amount,
|
|
35
|
+
label: params.label,
|
|
36
|
+
metadata,
|
|
37
|
+
...(params.applyTaxes === true ? { applyTaxes: true } : {}),
|
|
38
|
+
...(params.taxTableId ? { taxTableId: params.taxTableId } : {}),
|
|
39
|
+
timestamp: new Date().toISOString()
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add a non-revenue line to the host cart (e.g. gift card liability / load).
|
|
3
|
+
* Extensions send id, amount, and optional label/metadata; host sums into cart total.
|
|
4
|
+
*/
|
|
5
|
+
export interface AddNonRevenueItemParams {
|
|
6
|
+
/**
|
|
7
|
+
* Reference id from the extension (e.g. product/sku key). Stored in cart line metadata as refId.
|
|
8
|
+
* The cart uses a separate unique line id per add so multiple lines can share the same refId.
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
/** Amount in major currency units (e.g. dollars), same as cart totals */
|
|
12
|
+
amount: number;
|
|
13
|
+
/** Short label for receipts/UI */
|
|
14
|
+
label?: string;
|
|
15
|
+
/** Extra fields (customTableId, cardCode, etc.) */
|
|
16
|
+
metadata?: Record<string, unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* When true, line may be taxed (requires taxTableId when taxing is implemented). Default false — e.g. gift card load is typically not taxed.
|
|
19
|
+
*/
|
|
20
|
+
applyTaxes?: boolean;
|
|
21
|
+
taxTableId?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AddNonRevenueItemResponse {
|
|
24
|
+
success: true;
|
|
25
|
+
/** Unique cart line id (use for remove / correlation) */
|
|
26
|
+
id: string;
|
|
27
|
+
/** Same as request `id` — extension reference */
|
|
28
|
+
refId: string;
|
|
29
|
+
amount: number;
|
|
30
|
+
label?: string;
|
|
31
|
+
metadata?: Record<string, unknown>;
|
|
32
|
+
applyTaxes?: boolean;
|
|
33
|
+
taxTableId?: string;
|
|
34
|
+
timestamp: string;
|
|
35
|
+
}
|
|
36
|
+
export type AddNonRevenueItem = (params: AddNonRevenueItemParams) => Promise<AddNonRevenueItemResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { CFProductType } from "../../CommonTypes";
|
|
1
|
+
import { CFProductType, CurrencyCode } from "../../CommonTypes";
|
|
2
2
|
export const mockAddProduct = async (params) => {
|
|
3
3
|
console.log("[Mock] addProduct called", params);
|
|
4
4
|
const hasVariants = params.variants && params.variants.length > 0;
|
|
5
5
|
return {
|
|
6
6
|
product: {
|
|
7
7
|
_id: "mock_product_" + Date.now(),
|
|
8
|
+
currency: CurrencyCode.USD,
|
|
9
|
+
minorUnits: 2,
|
|
8
10
|
name: params.name,
|
|
9
11
|
description: params.description,
|
|
10
12
|
categories: params.categories || [],
|
|
@@ -14,15 +16,15 @@ export const mockAddProduct = async (params) => {
|
|
|
14
16
|
sku: params.sku,
|
|
15
17
|
productType: hasVariants ? CFProductType.VARIABLE : CFProductType.SIMPLE,
|
|
16
18
|
attributes: [],
|
|
17
|
-
minPrice: params.price ||
|
|
18
|
-
maxPrice: params.price ||
|
|
19
|
+
minPrice: params.price || 0,
|
|
20
|
+
maxPrice: params.price || 0,
|
|
19
21
|
variants: hasVariants
|
|
20
22
|
? params.variants.map((v, i) => ({ ...v, _id: `mock_variant_${Date.now()}_${i}` }))
|
|
21
23
|
: [{
|
|
22
24
|
_id: `mock_variant_${Date.now()}_0`,
|
|
23
25
|
sku: params.sku || "",
|
|
24
|
-
price: params.price ||
|
|
25
|
-
salePrice:
|
|
26
|
+
price: params.price || 0,
|
|
27
|
+
salePrice: 0,
|
|
26
28
|
isOnSale: false,
|
|
27
29
|
manageStock: params.manageStock || false,
|
|
28
30
|
attributes: [],
|
|
@@ -7,9 +7,9 @@ export interface AddProductParams {
|
|
|
7
7
|
images?: string[];
|
|
8
8
|
status?: 'active' | 'inactive';
|
|
9
9
|
/** For simple products: set price directly */
|
|
10
|
-
price?:
|
|
10
|
+
price?: number;
|
|
11
11
|
sku?: string;
|
|
12
|
-
costPrice?:
|
|
12
|
+
costPrice?: number;
|
|
13
13
|
manageStock?: boolean;
|
|
14
14
|
/** For variable products: provide variants array */
|
|
15
15
|
variants?: Omit<CFProductVariant, '_id'>[];
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { CFProductType } from "../../CommonTypes";
|
|
1
|
+
import { CFProductType, CurrencyCode } from "../../CommonTypes";
|
|
2
2
|
export const mockEditProduct = async (params) => {
|
|
3
3
|
console.log("[Mock] editProduct called", params);
|
|
4
4
|
return {
|
|
5
5
|
product: {
|
|
6
6
|
_id: params.productId,
|
|
7
|
+
currency: CurrencyCode.USD,
|
|
8
|
+
minorUnits: 2,
|
|
7
9
|
name: params.changes.name || "Updated Product",
|
|
8
10
|
description: params.changes.description,
|
|
9
11
|
categories: params.changes.categories || [],
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic extension payment — host handles wire action `extensionPayment` and routes by `paymentType`.
|
|
3
|
+
*/
|
|
4
|
+
import { commandFrameClient } from "../../client";
|
|
5
|
+
export const extensionPayment = async (params) => {
|
|
6
|
+
return await commandFrameClient.call("extensionPayment", params);
|
|
7
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MOCK_ORDERS } from "../../demo/database";
|
|
2
|
+
export const mockExtensionPayment = async (params) => {
|
|
3
|
+
const paymentType = params?.paymentType ?? "redeem";
|
|
4
|
+
return {
|
|
5
|
+
success: true,
|
|
6
|
+
amount: params?.amount ?? null,
|
|
7
|
+
paymentType,
|
|
8
|
+
order: MOCK_ORDERS[0],
|
|
9
|
+
timestamp: new Date().toISOString()
|
|
10
|
+
};
|
|
11
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
/** Params for extension-initiated payments; host routes by `paymentType`. */
|
|
3
|
+
export interface ExtensionPaymentParams {
|
|
4
|
+
paymentType: string;
|
|
5
|
+
processor?: string;
|
|
6
|
+
amount?: number;
|
|
7
|
+
label?: string;
|
|
8
|
+
referenceId?: string;
|
|
9
|
+
extensionId?: string;
|
|
10
|
+
metadata?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface ExtensionPaymentResponse {
|
|
13
|
+
success: boolean;
|
|
14
|
+
amount: number | null;
|
|
15
|
+
paymentType: string;
|
|
16
|
+
order: CFOrder | null;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
export type ExtensionPayment = (params?: ExtensionPaymentParams) => Promise<ExtensionPaymentResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExtensionRefundParams, ExtensionRefundResponse } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Install a message listener in the **extension iframe** so the host can request refunds.
|
|
4
|
+
* Validates `event.source === window.parent` before handling.
|
|
5
|
+
*
|
|
6
|
+
* Replies with {@link PostMessageResponse}, using `event.origin` as the target origin when posting back to the parent.
|
|
7
|
+
*
|
|
8
|
+
* @param handler - Perform the extension-side refund (API call, etc.)
|
|
9
|
+
* @returns Unsubscribe function
|
|
10
|
+
*/
|
|
11
|
+
export declare function installExtensionRefundListener(handler: (params: ExtensionRefundParams) => Promise<ExtensionRefundResponse>): () => void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EXTENSION_REFUND_REQUEST_ACTION } from "./constants";
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Install a message listener in the **extension iframe** so the host can request refunds.
|
|
7
|
+
* Validates `event.source === window.parent` before handling.
|
|
8
|
+
*
|
|
9
|
+
* Replies with {@link PostMessageResponse}, using `event.origin` as the target origin when posting back to the parent.
|
|
10
|
+
*
|
|
11
|
+
* @param handler - Perform the extension-side refund (API call, etc.)
|
|
12
|
+
* @returns Unsubscribe function
|
|
13
|
+
*/
|
|
14
|
+
export function installExtensionRefundListener(handler) {
|
|
15
|
+
const onMessage = async (event) => {
|
|
16
|
+
const raw = event.data;
|
|
17
|
+
if (!isRecord(raw) || raw.action !== EXTENSION_REFUND_REQUEST_ACTION) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const requestId = raw.requestId;
|
|
21
|
+
if (typeof requestId !== "string") {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (event.source !== window.parent || !event.source) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const source = event.source;
|
|
28
|
+
const replyOrigin = typeof event.origin === "string" ? event.origin : "*";
|
|
29
|
+
const params = raw.params;
|
|
30
|
+
try {
|
|
31
|
+
if (!params || typeof params.paymentType !== "string" || typeof params.amount !== "number" || typeof params.saleId !== "string") {
|
|
32
|
+
const errPayload = {
|
|
33
|
+
requestId,
|
|
34
|
+
success: false,
|
|
35
|
+
error: "Invalid extension refund params"
|
|
36
|
+
};
|
|
37
|
+
source.postMessage(errPayload, replyOrigin);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const result = await handler(params);
|
|
41
|
+
const payload = {
|
|
42
|
+
requestId,
|
|
43
|
+
success: true,
|
|
44
|
+
data: result
|
|
45
|
+
};
|
|
46
|
+
source.postMessage(payload, replyOrigin);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
50
|
+
const errPayload = {
|
|
51
|
+
requestId,
|
|
52
|
+
success: false,
|
|
53
|
+
error: message
|
|
54
|
+
};
|
|
55
|
+
source.postMessage(errPayload, replyOrigin);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
window.addEventListener("message", onMessage);
|
|
59
|
+
return () => window.removeEventListener("message", onMessage);
|
|
60
|
+
}
|