@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,885 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/providers/hyp/index.ts
21
+ var hyp_exports = {};
22
+ __export(hyp_exports, {
23
+ DEFAULT_HYP_ENDPOINTS: () => DEFAULT_HYP_ENDPOINTS,
24
+ HYP_CREDIT_TYPES: () => HYP_CREDIT_TYPES,
25
+ HYP_ERROR_MAP: () => HYP_ERROR_MAP,
26
+ HYP_RESULT_CODE_MAP: () => HYP_RESULT_CODE_MAP,
27
+ HYP_SUPPORTED_CURRENCIES: () => HYP_SUPPORTED_CURRENCIES,
28
+ HYP_TRANSACTION_CODES: () => HYP_TRANSACTION_CODES,
29
+ HYP_TRANSACTION_TYPES: () => HYP_TRANSACTION_TYPES,
30
+ HYP_VALIDATION_MODES: () => HYP_VALIDATION_MODES,
31
+ HypProvider: () => HypProvider,
32
+ HypWebhookHandler: () => HypWebhookHandler,
33
+ createHypWebhookHandler: () => createHypWebhookHandler,
34
+ formatCardExpiration: () => formatCardExpiration,
35
+ formatHypAmount: () => formatHypAmount,
36
+ isHypSuccess: () => isHypSuccess,
37
+ isHypSupportedCurrency: () => isHypSupportedCurrency,
38
+ mapHypError: () => mapHypError,
39
+ mapHypStatus: () => mapHypStatus,
40
+ parseCardExpiration: () => parseCardExpiration
41
+ });
42
+ module.exports = __toCommonJS(hyp_exports);
43
+
44
+ // src/providers/hyp/hyp-provider.ts
45
+ var import_types = require("@nehorai/payments/types");
46
+
47
+ // src/providers/hyp/hyp-types.ts
48
+ var DEFAULT_HYP_ENDPOINTS = {
49
+ test: "https://cguat2.creditguard.co.il",
50
+ production: "https://cgpay3.creditguard.co.il"
51
+ };
52
+ var HYP_SUPPORTED_CURRENCIES = [
53
+ "ILS",
54
+ // Israeli Shekel (primary)
55
+ "USD",
56
+ // US Dollar
57
+ "EUR",
58
+ // Euro
59
+ "GBP"
60
+ // British Pound
61
+ ];
62
+ var HYP_RESULT_CODE_MAP = {
63
+ "000": "captured",
64
+ // Success
65
+ "001": "failed",
66
+ // Declined
67
+ "002": "failed",
68
+ // Invalid card
69
+ "003": "failed",
70
+ // Expired card
71
+ "004": "failed",
72
+ // Insufficient funds
73
+ "005": "failed",
74
+ // Invalid CVV
75
+ "006": "failed",
76
+ // Card not permitted
77
+ "033": "failed",
78
+ // Lost/Stolen card
79
+ "034": "failed",
80
+ // Suspected fraud
81
+ "051": "failed",
82
+ // Insufficient funds
83
+ "054": "failed",
84
+ // Expired card
85
+ "057": "failed",
86
+ // Transaction not permitted
87
+ "100": "failed",
88
+ // System error
89
+ "200": "pending_authorization"
90
+ // Pending
91
+ };
92
+ var HYP_TRANSACTION_TYPES = {
93
+ /** Regular charge (immediate capture) */
94
+ DEBIT: "Debit",
95
+ /** Refund */
96
+ CREDIT: "Credit",
97
+ /** Authorization only (J5) */
98
+ DEBIT_J5: "Debit"
99
+ };
100
+ var HYP_TRANSACTION_CODES = {
101
+ /** Regular transaction */
102
+ REGULAR: "Regular",
103
+ /** Verify only (authorization) */
104
+ VERIFY: "Verify",
105
+ /** Force transaction */
106
+ FORCE: "Force"
107
+ };
108
+ var HYP_VALIDATION_MODES = {
109
+ /** Auto commit (immediate capture) */
110
+ AUTO_COMM: "AutoComm",
111
+ /** Transaction only (authorization, requires manual capture) */
112
+ TX_ONLY: "TxOnly"
113
+ };
114
+ var HYP_CREDIT_TYPES = {
115
+ /** Regular credit card */
116
+ REGULAR: "1",
117
+ /** Token/Saved card */
118
+ TOKEN: "8"
119
+ };
120
+ var HYP_ERROR_MAP = {
121
+ "001": "card_declined",
122
+ "002": "invalid_card",
123
+ "003": "expired_card",
124
+ "004": "insufficient_funds",
125
+ "005": "invalid_cvc",
126
+ "006": "card_declined",
127
+ "033": "card_declined",
128
+ "034": "card_declined",
129
+ "051": "insufficient_funds",
130
+ "054": "expired_card",
131
+ "057": "card_declined",
132
+ "100": "processing_error",
133
+ "200": "authentication_required"
134
+ };
135
+ function mapHypStatus(resultCode) {
136
+ return HYP_RESULT_CODE_MAP[resultCode] ?? null;
137
+ }
138
+ function mapHypError(resultCode) {
139
+ return HYP_ERROR_MAP[resultCode] ?? "unknown";
140
+ }
141
+ function isHypSupportedCurrency(currency) {
142
+ return HYP_SUPPORTED_CURRENCIES.includes(
143
+ currency
144
+ );
145
+ }
146
+ function isHypSuccess(resultCode) {
147
+ return resultCode === "000";
148
+ }
149
+ function formatHypAmount(amountMinor) {
150
+ return amountMinor;
151
+ }
152
+ function formatCardExpiration(month, year) {
153
+ const mm = month.padStart(2, "0");
154
+ const yy = year.slice(-2);
155
+ return `${mm}${yy}`;
156
+ }
157
+ function parseCardExpiration(expiration) {
158
+ const mm = expiration.substring(0, 2);
159
+ const yy = expiration.substring(2, 4);
160
+ return {
161
+ month: mm,
162
+ year: `20${yy}`
163
+ };
164
+ }
165
+
166
+ // src/providers/hyp/hyp-provider.ts
167
+ var HypProvider = class {
168
+ name = "hyp";
169
+ supportedCurrencies = HYP_SUPPORTED_CURRENCIES;
170
+ supportsRecurring = true;
171
+ supportsSplitPayments = false;
172
+ config;
173
+ constructor(config) {
174
+ if (!config.terminalNumber || !config.user || !config.password) {
175
+ throw new Error(
176
+ "HypProvider requires terminalNumber, user, and password in config"
177
+ );
178
+ }
179
+ const baseUrl = config.baseUrl ?? (config.environment === "production" ? DEFAULT_HYP_ENDPOINTS.production : DEFAULT_HYP_ENDPOINTS.test);
180
+ this.config = { ...config, baseUrl };
181
+ }
182
+ // ==========================================================================
183
+ // Payment Intent Operations
184
+ // ==========================================================================
185
+ /**
186
+ * Create a payment intent
187
+ *
188
+ * For Hyp, this generates a hosted payment page or prepares for direct charge.
189
+ */
190
+ async createPaymentIntent(params) {
191
+ try {
192
+ if (!params.paymentMethodId) {
193
+ return await this.createHostedPage(params);
194
+ }
195
+ return await this.chargeWithToken(params);
196
+ } catch (error) {
197
+ return this.handleError(error);
198
+ }
199
+ }
200
+ /**
201
+ * Create hosted payment page
202
+ */
203
+ async createHostedPage(params) {
204
+ const uniqueid = params.idempotencyKey;
205
+ const request = {
206
+ terminalNumber: this.config.terminalNumber,
207
+ user: this.config.user,
208
+ password: this.config.password,
209
+ total: formatHypAmount(params.amount.amountMinor),
210
+ currency: params.amount.currency,
211
+ transactionType: HYP_TRANSACTION_TYPES.DEBIT,
212
+ transactionCode: params.captureMethod === "manual" ? HYP_TRANSACTION_CODES.VERIFY : HYP_TRANSACTION_CODES.REGULAR,
213
+ validation: params.captureMethod === "manual" ? HYP_VALIDATION_MODES.TX_ONLY : HYP_VALIDATION_MODES.AUTO_COMM,
214
+ uniqueid,
215
+ successUrl: params.returnUrl,
216
+ errorUrl: params.returnUrl,
217
+ cancelUrl: params.returnUrl,
218
+ language: "en"
219
+ };
220
+ const response = await this.sendDoDealRequest(request);
221
+ if (!isHypSuccess(response.resultCode)) {
222
+ return {
223
+ success: false,
224
+ error: response.resultDescription ?? "Transaction failed",
225
+ errorCode: mapHypError(response.resultCode)
226
+ };
227
+ }
228
+ return {
229
+ success: true,
230
+ providerIntentId: response.transactionId ?? uniqueid,
231
+ redirectUrl: response.redirectUrl,
232
+ status: "created"
233
+ };
234
+ }
235
+ /**
236
+ * Charge with saved payment method token
237
+ */
238
+ async chargeWithToken(params) {
239
+ const uniqueid = params.idempotencyKey;
240
+ const request = {
241
+ terminalNumber: this.config.terminalNumber,
242
+ user: this.config.user,
243
+ password: this.config.password,
244
+ total: formatHypAmount(params.amount.amountMinor),
245
+ currency: params.amount.currency,
246
+ transactionType: HYP_TRANSACTION_TYPES.DEBIT,
247
+ transactionCode: params.captureMethod === "manual" ? HYP_TRANSACTION_CODES.VERIFY : HYP_TRANSACTION_CODES.REGULAR,
248
+ validation: params.captureMethod === "manual" ? HYP_VALIDATION_MODES.TX_ONLY : HYP_VALIDATION_MODES.AUTO_COMM,
249
+ creditType: HYP_CREDIT_TYPES.TOKEN,
250
+ cardToken: params.paymentMethodId,
251
+ uniqueid
252
+ };
253
+ const response = await this.sendDoDealRequest(request);
254
+ if (!isHypSuccess(response.resultCode)) {
255
+ return {
256
+ success: false,
257
+ error: response.resultDescription ?? "Transaction failed",
258
+ errorCode: mapHypError(response.resultCode)
259
+ };
260
+ }
261
+ const status = mapHypStatus(response.resultCode) ?? "created";
262
+ return {
263
+ success: true,
264
+ providerIntentId: response.transactionId ?? uniqueid,
265
+ status
266
+ };
267
+ }
268
+ async authorize(params) {
269
+ try {
270
+ return {
271
+ success: true,
272
+ authorizationCode: params.providerIntentId,
273
+ status: "authorized",
274
+ captureDeadline: (0, import_types.calculateCaptureDeadline)(/* @__PURE__ */ new Date())
275
+ };
276
+ } catch (error) {
277
+ return this.handleError(error);
278
+ }
279
+ }
280
+ async capture(params) {
281
+ try {
282
+ const request = {
283
+ terminalNumber: this.config.terminalNumber,
284
+ user: this.config.user,
285
+ password: this.config.password,
286
+ total: params.amount ? formatHypAmount(params.amount.amountMinor) : void 0,
287
+ currency: params.amount?.currency ?? "ILS",
288
+ transactionType: HYP_TRANSACTION_TYPES.DEBIT,
289
+ transactionCode: HYP_TRANSACTION_CODES.FORCE,
290
+ validation: HYP_VALIDATION_MODES.AUTO_COMM,
291
+ uniqueid: params.idempotencyKey,
292
+ authorizationCode: params.providerIntentId
293
+ };
294
+ const response = await this.sendDoDealRequest(request);
295
+ if (!isHypSuccess(response.resultCode)) {
296
+ return {
297
+ success: false,
298
+ error: response.resultDescription ?? "Capture failed",
299
+ errorCode: mapHypError(response.resultCode)
300
+ };
301
+ }
302
+ return {
303
+ success: true,
304
+ providerTransactionId: response.transactionId ?? params.providerIntentId,
305
+ status: "captured",
306
+ capturedAmount: params.amount ?? {
307
+ amountMinor: 0,
308
+ currency: "ILS"
309
+ }
310
+ };
311
+ } catch (error) {
312
+ return this.handleError(error);
313
+ }
314
+ }
315
+ async void(params) {
316
+ try {
317
+ const request = {
318
+ terminalNumber: this.config.terminalNumber,
319
+ user: this.config.user,
320
+ password: this.config.password,
321
+ transactionId: params.providerIntentId,
322
+ currency: "ILS",
323
+ uniqueid: params.providerIntentId
324
+ };
325
+ const response = await this.sendRefundRequest(request);
326
+ if (!isHypSuccess(response.resultCode)) {
327
+ return {
328
+ success: false,
329
+ error: response.resultDescription ?? "Void failed"
330
+ };
331
+ }
332
+ return { success: true, status: "voided" };
333
+ } catch (error) {
334
+ return this.handleError(error);
335
+ }
336
+ }
337
+ // ==========================================================================
338
+ // Refunds
339
+ // ==========================================================================
340
+ async refund(params) {
341
+ try {
342
+ const request = {
343
+ terminalNumber: this.config.terminalNumber,
344
+ user: this.config.user,
345
+ password: this.config.password,
346
+ transactionId: params.providerTransactionId,
347
+ total: params.amount ? formatHypAmount(params.amount.amountMinor) : void 0,
348
+ currency: params.amount?.currency ?? "ILS",
349
+ uniqueid: params.idempotencyKey
350
+ };
351
+ const response = await this.sendRefundRequest(request);
352
+ if (!isHypSuccess(response.resultCode)) {
353
+ return {
354
+ success: false,
355
+ error: response.resultDescription ?? "Refund failed"
356
+ };
357
+ }
358
+ return {
359
+ success: true,
360
+ providerRefundId: response.transactionId ?? params.idempotencyKey,
361
+ refundedAmount: params.amount ?? {
362
+ amountMinor: 0,
363
+ currency: "ILS"
364
+ },
365
+ status: "succeeded"
366
+ };
367
+ } catch (error) {
368
+ return this.handleError(error);
369
+ }
370
+ }
371
+ // ==========================================================================
372
+ // Payment Methods (Tokenization)
373
+ // ==========================================================================
374
+ async createSetupIntent(params) {
375
+ try {
376
+ const uniqueid = `setup_${params.userId}_${Date.now()}`;
377
+ const request = {
378
+ terminalNumber: this.config.terminalNumber,
379
+ user: this.config.user,
380
+ password: this.config.password,
381
+ total: 0,
382
+ currency: "ILS",
383
+ transactionType: HYP_TRANSACTION_TYPES.DEBIT,
384
+ transactionCode: HYP_TRANSACTION_CODES.VERIFY,
385
+ validation: HYP_VALIDATION_MODES.TX_ONLY,
386
+ creditType: HYP_CREDIT_TYPES.TOKEN,
387
+ customerData: params.customerId ?? params.userId,
388
+ uniqueid,
389
+ language: "en"
390
+ };
391
+ const response = await this.sendDoDealRequest(request);
392
+ if (!isHypSuccess(response.resultCode)) {
393
+ return {
394
+ success: false,
395
+ error: response.resultDescription ?? "Setup failed"
396
+ };
397
+ }
398
+ return {
399
+ success: true,
400
+ setupIntentId: response.transactionId ?? uniqueid,
401
+ clientSecret: response.redirectUrl
402
+ };
403
+ } catch (error) {
404
+ return this.handleError(error);
405
+ }
406
+ }
407
+ async savePaymentMethod(params) {
408
+ try {
409
+ const cardToken = params.setupData.cardToken;
410
+ const cardMask = params.setupData.cardMask;
411
+ const cardBrand = params.setupData.cardBrand;
412
+ const cardExpiration = params.setupData.cardExpiration;
413
+ if (!cardToken) {
414
+ return {
415
+ success: false,
416
+ error: "No card token received"
417
+ };
418
+ }
419
+ return {
420
+ success: true,
421
+ paymentMethodId: cardToken,
422
+ cardBrand: cardBrand ?? "unknown",
423
+ cardLast4: cardMask?.slice(-4) ?? "0000",
424
+ cardExpMonth: cardExpiration?.substring(0, 2) ?? "01",
425
+ cardExpYear: `20${cardExpiration?.substring(2, 4) ?? "99"}`
426
+ };
427
+ } catch (error) {
428
+ return this.handleError(error);
429
+ }
430
+ }
431
+ async deletePaymentMethod(_paymentMethodId) {
432
+ return {
433
+ success: true
434
+ };
435
+ }
436
+ // ==========================================================================
437
+ // Customer Management
438
+ // ==========================================================================
439
+ async createCustomer(params) {
440
+ return {
441
+ success: true,
442
+ customerId: params.userId
443
+ };
444
+ }
445
+ async getOrCreateCustomer(userId, _email) {
446
+ return {
447
+ success: true,
448
+ customerId: userId
449
+ };
450
+ }
451
+ // ==========================================================================
452
+ // Health & Security
453
+ // ==========================================================================
454
+ async getHealth() {
455
+ const start = Date.now();
456
+ try {
457
+ const response = await fetch(`${this.config.baseUrl}/xpo/Relay`, {
458
+ method: "POST",
459
+ headers: {
460
+ "Content-Type": "text/xml"
461
+ },
462
+ body: this.buildTestXML(),
463
+ signal: AbortSignal.timeout(5e3)
464
+ });
465
+ const healthy = response.ok;
466
+ return {
467
+ provider: "hyp",
468
+ healthy,
469
+ lastChecked: /* @__PURE__ */ new Date(),
470
+ avgLatencyMs: Date.now() - start,
471
+ circuitBreakerOpen: false
472
+ };
473
+ } catch {
474
+ return {
475
+ provider: "hyp",
476
+ healthy: false,
477
+ lastChecked: /* @__PURE__ */ new Date(),
478
+ circuitBreakerOpen: false
479
+ };
480
+ }
481
+ }
482
+ validateWebhookSignature(_payload, _signature) {
483
+ if (!this.config.webhookSecret) return false;
484
+ return !!this.config.webhookSecret;
485
+ }
486
+ async getPaymentIntentStatus(_providerIntentId) {
487
+ return {
488
+ status: "unknown",
489
+ error: "Status query not supported by Hyp basic integration"
490
+ };
491
+ }
492
+ // ==========================================================================
493
+ // XML Request Builders
494
+ // ==========================================================================
495
+ buildDoDealXML(request) {
496
+ const parts = [];
497
+ parts.push('<?xml version="1.0" encoding="utf-8"?>');
498
+ parts.push("<ashrait>");
499
+ parts.push("<request>");
500
+ parts.push(`<version>1000</version>`);
501
+ parts.push("<language>ENG</language>");
502
+ parts.push("<command>doDeal</command>");
503
+ parts.push(`<terminalNumber>${this.escapeXml(request.terminalNumber)}</terminalNumber>`);
504
+ parts.push(`<user>${this.escapeXml(request.user)}</user>`);
505
+ parts.push(`<password>${this.escapeXml(request.password)}</password>`);
506
+ if (request.cardNo) {
507
+ parts.push(`<cardNo>${this.escapeXml(request.cardNo)}</cardNo>`);
508
+ }
509
+ if (request.cardExpiration) {
510
+ parts.push(`<cardExpiration>${this.escapeXml(request.cardExpiration)}</cardExpiration>`);
511
+ }
512
+ if (request.cvv) {
513
+ parts.push(`<cvv>${this.escapeXml(request.cvv)}</cvv>`);
514
+ }
515
+ if (request.cardToken) {
516
+ parts.push(`<cardToken>${this.escapeXml(request.cardToken)}</cardToken>`);
517
+ }
518
+ if (request.authorizationCode) {
519
+ parts.push(`<authNumber>${this.escapeXml(request.authorizationCode)}</authNumber>`);
520
+ }
521
+ if (request.total !== void 0) {
522
+ parts.push(`<total>${request.total}</total>`);
523
+ }
524
+ parts.push(`<currency>${this.escapeXml(request.currency)}</currency>`);
525
+ parts.push(`<transactionType>${this.escapeXml(request.transactionType)}</transactionType>`);
526
+ if (request.transactionCode) {
527
+ parts.push(`<transactionCode>${this.escapeXml(request.transactionCode)}</transactionCode>`);
528
+ }
529
+ if (request.creditType) {
530
+ parts.push(`<creditType>${request.creditType}</creditType>`);
531
+ }
532
+ if (request.validation) {
533
+ parts.push(`<validation>${this.escapeXml(request.validation)}</validation>`);
534
+ }
535
+ if (request.uniqueid) {
536
+ parts.push(`<uniqueid>${this.escapeXml(request.uniqueid)}</uniqueid>`);
537
+ }
538
+ if (request.customerData) {
539
+ parts.push(`<customerData>${this.escapeXml(request.customerData)}</customerData>`);
540
+ }
541
+ if (request.successUrl) {
542
+ parts.push(`<successUrl>${this.escapeXml(request.successUrl)}</successUrl>`);
543
+ }
544
+ if (request.errorUrl) {
545
+ parts.push(`<errorUrl>${this.escapeXml(request.errorUrl)}</errorUrl>`);
546
+ }
547
+ if (request.cancelUrl) {
548
+ parts.push(`<cancelUrl>${this.escapeXml(request.cancelUrl)}</cancelUrl>`);
549
+ }
550
+ parts.push("</request>");
551
+ parts.push("</ashrait>");
552
+ return parts.join("");
553
+ }
554
+ buildRefundXML(request) {
555
+ const parts = [];
556
+ parts.push('<?xml version="1.0" encoding="utf-8"?>');
557
+ parts.push("<ashrait>");
558
+ parts.push("<request>");
559
+ parts.push(`<version>1000</version>`);
560
+ parts.push("<language>ENG</language>");
561
+ parts.push("<command>refundDeal</command>");
562
+ parts.push(`<terminalNumber>${this.escapeXml(request.terminalNumber)}</terminalNumber>`);
563
+ parts.push(`<user>${this.escapeXml(request.user)}</user>`);
564
+ parts.push(`<password>${this.escapeXml(request.password)}</password>`);
565
+ parts.push(`<transactionId>${this.escapeXml(request.transactionId)}</transactionId>`);
566
+ parts.push(`<currency>${this.escapeXml(request.currency)}</currency>`);
567
+ if (request.total !== void 0) {
568
+ parts.push(`<total>${request.total}</total>`);
569
+ }
570
+ if (request.uniqueid) {
571
+ parts.push(`<uniqueid>${this.escapeXml(request.uniqueid)}</uniqueid>`);
572
+ }
573
+ parts.push("</request>");
574
+ parts.push("</ashrait>");
575
+ return parts.join("");
576
+ }
577
+ buildTestXML() {
578
+ return `<?xml version="1.0" encoding="utf-8"?>
579
+ <ashrait>
580
+ <request>
581
+ <version>1000</version>
582
+ <language>ENG</language>
583
+ <command>echo</command>
584
+ </request>
585
+ </ashrait>`;
586
+ }
587
+ // ==========================================================================
588
+ // HTTP Helpers
589
+ // ==========================================================================
590
+ async sendDoDealRequest(request) {
591
+ const xmlBody = this.buildDoDealXML(request);
592
+ const response = await fetch(`${this.config.baseUrl}/xpo/Relay`, {
593
+ method: "POST",
594
+ headers: {
595
+ "Content-Type": "text/xml; charset=utf-8"
596
+ },
597
+ body: xmlBody
598
+ });
599
+ if (!response.ok) {
600
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
601
+ }
602
+ const xmlResponse = await response.text();
603
+ return this.parseDoDealResponse(xmlResponse);
604
+ }
605
+ async sendRefundRequest(request) {
606
+ const xmlBody = this.buildRefundXML(request);
607
+ const response = await fetch(`${this.config.baseUrl}/xpo/Relay`, {
608
+ method: "POST",
609
+ headers: {
610
+ "Content-Type": "text/xml; charset=utf-8"
611
+ },
612
+ body: xmlBody
613
+ });
614
+ if (!response.ok) {
615
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
616
+ }
617
+ const xmlResponse = await response.text();
618
+ return this.parseRefundResponse(xmlResponse);
619
+ }
620
+ // ==========================================================================
621
+ // XML Parsing
622
+ // ==========================================================================
623
+ parseDoDealResponse(xml) {
624
+ return {
625
+ resultCode: this.extractXmlValue(xml, "resultCode") ?? "100",
626
+ resultDescription: this.extractXmlValue(xml, "resultDescription"),
627
+ transactionId: this.extractXmlValue(xml, "transactionId"),
628
+ authorizationCode: this.extractXmlValue(xml, "authorizationCode"),
629
+ voucherNumber: this.extractXmlValue(xml, "voucherNumber"),
630
+ cardToken: this.extractXmlValue(xml, "cardToken"),
631
+ cardMask: this.extractXmlValue(xml, "cardMask"),
632
+ cardBrand: this.extractXmlValue(xml, "cardBrand"),
633
+ cardExpiration: this.extractXmlValue(xml, "cardExpiration"),
634
+ redirectUrl: this.extractXmlValue(xml, "redirectUrl"),
635
+ uniqueid: this.extractXmlValue(xml, "uniqueid"),
636
+ rawXml: xml
637
+ };
638
+ }
639
+ parseRefundResponse(xml) {
640
+ return {
641
+ resultCode: this.extractXmlValue(xml, "resultCode") ?? "100",
642
+ resultDescription: this.extractXmlValue(xml, "resultDescription"),
643
+ transactionId: this.extractXmlValue(xml, "transactionId"),
644
+ authorizationCode: this.extractXmlValue(xml, "authorizationCode"),
645
+ uniqueid: this.extractXmlValue(xml, "uniqueid")
646
+ };
647
+ }
648
+ extractXmlValue(xml, tagName) {
649
+ const regex = new RegExp(`<${tagName}>([^<]*)</${tagName}>`, "i");
650
+ const match = xml.match(regex);
651
+ return match ? match[1].trim() : void 0;
652
+ }
653
+ escapeXml(str) {
654
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
655
+ }
656
+ // ==========================================================================
657
+ // Error Handling
658
+ // ==========================================================================
659
+ handleError(error) {
660
+ if (error instanceof Error) {
661
+ return {
662
+ success: false,
663
+ error: error.message,
664
+ errorCode: "unknown"
665
+ };
666
+ }
667
+ return {
668
+ success: false,
669
+ error: "Unknown error occurred",
670
+ errorCode: "unknown"
671
+ };
672
+ }
673
+ };
674
+
675
+ // src/providers/hyp/hyp-webhook-handler.ts
676
+ var HYP_EVENT_TYPES = {
677
+ TRANSACTION_SUCCESS: "transaction.success",
678
+ TRANSACTION_FAILED: "transaction.failed",
679
+ TRANSACTION_PENDING: "transaction.pending",
680
+ REFUND_SUCCESS: "refund.success",
681
+ REFUND_FAILED: "refund.failed"
682
+ };
683
+ var HypWebhookHandler = class {
684
+ provider = "hyp";
685
+ supportedEventTypes = Object.values(HYP_EVENT_TYPES);
686
+ parseEvent(rawPayload) {
687
+ try {
688
+ const resultCode = String(rawPayload.resultCode ?? "100");
689
+ const resultDescription = String(rawPayload.resultDescription ?? "");
690
+ const transactionId = String(rawPayload.transactionId ?? "");
691
+ const uniqueid = String(rawPayload.uniqueid ?? "");
692
+ const total = Number(rawPayload.total ?? 0);
693
+ const currency = String(rawPayload.currency ?? "ILS");
694
+ const eventType = this.determineEventType(resultCode);
695
+ const newStatus = mapHypStatus(resultCode);
696
+ const event = {
697
+ provider: "hyp",
698
+ eventId: uniqueid || transactionId || `hyp_${Date.now()}`,
699
+ eventType,
700
+ providerTransactionId: transactionId,
701
+ amountMinor: total,
702
+ currency,
703
+ newStatus: newStatus ?? void 0,
704
+ error: isHypSuccess(resultCode) ? void 0 : {
705
+ code: resultCode,
706
+ message: resultDescription
707
+ },
708
+ timestamp: /* @__PURE__ */ new Date(),
709
+ rawPayload
710
+ };
711
+ return {
712
+ success: true,
713
+ event
714
+ };
715
+ } catch (error) {
716
+ return {
717
+ success: false,
718
+ error: error instanceof Error ? error.message : "Failed to parse webhook"
719
+ };
720
+ }
721
+ }
722
+ async processEvent(event) {
723
+ try {
724
+ if (!event.providerTransactionId) {
725
+ return {
726
+ success: false,
727
+ error: "Missing transaction ID in webhook",
728
+ action: "ignored_event_type"
729
+ };
730
+ }
731
+ const action = this.determineAction(event);
732
+ return {
733
+ success: true,
734
+ transactionId: event.providerTransactionId,
735
+ action
736
+ };
737
+ } catch (error) {
738
+ return {
739
+ success: false,
740
+ error: error instanceof Error ? error.message : "Processing failed",
741
+ action: "ignored_event_type"
742
+ };
743
+ }
744
+ }
745
+ canHandle(eventType) {
746
+ return this.supportedEventTypes.includes(
747
+ eventType
748
+ );
749
+ }
750
+ async reconcile(_transactionId, _providerTransactionId) {
751
+ return {
752
+ reconciled: false,
753
+ finalStatus: "pending_authorization",
754
+ source: "webhook",
755
+ statusChanged: false
756
+ };
757
+ }
758
+ mapEventType(providerEventType) {
759
+ return providerEventType;
760
+ }
761
+ mapStatus(providerStatus) {
762
+ return mapHypStatus(providerStatus);
763
+ }
764
+ // ==========================================================================
765
+ // Helper Methods
766
+ // ==========================================================================
767
+ determineEventType(resultCode) {
768
+ if (isHypSuccess(resultCode)) {
769
+ return HYP_EVENT_TYPES.TRANSACTION_SUCCESS;
770
+ }
771
+ if (resultCode === "200") {
772
+ return HYP_EVENT_TYPES.TRANSACTION_PENDING;
773
+ }
774
+ return HYP_EVENT_TYPES.TRANSACTION_FAILED;
775
+ }
776
+ determineAction(event) {
777
+ switch (event.eventType) {
778
+ case HYP_EVENT_TYPES.TRANSACTION_SUCCESS:
779
+ case HYP_EVENT_TYPES.TRANSACTION_FAILED:
780
+ case HYP_EVENT_TYPES.TRANSACTION_PENDING:
781
+ case HYP_EVENT_TYPES.REFUND_FAILED:
782
+ return "status_updated";
783
+ case HYP_EVENT_TYPES.REFUND_SUCCESS:
784
+ return "refund_processed";
785
+ default:
786
+ return "ignored_event_type";
787
+ }
788
+ }
789
+ // ==========================================================================
790
+ // Webhook Validation
791
+ // ==========================================================================
792
+ validateSignature(payload, signature, secret) {
793
+ const hasRequiredParams = payload.resultCode !== void 0 && (payload.transactionId !== void 0 || payload.uniqueid !== void 0);
794
+ if (!hasRequiredParams) {
795
+ return false;
796
+ }
797
+ if (secret && signature) {
798
+ return this.validateHMAC(payload, signature, secret);
799
+ }
800
+ return hasRequiredParams;
801
+ }
802
+ validateHMAC(_payload, signature, _secret) {
803
+ try {
804
+ return !!signature;
805
+ } catch {
806
+ return false;
807
+ }
808
+ }
809
+ // ==========================================================================
810
+ // Callback URL Builders
811
+ // ==========================================================================
812
+ buildSuccessUrl(baseUrl, transactionId) {
813
+ return `${baseUrl}/api/payments/hyp/callback?status=success&txId=${transactionId}`;
814
+ }
815
+ buildErrorUrl(baseUrl, transactionId) {
816
+ return `${baseUrl}/api/payments/hyp/callback?status=error&txId=${transactionId}`;
817
+ }
818
+ buildCancelUrl(baseUrl, transactionId) {
819
+ return `${baseUrl}/api/payments/hyp/callback?status=cancel&txId=${transactionId}`;
820
+ }
821
+ // ==========================================================================
822
+ // Response Parsing
823
+ // ==========================================================================
824
+ extractErrorDetails(payload) {
825
+ const resultCode = String(payload.resultCode ?? "unknown");
826
+ const resultDescription = String(payload.resultDescription ?? "Unknown error");
827
+ return {
828
+ code: resultCode,
829
+ message: resultDescription,
830
+ userMessage: this.getUserFriendlyMessage(resultCode)
831
+ };
832
+ }
833
+ getUserFriendlyMessage(resultCode) {
834
+ const errorCode = mapHypError(resultCode);
835
+ const messages = {
836
+ card_declined: "Your card was declined. Please try another payment method.",
837
+ invalid_card: "The card information is invalid. Please check and try again.",
838
+ expired_card: "Your card has expired. Please use a different card.",
839
+ insufficient_funds: "Insufficient funds. Please try another payment method.",
840
+ invalid_cvc: "The security code (CVV) is incorrect.",
841
+ processing_error: "A processing error occurred. Please try again.",
842
+ authentication_required: "Additional authentication is required. Please complete the verification.",
843
+ unknown: "An error occurred. Please try again or contact support."
844
+ };
845
+ return messages[errorCode] ?? messages.unknown;
846
+ }
847
+ extractCardDetails(payload) {
848
+ const cardToken = payload.cardToken;
849
+ const cardMask = payload.cardMask;
850
+ const cardBrand = payload.cardBrand;
851
+ const cardExpiration = payload.cardExpiration;
852
+ return {
853
+ cardToken,
854
+ cardMask,
855
+ cardBrand,
856
+ cardExpiration,
857
+ last4: cardMask?.slice(-4)
858
+ };
859
+ }
860
+ };
861
+ function createHypWebhookHandler() {
862
+ return new HypWebhookHandler();
863
+ }
864
+ // Annotate the CommonJS export names for ESM import in node:
865
+ 0 && (module.exports = {
866
+ DEFAULT_HYP_ENDPOINTS,
867
+ HYP_CREDIT_TYPES,
868
+ HYP_ERROR_MAP,
869
+ HYP_RESULT_CODE_MAP,
870
+ HYP_SUPPORTED_CURRENCIES,
871
+ HYP_TRANSACTION_CODES,
872
+ HYP_TRANSACTION_TYPES,
873
+ HYP_VALIDATION_MODES,
874
+ HypProvider,
875
+ HypWebhookHandler,
876
+ createHypWebhookHandler,
877
+ formatCardExpiration,
878
+ formatHypAmount,
879
+ isHypSuccess,
880
+ isHypSupportedCurrency,
881
+ mapHypError,
882
+ mapHypStatus,
883
+ parseCardExpiration
884
+ });
885
+ //# sourceMappingURL=index.cjs.map