@porulle/plugin-pos 0.8.0 → 0.10.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/hooks/checkout-pos.d.ts.map +1 -1
- package/dist/hooks/checkout-pos.js +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/routes/auth.d.ts +1 -1
- package/dist/routes/auth.d.ts.map +1 -1
- package/dist/routes/auth.js +10 -4
- package/dist/routes/returns.d.ts.map +1 -1
- package/dist/routes/returns.js +25 -4
- package/dist/schema.d.ts +109 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +11 -0
- package/dist/services/exchange-service.d.ts.map +1 -1
- package/dist/services/exchange-service.js +34 -11
- package/dist/services/pin-service.d.ts +11 -0
- package/dist/services/pin-service.d.ts.map +1 -1
- package/dist/services/pin-service.js +122 -24
- package/dist/services/transaction-service.d.ts.map +1 -1
- package/dist/services/transaction-service.js +5 -5
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/hooks/checkout-pos.ts +9 -2
- package/src/index.ts +3 -0
- package/src/routes/auth.ts +10 -5
- package/src/routes/returns.ts +47 -5
- package/src/schema.ts +13 -0
- package/src/services/exchange-service.ts +56 -11
- package/src/services/pin-service.ts +147 -25
- package/src/services/transaction-service.ts +5 -4
- package/src/types.ts +8 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout-pos.d.ts","sourceRoot":"","sources":["../../src/hooks/checkout-pos.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAaD;;GAEG;AACH,wBAAgB,oBAAoB;;uBAGP,OAAO,EAAE;EAWrC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE;;uBAG3B,OAAO,EAAE;
|
|
1
|
+
{"version":3,"file":"checkout-pos.d.ts","sourceRoot":"","sources":["../../src/hooks/checkout-pos.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAaD;;GAEG;AACH,wBAAgB,oBAAoB;;uBAGP,OAAO,EAAE;EAWrC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE;;uBAG3B,OAAO,EAAE;EA6CrC"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* checkout.beforePayment: Sets shippingTotal = 0 for POS transactions (no shipping).
|
|
5
5
|
* checkout.afterCreate: Updates POS transaction with orderId, increments shift counters.
|
|
6
6
|
*/
|
|
7
|
-
import { eq, sql } from "@porulle/core/drizzle";
|
|
7
|
+
import { eq, and, sql } from "@porulle/core/drizzle";
|
|
8
8
|
import { posTransactions, posShifts } from "../schema.js";
|
|
9
9
|
/**
|
|
10
10
|
* Before payment hook: zero out shipping for POS transactions.
|
|
@@ -55,6 +55,13 @@ export function buildPOSFinalizationHook(getDb) {
|
|
|
55
55
|
// Increment shift sales counters
|
|
56
56
|
const posShiftId = metadata?.posShiftId;
|
|
57
57
|
if (posShiftId) {
|
|
58
|
+
const txnRows = await db
|
|
59
|
+
.select({ organizationId: posTransactions.organizationId })
|
|
60
|
+
.from(posTransactions)
|
|
61
|
+
.where(eq(posTransactions.id, posTransactionId));
|
|
62
|
+
const orgId = txnRows[0]?.organizationId;
|
|
63
|
+
if (!orgId)
|
|
64
|
+
return;
|
|
58
65
|
await db
|
|
59
66
|
.update(posShifts)
|
|
60
67
|
.set({
|
|
@@ -62,7 +69,7 @@ export function buildPOSFinalizationHook(getDb) {
|
|
|
62
69
|
salesTotal: sql `${posShifts.salesTotal} + ${result.grandTotal ?? 0}`,
|
|
63
70
|
updatedAt: new Date(),
|
|
64
71
|
})
|
|
65
|
-
.where(eq(posShifts.id, posShiftId));
|
|
72
|
+
.where(and(eq(posShifts.id, posShiftId), eq(posShifts.organizationId, orgId)));
|
|
66
73
|
}
|
|
67
74
|
},
|
|
68
75
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,gBAAgB,EAAM,MAAM,YAAY,CAAC;AAGvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,WAAW,GAAE,gBAAqB,0CAwG3D"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineCommercePlugin } from "@porulle/core";
|
|
2
|
-
import { posTerminals, posShifts, posCashEvents, posTransactions, posPayments, posReturnItems, posOperatorPins, } from "./schema.js";
|
|
2
|
+
import { posTerminals, posShifts, posCashEvents, posTransactions, posPayments, posReturnItems, posOperatorPins, posPinAttempts, } from "./schema.js";
|
|
3
3
|
import { TerminalService } from "./services/terminal-service.js";
|
|
4
4
|
import { ShiftService } from "./services/shift-service.js";
|
|
5
5
|
import { TransactionService } from "./services/transaction-service.js";
|
|
@@ -76,6 +76,7 @@ export function posPlugin(userOptions = {}) {
|
|
|
76
76
|
posPayments,
|
|
77
77
|
posReturnItems,
|
|
78
78
|
posOperatorPins,
|
|
79
|
+
posPinAttempts,
|
|
79
80
|
}),
|
|
80
81
|
// Scope used by PIN login to mint per-shift credentials (issue #51).
|
|
81
82
|
apiKeyScopes: () => ({
|
|
@@ -83,6 +84,7 @@ export function posPlugin(userOptions = {}) {
|
|
|
83
84
|
prefix: "pos_shift_",
|
|
84
85
|
description: "Short-lived per-shift POS credentials minted by PIN login.",
|
|
85
86
|
permissions: { pos: ["operate"] },
|
|
87
|
+
enableMetadata: true,
|
|
86
88
|
// Allow credentials as short as 15 minutes (Better Auth default
|
|
87
89
|
// minimum is 1 day — too long for per-shift keys).
|
|
88
90
|
keyExpiration: { minExpiresIn: 1 / 96 },
|
package/dist/routes/auth.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,UAAU,EAAmB,MAAM,4BAA4B,CAAC;AACjG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAO7D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GACtF,uBAAuB,EAAE,CAiD3B"}
|
package/dist/routes/auth.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { router } from "@porulle/core";
|
|
1
|
+
import { router, CommerceValidationError } from "@porulle/core";
|
|
2
2
|
import { z } from "@hono/zod-openapi";
|
|
3
|
+
import { PIN_LOCKOUT_ERROR } from "../services/pin-service.js";
|
|
4
|
+
function throwPinRouteError(message) {
|
|
5
|
+
if (message === PIN_LOCKOUT_ERROR)
|
|
6
|
+
throw new CommerceValidationError(message);
|
|
7
|
+
throw new Error(message);
|
|
8
|
+
}
|
|
3
9
|
/**
|
|
4
10
|
* PIN auth runtime (issue #51).
|
|
5
11
|
*
|
|
@@ -23,7 +29,7 @@ export function buildPinAuthRoutes(service, ctx) {
|
|
|
23
29
|
const body = input;
|
|
24
30
|
const result = await service.setPin(orgId, body);
|
|
25
31
|
if (!result.ok)
|
|
26
|
-
|
|
32
|
+
throwPinRouteError(result.error);
|
|
27
33
|
return result.value;
|
|
28
34
|
});
|
|
29
35
|
r.post("/pin-login")
|
|
@@ -38,7 +44,7 @@ export function buildPinAuthRoutes(service, ctx) {
|
|
|
38
44
|
const body = input;
|
|
39
45
|
const result = await service.pinLogin(orgId, body, ctx.auth);
|
|
40
46
|
if (!result.ok)
|
|
41
|
-
|
|
47
|
+
throwPinRouteError(result.error);
|
|
42
48
|
return result.value;
|
|
43
49
|
});
|
|
44
50
|
r.post("/override")
|
|
@@ -53,7 +59,7 @@ export function buildPinAuthRoutes(service, ctx) {
|
|
|
53
59
|
const body = input;
|
|
54
60
|
const result = await service.override(orgId, body);
|
|
55
61
|
if (!result.ok)
|
|
56
|
-
|
|
62
|
+
throwPinRouteError(result.error);
|
|
57
63
|
return result.value;
|
|
58
64
|
});
|
|
59
65
|
return r.routes();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"returns.d.ts","sourceRoot":"","sources":["../../src/routes/returns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,aAAa,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE;IAAE,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CAAE,EAC/J,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE,uBAAuB,EAAE,
|
|
1
|
+
{"version":3,"file":"returns.d.ts","sourceRoot":"","sources":["../../src/routes/returns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,aAAa,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE;IAAE,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CAAE,EAC/J,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE,uBAAuB,EAAE,CAgJ3B"}
|
package/dist/routes/returns.js
CHANGED
|
@@ -14,7 +14,8 @@ export function buildReturnRoutes(returnService, transactionService, paymentServ
|
|
|
14
14
|
quantity: z.number().int().positive(),
|
|
15
15
|
reason: z.enum(["defective", "wrong_item", "changed_mind", "other"]),
|
|
16
16
|
restockingFee: z.number().int().min(0).optional(),
|
|
17
|
-
refundAmount
|
|
17
|
+
// refundAmount is NOT accepted from the client — it is computed
|
|
18
|
+
// server-side from the original order (SEC-08).
|
|
18
19
|
})).min(1),
|
|
19
20
|
}))
|
|
20
21
|
.handler(async ({ input, actor, orgId }) => {
|
|
@@ -34,15 +35,35 @@ export function buildReturnRoutes(returnService, transactionService, paymentServ
|
|
|
34
35
|
});
|
|
35
36
|
if (!txnResult.ok)
|
|
36
37
|
throw new Error(txnResult.error);
|
|
37
|
-
//
|
|
38
|
+
// SEC-08: derive the refund server-side via orders.refundLines (the same
|
|
39
|
+
// primitive the exchange flow uses). It validates the order + line items
|
|
40
|
+
// belong to this org, enforces the per-line refundable quantity
|
|
41
|
+
// (quantity - refundedQuantity) and updates that ledger — so a fabricated
|
|
42
|
+
// or foreign order, an over-refund, or a repeat refund of the same units
|
|
43
|
+
// is rejected — and computes the amount from the recorded sale price. The
|
|
44
|
+
// client can no longer set the refund amount.
|
|
45
|
+
const orders = ctx.services?.orders;
|
|
46
|
+
if (!orders?.refundLines) {
|
|
47
|
+
throw new Error("Orders service unavailable for return refund");
|
|
48
|
+
}
|
|
49
|
+
const refundResult = await orders.refundLines(body.originalOrderId, {
|
|
50
|
+
lines: body.items.map((i) => ({ lineItemId: i.originalLineItemId, quantity: i.quantity })),
|
|
51
|
+
reason: "pos_return",
|
|
52
|
+
}, actor);
|
|
53
|
+
if (!refundResult.ok || !refundResult.value) {
|
|
54
|
+
throw new Error(refundResult.error?.message ??
|
|
55
|
+
"Return could not be validated against the original order.");
|
|
56
|
+
}
|
|
57
|
+
const serverAmountByLine = new Map(refundResult.value.refund.lines.map((l) => [l.lineItemId, l.amount]));
|
|
58
|
+
const refundTotal = refundResult.value.refund.amount;
|
|
59
|
+
// Record return items with the SERVER-computed refund amounts
|
|
38
60
|
const itemsResult = await returnService.addReturnItems(txnResult.value.id, body.items.map((item) => ({
|
|
39
61
|
...item,
|
|
40
62
|
originalOrderId: body.originalOrderId,
|
|
63
|
+
refundAmount: serverAmountByLine.get(item.originalLineItemId) ?? 0,
|
|
41
64
|
})));
|
|
42
65
|
if (!itemsResult.ok)
|
|
43
66
|
throw new Error(itemsResult.error);
|
|
44
|
-
// Update transaction totals
|
|
45
|
-
const refundTotal = body.items.reduce((sum, i) => sum + i.refundAmount, 0);
|
|
46
67
|
await transactionService.updateTotals(txnResult.value.id, {
|
|
47
68
|
subtotal: refundTotal,
|
|
48
69
|
taxTotal: 0,
|
package/dist/schema.d.ts
CHANGED
|
@@ -1300,6 +1300,115 @@ export declare const posOperatorPins: import("drizzle-orm/pg-core/table").PgTabl
|
|
|
1300
1300
|
};
|
|
1301
1301
|
dialect: "pg";
|
|
1302
1302
|
}>;
|
|
1303
|
+
export declare const posPinAttempts: import("drizzle-orm/pg-core/table").PgTableWithColumns<{
|
|
1304
|
+
name: "pos_pin_attempts";
|
|
1305
|
+
schema: undefined;
|
|
1306
|
+
columns: {
|
|
1307
|
+
id: import("@porulle/core/drizzle").PgColumn<{
|
|
1308
|
+
name: "id";
|
|
1309
|
+
tableName: "pos_pin_attempts";
|
|
1310
|
+
dataType: "string";
|
|
1311
|
+
columnType: "PgUUID";
|
|
1312
|
+
data: string;
|
|
1313
|
+
driverParam: string;
|
|
1314
|
+
notNull: true;
|
|
1315
|
+
hasDefault: true;
|
|
1316
|
+
isPrimaryKey: true;
|
|
1317
|
+
isAutoincrement: false;
|
|
1318
|
+
hasRuntimeDefault: false;
|
|
1319
|
+
enumValues: undefined;
|
|
1320
|
+
baseColumn: never;
|
|
1321
|
+
identity: undefined;
|
|
1322
|
+
generated: undefined;
|
|
1323
|
+
}, {}, {}>;
|
|
1324
|
+
organizationId: import("@porulle/core/drizzle").PgColumn<{
|
|
1325
|
+
name: "organization_id";
|
|
1326
|
+
tableName: "pos_pin_attempts";
|
|
1327
|
+
dataType: "string";
|
|
1328
|
+
columnType: "PgText";
|
|
1329
|
+
data: string;
|
|
1330
|
+
driverParam: string;
|
|
1331
|
+
notNull: true;
|
|
1332
|
+
hasDefault: false;
|
|
1333
|
+
isPrimaryKey: false;
|
|
1334
|
+
isAutoincrement: false;
|
|
1335
|
+
hasRuntimeDefault: false;
|
|
1336
|
+
enumValues: [string, ...string[]];
|
|
1337
|
+
baseColumn: never;
|
|
1338
|
+
identity: undefined;
|
|
1339
|
+
generated: undefined;
|
|
1340
|
+
}, {}, {}>;
|
|
1341
|
+
operatorId: import("@porulle/core/drizzle").PgColumn<{
|
|
1342
|
+
name: "operator_id";
|
|
1343
|
+
tableName: "pos_pin_attempts";
|
|
1344
|
+
dataType: "string";
|
|
1345
|
+
columnType: "PgText";
|
|
1346
|
+
data: string;
|
|
1347
|
+
driverParam: string;
|
|
1348
|
+
notNull: true;
|
|
1349
|
+
hasDefault: false;
|
|
1350
|
+
isPrimaryKey: false;
|
|
1351
|
+
isAutoincrement: false;
|
|
1352
|
+
hasRuntimeDefault: false;
|
|
1353
|
+
enumValues: [string, ...string[]];
|
|
1354
|
+
baseColumn: never;
|
|
1355
|
+
identity: undefined;
|
|
1356
|
+
generated: undefined;
|
|
1357
|
+
}, {}, {}>;
|
|
1358
|
+
failedCount: import("@porulle/core/drizzle").PgColumn<{
|
|
1359
|
+
name: "failed_count";
|
|
1360
|
+
tableName: "pos_pin_attempts";
|
|
1361
|
+
dataType: "number";
|
|
1362
|
+
columnType: "PgInteger";
|
|
1363
|
+
data: number;
|
|
1364
|
+
driverParam: string | number;
|
|
1365
|
+
notNull: true;
|
|
1366
|
+
hasDefault: true;
|
|
1367
|
+
isPrimaryKey: false;
|
|
1368
|
+
isAutoincrement: false;
|
|
1369
|
+
hasRuntimeDefault: false;
|
|
1370
|
+
enumValues: undefined;
|
|
1371
|
+
baseColumn: never;
|
|
1372
|
+
identity: undefined;
|
|
1373
|
+
generated: undefined;
|
|
1374
|
+
}, {}, {}>;
|
|
1375
|
+
lockedUntil: import("@porulle/core/drizzle").PgColumn<{
|
|
1376
|
+
name: "locked_until";
|
|
1377
|
+
tableName: "pos_pin_attempts";
|
|
1378
|
+
dataType: "date";
|
|
1379
|
+
columnType: "PgTimestamp";
|
|
1380
|
+
data: Date;
|
|
1381
|
+
driverParam: string;
|
|
1382
|
+
notNull: false;
|
|
1383
|
+
hasDefault: false;
|
|
1384
|
+
isPrimaryKey: false;
|
|
1385
|
+
isAutoincrement: false;
|
|
1386
|
+
hasRuntimeDefault: false;
|
|
1387
|
+
enumValues: undefined;
|
|
1388
|
+
baseColumn: never;
|
|
1389
|
+
identity: undefined;
|
|
1390
|
+
generated: undefined;
|
|
1391
|
+
}, {}, {}>;
|
|
1392
|
+
updatedAt: import("@porulle/core/drizzle").PgColumn<{
|
|
1393
|
+
name: "updated_at";
|
|
1394
|
+
tableName: "pos_pin_attempts";
|
|
1395
|
+
dataType: "date";
|
|
1396
|
+
columnType: "PgTimestamp";
|
|
1397
|
+
data: Date;
|
|
1398
|
+
driverParam: string;
|
|
1399
|
+
notNull: true;
|
|
1400
|
+
hasDefault: true;
|
|
1401
|
+
isPrimaryKey: false;
|
|
1402
|
+
isAutoincrement: false;
|
|
1403
|
+
hasRuntimeDefault: false;
|
|
1404
|
+
enumValues: undefined;
|
|
1405
|
+
baseColumn: never;
|
|
1406
|
+
identity: undefined;
|
|
1407
|
+
generated: undefined;
|
|
1408
|
+
}, {}, {}>;
|
|
1409
|
+
};
|
|
1410
|
+
dialect: "pg";
|
|
1411
|
+
}>;
|
|
1303
1412
|
export declare const posReturnItems: import("drizzle-orm/pg-core/table").PgTableWithColumns<{
|
|
1304
1413
|
name: "pos_return_items";
|
|
1305
1414
|
schema: undefined;
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAatB,CAAC;AAIJ,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBnB,CAAC;AAIJ,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWvB,CAAC;AAIJ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BzB,CAAC;AAIJ,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAarB,CAAC;AAIJ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYzB,CAAC;AAIJ,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAaxB,CAAC"}
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAatB,CAAC;AAIJ,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBnB,CAAC;AAIJ,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWvB,CAAC;AAIJ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BzB,CAAC;AAIJ,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAarB,CAAC;AAIJ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYzB,CAAC;AAIJ,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASxB,CAAC;AAIJ,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAaxB,CAAC"}
|
package/dist/schema.js
CHANGED
|
@@ -120,6 +120,17 @@ export const posOperatorPins = pgTable("pos_operator_pins", {
|
|
|
120
120
|
}, (table) => ({
|
|
121
121
|
orgOperatorUnique: uniqueIndex("pos_operator_pins_org_operator_unique").on(table.organizationId, table.operatorId),
|
|
122
122
|
}));
|
|
123
|
+
// ─── PIN attempt lockout (SEC-15) ───────────────────────────────────────
|
|
124
|
+
export const posPinAttempts = pgTable("pos_pin_attempts", {
|
|
125
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
126
|
+
organizationId: text("organization_id").notNull(),
|
|
127
|
+
operatorId: text("operator_id").notNull(),
|
|
128
|
+
failedCount: integer("failed_count").notNull().default(0),
|
|
129
|
+
lockedUntil: timestamp("locked_until", { withTimezone: true }),
|
|
130
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
|
|
131
|
+
}, (table) => ({
|
|
132
|
+
orgOperatorUnique: uniqueIndex("pos_pin_attempts_org_operator_unique").on(table.organizationId, table.operatorId),
|
|
133
|
+
}));
|
|
123
134
|
// ─── Return Items ───────────────────────────────────────────────────────
|
|
124
135
|
export const posReturnItems = pgTable("pos_return_items", {
|
|
125
136
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exchange-service.d.ts","sourceRoot":"","sources":["../../src/services/exchange-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,KAAK,CAAC;QACjB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,OAAO,CAAC;KAC/D,CAAC,CAAC;IACH,gBAAgB,EAAE,KAAK,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,CAAC,CAAC;CACJ;
|
|
1
|
+
{"version":3,"file":"exchange-service.d.ts","sourceRoot":"","sources":["../../src/services/exchange-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,KAAK,CAAC;QACjB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,OAAO,CAAC;KAC/D,CAAC,CAAC;IACH,gBAAgB,EAAE,KAAK,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,CAAC,CAAC;CACJ;AA8BD,qBAAa,eAAe;IAExB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,aAAa;gBAJb,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1D,kBAAkB,EAAE,kBAAkB,EACtC,aAAa,EAAE,aAAa;IAGhC,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClD,OAAO,CAAC,YAAY,CAAC;QACtB,WAAW,EAAE,WAAW,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CAqLJ"}
|
|
@@ -20,13 +20,27 @@ export class ExchangeService {
|
|
|
20
20
|
if (input.replacementItems.length === 0)
|
|
21
21
|
return Err("At least one replacement item is required");
|
|
22
22
|
const orders = this.services.orders;
|
|
23
|
+
const pricing = this.services.pricing;
|
|
23
24
|
const currency = input.currency ?? "USD";
|
|
24
|
-
const subtotal = input.replacementItems.reduce((sum, i) => sum + i.unitPrice * i.quantity, 0);
|
|
25
|
-
const taxTotal = input.replacementItems.reduce((sum, i) => sum + (i.taxAmount ?? 0), 0);
|
|
26
|
-
const replacementTotal = subtotal + taxTotal;
|
|
27
25
|
// Atomic core half: refund the returned lines + create the replacement.
|
|
28
26
|
const core = await this.transaction(async (tx) => {
|
|
29
27
|
const txCtx = { tx, actor };
|
|
28
|
+
const pricedReplacements = [];
|
|
29
|
+
for (const item of input.replacementItems) {
|
|
30
|
+
const resolved = await pricing.resolve({
|
|
31
|
+
entityId: item.entityId,
|
|
32
|
+
currency,
|
|
33
|
+
quantity: item.quantity,
|
|
34
|
+
...(item.variantId ? { variantId: item.variantId } : {}),
|
|
35
|
+
}, actor, txCtx);
|
|
36
|
+
if (!resolved.ok || !resolved.value) {
|
|
37
|
+
throw new Error(resolved.error?.message ?? `Cannot resolve price for replacement item ${item.entityId}`);
|
|
38
|
+
}
|
|
39
|
+
pricedReplacements.push({ ...item, unitPrice: resolved.value.finalAmount });
|
|
40
|
+
}
|
|
41
|
+
const subtotal = pricedReplacements.reduce((sum, i) => sum + i.unitPrice * i.quantity, 0);
|
|
42
|
+
const taxTotal = pricedReplacements.reduce((sum, i) => sum + (i.taxAmount ?? 0), 0);
|
|
43
|
+
const replacementTotal = subtotal + taxTotal;
|
|
30
44
|
const refund = await orders.refundLines(input.originalOrderId, {
|
|
31
45
|
lines: input.returnItems.map((item) => ({
|
|
32
46
|
lineItemId: item.originalLineItemId,
|
|
@@ -50,7 +64,7 @@ export class ExchangeService {
|
|
|
50
64
|
refundId: refund.value.refund.id,
|
|
51
65
|
},
|
|
52
66
|
},
|
|
53
|
-
lineItems:
|
|
67
|
+
lineItems: pricedReplacements.map((item) => ({
|
|
54
68
|
entityId: item.entityId,
|
|
55
69
|
entityType: "product",
|
|
56
70
|
...(item.variantId ? { variantId: item.variantId } : {}),
|
|
@@ -61,16 +75,25 @@ export class ExchangeService {
|
|
|
61
75
|
totalPrice: item.unitPrice * item.quantity,
|
|
62
76
|
taxAmount: item.taxAmount ?? 0,
|
|
63
77
|
})),
|
|
64
|
-
}, actor, txCtx
|
|
78
|
+
}, actor, txCtx,
|
|
79
|
+
// Prices are server-resolved above (pricing.resolve); reserve stock for
|
|
80
|
+
// the replacement items so an exchange cannot oversell.
|
|
81
|
+
{ trustedPricing: true, stockPolicy: "reserve" });
|
|
65
82
|
if (!replacement.ok || !replacement.value) {
|
|
66
83
|
throw new Error(replacement.error?.message ?? "Replacement order creation failed");
|
|
67
84
|
}
|
|
68
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
refund: refund.value.refund,
|
|
87
|
+
replacementOrder: replacement.value,
|
|
88
|
+
subtotal,
|
|
89
|
+
taxTotal,
|
|
90
|
+
replacementTotal,
|
|
91
|
+
};
|
|
69
92
|
}).catch((error) => error);
|
|
70
93
|
if (core instanceof Error)
|
|
71
94
|
return Err(core.message);
|
|
72
95
|
const returnTotal = core.refund.amount;
|
|
73
|
-
const netDelta = replacementTotal - returnTotal;
|
|
96
|
+
const netDelta = core.replacementTotal - returnTotal;
|
|
74
97
|
// POS bookkeeping (post-commit): cart + exchange transaction + return rows.
|
|
75
98
|
const cart = this.services.cart;
|
|
76
99
|
const cartResult = await cart.create({ currency, metadata: { posExchange: true, originalOrderId: input.originalOrderId } }, actor);
|
|
@@ -95,8 +118,8 @@ export class ExchangeService {
|
|
|
95
118
|
refundAmount: core.refund.lines.find((l) => l.lineItemId === item.originalLineItemId)?.amount ?? 0,
|
|
96
119
|
})));
|
|
97
120
|
await this.transactionService.updateTotals(txn.id, {
|
|
98
|
-
subtotal,
|
|
99
|
-
taxTotal,
|
|
121
|
+
subtotal: core.subtotal,
|
|
122
|
+
taxTotal: core.taxTotal,
|
|
100
123
|
total: netDelta,
|
|
101
124
|
discountTotal: 0,
|
|
102
125
|
});
|
|
@@ -111,7 +134,7 @@ export class ExchangeService {
|
|
|
111
134
|
refundId: core.refund.id,
|
|
112
135
|
replacementOrderId: core.replacementOrder.id,
|
|
113
136
|
returnTotal,
|
|
114
|
-
replacementTotal,
|
|
137
|
+
replacementTotal: core.replacementTotal,
|
|
115
138
|
netDelta,
|
|
116
139
|
},
|
|
117
140
|
},
|
|
@@ -138,7 +161,7 @@ export class ExchangeService {
|
|
|
138
161
|
refundId: core.refund.id,
|
|
139
162
|
returnTotal,
|
|
140
163
|
replacementOrderId: core.replacementOrder.id,
|
|
141
|
-
replacementTotal,
|
|
164
|
+
replacementTotal: core.replacementTotal,
|
|
142
165
|
netDelta,
|
|
143
166
|
});
|
|
144
167
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PluginResult } from "@porulle/core";
|
|
2
2
|
import type { Db } from "../types.js";
|
|
3
|
+
export declare const PIN_LOCKOUT_ERROR = "Too many failed PIN attempts; try again later";
|
|
3
4
|
/** Narrow Better Auth surface the PIN runtime uses. */
|
|
4
5
|
export interface PinAuthApi {
|
|
5
6
|
api: {
|
|
@@ -18,7 +19,17 @@ export declare class PinService {
|
|
|
18
19
|
apiKeyScope?: string | undefined;
|
|
19
20
|
/** Shift-credential lifetime in seconds. Default: 12h. */
|
|
20
21
|
credentialTtlSeconds?: number | undefined;
|
|
22
|
+
/** Failed PIN attempts before lockout (SEC-15). Default: 5. */
|
|
23
|
+
lockoutMaxAttempts?: number | undefined;
|
|
24
|
+
/** Failure window and lockout duration in minutes (SEC-15). Default: 15. */
|
|
25
|
+
lockoutWindowMinutes?: number | undefined;
|
|
21
26
|
});
|
|
27
|
+
private lockoutMaxAttempts;
|
|
28
|
+
private lockoutWindowMs;
|
|
29
|
+
private getPinAttempt;
|
|
30
|
+
private isLockedOut;
|
|
31
|
+
private recordFailedPinAttempt;
|
|
32
|
+
private clearPinAttempts;
|
|
22
33
|
setPin(orgId: string, input: {
|
|
23
34
|
operatorId: string;
|
|
24
35
|
pin: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pin-service.d.ts","sourceRoot":"","sources":["../../src/services/pin-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"pin-service.d.ts","sourceRoot":"","sources":["../../src/services/pin-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,KAAK,EAAE,EAAE,EAAkC,MAAM,aAAa,CAAC;AAgBtE,eAAO,MAAM,iBAAiB,kDAAkD,CAAC;AAOjF,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE;QACH,YAAY,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC1E,CAAC;CACH;AA+BD,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1D;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASlF;AAmBD,qBAAa,UAAU;IAEnB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,OAAO;gBADP,EAAE,EAAE,EAAE,EACN,OAAO,GAAE;QACf,yEAAyE;QACzE,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,0DAA0D;QAC1D,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1C,+DAA+D;QAC/D,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxC,4EAA4E;QAC5E,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KACtC;IAGR,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,eAAe;YAKT,aAAa;IAc3B,OAAO,CAAC,WAAW;YAIL,sBAAsB;YA+BtB,gBAAgB;IASxB,MAAM,CACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;KAAE,GAC5E,OAAO,CAAC,YAAY,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;YAgCxD,iBAAiB;IA+B/B;;;;OAIG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,EACxE,IAAI,EAAE,UAAU,GAAG,SAAS,GAC3B,OAAO,CAAC,YAAY,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IAgEH;;;OAGG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GACzD,OAAO,CAAC,YAAY,CAAC;QACtB,QAAQ,EAAE,IAAI,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CAYJ"}
|