@nehorai/payments-il 0.1.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.
@@ -0,0 +1,1418 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/factory.ts
31
+ var factory_exports = {};
32
+ __export(factory_exports, {
33
+ addIsraeliProviders: () => addIsraeliProviders
34
+ });
35
+ module.exports = __toCommonJS(factory_exports);
36
+
37
+ // src/providers/hyp/hyp-provider.ts
38
+ var import_types = require("@nehorai/payments/types");
39
+
40
+ // src/providers/hyp/hyp-types.ts
41
+ var DEFAULT_HYP_ENDPOINTS = {
42
+ test: "https://cguat2.creditguard.co.il",
43
+ production: "https://cgpay3.creditguard.co.il"
44
+ };
45
+ var HYP_SUPPORTED_CURRENCIES = [
46
+ "ILS",
47
+ // Israeli Shekel (primary)
48
+ "USD",
49
+ // US Dollar
50
+ "EUR",
51
+ // Euro
52
+ "GBP"
53
+ // British Pound
54
+ ];
55
+ var HYP_RESULT_CODE_MAP = {
56
+ "000": "captured",
57
+ // Success
58
+ "001": "failed",
59
+ // Declined
60
+ "002": "failed",
61
+ // Invalid card
62
+ "003": "failed",
63
+ // Expired card
64
+ "004": "failed",
65
+ // Insufficient funds
66
+ "005": "failed",
67
+ // Invalid CVV
68
+ "006": "failed",
69
+ // Card not permitted
70
+ "033": "failed",
71
+ // Lost/Stolen card
72
+ "034": "failed",
73
+ // Suspected fraud
74
+ "051": "failed",
75
+ // Insufficient funds
76
+ "054": "failed",
77
+ // Expired card
78
+ "057": "failed",
79
+ // Transaction not permitted
80
+ "100": "failed",
81
+ // System error
82
+ "200": "pending_authorization"
83
+ // Pending
84
+ };
85
+ var HYP_TRANSACTION_TYPES = {
86
+ /** Regular charge (immediate capture) */
87
+ DEBIT: "Debit",
88
+ /** Refund */
89
+ CREDIT: "Credit",
90
+ /** Authorization only (J5) */
91
+ DEBIT_J5: "Debit"
92
+ };
93
+ var HYP_TRANSACTION_CODES = {
94
+ /** Regular transaction */
95
+ REGULAR: "Regular",
96
+ /** Verify only (authorization) */
97
+ VERIFY: "Verify",
98
+ /** Force transaction */
99
+ FORCE: "Force"
100
+ };
101
+ var HYP_VALIDATION_MODES = {
102
+ /** Auto commit (immediate capture) */
103
+ AUTO_COMM: "AutoComm",
104
+ /** Transaction only (authorization, requires manual capture) */
105
+ TX_ONLY: "TxOnly"
106
+ };
107
+ var HYP_CREDIT_TYPES = {
108
+ /** Regular credit card */
109
+ REGULAR: "1",
110
+ /** Token/Saved card */
111
+ TOKEN: "8"
112
+ };
113
+ var HYP_ERROR_MAP = {
114
+ "001": "card_declined",
115
+ "002": "invalid_card",
116
+ "003": "expired_card",
117
+ "004": "insufficient_funds",
118
+ "005": "invalid_cvc",
119
+ "006": "card_declined",
120
+ "033": "card_declined",
121
+ "034": "card_declined",
122
+ "051": "insufficient_funds",
123
+ "054": "expired_card",
124
+ "057": "card_declined",
125
+ "100": "processing_error",
126
+ "200": "authentication_required"
127
+ };
128
+ function mapHypStatus(resultCode) {
129
+ return HYP_RESULT_CODE_MAP[resultCode] ?? null;
130
+ }
131
+ function mapHypError(resultCode) {
132
+ return HYP_ERROR_MAP[resultCode] ?? "unknown";
133
+ }
134
+ function isHypSuccess(resultCode) {
135
+ return resultCode === "000";
136
+ }
137
+ function formatHypAmount(amountMinor) {
138
+ return amountMinor;
139
+ }
140
+
141
+ // src/providers/hyp/hyp-provider.ts
142
+ var HypProvider = class {
143
+ name = "hyp";
144
+ supportedCurrencies = HYP_SUPPORTED_CURRENCIES;
145
+ supportsRecurring = true;
146
+ supportsSplitPayments = false;
147
+ config;
148
+ constructor(config) {
149
+ if (!config.terminalNumber || !config.user || !config.password) {
150
+ throw new Error(
151
+ "HypProvider requires terminalNumber, user, and password in config"
152
+ );
153
+ }
154
+ const baseUrl = config.baseUrl ?? (config.environment === "production" ? DEFAULT_HYP_ENDPOINTS.production : DEFAULT_HYP_ENDPOINTS.test);
155
+ this.config = { ...config, baseUrl };
156
+ }
157
+ // ==========================================================================
158
+ // Payment Intent Operations
159
+ // ==========================================================================
160
+ /**
161
+ * Create a payment intent
162
+ *
163
+ * For Hyp, this generates a hosted payment page or prepares for direct charge.
164
+ */
165
+ async createPaymentIntent(params) {
166
+ try {
167
+ if (!params.paymentMethodId) {
168
+ return await this.createHostedPage(params);
169
+ }
170
+ return await this.chargeWithToken(params);
171
+ } catch (error) {
172
+ return this.handleError(error);
173
+ }
174
+ }
175
+ /**
176
+ * Create hosted payment page
177
+ */
178
+ async createHostedPage(params) {
179
+ const uniqueid = params.idempotencyKey;
180
+ const request = {
181
+ terminalNumber: this.config.terminalNumber,
182
+ user: this.config.user,
183
+ password: this.config.password,
184
+ total: formatHypAmount(params.amount.amountMinor),
185
+ currency: params.amount.currency,
186
+ transactionType: HYP_TRANSACTION_TYPES.DEBIT,
187
+ transactionCode: params.captureMethod === "manual" ? HYP_TRANSACTION_CODES.VERIFY : HYP_TRANSACTION_CODES.REGULAR,
188
+ validation: params.captureMethod === "manual" ? HYP_VALIDATION_MODES.TX_ONLY : HYP_VALIDATION_MODES.AUTO_COMM,
189
+ uniqueid,
190
+ successUrl: params.returnUrl,
191
+ errorUrl: params.returnUrl,
192
+ cancelUrl: params.returnUrl,
193
+ language: "en"
194
+ };
195
+ const response = await this.sendDoDealRequest(request);
196
+ if (!isHypSuccess(response.resultCode)) {
197
+ return {
198
+ success: false,
199
+ error: response.resultDescription ?? "Transaction failed",
200
+ errorCode: mapHypError(response.resultCode)
201
+ };
202
+ }
203
+ return {
204
+ success: true,
205
+ providerIntentId: response.transactionId ?? uniqueid,
206
+ redirectUrl: response.redirectUrl,
207
+ status: "created"
208
+ };
209
+ }
210
+ /**
211
+ * Charge with saved payment method token
212
+ */
213
+ async chargeWithToken(params) {
214
+ const uniqueid = params.idempotencyKey;
215
+ const request = {
216
+ terminalNumber: this.config.terminalNumber,
217
+ user: this.config.user,
218
+ password: this.config.password,
219
+ total: formatHypAmount(params.amount.amountMinor),
220
+ currency: params.amount.currency,
221
+ transactionType: HYP_TRANSACTION_TYPES.DEBIT,
222
+ transactionCode: params.captureMethod === "manual" ? HYP_TRANSACTION_CODES.VERIFY : HYP_TRANSACTION_CODES.REGULAR,
223
+ validation: params.captureMethod === "manual" ? HYP_VALIDATION_MODES.TX_ONLY : HYP_VALIDATION_MODES.AUTO_COMM,
224
+ creditType: HYP_CREDIT_TYPES.TOKEN,
225
+ cardToken: params.paymentMethodId,
226
+ uniqueid
227
+ };
228
+ const response = await this.sendDoDealRequest(request);
229
+ if (!isHypSuccess(response.resultCode)) {
230
+ return {
231
+ success: false,
232
+ error: response.resultDescription ?? "Transaction failed",
233
+ errorCode: mapHypError(response.resultCode)
234
+ };
235
+ }
236
+ const status = mapHypStatus(response.resultCode) ?? "created";
237
+ return {
238
+ success: true,
239
+ providerIntentId: response.transactionId ?? uniqueid,
240
+ status
241
+ };
242
+ }
243
+ async authorize(params) {
244
+ try {
245
+ return {
246
+ success: true,
247
+ authorizationCode: params.providerIntentId,
248
+ status: "authorized",
249
+ captureDeadline: (0, import_types.calculateCaptureDeadline)(/* @__PURE__ */ new Date())
250
+ };
251
+ } catch (error) {
252
+ return this.handleError(error);
253
+ }
254
+ }
255
+ async capture(params) {
256
+ try {
257
+ const request = {
258
+ terminalNumber: this.config.terminalNumber,
259
+ user: this.config.user,
260
+ password: this.config.password,
261
+ total: params.amount ? formatHypAmount(params.amount.amountMinor) : void 0,
262
+ currency: params.amount?.currency ?? "ILS",
263
+ transactionType: HYP_TRANSACTION_TYPES.DEBIT,
264
+ transactionCode: HYP_TRANSACTION_CODES.FORCE,
265
+ validation: HYP_VALIDATION_MODES.AUTO_COMM,
266
+ uniqueid: params.idempotencyKey,
267
+ authorizationCode: params.providerIntentId
268
+ };
269
+ const response = await this.sendDoDealRequest(request);
270
+ if (!isHypSuccess(response.resultCode)) {
271
+ return {
272
+ success: false,
273
+ error: response.resultDescription ?? "Capture failed",
274
+ errorCode: mapHypError(response.resultCode)
275
+ };
276
+ }
277
+ return {
278
+ success: true,
279
+ providerTransactionId: response.transactionId ?? params.providerIntentId,
280
+ status: "captured",
281
+ capturedAmount: params.amount ?? {
282
+ amountMinor: 0,
283
+ currency: "ILS"
284
+ }
285
+ };
286
+ } catch (error) {
287
+ return this.handleError(error);
288
+ }
289
+ }
290
+ async void(params) {
291
+ try {
292
+ const request = {
293
+ terminalNumber: this.config.terminalNumber,
294
+ user: this.config.user,
295
+ password: this.config.password,
296
+ transactionId: params.providerIntentId,
297
+ currency: "ILS",
298
+ uniqueid: params.providerIntentId
299
+ };
300
+ const response = await this.sendRefundRequest(request);
301
+ if (!isHypSuccess(response.resultCode)) {
302
+ return {
303
+ success: false,
304
+ error: response.resultDescription ?? "Void failed"
305
+ };
306
+ }
307
+ return { success: true, status: "voided" };
308
+ } catch (error) {
309
+ return this.handleError(error);
310
+ }
311
+ }
312
+ // ==========================================================================
313
+ // Refunds
314
+ // ==========================================================================
315
+ async refund(params) {
316
+ try {
317
+ const request = {
318
+ terminalNumber: this.config.terminalNumber,
319
+ user: this.config.user,
320
+ password: this.config.password,
321
+ transactionId: params.providerTransactionId,
322
+ total: params.amount ? formatHypAmount(params.amount.amountMinor) : void 0,
323
+ currency: params.amount?.currency ?? "ILS",
324
+ uniqueid: params.idempotencyKey
325
+ };
326
+ const response = await this.sendRefundRequest(request);
327
+ if (!isHypSuccess(response.resultCode)) {
328
+ return {
329
+ success: false,
330
+ error: response.resultDescription ?? "Refund failed"
331
+ };
332
+ }
333
+ return {
334
+ success: true,
335
+ providerRefundId: response.transactionId ?? params.idempotencyKey,
336
+ refundedAmount: params.amount ?? {
337
+ amountMinor: 0,
338
+ currency: "ILS"
339
+ },
340
+ status: "succeeded"
341
+ };
342
+ } catch (error) {
343
+ return this.handleError(error);
344
+ }
345
+ }
346
+ // ==========================================================================
347
+ // Payment Methods (Tokenization)
348
+ // ==========================================================================
349
+ async createSetupIntent(params) {
350
+ try {
351
+ const uniqueid = `setup_${params.userId}_${Date.now()}`;
352
+ const request = {
353
+ terminalNumber: this.config.terminalNumber,
354
+ user: this.config.user,
355
+ password: this.config.password,
356
+ total: 0,
357
+ currency: "ILS",
358
+ transactionType: HYP_TRANSACTION_TYPES.DEBIT,
359
+ transactionCode: HYP_TRANSACTION_CODES.VERIFY,
360
+ validation: HYP_VALIDATION_MODES.TX_ONLY,
361
+ creditType: HYP_CREDIT_TYPES.TOKEN,
362
+ customerData: params.customerId ?? params.userId,
363
+ uniqueid,
364
+ language: "en"
365
+ };
366
+ const response = await this.sendDoDealRequest(request);
367
+ if (!isHypSuccess(response.resultCode)) {
368
+ return {
369
+ success: false,
370
+ error: response.resultDescription ?? "Setup failed"
371
+ };
372
+ }
373
+ return {
374
+ success: true,
375
+ setupIntentId: response.transactionId ?? uniqueid,
376
+ clientSecret: response.redirectUrl
377
+ };
378
+ } catch (error) {
379
+ return this.handleError(error);
380
+ }
381
+ }
382
+ async savePaymentMethod(params) {
383
+ try {
384
+ const cardToken = params.setupData.cardToken;
385
+ const cardMask = params.setupData.cardMask;
386
+ const cardBrand = params.setupData.cardBrand;
387
+ const cardExpiration = params.setupData.cardExpiration;
388
+ if (!cardToken) {
389
+ return {
390
+ success: false,
391
+ error: "No card token received"
392
+ };
393
+ }
394
+ return {
395
+ success: true,
396
+ paymentMethodId: cardToken,
397
+ cardBrand: cardBrand ?? "unknown",
398
+ cardLast4: cardMask?.slice(-4) ?? "0000",
399
+ cardExpMonth: cardExpiration?.substring(0, 2) ?? "01",
400
+ cardExpYear: `20${cardExpiration?.substring(2, 4) ?? "99"}`
401
+ };
402
+ } catch (error) {
403
+ return this.handleError(error);
404
+ }
405
+ }
406
+ async deletePaymentMethod(_paymentMethodId) {
407
+ return {
408
+ success: true
409
+ };
410
+ }
411
+ // ==========================================================================
412
+ // Customer Management
413
+ // ==========================================================================
414
+ async createCustomer(params) {
415
+ return {
416
+ success: true,
417
+ customerId: params.userId
418
+ };
419
+ }
420
+ async getOrCreateCustomer(userId, _email) {
421
+ return {
422
+ success: true,
423
+ customerId: userId
424
+ };
425
+ }
426
+ // ==========================================================================
427
+ // Health & Security
428
+ // ==========================================================================
429
+ async getHealth() {
430
+ const start = Date.now();
431
+ try {
432
+ const response = await fetch(`${this.config.baseUrl}/xpo/Relay`, {
433
+ method: "POST",
434
+ headers: {
435
+ "Content-Type": "text/xml"
436
+ },
437
+ body: this.buildTestXML(),
438
+ signal: AbortSignal.timeout(5e3)
439
+ });
440
+ const healthy = response.ok;
441
+ return {
442
+ provider: "hyp",
443
+ healthy,
444
+ lastChecked: /* @__PURE__ */ new Date(),
445
+ avgLatencyMs: Date.now() - start,
446
+ circuitBreakerOpen: false
447
+ };
448
+ } catch {
449
+ return {
450
+ provider: "hyp",
451
+ healthy: false,
452
+ lastChecked: /* @__PURE__ */ new Date(),
453
+ circuitBreakerOpen: false
454
+ };
455
+ }
456
+ }
457
+ validateWebhookSignature(_payload, _signature) {
458
+ if (!this.config.webhookSecret) return false;
459
+ return !!this.config.webhookSecret;
460
+ }
461
+ async getPaymentIntentStatus(_providerIntentId) {
462
+ return {
463
+ status: "unknown",
464
+ error: "Status query not supported by Hyp basic integration"
465
+ };
466
+ }
467
+ // ==========================================================================
468
+ // XML Request Builders
469
+ // ==========================================================================
470
+ buildDoDealXML(request) {
471
+ const parts = [];
472
+ parts.push('<?xml version="1.0" encoding="utf-8"?>');
473
+ parts.push("<ashrait>");
474
+ parts.push("<request>");
475
+ parts.push(`<version>1000</version>`);
476
+ parts.push("<language>ENG</language>");
477
+ parts.push("<command>doDeal</command>");
478
+ parts.push(`<terminalNumber>${this.escapeXml(request.terminalNumber)}</terminalNumber>`);
479
+ parts.push(`<user>${this.escapeXml(request.user)}</user>`);
480
+ parts.push(`<password>${this.escapeXml(request.password)}</password>`);
481
+ if (request.cardNo) {
482
+ parts.push(`<cardNo>${this.escapeXml(request.cardNo)}</cardNo>`);
483
+ }
484
+ if (request.cardExpiration) {
485
+ parts.push(`<cardExpiration>${this.escapeXml(request.cardExpiration)}</cardExpiration>`);
486
+ }
487
+ if (request.cvv) {
488
+ parts.push(`<cvv>${this.escapeXml(request.cvv)}</cvv>`);
489
+ }
490
+ if (request.cardToken) {
491
+ parts.push(`<cardToken>${this.escapeXml(request.cardToken)}</cardToken>`);
492
+ }
493
+ if (request.authorizationCode) {
494
+ parts.push(`<authNumber>${this.escapeXml(request.authorizationCode)}</authNumber>`);
495
+ }
496
+ if (request.total !== void 0) {
497
+ parts.push(`<total>${request.total}</total>`);
498
+ }
499
+ parts.push(`<currency>${this.escapeXml(request.currency)}</currency>`);
500
+ parts.push(`<transactionType>${this.escapeXml(request.transactionType)}</transactionType>`);
501
+ if (request.transactionCode) {
502
+ parts.push(`<transactionCode>${this.escapeXml(request.transactionCode)}</transactionCode>`);
503
+ }
504
+ if (request.creditType) {
505
+ parts.push(`<creditType>${request.creditType}</creditType>`);
506
+ }
507
+ if (request.validation) {
508
+ parts.push(`<validation>${this.escapeXml(request.validation)}</validation>`);
509
+ }
510
+ if (request.uniqueid) {
511
+ parts.push(`<uniqueid>${this.escapeXml(request.uniqueid)}</uniqueid>`);
512
+ }
513
+ if (request.customerData) {
514
+ parts.push(`<customerData>${this.escapeXml(request.customerData)}</customerData>`);
515
+ }
516
+ if (request.successUrl) {
517
+ parts.push(`<successUrl>${this.escapeXml(request.successUrl)}</successUrl>`);
518
+ }
519
+ if (request.errorUrl) {
520
+ parts.push(`<errorUrl>${this.escapeXml(request.errorUrl)}</errorUrl>`);
521
+ }
522
+ if (request.cancelUrl) {
523
+ parts.push(`<cancelUrl>${this.escapeXml(request.cancelUrl)}</cancelUrl>`);
524
+ }
525
+ parts.push("</request>");
526
+ parts.push("</ashrait>");
527
+ return parts.join("");
528
+ }
529
+ buildRefundXML(request) {
530
+ const parts = [];
531
+ parts.push('<?xml version="1.0" encoding="utf-8"?>');
532
+ parts.push("<ashrait>");
533
+ parts.push("<request>");
534
+ parts.push(`<version>1000</version>`);
535
+ parts.push("<language>ENG</language>");
536
+ parts.push("<command>refundDeal</command>");
537
+ parts.push(`<terminalNumber>${this.escapeXml(request.terminalNumber)}</terminalNumber>`);
538
+ parts.push(`<user>${this.escapeXml(request.user)}</user>`);
539
+ parts.push(`<password>${this.escapeXml(request.password)}</password>`);
540
+ parts.push(`<transactionId>${this.escapeXml(request.transactionId)}</transactionId>`);
541
+ parts.push(`<currency>${this.escapeXml(request.currency)}</currency>`);
542
+ if (request.total !== void 0) {
543
+ parts.push(`<total>${request.total}</total>`);
544
+ }
545
+ if (request.uniqueid) {
546
+ parts.push(`<uniqueid>${this.escapeXml(request.uniqueid)}</uniqueid>`);
547
+ }
548
+ parts.push("</request>");
549
+ parts.push("</ashrait>");
550
+ return parts.join("");
551
+ }
552
+ buildTestXML() {
553
+ return `<?xml version="1.0" encoding="utf-8"?>
554
+ <ashrait>
555
+ <request>
556
+ <version>1000</version>
557
+ <language>ENG</language>
558
+ <command>echo</command>
559
+ </request>
560
+ </ashrait>`;
561
+ }
562
+ // ==========================================================================
563
+ // HTTP Helpers
564
+ // ==========================================================================
565
+ async sendDoDealRequest(request) {
566
+ const xmlBody = this.buildDoDealXML(request);
567
+ const response = await fetch(`${this.config.baseUrl}/xpo/Relay`, {
568
+ method: "POST",
569
+ headers: {
570
+ "Content-Type": "text/xml; charset=utf-8"
571
+ },
572
+ body: xmlBody
573
+ });
574
+ if (!response.ok) {
575
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
576
+ }
577
+ const xmlResponse = await response.text();
578
+ return this.parseDoDealResponse(xmlResponse);
579
+ }
580
+ async sendRefundRequest(request) {
581
+ const xmlBody = this.buildRefundXML(request);
582
+ const response = await fetch(`${this.config.baseUrl}/xpo/Relay`, {
583
+ method: "POST",
584
+ headers: {
585
+ "Content-Type": "text/xml; charset=utf-8"
586
+ },
587
+ body: xmlBody
588
+ });
589
+ if (!response.ok) {
590
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
591
+ }
592
+ const xmlResponse = await response.text();
593
+ return this.parseRefundResponse(xmlResponse);
594
+ }
595
+ // ==========================================================================
596
+ // XML Parsing
597
+ // ==========================================================================
598
+ parseDoDealResponse(xml) {
599
+ return {
600
+ resultCode: this.extractXmlValue(xml, "resultCode") ?? "100",
601
+ resultDescription: this.extractXmlValue(xml, "resultDescription"),
602
+ transactionId: this.extractXmlValue(xml, "transactionId"),
603
+ authorizationCode: this.extractXmlValue(xml, "authorizationCode"),
604
+ voucherNumber: this.extractXmlValue(xml, "voucherNumber"),
605
+ cardToken: this.extractXmlValue(xml, "cardToken"),
606
+ cardMask: this.extractXmlValue(xml, "cardMask"),
607
+ cardBrand: this.extractXmlValue(xml, "cardBrand"),
608
+ cardExpiration: this.extractXmlValue(xml, "cardExpiration"),
609
+ redirectUrl: this.extractXmlValue(xml, "redirectUrl"),
610
+ uniqueid: this.extractXmlValue(xml, "uniqueid"),
611
+ rawXml: xml
612
+ };
613
+ }
614
+ parseRefundResponse(xml) {
615
+ return {
616
+ resultCode: this.extractXmlValue(xml, "resultCode") ?? "100",
617
+ resultDescription: this.extractXmlValue(xml, "resultDescription"),
618
+ transactionId: this.extractXmlValue(xml, "transactionId"),
619
+ authorizationCode: this.extractXmlValue(xml, "authorizationCode"),
620
+ uniqueid: this.extractXmlValue(xml, "uniqueid")
621
+ };
622
+ }
623
+ extractXmlValue(xml, tagName) {
624
+ const regex = new RegExp(`<${tagName}>([^<]*)</${tagName}>`, "i");
625
+ const match = xml.match(regex);
626
+ return match ? match[1].trim() : void 0;
627
+ }
628
+ escapeXml(str) {
629
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
630
+ }
631
+ // ==========================================================================
632
+ // Error Handling
633
+ // ==========================================================================
634
+ handleError(error) {
635
+ if (error instanceof Error) {
636
+ return {
637
+ success: false,
638
+ error: error.message,
639
+ errorCode: "unknown"
640
+ };
641
+ }
642
+ return {
643
+ success: false,
644
+ error: "Unknown error occurred",
645
+ errorCode: "unknown"
646
+ };
647
+ }
648
+ };
649
+
650
+ // src/providers/cardcom/cardcom-provider.ts
651
+ var crypto = __toESM(require("crypto"), 1);
652
+ var import_types2 = require("@nehorai/payments/types");
653
+
654
+ // src/providers/cardcom/cardcom-types.ts
655
+ var CARDCOM_API_BASE = "https://secure.cardcom.solutions";
656
+ var CARDCOM_ENDPOINTS = {
657
+ LOW_PROFILE_CREATE: "/api/v11/LowProfile/Create",
658
+ LOW_PROFILE_STATUS: "/Interface/BillGoldGetLowProfileIndicator.aspx",
659
+ DIRECT_CHARGE: "/api/v11/Transactions/Transaction",
660
+ REFUND: "/api/v11/Transactions/RefundByTransactionId"
661
+ };
662
+ var CARDCOM_SUPPORTED_CURRENCIES = ["ILS", "USD", "EUR", "GBP"];
663
+ var CARDCOM_WEBHOOK_EVENTS = [
664
+ "payment.completed",
665
+ "payment.declined",
666
+ "payment.authorized"
667
+ ];
668
+ var CARDCOM_DEAL_RESPONSE_ACTIONS = {
669
+ 0: "pending",
670
+ 1: "approved",
671
+ 2: "declined",
672
+ 3: "error"
673
+ };
674
+ function mapCardcomDealResponseToStatus(dealResponse) {
675
+ switch (dealResponse) {
676
+ case 0:
677
+ return "pending_authorization";
678
+ case 1:
679
+ return "captured";
680
+ case 2:
681
+ return "failed";
682
+ case 3:
683
+ return "failed";
684
+ default:
685
+ return "failed";
686
+ }
687
+ }
688
+ function mapCardcomError(responseCode) {
689
+ const errorMessages = {
690
+ 0: "Success",
691
+ 1: "General error",
692
+ 2: "Invalid API credentials",
693
+ 3: "Invalid terminal number",
694
+ 4: "Invalid operation type",
695
+ 5: "Invalid card details",
696
+ 6: "Card declined by issuer",
697
+ 7: "Insufficient funds",
698
+ 8: "Invalid amount",
699
+ 9: "Transaction not found",
700
+ 10: "Duplicate transaction",
701
+ 11: "Terminal not active",
702
+ 12: "CVV validation failed",
703
+ 13: "Card expired",
704
+ 14: "Invalid currency",
705
+ 15: "Operation not supported"
706
+ };
707
+ return errorMessages[responseCode] ?? `Error code ${responseCode}`;
708
+ }
709
+ var CARDCOM_CURRENCY_CODES = {
710
+ ILS: 1,
711
+ USD: 2,
712
+ EUR: 3,
713
+ GBP: 4
714
+ };
715
+ function getCurrencyCode(currency) {
716
+ return CARDCOM_CURRENCY_CODES[currency.toUpperCase()] ?? 1;
717
+ }
718
+
719
+ // src/providers/cardcom/cardcom-provider.ts
720
+ var CardcomProvider = class {
721
+ name = "cardcom";
722
+ supportedCurrencies = CARDCOM_SUPPORTED_CURRENCIES;
723
+ supportsRecurring = true;
724
+ supportsSplitPayments = false;
725
+ config;
726
+ constructor(config) {
727
+ if (!config.terminalNumber || !config.apiName || !config.apiPassword) {
728
+ throw new Error(
729
+ "CardcomProvider requires terminalNumber, apiName, and apiPassword in config"
730
+ );
731
+ }
732
+ this.config = config;
733
+ }
734
+ // ==========================================================================
735
+ // Payment Intent Operations
736
+ // ==========================================================================
737
+ async createPaymentIntent(params) {
738
+ try {
739
+ const amountMajor = params.amount.amountMinor / 100;
740
+ const operation = params.captureMethod === "manual" ? 4 /* SUSPEND_DEAL_ONLY */ : params.metadata?.savePaymentMethod ? 2 /* BILL_AND_CREATE_TOKEN */ : 1 /* BILL_ONLY */;
741
+ const request = {
742
+ TerminalNumber: this.config.terminalNumber,
743
+ ApiName: this.config.apiName,
744
+ ApiPassword: this.config.apiPassword,
745
+ Sum: amountMajor,
746
+ CoinID: getCurrencyCode(params.amount.currency),
747
+ Operation: operation,
748
+ Language: "en",
749
+ ReturnUrl: params.returnUrl,
750
+ ErrorUrl: params.returnUrl,
751
+ ProductName: params.description ?? "Payment",
752
+ InternalDealNumber: params.idempotencyKey,
753
+ SendEmail: false
754
+ };
755
+ if (params.metadata?.customerName) {
756
+ request.CustomerName = String(params.metadata.customerName);
757
+ }
758
+ if (params.metadata?.customerEmail) {
759
+ request.Email = String(params.metadata.customerEmail);
760
+ }
761
+ const response = await this.makeRequest(
762
+ CARDCOM_ENDPOINTS.LOW_PROFILE_CREATE,
763
+ request
764
+ );
765
+ if (response.ResponseCode !== 0 || !response.PaymentUrl) {
766
+ return {
767
+ success: false,
768
+ error: mapCardcomError(response.ResponseCode),
769
+ errorCode: String(response.ResponseCode)
770
+ };
771
+ }
772
+ return {
773
+ success: true,
774
+ providerIntentId: response.LowProfileCode,
775
+ redirectUrl: response.PaymentUrl,
776
+ status: "created"
777
+ };
778
+ } catch (error) {
779
+ return this.handleError(error);
780
+ }
781
+ }
782
+ async authorize(params) {
783
+ try {
784
+ const statusResponse = await this.getLowProfileStatus(
785
+ params.providerIntentId
786
+ );
787
+ if (!statusResponse.success || !statusResponse.data) {
788
+ return {
789
+ success: false,
790
+ error: statusResponse.error ?? "Failed to check payment status"
791
+ };
792
+ }
793
+ const status = statusResponse.data;
794
+ if (status.DealResponse === 1) {
795
+ return {
796
+ success: true,
797
+ authorizationCode: status.InternalDealNumber ?? params.providerIntentId,
798
+ status: "authorized",
799
+ captureDeadline: (0, import_types2.calculateCaptureDeadline)(/* @__PURE__ */ new Date())
800
+ };
801
+ }
802
+ if (status.DealResponse === 2) {
803
+ return {
804
+ success: false,
805
+ error: "Payment declined",
806
+ status: "failed"
807
+ };
808
+ }
809
+ return {
810
+ success: false,
811
+ error: "Payment not yet completed",
812
+ status: "pending_authorization"
813
+ };
814
+ } catch (error) {
815
+ return this.handleError(error);
816
+ }
817
+ }
818
+ async capture(params) {
819
+ try {
820
+ const statusResponse = await this.getLowProfileStatus(
821
+ params.providerIntentId
822
+ );
823
+ if (!statusResponse.success || !statusResponse.data) {
824
+ return {
825
+ success: false,
826
+ error: statusResponse.error ?? "Failed to capture payment"
827
+ };
828
+ }
829
+ const status = statusResponse.data;
830
+ if (status.DealResponse === 1) {
831
+ return {
832
+ success: true,
833
+ providerTransactionId: status.InternalDealNumber ?? params.providerIntentId,
834
+ status: "captured",
835
+ capturedAmount: {
836
+ amountMinor: Math.round((status.Amount ?? 0) * 100),
837
+ currency: status.Currency ?? params.amount?.currency ?? "ILS"
838
+ }
839
+ };
840
+ }
841
+ return {
842
+ success: false,
843
+ error: "Payment not authorized for capture",
844
+ status: mapCardcomDealResponseToStatus(status.DealResponse ?? 3)
845
+ };
846
+ } catch (error) {
847
+ return this.handleError(error);
848
+ }
849
+ }
850
+ async void(_params) {
851
+ return {
852
+ success: false,
853
+ error: "Void operation not supported via API. Please use Cardcom merchant dashboard."
854
+ };
855
+ }
856
+ async refund(params) {
857
+ try {
858
+ const refundAmount = params.amount ? params.amount.amountMinor / 100 : void 0;
859
+ if (!refundAmount) {
860
+ return {
861
+ success: false,
862
+ error: "Refund amount is required"
863
+ };
864
+ }
865
+ const request = {
866
+ TerminalNumber: this.config.terminalNumber,
867
+ ApiName: this.config.apiName,
868
+ ApiPassword: this.config.apiPassword,
869
+ InternalDealNumber: params.providerTransactionId,
870
+ Amount: refundAmount,
871
+ CoinID: params.amount ? getCurrencyCode(params.amount.currency) : 1
872
+ };
873
+ const response = await this.makeRequest(
874
+ CARDCOM_ENDPOINTS.REFUND,
875
+ request
876
+ );
877
+ if (response.ResponseCode !== 0) {
878
+ return {
879
+ success: false,
880
+ error: mapCardcomError(response.ResponseCode)
881
+ };
882
+ }
883
+ return {
884
+ success: true,
885
+ providerRefundId: response.InternalDealNumber ?? params.providerTransactionId,
886
+ refundedAmount: {
887
+ amountMinor: Math.round((response.Amount ?? 0) * 100),
888
+ currency: params.amount?.currency ?? "ILS"
889
+ },
890
+ status: "succeeded"
891
+ };
892
+ } catch (error) {
893
+ return this.handleError(error);
894
+ }
895
+ }
896
+ // ==========================================================================
897
+ // Payment Method Tokenization
898
+ // ==========================================================================
899
+ async createSetupIntent(params) {
900
+ try {
901
+ const request = {
902
+ TerminalNumber: this.config.terminalNumber,
903
+ ApiName: this.config.apiName,
904
+ ApiPassword: this.config.apiPassword,
905
+ Sum: 0,
906
+ Operation: 3 /* CREATE_TOKEN_ONLY */,
907
+ Language: "en",
908
+ InternalDealNumber: `setup_${params.userId}_${Date.now()}`
909
+ };
910
+ const response = await this.makeRequest(
911
+ CARDCOM_ENDPOINTS.LOW_PROFILE_CREATE,
912
+ request
913
+ );
914
+ if (response.ResponseCode !== 0 || !response.PaymentUrl) {
915
+ return {
916
+ success: false,
917
+ error: mapCardcomError(response.ResponseCode)
918
+ };
919
+ }
920
+ return {
921
+ success: true,
922
+ setupIntentId: response.LowProfileCode,
923
+ clientSecret: response.PaymentUrl
924
+ };
925
+ } catch (error) {
926
+ return this.handleError(error);
927
+ }
928
+ }
929
+ async savePaymentMethod(params) {
930
+ try {
931
+ const lowProfileCode = params.setupData.lowProfileCode;
932
+ if (!lowProfileCode) {
933
+ return {
934
+ success: false,
935
+ error: "Low profile code is required"
936
+ };
937
+ }
938
+ const statusResponse = await this.getLowProfileStatus(lowProfileCode);
939
+ if (!statusResponse.success || !statusResponse.data) {
940
+ return {
941
+ success: false,
942
+ error: statusResponse.error ?? "Failed to retrieve payment method"
943
+ };
944
+ }
945
+ const status = statusResponse.data;
946
+ if (!status.Token) {
947
+ return {
948
+ success: false,
949
+ error: "No token created"
950
+ };
951
+ }
952
+ const [expMonth, expYear] = (status.CardExpiration ?? "/").split("/");
953
+ return {
954
+ success: true,
955
+ paymentMethodId: status.Token,
956
+ cardBrand: status.CardType ?? "unknown",
957
+ cardLast4: status.CardMask?.slice(-4),
958
+ cardExpMonth: expMonth?.padStart(2, "0"),
959
+ cardExpYear: expYear ? `20${expYear}` : void 0,
960
+ cardBin: status.CardBin
961
+ };
962
+ } catch (error) {
963
+ return this.handleError(error);
964
+ }
965
+ }
966
+ async deletePaymentMethod(_paymentMethodId) {
967
+ return {
968
+ success: true
969
+ };
970
+ }
971
+ // ==========================================================================
972
+ // Customer Management
973
+ // ==========================================================================
974
+ async createCustomer(params) {
975
+ return {
976
+ success: true,
977
+ customerId: params.userId
978
+ };
979
+ }
980
+ async getOrCreateCustomer(userId, email) {
981
+ return this.createCustomer({ userId, email });
982
+ }
983
+ // ==========================================================================
984
+ // Health & Status
985
+ // ==========================================================================
986
+ async getHealth() {
987
+ const start = Date.now();
988
+ try {
989
+ const request = {
990
+ TerminalNumber: this.config.terminalNumber,
991
+ ApiName: this.config.apiName,
992
+ ApiPassword: this.config.apiPassword,
993
+ Sum: 1,
994
+ Operation: 1 /* BILL_ONLY */,
995
+ InternalDealNumber: `health_check_${Date.now()}`
996
+ };
997
+ const response = await this.makeRequest(
998
+ CARDCOM_ENDPOINTS.LOW_PROFILE_CREATE,
999
+ request
1000
+ );
1001
+ const healthy = response.ResponseCode === 0 || response.ResponseCode === 1;
1002
+ return {
1003
+ provider: "cardcom",
1004
+ healthy,
1005
+ lastChecked: /* @__PURE__ */ new Date(),
1006
+ avgLatencyMs: Date.now() - start,
1007
+ circuitBreakerOpen: false
1008
+ };
1009
+ } catch {
1010
+ return {
1011
+ provider: "cardcom",
1012
+ healthy: false,
1013
+ lastChecked: /* @__PURE__ */ new Date(),
1014
+ circuitBreakerOpen: false
1015
+ };
1016
+ }
1017
+ }
1018
+ validateWebhookSignature(payload, signature) {
1019
+ if (!this.config.webhookSecret) {
1020
+ return false;
1021
+ }
1022
+ try {
1023
+ const expectedSignature = crypto.createHmac("sha256", this.config.webhookSecret).update(payload).digest("hex");
1024
+ return crypto.timingSafeEqual(
1025
+ Buffer.from(signature),
1026
+ Buffer.from(expectedSignature)
1027
+ );
1028
+ } catch {
1029
+ return false;
1030
+ }
1031
+ }
1032
+ async getPaymentIntentStatus(providerIntentId) {
1033
+ try {
1034
+ const result = await this.getLowProfileStatus(providerIntentId);
1035
+ if (!result.success || !result.data) {
1036
+ return {
1037
+ status: "unknown",
1038
+ error: result.error
1039
+ };
1040
+ }
1041
+ const status = mapCardcomDealResponseToStatus(
1042
+ result.data.DealResponse ?? 0
1043
+ );
1044
+ return { status };
1045
+ } catch (error) {
1046
+ return {
1047
+ status: "unknown",
1048
+ error: error instanceof Error ? error.message : "Unknown error"
1049
+ };
1050
+ }
1051
+ }
1052
+ // ==========================================================================
1053
+ // Helper Methods
1054
+ // ==========================================================================
1055
+ async makeRequest(endpoint, data) {
1056
+ const url = `${CARDCOM_API_BASE}${endpoint}`;
1057
+ const response = await fetch(url, {
1058
+ method: "POST",
1059
+ headers: {
1060
+ "Content-Type": "application/json"
1061
+ },
1062
+ body: JSON.stringify(data)
1063
+ });
1064
+ if (!response.ok) {
1065
+ throw new Error(`Cardcom API error: ${response.status} ${response.statusText}`);
1066
+ }
1067
+ return response.json();
1068
+ }
1069
+ async getLowProfileStatus(lowProfileCode) {
1070
+ try {
1071
+ const params = new URLSearchParams({
1072
+ terminalnumber: this.config.terminalNumber,
1073
+ lowprofilecode: lowProfileCode,
1074
+ username: this.config.apiName
1075
+ });
1076
+ const url = `${CARDCOM_API_BASE}${CARDCOM_ENDPOINTS.LOW_PROFILE_STATUS}?${params}`;
1077
+ const response = await fetch(url, {
1078
+ method: "GET"
1079
+ });
1080
+ if (!response.ok) {
1081
+ return {
1082
+ success: false,
1083
+ error: `Status check failed: ${response.status}`
1084
+ };
1085
+ }
1086
+ const data = await response.json();
1087
+ if (data.ResponseCode !== 0) {
1088
+ return {
1089
+ success: false,
1090
+ error: mapCardcomError(data.ResponseCode)
1091
+ };
1092
+ }
1093
+ return { success: true, data };
1094
+ } catch (error) {
1095
+ return {
1096
+ success: false,
1097
+ error: error instanceof Error ? error.message : "Status check failed"
1098
+ };
1099
+ }
1100
+ }
1101
+ handleError(error) {
1102
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1103
+ return {
1104
+ success: false,
1105
+ error: errorMessage
1106
+ };
1107
+ }
1108
+ };
1109
+
1110
+ // src/providers/hyp/hyp-webhook-handler.ts
1111
+ var HYP_EVENT_TYPES = {
1112
+ TRANSACTION_SUCCESS: "transaction.success",
1113
+ TRANSACTION_FAILED: "transaction.failed",
1114
+ TRANSACTION_PENDING: "transaction.pending",
1115
+ REFUND_SUCCESS: "refund.success",
1116
+ REFUND_FAILED: "refund.failed"
1117
+ };
1118
+ var HypWebhookHandler = class {
1119
+ provider = "hyp";
1120
+ supportedEventTypes = Object.values(HYP_EVENT_TYPES);
1121
+ parseEvent(rawPayload) {
1122
+ try {
1123
+ const resultCode = String(rawPayload.resultCode ?? "100");
1124
+ const resultDescription = String(rawPayload.resultDescription ?? "");
1125
+ const transactionId = String(rawPayload.transactionId ?? "");
1126
+ const uniqueid = String(rawPayload.uniqueid ?? "");
1127
+ const total = Number(rawPayload.total ?? 0);
1128
+ const currency = String(rawPayload.currency ?? "ILS");
1129
+ const eventType = this.determineEventType(resultCode);
1130
+ const newStatus = mapHypStatus(resultCode);
1131
+ const event = {
1132
+ provider: "hyp",
1133
+ eventId: uniqueid || transactionId || `hyp_${Date.now()}`,
1134
+ eventType,
1135
+ providerTransactionId: transactionId,
1136
+ amountMinor: total,
1137
+ currency,
1138
+ newStatus: newStatus ?? void 0,
1139
+ error: isHypSuccess(resultCode) ? void 0 : {
1140
+ code: resultCode,
1141
+ message: resultDescription
1142
+ },
1143
+ timestamp: /* @__PURE__ */ new Date(),
1144
+ rawPayload
1145
+ };
1146
+ return {
1147
+ success: true,
1148
+ event
1149
+ };
1150
+ } catch (error) {
1151
+ return {
1152
+ success: false,
1153
+ error: error instanceof Error ? error.message : "Failed to parse webhook"
1154
+ };
1155
+ }
1156
+ }
1157
+ async processEvent(event) {
1158
+ try {
1159
+ if (!event.providerTransactionId) {
1160
+ return {
1161
+ success: false,
1162
+ error: "Missing transaction ID in webhook",
1163
+ action: "ignored_event_type"
1164
+ };
1165
+ }
1166
+ const action = this.determineAction(event);
1167
+ return {
1168
+ success: true,
1169
+ transactionId: event.providerTransactionId,
1170
+ action
1171
+ };
1172
+ } catch (error) {
1173
+ return {
1174
+ success: false,
1175
+ error: error instanceof Error ? error.message : "Processing failed",
1176
+ action: "ignored_event_type"
1177
+ };
1178
+ }
1179
+ }
1180
+ canHandle(eventType) {
1181
+ return this.supportedEventTypes.includes(
1182
+ eventType
1183
+ );
1184
+ }
1185
+ async reconcile(_transactionId, _providerTransactionId) {
1186
+ return {
1187
+ reconciled: false,
1188
+ finalStatus: "pending_authorization",
1189
+ source: "webhook",
1190
+ statusChanged: false
1191
+ };
1192
+ }
1193
+ mapEventType(providerEventType) {
1194
+ return providerEventType;
1195
+ }
1196
+ mapStatus(providerStatus) {
1197
+ return mapHypStatus(providerStatus);
1198
+ }
1199
+ // ==========================================================================
1200
+ // Helper Methods
1201
+ // ==========================================================================
1202
+ determineEventType(resultCode) {
1203
+ if (isHypSuccess(resultCode)) {
1204
+ return HYP_EVENT_TYPES.TRANSACTION_SUCCESS;
1205
+ }
1206
+ if (resultCode === "200") {
1207
+ return HYP_EVENT_TYPES.TRANSACTION_PENDING;
1208
+ }
1209
+ return HYP_EVENT_TYPES.TRANSACTION_FAILED;
1210
+ }
1211
+ determineAction(event) {
1212
+ switch (event.eventType) {
1213
+ case HYP_EVENT_TYPES.TRANSACTION_SUCCESS:
1214
+ case HYP_EVENT_TYPES.TRANSACTION_FAILED:
1215
+ case HYP_EVENT_TYPES.TRANSACTION_PENDING:
1216
+ case HYP_EVENT_TYPES.REFUND_FAILED:
1217
+ return "status_updated";
1218
+ case HYP_EVENT_TYPES.REFUND_SUCCESS:
1219
+ return "refund_processed";
1220
+ default:
1221
+ return "ignored_event_type";
1222
+ }
1223
+ }
1224
+ // ==========================================================================
1225
+ // Webhook Validation
1226
+ // ==========================================================================
1227
+ validateSignature(payload, signature, secret) {
1228
+ const hasRequiredParams = payload.resultCode !== void 0 && (payload.transactionId !== void 0 || payload.uniqueid !== void 0);
1229
+ if (!hasRequiredParams) {
1230
+ return false;
1231
+ }
1232
+ if (secret && signature) {
1233
+ return this.validateHMAC(payload, signature, secret);
1234
+ }
1235
+ return hasRequiredParams;
1236
+ }
1237
+ validateHMAC(_payload, signature, _secret) {
1238
+ try {
1239
+ return !!signature;
1240
+ } catch {
1241
+ return false;
1242
+ }
1243
+ }
1244
+ // ==========================================================================
1245
+ // Callback URL Builders
1246
+ // ==========================================================================
1247
+ buildSuccessUrl(baseUrl, transactionId) {
1248
+ return `${baseUrl}/api/payments/hyp/callback?status=success&txId=${transactionId}`;
1249
+ }
1250
+ buildErrorUrl(baseUrl, transactionId) {
1251
+ return `${baseUrl}/api/payments/hyp/callback?status=error&txId=${transactionId}`;
1252
+ }
1253
+ buildCancelUrl(baseUrl, transactionId) {
1254
+ return `${baseUrl}/api/payments/hyp/callback?status=cancel&txId=${transactionId}`;
1255
+ }
1256
+ // ==========================================================================
1257
+ // Response Parsing
1258
+ // ==========================================================================
1259
+ extractErrorDetails(payload) {
1260
+ const resultCode = String(payload.resultCode ?? "unknown");
1261
+ const resultDescription = String(payload.resultDescription ?? "Unknown error");
1262
+ return {
1263
+ code: resultCode,
1264
+ message: resultDescription,
1265
+ userMessage: this.getUserFriendlyMessage(resultCode)
1266
+ };
1267
+ }
1268
+ getUserFriendlyMessage(resultCode) {
1269
+ const errorCode = mapHypError(resultCode);
1270
+ const messages = {
1271
+ card_declined: "Your card was declined. Please try another payment method.",
1272
+ invalid_card: "The card information is invalid. Please check and try again.",
1273
+ expired_card: "Your card has expired. Please use a different card.",
1274
+ insufficient_funds: "Insufficient funds. Please try another payment method.",
1275
+ invalid_cvc: "The security code (CVV) is incorrect.",
1276
+ processing_error: "A processing error occurred. Please try again.",
1277
+ authentication_required: "Additional authentication is required. Please complete the verification.",
1278
+ unknown: "An error occurred. Please try again or contact support."
1279
+ };
1280
+ return messages[errorCode] ?? messages.unknown;
1281
+ }
1282
+ extractCardDetails(payload) {
1283
+ const cardToken = payload.cardToken;
1284
+ const cardMask = payload.cardMask;
1285
+ const cardBrand = payload.cardBrand;
1286
+ const cardExpiration = payload.cardExpiration;
1287
+ return {
1288
+ cardToken,
1289
+ cardMask,
1290
+ cardBrand,
1291
+ cardExpiration,
1292
+ last4: cardMask?.slice(-4)
1293
+ };
1294
+ }
1295
+ };
1296
+
1297
+ // src/providers/cardcom/cardcom-webhook-handler.ts
1298
+ var CardcomWebhookHandler = class {
1299
+ provider = "cardcom";
1300
+ supportedEventTypes = CARDCOM_WEBHOOK_EVENTS;
1301
+ parseEvent(rawPayload) {
1302
+ try {
1303
+ const params = rawPayload;
1304
+ const responseCode = parseInt(params.ResponseCode ?? "1", 10);
1305
+ const dealResponse = parseInt(params.DealResponse ?? "0", 10);
1306
+ const lowProfileCode = params.LowProfileCode ?? "";
1307
+ const internalDealNumber = params.InternalDealNumber ?? "";
1308
+ if (!lowProfileCode && !internalDealNumber) {
1309
+ return {
1310
+ success: false,
1311
+ error: "Missing LowProfileCode or InternalDealNumber in callback"
1312
+ };
1313
+ }
1314
+ let eventType;
1315
+ if (dealResponse === 1) {
1316
+ eventType = "payment.completed";
1317
+ } else if (dealResponse === 2) {
1318
+ eventType = "payment.declined";
1319
+ } else if (dealResponse === 0 && responseCode === 0) {
1320
+ eventType = "payment.authorized";
1321
+ } else {
1322
+ eventType = "payment.declined";
1323
+ }
1324
+ const status = mapCardcomDealResponseToStatus(dealResponse);
1325
+ const amountString = params.Amount ?? "0";
1326
+ const amountMajor = parseFloat(amountString);
1327
+ const amountMinor = Math.round(amountMajor * 100);
1328
+ const parsed = {
1329
+ provider: "cardcom",
1330
+ eventId: `${lowProfileCode}_${internalDealNumber}_${Date.now()}`,
1331
+ eventType,
1332
+ providerTransactionId: internalDealNumber || lowProfileCode,
1333
+ timestamp: /* @__PURE__ */ new Date(),
1334
+ rawPayload,
1335
+ newStatus: status,
1336
+ amountMinor,
1337
+ currency: params.Currency ?? "ILS"
1338
+ };
1339
+ if (dealResponse === 2 || responseCode !== 0) {
1340
+ parsed.error = {
1341
+ code: String(responseCode),
1342
+ message: CARDCOM_DEAL_RESPONSE_ACTIONS[dealResponse] ?? "Payment failed"
1343
+ };
1344
+ }
1345
+ return { success: true, event: parsed };
1346
+ } catch (error) {
1347
+ return {
1348
+ success: false,
1349
+ error: error instanceof Error ? error.message : "Parse error"
1350
+ };
1351
+ }
1352
+ }
1353
+ async processEvent(event) {
1354
+ const action = this.getActionForEvent(event.eventType);
1355
+ if (action === "ignored") {
1356
+ return {
1357
+ success: true,
1358
+ action: "ignored_event_type"
1359
+ };
1360
+ }
1361
+ return {
1362
+ success: true,
1363
+ transactionId: event.providerTransactionId,
1364
+ action: "status_updated"
1365
+ };
1366
+ }
1367
+ canHandle(eventType) {
1368
+ return this.supportedEventTypes.includes(
1369
+ eventType
1370
+ );
1371
+ }
1372
+ async reconcile(_transactionId, _providerTransactionId) {
1373
+ return {
1374
+ reconciled: false,
1375
+ finalStatus: "created",
1376
+ source: "provider_query",
1377
+ statusChanged: false
1378
+ };
1379
+ }
1380
+ mapEventType(providerEventType) {
1381
+ return providerEventType;
1382
+ }
1383
+ mapStatus(providerStatus) {
1384
+ const dealResponse = parseInt(providerStatus, 10);
1385
+ if (isNaN(dealResponse)) {
1386
+ return null;
1387
+ }
1388
+ return mapCardcomDealResponseToStatus(dealResponse);
1389
+ }
1390
+ getActionForEvent(eventType) {
1391
+ switch (eventType) {
1392
+ case "payment.completed":
1393
+ case "payment.declined":
1394
+ case "payment.authorized":
1395
+ return "status_update";
1396
+ default:
1397
+ return "ignored";
1398
+ }
1399
+ }
1400
+ };
1401
+
1402
+ // src/factory.ts
1403
+ function addIsraeliProviders(services, config) {
1404
+ if (config.hyp) {
1405
+ services.providers.set("hyp", new HypProvider(config.hyp));
1406
+ services.webhookHandlers.set("hyp", new HypWebhookHandler());
1407
+ }
1408
+ if (config.cardcom) {
1409
+ services.providers.set("cardcom", new CardcomProvider(config.cardcom));
1410
+ services.webhookHandlers.set("cardcom", new CardcomWebhookHandler());
1411
+ }
1412
+ return services;
1413
+ }
1414
+ // Annotate the CommonJS export names for ESM import in node:
1415
+ 0 && (module.exports = {
1416
+ addIsraeliProviders
1417
+ });
1418
+ //# sourceMappingURL=factory.cjs.map