@unifiedcommerce/plugin-giftcards 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/code-generator.d.ts +21 -0
  2. package/dist/code-generator.d.ts.map +1 -0
  3. package/dist/code-generator.js +70 -0
  4. package/dist/hooks/checkout-deduction.d.ts +12 -0
  5. package/dist/hooks/checkout-deduction.d.ts.map +1 -0
  6. package/dist/hooks/checkout-deduction.js +64 -0
  7. package/dist/hooks/checkout-issuance.d.ts +8 -0
  8. package/dist/hooks/checkout-issuance.d.ts.map +1 -0
  9. package/dist/hooks/checkout-issuance.js +58 -0
  10. package/dist/hooks/refund-credit.d.ts +7 -0
  11. package/dist/hooks/refund-credit.d.ts.map +1 -0
  12. package/dist/hooks/refund-credit.js +25 -0
  13. package/dist/index.d.ts +13 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +110 -0
  16. package/dist/routes/admin.d.ts +9 -0
  17. package/dist/routes/admin.d.ts.map +1 -0
  18. package/dist/routes/admin.js +90 -0
  19. package/dist/routes/customer.d.ts +9 -0
  20. package/dist/routes/customer.d.ts.map +1 -0
  21. package/dist/routes/customer.js +23 -0
  22. package/dist/routes/public.d.ts +9 -0
  23. package/dist/routes/public.d.ts.map +1 -0
  24. package/dist/routes/public.js +21 -0
  25. package/dist/schema.d.ts +442 -0
  26. package/dist/schema.d.ts.map +1 -0
  27. package/dist/schema.js +60 -0
  28. package/dist/services/gift-card-repository.d.ts +45 -0
  29. package/dist/services/gift-card-repository.d.ts.map +1 -0
  30. package/dist/services/gift-card-repository.js +113 -0
  31. package/dist/services/gift-card-service.d.ts +45 -0
  32. package/dist/services/gift-card-service.d.ts.map +1 -0
  33. package/dist/services/gift-card-service.js +196 -0
  34. package/dist/tsconfig.tsbuildinfo +1 -0
  35. package/dist/types.d.ts +30 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +8 -0
  38. package/package.json +39 -0
  39. package/src/code-generator.ts +79 -0
  40. package/src/hooks/checkout-deduction.ts +115 -0
  41. package/src/hooks/checkout-issuance.ts +93 -0
  42. package/src/hooks/refund-credit.ts +56 -0
  43. package/src/index.ts +147 -0
  44. package/src/routes/admin.ts +115 -0
  45. package/src/routes/customer.ts +30 -0
  46. package/src/routes/public.ts +31 -0
  47. package/src/schema.ts +89 -0
  48. package/src/services/gift-card-repository.ts +157 -0
  49. package/src/services/gift-card-service.ts +286 -0
  50. package/src/types.ts +41 -0
@@ -0,0 +1,442 @@
1
+ export declare const giftCards: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "gift_cards";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "id";
7
+ tableName: "gift_cards";
8
+ dataType: "string";
9
+ columnType: "PgUUID";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: true;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: undefined;
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ organizationId: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "organization_id";
24
+ tableName: "gift_cards";
25
+ dataType: "string";
26
+ columnType: "PgText";
27
+ data: string;
28
+ driverParam: string;
29
+ notNull: true;
30
+ hasDefault: false;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: [string, ...string[]];
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ code: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "code";
41
+ tableName: "gift_cards";
42
+ dataType: "string";
43
+ columnType: "PgText";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: true;
47
+ hasDefault: false;
48
+ isPrimaryKey: false;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: [string, ...string[]];
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ initialAmount: import("drizzle-orm/pg-core").PgColumn<{
57
+ name: "initial_amount";
58
+ tableName: "gift_cards";
59
+ dataType: "number";
60
+ columnType: "PgInteger";
61
+ data: number;
62
+ driverParam: string | number;
63
+ notNull: true;
64
+ hasDefault: false;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: undefined;
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {}>;
73
+ balance: import("drizzle-orm/pg-core").PgColumn<{
74
+ name: "balance";
75
+ tableName: "gift_cards";
76
+ dataType: "number";
77
+ columnType: "PgInteger";
78
+ data: number;
79
+ driverParam: string | number;
80
+ notNull: true;
81
+ hasDefault: false;
82
+ isPrimaryKey: false;
83
+ isAutoincrement: false;
84
+ hasRuntimeDefault: false;
85
+ enumValues: undefined;
86
+ baseColumn: never;
87
+ identity: undefined;
88
+ generated: undefined;
89
+ }, {}, {}>;
90
+ currency: import("drizzle-orm/pg-core").PgColumn<{
91
+ name: "currency";
92
+ tableName: "gift_cards";
93
+ dataType: "string";
94
+ columnType: "PgText";
95
+ data: string;
96
+ driverParam: string;
97
+ notNull: true;
98
+ hasDefault: false;
99
+ isPrimaryKey: false;
100
+ isAutoincrement: false;
101
+ hasRuntimeDefault: false;
102
+ enumValues: [string, ...string[]];
103
+ baseColumn: never;
104
+ identity: undefined;
105
+ generated: undefined;
106
+ }, {}, {}>;
107
+ status: import("drizzle-orm/pg-core").PgColumn<{
108
+ name: "status";
109
+ tableName: "gift_cards";
110
+ dataType: "string";
111
+ columnType: "PgText";
112
+ data: "active" | "disabled" | "exhausted";
113
+ driverParam: string;
114
+ notNull: true;
115
+ hasDefault: true;
116
+ isPrimaryKey: false;
117
+ isAutoincrement: false;
118
+ hasRuntimeDefault: false;
119
+ enumValues: ["active", "disabled", "exhausted"];
120
+ baseColumn: never;
121
+ identity: undefined;
122
+ generated: undefined;
123
+ }, {}, {}>;
124
+ purchaserId: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "purchaser_id";
126
+ tableName: "gift_cards";
127
+ dataType: "string";
128
+ columnType: "PgText";
129
+ data: string;
130
+ driverParam: string;
131
+ notNull: false;
132
+ hasDefault: false;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: [string, ...string[]];
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {}>;
141
+ recipientEmail: import("drizzle-orm/pg-core").PgColumn<{
142
+ name: "recipient_email";
143
+ tableName: "gift_cards";
144
+ dataType: "string";
145
+ columnType: "PgText";
146
+ data: string;
147
+ driverParam: string;
148
+ notNull: false;
149
+ hasDefault: false;
150
+ isPrimaryKey: false;
151
+ isAutoincrement: false;
152
+ hasRuntimeDefault: false;
153
+ enumValues: [string, ...string[]];
154
+ baseColumn: never;
155
+ identity: undefined;
156
+ generated: undefined;
157
+ }, {}, {}>;
158
+ senderName: import("drizzle-orm/pg-core").PgColumn<{
159
+ name: "sender_name";
160
+ tableName: "gift_cards";
161
+ dataType: "string";
162
+ columnType: "PgText";
163
+ data: string;
164
+ driverParam: string;
165
+ notNull: false;
166
+ hasDefault: false;
167
+ isPrimaryKey: false;
168
+ isAutoincrement: false;
169
+ hasRuntimeDefault: false;
170
+ enumValues: [string, ...string[]];
171
+ baseColumn: never;
172
+ identity: undefined;
173
+ generated: undefined;
174
+ }, {}, {}>;
175
+ personalMessage: import("drizzle-orm/pg-core").PgColumn<{
176
+ name: "personal_message";
177
+ tableName: "gift_cards";
178
+ dataType: "string";
179
+ columnType: "PgText";
180
+ data: string;
181
+ driverParam: string;
182
+ notNull: false;
183
+ hasDefault: false;
184
+ isPrimaryKey: false;
185
+ isAutoincrement: false;
186
+ hasRuntimeDefault: false;
187
+ enumValues: [string, ...string[]];
188
+ baseColumn: never;
189
+ identity: undefined;
190
+ generated: undefined;
191
+ }, {}, {}>;
192
+ sourceOrderId: import("drizzle-orm/pg-core").PgColumn<{
193
+ name: "source_order_id";
194
+ tableName: "gift_cards";
195
+ dataType: "string";
196
+ columnType: "PgText";
197
+ data: string;
198
+ driverParam: string;
199
+ notNull: false;
200
+ hasDefault: false;
201
+ isPrimaryKey: false;
202
+ isAutoincrement: false;
203
+ hasRuntimeDefault: false;
204
+ enumValues: [string, ...string[]];
205
+ baseColumn: never;
206
+ identity: undefined;
207
+ generated: undefined;
208
+ }, {}, {}>;
209
+ expiresAt: import("drizzle-orm/pg-core").PgColumn<{
210
+ name: "expires_at";
211
+ tableName: "gift_cards";
212
+ dataType: "date";
213
+ columnType: "PgTimestamp";
214
+ data: Date;
215
+ driverParam: string;
216
+ notNull: false;
217
+ hasDefault: false;
218
+ isPrimaryKey: false;
219
+ isAutoincrement: false;
220
+ hasRuntimeDefault: false;
221
+ enumValues: undefined;
222
+ baseColumn: never;
223
+ identity: undefined;
224
+ generated: undefined;
225
+ }, {}, {}>;
226
+ version: import("drizzle-orm/pg-core").PgColumn<{
227
+ name: "version";
228
+ tableName: "gift_cards";
229
+ dataType: "number";
230
+ columnType: "PgInteger";
231
+ data: number;
232
+ driverParam: string | number;
233
+ notNull: true;
234
+ hasDefault: true;
235
+ isPrimaryKey: false;
236
+ isAutoincrement: false;
237
+ hasRuntimeDefault: false;
238
+ enumValues: undefined;
239
+ baseColumn: never;
240
+ identity: undefined;
241
+ generated: undefined;
242
+ }, {}, {}>;
243
+ metadata: import("drizzle-orm/pg-core").PgColumn<{
244
+ name: "metadata";
245
+ tableName: "gift_cards";
246
+ dataType: "json";
247
+ columnType: "PgJsonb";
248
+ data: Record<string, unknown>;
249
+ driverParam: unknown;
250
+ notNull: false;
251
+ hasDefault: true;
252
+ isPrimaryKey: false;
253
+ isAutoincrement: false;
254
+ hasRuntimeDefault: false;
255
+ enumValues: undefined;
256
+ baseColumn: never;
257
+ identity: undefined;
258
+ generated: undefined;
259
+ }, {}, {
260
+ $type: Record<string, unknown>;
261
+ }>;
262
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
263
+ name: "created_at";
264
+ tableName: "gift_cards";
265
+ dataType: "date";
266
+ columnType: "PgTimestamp";
267
+ data: Date;
268
+ driverParam: string;
269
+ notNull: true;
270
+ hasDefault: true;
271
+ isPrimaryKey: false;
272
+ isAutoincrement: false;
273
+ hasRuntimeDefault: false;
274
+ enumValues: undefined;
275
+ baseColumn: never;
276
+ identity: undefined;
277
+ generated: undefined;
278
+ }, {}, {}>;
279
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
280
+ name: "updated_at";
281
+ tableName: "gift_cards";
282
+ dataType: "date";
283
+ columnType: "PgTimestamp";
284
+ data: Date;
285
+ driverParam: string;
286
+ notNull: true;
287
+ hasDefault: true;
288
+ isPrimaryKey: false;
289
+ isAutoincrement: false;
290
+ hasRuntimeDefault: false;
291
+ enumValues: undefined;
292
+ baseColumn: never;
293
+ identity: undefined;
294
+ generated: undefined;
295
+ }, {}, {}>;
296
+ };
297
+ dialect: "pg";
298
+ }>;
299
+ export declare const giftCardTransactions: import("drizzle-orm/pg-core").PgTableWithColumns<{
300
+ name: "gift_card_transactions";
301
+ schema: undefined;
302
+ columns: {
303
+ id: import("drizzle-orm/pg-core").PgColumn<{
304
+ name: "id";
305
+ tableName: "gift_card_transactions";
306
+ dataType: "string";
307
+ columnType: "PgUUID";
308
+ data: string;
309
+ driverParam: string;
310
+ notNull: true;
311
+ hasDefault: true;
312
+ isPrimaryKey: true;
313
+ isAutoincrement: false;
314
+ hasRuntimeDefault: false;
315
+ enumValues: undefined;
316
+ baseColumn: never;
317
+ identity: undefined;
318
+ generated: undefined;
319
+ }, {}, {}>;
320
+ giftCardId: import("drizzle-orm/pg-core").PgColumn<{
321
+ name: "gift_card_id";
322
+ tableName: "gift_card_transactions";
323
+ dataType: "string";
324
+ columnType: "PgUUID";
325
+ data: string;
326
+ driverParam: string;
327
+ notNull: true;
328
+ hasDefault: false;
329
+ isPrimaryKey: false;
330
+ isAutoincrement: false;
331
+ hasRuntimeDefault: false;
332
+ enumValues: undefined;
333
+ baseColumn: never;
334
+ identity: undefined;
335
+ generated: undefined;
336
+ }, {}, {}>;
337
+ type: import("drizzle-orm/pg-core").PgColumn<{
338
+ name: "type";
339
+ tableName: "gift_card_transactions";
340
+ dataType: "string";
341
+ columnType: "PgText";
342
+ data: "debit" | "credit" | "refund";
343
+ driverParam: string;
344
+ notNull: true;
345
+ hasDefault: false;
346
+ isPrimaryKey: false;
347
+ isAutoincrement: false;
348
+ hasRuntimeDefault: false;
349
+ enumValues: ["debit", "credit", "refund"];
350
+ baseColumn: never;
351
+ identity: undefined;
352
+ generated: undefined;
353
+ }, {}, {}>;
354
+ amount: import("drizzle-orm/pg-core").PgColumn<{
355
+ name: "amount";
356
+ tableName: "gift_card_transactions";
357
+ dataType: "number";
358
+ columnType: "PgInteger";
359
+ data: number;
360
+ driverParam: string | number;
361
+ notNull: true;
362
+ hasDefault: false;
363
+ isPrimaryKey: false;
364
+ isAutoincrement: false;
365
+ hasRuntimeDefault: false;
366
+ enumValues: undefined;
367
+ baseColumn: never;
368
+ identity: undefined;
369
+ generated: undefined;
370
+ }, {}, {}>;
371
+ balanceAfter: import("drizzle-orm/pg-core").PgColumn<{
372
+ name: "balance_after";
373
+ tableName: "gift_card_transactions";
374
+ dataType: "number";
375
+ columnType: "PgInteger";
376
+ data: number;
377
+ driverParam: string | number;
378
+ notNull: true;
379
+ hasDefault: false;
380
+ isPrimaryKey: false;
381
+ isAutoincrement: false;
382
+ hasRuntimeDefault: false;
383
+ enumValues: undefined;
384
+ baseColumn: never;
385
+ identity: undefined;
386
+ generated: undefined;
387
+ }, {}, {}>;
388
+ orderId: import("drizzle-orm/pg-core").PgColumn<{
389
+ name: "order_id";
390
+ tableName: "gift_card_transactions";
391
+ dataType: "string";
392
+ columnType: "PgText";
393
+ data: string;
394
+ driverParam: string;
395
+ notNull: false;
396
+ hasDefault: false;
397
+ isPrimaryKey: false;
398
+ isAutoincrement: false;
399
+ hasRuntimeDefault: false;
400
+ enumValues: [string, ...string[]];
401
+ baseColumn: never;
402
+ identity: undefined;
403
+ generated: undefined;
404
+ }, {}, {}>;
405
+ note: import("drizzle-orm/pg-core").PgColumn<{
406
+ name: "note";
407
+ tableName: "gift_card_transactions";
408
+ dataType: "string";
409
+ columnType: "PgText";
410
+ data: string;
411
+ driverParam: string;
412
+ notNull: false;
413
+ hasDefault: false;
414
+ isPrimaryKey: false;
415
+ isAutoincrement: false;
416
+ hasRuntimeDefault: false;
417
+ enumValues: [string, ...string[]];
418
+ baseColumn: never;
419
+ identity: undefined;
420
+ generated: undefined;
421
+ }, {}, {}>;
422
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
423
+ name: "created_at";
424
+ tableName: "gift_card_transactions";
425
+ dataType: "date";
426
+ columnType: "PgTimestamp";
427
+ data: Date;
428
+ driverParam: string;
429
+ notNull: true;
430
+ hasDefault: true;
431
+ isPrimaryKey: false;
432
+ isAutoincrement: false;
433
+ hasRuntimeDefault: false;
434
+ enumValues: undefined;
435
+ baseColumn: never;
436
+ identity: undefined;
437
+ generated: undefined;
438
+ }, {}, {}>;
439
+ };
440
+ dialect: "pg";
441
+ }>;
442
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CrB,CAAC;AAIF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBhC,CAAC"}
package/dist/schema.js ADDED
@@ -0,0 +1,60 @@
1
+ import { pgTable, uuid, text, integer, timestamp, jsonb, index, check, uniqueIndex, } from "drizzle-orm/pg-core";
2
+ import { sql } from "drizzle-orm";
3
+ // ─── Gift Cards ──────────────────────────────────────────────────────────────
4
+ export const giftCards = pgTable("gift_cards", {
5
+ id: uuid("id").defaultRandom().primaryKey(),
6
+ organizationId: text("organization_id").notNull(),
7
+ code: text("code").notNull(),
8
+ initialAmount: integer("initial_amount").notNull(),
9
+ balance: integer("balance").notNull(),
10
+ currency: text("currency").notNull(),
11
+ status: text("status", {
12
+ enum: ["active", "disabled", "exhausted"],
13
+ })
14
+ .notNull()
15
+ .default("active"),
16
+ purchaserId: text("purchaser_id"),
17
+ recipientEmail: text("recipient_email"),
18
+ senderName: text("sender_name"),
19
+ personalMessage: text("personal_message"),
20
+ sourceOrderId: text("source_order_id"),
21
+ expiresAt: timestamp("expires_at", { withTimezone: true }),
22
+ version: integer("version").notNull().default(0),
23
+ metadata: jsonb("metadata")
24
+ .$type()
25
+ .default({}),
26
+ createdAt: timestamp("created_at", { withTimezone: true })
27
+ .defaultNow()
28
+ .notNull(),
29
+ updatedAt: timestamp("updated_at", { withTimezone: true })
30
+ .defaultNow()
31
+ .notNull(),
32
+ }, (table) => ({
33
+ orgCodeUnique: uniqueIndex("gift_cards_org_code_unique").on(table.organizationId, table.code),
34
+ orgIdx: index("idx_gift_cards_org").on(table.organizationId),
35
+ codeIdx: index("idx_gift_cards_code").on(table.code),
36
+ purchaserIdx: index("idx_gift_cards_purchaser").on(table.purchaserId),
37
+ statusIdx: index("idx_gift_cards_status").on(table.status),
38
+ balanceCheck: check("gift_cards_balance_non_negative", sql `${table.balance} >= 0`),
39
+ initialAmountCheck: check("gift_cards_initial_amount_positive", sql `${table.initialAmount} > 0`),
40
+ }));
41
+ // ─── Gift Card Transactions ─────────────────────────────────────────────────
42
+ export const giftCardTransactions = pgTable("gift_card_transactions", {
43
+ id: uuid("id").defaultRandom().primaryKey(),
44
+ giftCardId: uuid("gift_card_id")
45
+ .notNull()
46
+ .references(() => giftCards.id, { onDelete: "cascade" }),
47
+ type: text("type", {
48
+ enum: ["debit", "credit", "refund"],
49
+ }).notNull(),
50
+ amount: integer("amount").notNull(),
51
+ balanceAfter: integer("balance_after").notNull(),
52
+ orderId: text("order_id"),
53
+ note: text("note"),
54
+ createdAt: timestamp("created_at", { withTimezone: true })
55
+ .defaultNow()
56
+ .notNull(),
57
+ }, (table) => ({
58
+ cardIdx: index("idx_gc_txn_card").on(table.giftCardId),
59
+ orderIdx: index("idx_gc_txn_order").on(table.orderId),
60
+ }));
@@ -0,0 +1,45 @@
1
+ import type { Db, GiftCard, GiftCardInsert, GiftCardTransaction, GiftCardStatus, TransactionType } from "../types";
2
+ export declare class GiftCardRepository {
3
+ private db;
4
+ constructor(db: Db);
5
+ private getDb;
6
+ create(data: GiftCardInsert, ctx?: {
7
+ tx?: Db;
8
+ }): Promise<GiftCard>;
9
+ findById(id: string, ctx?: {
10
+ tx?: Db;
11
+ }): Promise<GiftCard | undefined>;
12
+ findByCode(code: string, ctx?: {
13
+ tx?: Db;
14
+ }): Promise<GiftCard | undefined>;
15
+ list(filters?: {
16
+ status?: GiftCardStatus;
17
+ purchaserId?: string;
18
+ }, ctx?: {
19
+ tx?: Db;
20
+ }): Promise<GiftCard[]>;
21
+ disable(id: string, ctx?: {
22
+ tx?: Db;
23
+ }): Promise<GiftCard | undefined>;
24
+ findByCodeForUpdate(code: string, tx: Db): Promise<GiftCard | undefined>;
25
+ findByIdForUpdate(id: string, tx: Db): Promise<GiftCard | undefined>;
26
+ updateBalance(id: string, balance: number, status: GiftCardStatus, currentVersion: number, tx: Db): Promise<GiftCard>;
27
+ adjustBalance(id: string, delta: number, tx: Db): Promise<GiftCard>;
28
+ recordTransaction(data: {
29
+ giftCardId: string;
30
+ type: TransactionType;
31
+ amount: number;
32
+ balanceAfter: number;
33
+ orderId?: string;
34
+ note?: string;
35
+ }, ctx?: {
36
+ tx?: Db;
37
+ }): Promise<GiftCardTransaction>;
38
+ listTransactions(giftCardId: string, ctx?: {
39
+ tx?: Db;
40
+ }): Promise<GiftCardTransaction[]>;
41
+ findTransactionsByOrderId(orderId: string, ctx?: {
42
+ tx?: Db;
43
+ }): Promise<GiftCardTransaction[]>;
44
+ }
45
+ //# sourceMappingURL=gift-card-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gift-card-repository.d.ts","sourceRoot":"","sources":["../../src/services/gift-card-repository.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEnH,qBAAa,kBAAkB;IACjB,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,EAAE;IAE1B,OAAO,CAAC,KAAK;IAMP,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQlE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAQtE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAQ1E,IAAI,CACR,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,cAAc,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAC3D,GAAG,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,EAAE,CAAA;KAAE,GAChB,OAAO,CAAC,QAAQ,EAAE,CAAC;IAYhB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAWrE,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IASxE,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IASpE,aAAa,CACjB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,cAAc,EACtB,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,EAAE,GACL,OAAO,CAAC,QAAQ,CAAC;IAcd,aAAa,CACjB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,EAAE,GACL,OAAO,CAAC,QAAQ,CAAC;IAYd,iBAAiB,CACrB,IAAI,EAAE;QACJ,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,eAAe,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,EACD,GAAG,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,EAAE,CAAA;KAAE,GAChB,OAAO,CAAC,mBAAmB,CAAC;IAQzB,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,EAAE,CAAA;KAAE,GAChB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAQ3B,yBAAyB,CAC7B,OAAO,EAAE,MAAM,EACf,GAAG,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,EAAE,CAAA;KAAE,GAChB,OAAO,CAAC,mBAAmB,EAAE,CAAC;CAOlC"}
@@ -0,0 +1,113 @@
1
+ import { eq, desc, and } from "drizzle-orm";
2
+ import { giftCards, giftCardTransactions } from "../schema";
3
+ export class GiftCardRepository {
4
+ db;
5
+ constructor(db) {
6
+ this.db = db;
7
+ }
8
+ getDb(ctx) {
9
+ return ctx?.tx ?? this.db;
10
+ }
11
+ // ─── Gift Card CRUD ─────────────────────────────────────────────────
12
+ async create(data, ctx) {
13
+ const rows = await this.getDb(ctx)
14
+ .insert(giftCards)
15
+ .values(data)
16
+ .returning();
17
+ return rows[0];
18
+ }
19
+ async findById(id, ctx) {
20
+ const rows = await this.getDb(ctx)
21
+ .select()
22
+ .from(giftCards)
23
+ .where(eq(giftCards.id, id));
24
+ return rows[0];
25
+ }
26
+ async findByCode(code, ctx) {
27
+ const rows = await this.getDb(ctx)
28
+ .select()
29
+ .from(giftCards)
30
+ .where(eq(giftCards.code, code));
31
+ return rows[0];
32
+ }
33
+ async list(filters, ctx) {
34
+ const conditions = [];
35
+ if (filters?.status)
36
+ conditions.push(eq(giftCards.status, filters.status));
37
+ if (filters?.purchaserId)
38
+ conditions.push(eq(giftCards.purchaserId, filters.purchaserId));
39
+ return this.getDb(ctx)
40
+ .select()
41
+ .from(giftCards)
42
+ .where(conditions.length > 0 ? and(...conditions) : undefined)
43
+ .orderBy(desc(giftCards.createdAt));
44
+ }
45
+ async disable(id, ctx) {
46
+ const rows = await this.getDb(ctx)
47
+ .update(giftCards)
48
+ .set({ status: "disabled", updatedAt: new Date() })
49
+ .where(eq(giftCards.id, id))
50
+ .returning();
51
+ return rows[0];
52
+ }
53
+ // ─── SELECT FOR UPDATE (Concurrency-Safe Balance Operations) ───────
54
+ async findByCodeForUpdate(code, tx) {
55
+ const rows = await tx
56
+ .select()
57
+ .from(giftCards)
58
+ .where(eq(giftCards.code, code))
59
+ .for("update");
60
+ return rows[0];
61
+ }
62
+ async findByIdForUpdate(id, tx) {
63
+ const rows = await tx
64
+ .select()
65
+ .from(giftCards)
66
+ .where(eq(giftCards.id, id))
67
+ .for("update");
68
+ return rows[0];
69
+ }
70
+ async updateBalance(id, balance, status, currentVersion, tx) {
71
+ const rows = await tx
72
+ .update(giftCards)
73
+ .set({
74
+ balance,
75
+ status,
76
+ version: currentVersion + 1,
77
+ updatedAt: new Date(),
78
+ })
79
+ .where(eq(giftCards.id, id))
80
+ .returning();
81
+ return rows[0];
82
+ }
83
+ async adjustBalance(id, delta, tx) {
84
+ const card = await this.findByIdForUpdate(id, tx);
85
+ if (!card)
86
+ throw new Error("Gift card not found");
87
+ const newBalance = Math.max(0, Math.min(card.initialAmount, card.balance + delta));
88
+ const newStatus = newBalance === 0 ? "exhausted" : "active";
89
+ return this.updateBalance(id, newBalance, newStatus, card.version, tx);
90
+ }
91
+ // ─── Transactions ───────────────────────────────────────────────────
92
+ async recordTransaction(data, ctx) {
93
+ const rows = await this.getDb(ctx)
94
+ .insert(giftCardTransactions)
95
+ .values(data)
96
+ .returning();
97
+ return rows[0];
98
+ }
99
+ async listTransactions(giftCardId, ctx) {
100
+ return this.getDb(ctx)
101
+ .select()
102
+ .from(giftCardTransactions)
103
+ .where(eq(giftCardTransactions.giftCardId, giftCardId))
104
+ .orderBy(desc(giftCardTransactions.createdAt));
105
+ }
106
+ async findTransactionsByOrderId(orderId, ctx) {
107
+ return this.getDb(ctx)
108
+ .select()
109
+ .from(giftCardTransactions)
110
+ .where(eq(giftCardTransactions.orderId, orderId))
111
+ .orderBy(desc(giftCardTransactions.createdAt));
112
+ }
113
+ }