@prakashacharya/vendure-plugin-nepal-payments 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.
Files changed (80) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE.md +9 -0
  4. package/README.md +223 -0
  5. package/dist/api/api-extensions.d.ts +1 -0
  6. package/dist/api/api-extensions.js +36 -0
  7. package/dist/api/api-extensions.js.map +1 -0
  8. package/dist/api/payment-callback.controller.d.ts +9 -0
  9. package/dist/api/payment-callback.controller.js +83 -0
  10. package/dist/api/payment-callback.controller.js.map +1 -0
  11. package/dist/api/shop.resolver.d.ts +9 -0
  12. package/dist/api/shop.resolver.js +41 -0
  13. package/dist/api/shop.resolver.js.map +1 -0
  14. package/dist/config.d.ts +56 -0
  15. package/dist/config.js +52 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/entities/payment-attempt.entity.d.ts +29 -0
  18. package/dist/entities/payment-attempt.entity.js +102 -0
  19. package/dist/entities/payment-attempt.entity.js.map +1 -0
  20. package/dist/handlers/payment-handlers.d.ts +3 -0
  21. package/dist/handlers/payment-handlers.js +85 -0
  22. package/dist/handlers/payment-handlers.js.map +1 -0
  23. package/dist/handlers/payment-metadata.d.ts +7 -0
  24. package/dist/handlers/payment-metadata.js +13 -0
  25. package/dist/handlers/payment-metadata.js.map +1 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.js +11 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/nepal-payments.plugin.d.ts +29 -0
  30. package/dist/nepal-payments.plugin.js +91 -0
  31. package/dist/nepal-payments.plugin.js.map +1 -0
  32. package/dist/providers/amount.d.ts +2 -0
  33. package/dist/providers/amount.js +22 -0
  34. package/dist/providers/amount.js.map +1 -0
  35. package/dist/providers/esewa.provider.d.ts +15 -0
  36. package/dist/providers/esewa.provider.js +117 -0
  37. package/dist/providers/esewa.provider.js.map +1 -0
  38. package/dist/providers/http-client.d.ts +3 -0
  39. package/dist/providers/http-client.js +46 -0
  40. package/dist/providers/http-client.js.map +1 -0
  41. package/dist/providers/khalti.provider.d.ts +15 -0
  42. package/dist/providers/khalti.provider.js +91 -0
  43. package/dist/providers/khalti.provider.js.map +1 -0
  44. package/dist/providers/provider-error.d.ts +5 -0
  45. package/dist/providers/provider-error.js +13 -0
  46. package/dist/providers/provider-error.js.map +1 -0
  47. package/dist/providers/provider.factory.d.ts +2 -0
  48. package/dist/providers/provider.factory.js +18 -0
  49. package/dist/providers/provider.factory.js.map +1 -0
  50. package/dist/providers/sanitize-provider-data.d.ts +4 -0
  51. package/dist/providers/sanitize-provider-data.js +35 -0
  52. package/dist/providers/sanitize-provider-data.js.map +1 -0
  53. package/dist/providers/signing.d.ts +9 -0
  54. package/dist/providers/signing.js +18 -0
  55. package/dist/providers/signing.js.map +1 -0
  56. package/dist/services/payment-orchestrator.service.d.ts +18 -0
  57. package/dist/services/payment-orchestrator.service.js +188 -0
  58. package/dist/services/payment-orchestrator.service.js.map +1 -0
  59. package/dist/services/reconciliation.service.d.ts +11 -0
  60. package/dist/services/reconciliation.service.js +55 -0
  61. package/dist/services/reconciliation.service.js.map +1 -0
  62. package/dist/services/reconciliation.task.d.ts +2 -0
  63. package/dist/services/reconciliation.task.js +14 -0
  64. package/dist/services/reconciliation.task.js.map +1 -0
  65. package/dist/types.d.ts +76 -0
  66. package/dist/types.js +16 -0
  67. package/dist/types.js.map +1 -0
  68. package/docs/ADDING_A_PROVIDER.md +74 -0
  69. package/docs/ARCHITECTURE.md +75 -0
  70. package/docs/CONFIGURATION.md +49 -0
  71. package/docs/DATA_HANDLING.md +46 -0
  72. package/docs/INSTALLATION.md +132 -0
  73. package/docs/LICENSING.md +29 -0
  74. package/docs/PRODUCTION.md +50 -0
  75. package/docs/PROVIDERS.md +57 -0
  76. package/docs/README.md +15 -0
  77. package/docs/RELEASING.md +74 -0
  78. package/docs/STOREFRONT.md +111 -0
  79. package/docs/TROUBLESHOOTING.md +71 -0
  80. package/package.json +79 -0
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KhaltiProvider = void 0;
4
+ exports.mapKhaltiStatus = mapKhaltiStatus;
5
+ const http_client_1 = require("./http-client");
6
+ class KhaltiProvider {
7
+ constructor(options, http = new http_client_1.PaymentHttpClient()) {
8
+ this.options = options;
9
+ this.http = http;
10
+ this.code = 'khalti';
11
+ this.baseUrl = options.environment === 'production'
12
+ ? 'https://khalti.com/api/v2'
13
+ : 'https://dev.khalti.com/api/v2';
14
+ }
15
+ async initiate(input) {
16
+ const response = await this.http.request(`${this.baseUrl}/epayment/initiate/`, {
17
+ method: 'POST',
18
+ headers: this.headers(),
19
+ body: JSON.stringify({
20
+ return_url: input.returnUrl,
21
+ website_url: this.options.websiteUrl,
22
+ amount: input.amount,
23
+ purchase_order_id: input.attemptId,
24
+ purchase_order_name: `Order ${input.orderCode}`,
25
+ customer_info: input.customer,
26
+ }),
27
+ });
28
+ if (!response.pidx || !response.payment_url) {
29
+ throw new Error('Khalti initiation response is missing pidx or payment_url');
30
+ }
31
+ return {
32
+ providerReference: response.pidx,
33
+ redirectUrl: response.payment_url,
34
+ expiresAt: response.expires_at ? new Date(response.expires_at) : undefined,
35
+ raw: response,
36
+ };
37
+ }
38
+ async verify(input) {
39
+ if (!input.providerReference)
40
+ throw new Error('Khalti verification requires pidx');
41
+ const response = await this.http.request(`${this.baseUrl}/epayment/lookup/`, {
42
+ method: 'POST',
43
+ headers: this.headers(),
44
+ body: JSON.stringify({ pidx: input.providerReference }),
45
+ });
46
+ if (response.pidx !== input.providerReference) {
47
+ throw new Error('Khalti lookup reference does not match the payment attempt');
48
+ }
49
+ return {
50
+ providerReference: response.pidx,
51
+ transactionId: response.transaction_id ?? undefined,
52
+ amount: response.total_amount,
53
+ status: mapKhaltiStatus(response.status),
54
+ raw: response,
55
+ };
56
+ }
57
+ async refund(input) {
58
+ const base = this.options.environment === 'production' ? 'https://khalti.com/api' : 'https://dev.khalti.com/api';
59
+ const body = {};
60
+ if (input.amount != null)
61
+ body.amount = input.amount;
62
+ if (input.mobile)
63
+ body.mobile = input.mobile;
64
+ const response = await this.http.request(`${base}/merchant-transaction/${encodeURIComponent(input.transactionId)}/refund/`, { method: 'POST', headers: this.headers(), body: JSON.stringify(body) });
65
+ return { status: 'settled', transactionId: input.transactionId, raw: response };
66
+ }
67
+ headers() {
68
+ return {
69
+ Authorization: `Key ${this.options.secretKey}`,
70
+ 'Content-Type': 'application/json',
71
+ Accept: 'application/json',
72
+ };
73
+ }
74
+ }
75
+ exports.KhaltiProvider = KhaltiProvider;
76
+ function mapKhaltiStatus(status) {
77
+ switch (status.trim().toLowerCase()) {
78
+ case 'completed': return 'settled';
79
+ case 'pending':
80
+ case 'initiated': return 'pending';
81
+ case 'refunded': return 'refunded';
82
+ case 'partially refunded': return 'partially-refunded';
83
+ case 'expired': return 'expired';
84
+ case 'user canceled':
85
+ case 'cancelled':
86
+ case 'canceled': return 'cancelled';
87
+ case 'failed': return 'failed';
88
+ default: return 'unknown';
89
+ }
90
+ }
91
+ //# sourceMappingURL=khalti.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"khalti.provider.js","sourceRoot":"","sources":["../../src/providers/khalti.provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare class PaymentProviderError extends Error {
2
+ readonly statusCode?: number | undefined;
3
+ readonly response?: unknown | undefined;
4
+ constructor(message: string, statusCode?: number | undefined, response?: unknown | undefined);
5
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaymentProviderError = void 0;
4
+ class PaymentProviderError extends Error {
5
+ constructor(message, statusCode, response) {
6
+ super(message);
7
+ this.statusCode = statusCode;
8
+ this.response = response;
9
+ this.name = 'PaymentProviderError';
10
+ }
11
+ }
12
+ exports.PaymentProviderError = PaymentProviderError;
13
+ //# sourceMappingURL=provider-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-error.js","sourceRoot":"","sources":["../../src/providers/provider-error.ts"],"names":[],"mappings":";;;AAAA,MAAa,oBAAqB,SAAQ,KAAK;IAC7C,YACE,OAAe,EACN,UAAmB,EACnB,QAAkB;QAE3B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,eAAU,GAAV,UAAU,CAAS;QACnB,aAAQ,GAAR,QAAQ,CAAU;QAG3B,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AATD,oDASC"}
@@ -0,0 +1,2 @@
1
+ import type { NepalPaymentProvider, NepalPaymentProviderCode } from '../types';
2
+ export declare function createProvider(code: NepalPaymentProviderCode): NepalPaymentProvider;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createProvider = createProvider;
4
+ const config_1 = require("../config");
5
+ const esewa_provider_1 = require("./esewa.provider");
6
+ const khalti_provider_1 = require("./khalti.provider");
7
+ function createProvider(code) {
8
+ const options = (0, config_1.getPluginOptions)();
9
+ if (code === 'khalti' && options.khalti)
10
+ return new khalti_provider_1.KhaltiProvider(options.khalti);
11
+ if (code === 'esewa' && options.esewa)
12
+ return new esewa_provider_1.EsewaProvider(options.esewa);
13
+ if (code === 'fonepay') {
14
+ throw new Error('Fonepay is not enabled until merchant-specific API documentation is configured');
15
+ }
16
+ throw new Error(`Payment provider "${code}" is not configured`);
17
+ }
18
+ //# sourceMappingURL=provider.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.factory.js","sourceRoot":"","sources":["../../src/providers/provider.factory.ts"],"names":[],"mappings":";;AAKA,wCAQC;AAbD,sCAA6C;AAE7C,qDAAiD;AACjD,uDAAmD;AAEnD,SAAgB,cAAc,CAAC,IAA8B;IAC3D,MAAM,OAAO,GAAG,IAAA,yBAAgB,GAAE,CAAC;IACnC,IAAI,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,IAAI,gCAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnF,IAAI,IAAI,KAAK,OAAO,IAAI,OAAO,CAAC,KAAK;QAAE,OAAO,IAAI,8BAAa,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/E,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAC;IACpG,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,qBAAqB,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** @internal */
2
+ export declare function sanitizeProviderData(value: unknown, depth?: number): unknown;
3
+ /** @internal */
4
+ export declare function sanitizedProviderRecord(value: unknown): Record<string, unknown> | null;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sanitizeProviderData = sanitizeProviderData;
4
+ exports.sanitizedProviderRecord = sanitizedProviderRecord;
5
+ const SENSITIVE_KEY = /(?:authorization|secret|api.?key|password|passcode|pin|otp|token|mobile|phone|email|customer|signature|signed.?data|payment.?url|redirect.?url)/i;
6
+ const MAX_DEPTH = 5;
7
+ const MAX_ARRAY_ITEMS = 50;
8
+ const MAX_STRING_LENGTH = 500;
9
+ /** @internal */
10
+ function sanitizeProviderData(value, depth = 0) {
11
+ if (depth > MAX_DEPTH)
12
+ return '[MAX_DEPTH]';
13
+ if (value == null || typeof value === 'number' || typeof value === 'boolean')
14
+ return value;
15
+ if (typeof value === 'string') {
16
+ return value.length > MAX_STRING_LENGTH ? `${value.slice(0, MAX_STRING_LENGTH)}…` : value;
17
+ }
18
+ if (Array.isArray(value)) {
19
+ return value.slice(0, MAX_ARRAY_ITEMS).map(item => sanitizeProviderData(item, depth + 1));
20
+ }
21
+ if (typeof value === 'object') {
22
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
23
+ key,
24
+ SENSITIVE_KEY.test(key) ? '[REDACTED]' : sanitizeProviderData(item, depth + 1),
25
+ ]));
26
+ }
27
+ return String(value).slice(0, MAX_STRING_LENGTH);
28
+ }
29
+ /** @internal */
30
+ function sanitizedProviderRecord(value) {
31
+ if (!value || typeof value !== 'object' || Array.isArray(value))
32
+ return null;
33
+ return sanitizeProviderData(value);
34
+ }
35
+ //# sourceMappingURL=sanitize-provider-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-provider-data.js","sourceRoot":"","sources":["../../src/providers/sanitize-provider-data.ts"],"names":[],"mappings":";;AAMA,oDAgBC;AAGD,0DAGC;AA5BD,MAAM,aAAa,GAAG,kJAAkJ,CAAC;AACzK,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B,gBAAgB;AAChB,SAAgB,oBAAoB,CAAC,KAAc,EAAE,KAAK,GAAG,CAAC;IAC5D,IAAI,KAAK,GAAG,SAAS;QAAE,OAAO,aAAa,CAAC;IAC5C,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC3F,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;IAC5F,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;YAC9F,GAAG;YACH,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,oBAAoB,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC;SAC/E,CAAC,CAAC,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;AACnD,CAAC;AAED,gBAAgB;AAChB,SAAgB,uBAAuB,CAAC,KAAc;IACpD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,OAAO,oBAAoB,CAAC,KAAK,CAA4B,CAAC;AAChE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare function hmacSha256Base64(message: string, secret: string): string;
2
+ export declare function secureStringEqual(left: string, right: string): boolean;
3
+ export declare function signSettlementProof(input: {
4
+ provider: string;
5
+ attemptId: string;
6
+ orderId: string;
7
+ amount: number;
8
+ providerReference: string;
9
+ }, secret: string): string;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hmacSha256Base64 = hmacSha256Base64;
4
+ exports.secureStringEqual = secureStringEqual;
5
+ exports.signSettlementProof = signSettlementProof;
6
+ const node_crypto_1 = require("node:crypto");
7
+ function hmacSha256Base64(message, secret) {
8
+ return (0, node_crypto_1.createHmac)('sha256', secret).update(message, 'utf8').digest('base64');
9
+ }
10
+ function secureStringEqual(left, right) {
11
+ const a = Buffer.from(left, 'utf8');
12
+ const b = Buffer.from(right, 'utf8');
13
+ return a.length === b.length && (0, node_crypto_1.timingSafeEqual)(a, b);
14
+ }
15
+ function signSettlementProof(input, secret) {
16
+ return hmacSha256Base64([input.provider, input.attemptId, input.orderId, input.amount, input.providerReference].join('|'), secret);
17
+ }
18
+ //# sourceMappingURL=signing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.js","sourceRoot":"","sources":["../../src/providers/signing.ts"],"names":[],"mappings":";;AAEA,4CAEC;AAED,8CAIC;AAED,kDAWC;AAvBD,6CAA0D;AAE1D,SAAgB,gBAAgB,CAAC,OAAe,EAAE,MAAc;IAC9D,OAAO,IAAA,wBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC/E,CAAC;AAED,SAAgB,iBAAiB,CAAC,IAAY,EAAE,KAAa;IAC3D,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,IAAA,6BAAe,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAgB,mBAAmB,CAAC,KAMnC,EAAE,MAAc;IACf,OAAO,gBAAgB,CACrB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EACjG,MAAM,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { ActiveOrderService, OrderService, PaymentMethodService, RequestContext, RequestContextService, TransactionalConnection } from '@vendure/core';
2
+ import { NepalPaymentAttempt } from '../entities/payment-attempt.entity';
3
+ import type { NepalPaymentProviderCode, PaymentInitiation } from '../types';
4
+ export interface InitiatedPayment extends PaymentInitiation {
5
+ attemptId: string;
6
+ provider: NepalPaymentProviderCode;
7
+ status: string;
8
+ }
9
+ export declare class PaymentOrchestratorService {
10
+ private readonly connection;
11
+ private readonly activeOrderService;
12
+ private readonly orderService;
13
+ private readonly paymentMethodService;
14
+ private readonly requestContextService;
15
+ constructor(connection: TransactionalConnection, activeOrderService: ActiveOrderService, orderService: OrderService, paymentMethodService: PaymentMethodService, requestContextService: RequestContextService);
16
+ initiate(ctx: RequestContext, providerCode: NepalPaymentProviderCode): Promise<InitiatedPayment>;
17
+ processCallback(providerCode: NepalPaymentProviderCode, attemptId: string, callbackPayload: Record<string, unknown>): Promise<NepalPaymentAttempt>;
18
+ }
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PaymentOrchestratorService = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const core_1 = require("@vendure/core");
15
+ const node_crypto_1 = require("node:crypto");
16
+ const typeorm_1 = require("typeorm");
17
+ const config_1 = require("../config");
18
+ const payment_attempt_entity_1 = require("../entities/payment-attempt.entity");
19
+ const provider_factory_1 = require("../providers/provider.factory");
20
+ const sanitize_provider_data_1 = require("../providers/sanitize-provider-data");
21
+ const signing_1 = require("../providers/signing");
22
+ let PaymentOrchestratorService = class PaymentOrchestratorService {
23
+ constructor(connection, activeOrderService, orderService, paymentMethodService, requestContextService) {
24
+ this.connection = connection;
25
+ this.activeOrderService = activeOrderService;
26
+ this.orderService = orderService;
27
+ this.paymentMethodService = paymentMethodService;
28
+ this.requestContextService = requestContextService;
29
+ }
30
+ async initiate(ctx, providerCode) {
31
+ const order = await this.activeOrderService.getActiveOrder(ctx, undefined);
32
+ if (!order)
33
+ throw new Error('No active order exists');
34
+ if (order.state !== 'ArrangingPayment') {
35
+ throw new Error(`Order must be in ArrangingPayment, but is in ${order.state}`);
36
+ }
37
+ if (order.currencyCode !== 'NPR')
38
+ throw new Error('Nepal payment providers require NPR');
39
+ const repository = this.connection.getRepository(ctx, payment_attempt_entity_1.NepalPaymentAttempt);
40
+ const existing = await repository.findOne({
41
+ where: {
42
+ orderId: String(order.id),
43
+ provider: providerCode,
44
+ status: (0, typeorm_1.In)(['initiated', 'pending']),
45
+ },
46
+ order: { createdAt: 'DESC' },
47
+ });
48
+ if (existing && (!existing.expiresAt || existing.expiresAt > new Date())) {
49
+ throw new Error(`An active ${providerCode} payment attempt already exists: ${existing.id}`);
50
+ }
51
+ const attemptId = (0, node_crypto_1.randomUUID)();
52
+ const options = (0, config_1.getPluginOptions)();
53
+ const providerOptions = options[providerCode];
54
+ const paymentMethodCode = providerOptions?.paymentMethodCode ?? config_1.defaultPaymentMethodCodes[providerCode];
55
+ const provider = (0, provider_factory_1.createProvider)(providerCode);
56
+ const eligibleMethods = await this.paymentMethodService.getEligiblePaymentMethods(ctx, order);
57
+ if (!eligibleMethods.some(method => method.code === paymentMethodCode)) {
58
+ throw new Error(`Payment method "${paymentMethodCode}" is not eligible for this order`);
59
+ }
60
+ const attempt = repository.create({
61
+ orderId: String(order.id),
62
+ orderCode: order.code,
63
+ channelId: String(ctx.channelId),
64
+ channelToken: ctx.channel.token,
65
+ provider: providerCode,
66
+ paymentMethodCode,
67
+ merchantReference: attemptId,
68
+ providerReference: null,
69
+ amount: order.totalWithTax,
70
+ currencyCode: order.currencyCode,
71
+ status: 'initiated',
72
+ idempotencyKey: `${providerCode}:${attemptId}`,
73
+ expiresAt: undefined,
74
+ verifiedAt: undefined,
75
+ providerTransactionId: null,
76
+ vendurePaymentId: null,
77
+ providerResponse: null,
78
+ });
79
+ await repository.save(attempt);
80
+ try {
81
+ const initiation = await provider.initiate({
82
+ attemptId,
83
+ orderId: order.id,
84
+ orderCode: order.code,
85
+ amount: order.totalWithTax,
86
+ currencyCode: order.currencyCode,
87
+ returnUrl: `${options.publicServerUrl.replace(/\/$/, '')}/nepal-payments/callback/${providerCode}/${attemptId}`,
88
+ customer: order.customer ? {
89
+ name: `${order.customer.firstName} ${order.customer.lastName}`.trim(),
90
+ email: order.customer.emailAddress,
91
+ phone: order.customer.phoneNumber,
92
+ } : undefined,
93
+ });
94
+ attempt.providerReference = initiation.providerReference;
95
+ attempt.expiresAt = initiation.expiresAt;
96
+ attempt.providerResponse = (0, sanitize_provider_data_1.sanitizedProviderRecord)(initiation.raw);
97
+ await repository.save(attempt);
98
+ return { ...initiation, attemptId, provider: providerCode, status: attempt.status };
99
+ }
100
+ catch (error) {
101
+ attempt.status = 'failed';
102
+ attempt.providerResponse = { error: error instanceof Error ? error.message : 'Initiation failed' };
103
+ await repository.save(attempt);
104
+ throw error;
105
+ }
106
+ }
107
+ async processCallback(providerCode, attemptId, callbackPayload) {
108
+ const rawRepository = this.connection.rawConnection.getRepository(payment_attempt_entity_1.NepalPaymentAttempt);
109
+ let attempt = await rawRepository.findOne({ where: { merchantReference: attemptId, provider: providerCode } });
110
+ if (!attempt)
111
+ throw new Error('Payment attempt was not found');
112
+ if (attempt.status === 'settled' || attempt.status === 'refunded' || attempt.status === 'partially-refunded') {
113
+ return attempt;
114
+ }
115
+ const claim = await rawRepository.createQueryBuilder()
116
+ .update(payment_attempt_entity_1.NepalPaymentAttempt)
117
+ .set({ status: 'verifying' })
118
+ .where('id = :id', { id: attempt.id })
119
+ .andWhere('status IN (:...statuses)', { statuses: ['initiated', 'pending', 'unknown', 'failed'] })
120
+ .execute();
121
+ if (!claim.affected) {
122
+ return (await rawRepository.findOneByOrFail({ id: attempt.id }));
123
+ }
124
+ try {
125
+ const verified = await (0, provider_factory_1.createProvider)(providerCode).verify({
126
+ merchantReference: attempt.merchantReference,
127
+ providerReference: attempt.providerReference ?? undefined,
128
+ amount: attempt.amount,
129
+ callbackPayload,
130
+ });
131
+ if (verified.amount !== attempt.amount) {
132
+ throw new Error('Verified payment amount does not match the payment attempt');
133
+ }
134
+ attempt.status = verified.status;
135
+ attempt.providerTransactionId = verified.transactionId ?? null;
136
+ attempt.providerResponse = (0, sanitize_provider_data_1.sanitizedProviderRecord)(verified.raw);
137
+ if (verified.status !== 'settled') {
138
+ await rawRepository.save(attempt);
139
+ return attempt;
140
+ }
141
+ const ctx = await this.requestContextService.create({
142
+ apiType: 'shop',
143
+ channelOrToken: attempt.channelToken,
144
+ activeOrderId: attempt.orderId,
145
+ });
146
+ const proof = (0, signing_1.signSettlementProof)({
147
+ provider: providerCode,
148
+ attemptId: attempt.merchantReference,
149
+ orderId: attempt.orderId,
150
+ amount: attempt.amount,
151
+ providerReference: attempt.providerReference ?? verified.providerReference ?? '',
152
+ }, (0, config_1.getPluginOptions)().internalSigningSecret);
153
+ const result = await this.orderService.addPaymentToOrder(ctx, attempt.orderId, {
154
+ method: attempt.paymentMethodCode,
155
+ metadata: {
156
+ attemptId: attempt.merchantReference,
157
+ providerReference: attempt.providerReference ?? verified.providerReference,
158
+ proof,
159
+ },
160
+ });
161
+ if ((0, core_1.isGraphQlErrorResult)(result)) {
162
+ throw new Error(result.message);
163
+ }
164
+ attempt.status = 'settled';
165
+ attempt.verifiedAt = new Date();
166
+ const payment = result.payments?.find(item => item.metadata?.public?.attemptId === attempt.merchantReference);
167
+ attempt.vendurePaymentId = payment ? String(payment.id) : null;
168
+ await rawRepository.save(attempt);
169
+ return attempt;
170
+ }
171
+ catch (error) {
172
+ attempt.status = 'pending';
173
+ attempt.providerResponse = { error: error instanceof Error ? error.message : 'Callback processing failed' };
174
+ await rawRepository.save(attempt);
175
+ throw error;
176
+ }
177
+ }
178
+ };
179
+ exports.PaymentOrchestratorService = PaymentOrchestratorService;
180
+ exports.PaymentOrchestratorService = PaymentOrchestratorService = __decorate([
181
+ (0, common_1.Injectable)(),
182
+ __metadata("design:paramtypes", [core_1.TransactionalConnection,
183
+ core_1.ActiveOrderService,
184
+ core_1.OrderService,
185
+ core_1.PaymentMethodService,
186
+ core_1.RequestContextService])
187
+ ], PaymentOrchestratorService);
188
+ //# sourceMappingURL=payment-orchestrator.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment-orchestrator.service.js","sourceRoot":"","sources":["../../src/services/payment-orchestrator.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { RequestContext, TransactionalConnection } from '@vendure/core';
2
+ import { PaymentOrchestratorService } from './payment-orchestrator.service';
3
+ export declare class ReconciliationService {
4
+ private readonly connection;
5
+ private readonly orchestrator;
6
+ constructor(connection: TransactionalConnection, orchestrator: PaymentOrchestratorService);
7
+ reconcile(ctx: RequestContext, limit?: number): Promise<{
8
+ checked: number;
9
+ settled: number;
10
+ }>;
11
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ReconciliationService = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const core_1 = require("@vendure/core");
15
+ const typeorm_1 = require("typeorm");
16
+ const payment_attempt_entity_1 = require("../entities/payment-attempt.entity");
17
+ const payment_orchestrator_service_1 = require("./payment-orchestrator.service");
18
+ let ReconciliationService = class ReconciliationService {
19
+ constructor(connection, orchestrator) {
20
+ this.connection = connection;
21
+ this.orchestrator = orchestrator;
22
+ }
23
+ async reconcile(ctx, limit = 50) {
24
+ const repository = this.connection.getRepository(ctx, payment_attempt_entity_1.NepalPaymentAttempt);
25
+ const staleBefore = new Date(Date.now() - 5 * 60_000);
26
+ await repository.update({ status: 'verifying', updatedAt: (0, typeorm_1.LessThan)(staleBefore) }, { status: 'pending' });
27
+ const attempts = await repository.find({
28
+ where: {
29
+ status: (0, typeorm_1.In)(['initiated', 'pending', 'unknown']),
30
+ updatedAt: (0, typeorm_1.LessThan)(new Date(Date.now() - 30_000)),
31
+ },
32
+ order: { updatedAt: 'ASC' },
33
+ take: Math.max(1, Math.min(limit, 200)),
34
+ });
35
+ let settled = 0;
36
+ for (const attempt of attempts) {
37
+ try {
38
+ const result = await this.orchestrator.processCallback(attempt.provider, attempt.merchantReference, {});
39
+ if (result.status === 'settled')
40
+ settled += 1;
41
+ }
42
+ catch {
43
+ // A future run will retry attempts that remain pending.
44
+ }
45
+ }
46
+ return { checked: attempts.length, settled };
47
+ }
48
+ };
49
+ exports.ReconciliationService = ReconciliationService;
50
+ exports.ReconciliationService = ReconciliationService = __decorate([
51
+ (0, common_1.Injectable)(),
52
+ __metadata("design:paramtypes", [core_1.TransactionalConnection,
53
+ payment_orchestrator_service_1.PaymentOrchestratorService])
54
+ ], ReconciliationService);
55
+ //# sourceMappingURL=reconciliation.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciliation.service.js","sourceRoot":"","sources":["../../src/services/reconciliation.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,wCAAwE;AACxE,qCAAuC;AACvC,+EAAyE;AACzE,iFAA4E;AAGrE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAChC,YACmB,UAAmC,EACnC,YAAwC;QADxC,eAAU,GAAV,UAAU,CAAyB;QACnC,iBAAY,GAAZ,YAAY,CAA4B;IACxD,CAAC;IAEJ,KAAK,CAAC,SAAS,CAAC,GAAmB,EAAE,KAAK,GAAG,EAAE;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,GAAG,EAAE,4CAAmB,CAAC,CAAC;QAC3E,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QACtD,MAAM,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,IAAA,kBAAQ,EAAC,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAC1G,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC;YACrC,KAAK,EAAE;gBACL,MAAM,EAAE,IAAA,YAAE,EAAC,CAAC,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;gBAC/C,SAAS,EAAE,IAAA,kBAAQ,EAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC;aACnD;YACD,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;YAC3B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;SACxC,CAAC,CAAC;QACH,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;gBACxG,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;oBAAE,OAAO,IAAI,CAAC,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;YAC1D,CAAC;QACH,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;IAC/C,CAAC;CACF,CAAA;AA7BY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAGoB,8BAAuB;QACrB,yDAA0B;GAHhD,qBAAqB,CA6BjC"}
@@ -0,0 +1,2 @@
1
+ import { ScheduledTask } from '@vendure/core';
2
+ export declare const reconcileNepalPaymentsTask: ScheduledTask<Record<string, any>>;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reconcileNepalPaymentsTask = void 0;
4
+ const core_1 = require("@vendure/core");
5
+ const reconciliation_service_1 = require("./reconciliation.service");
6
+ exports.reconcileNepalPaymentsTask = new core_1.ScheduledTask({
7
+ id: 'reconcile-nepal-payments',
8
+ description: 'Verify pending Khalti and eSewa payment attempts',
9
+ schedule: '*/5 * * * *',
10
+ preventOverlap: true,
11
+ timeout: '4m',
12
+ execute: ({ injector, scheduledContext }) => injector.get(reconciliation_service_1.ReconciliationService).reconcile(scheduledContext),
13
+ });
14
+ //# sourceMappingURL=reconciliation.task.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciliation.task.js","sourceRoot":"","sources":["../../src/services/reconciliation.task.ts"],"names":[],"mappings":";;;AAAA,wCAA8C;AAC9C,qEAAiE;AAEpD,QAAA,0BAA0B,GAAG,IAAI,oBAAa,CAAC;IAC1D,EAAE,EAAE,0BAA0B;IAC9B,WAAW,EAAE,kDAAkD;IAC/D,QAAQ,EAAE,aAAa;IACvB,cAAc,EAAE,IAAI;IACpB,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAC1C,QAAQ,CAAC,GAAG,CAAC,8CAAqB,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC;CAClE,CAAC,CAAC"}
@@ -0,0 +1,76 @@
1
+ import type { CurrencyCode, ID } from '@vendure/core';
2
+ export declare const NEPAL_PAYMENT_PROVIDERS: readonly ["khalti", "esewa", "fonepay"];
3
+ /** Provider identifiers used by the shared adapter layer. */
4
+ export type NepalPaymentProviderCode = (typeof NEPAL_PAYMENT_PROVIDERS)[number];
5
+ export declare const PAYMENT_ATTEMPT_STATUSES: readonly ["initiated", "pending", "settled", "failed", "cancelled", "expired", "partially-refunded", "refunded", "unknown"];
6
+ /** Conservative normalized payment states shared across providers. */
7
+ export type PaymentAttemptStatus = (typeof PAYMENT_ATTEMPT_STATUSES)[number];
8
+ /** Optional customer fields accepted by provider initiation APIs. */
9
+ export interface CustomerDetails {
10
+ name?: string;
11
+ email?: string;
12
+ phone?: string;
13
+ }
14
+ /** Provider-neutral initiation input created from the active Vendure order. */
15
+ export interface InitiatePaymentInput {
16
+ attemptId: string;
17
+ orderId: ID;
18
+ orderCode: string;
19
+ amount: number;
20
+ currencyCode: CurrencyCode;
21
+ returnUrl: string;
22
+ customer?: CustomerDetails;
23
+ }
24
+ /** Provider-neutral redirect, QR, or signed-form initiation result. */
25
+ export interface PaymentInitiation {
26
+ providerReference: string;
27
+ redirectUrl?: string;
28
+ qrPayload?: string;
29
+ form?: {
30
+ action: string;
31
+ fields: Array<{
32
+ name: string;
33
+ value: string;
34
+ }>;
35
+ };
36
+ expiresAt?: Date;
37
+ raw?: unknown;
38
+ }
39
+ /** Input used for a trusted server-to-server payment verification. */
40
+ export interface VerifyPaymentInput {
41
+ merchantReference: string;
42
+ providerReference?: string;
43
+ amount: number;
44
+ callbackPayload?: Record<string, unknown>;
45
+ }
46
+ /** Normalized result of a provider status lookup. */
47
+ export interface VerifiedPayment {
48
+ providerReference?: string;
49
+ transactionId?: string;
50
+ amount: number;
51
+ status: PaymentAttemptStatus;
52
+ raw?: unknown;
53
+ }
54
+ /** Provider-neutral refund request. */
55
+ export interface RefundPaymentInput {
56
+ transactionId: string;
57
+ amount?: number;
58
+ mobile?: string;
59
+ }
60
+ /** Normalized result of a provider refund request. */
61
+ export interface RefundResult {
62
+ transactionId?: string;
63
+ status: 'settled' | 'pending' | 'failed';
64
+ raw?: unknown;
65
+ }
66
+ /**
67
+ * Contract implemented by Nepalese payment provider adapters.
68
+ *
69
+ * This interface is exported for experimentation but may evolve before 1.0.
70
+ */
71
+ export interface NepalPaymentProvider {
72
+ readonly code: NepalPaymentProviderCode;
73
+ initiate(input: InitiatePaymentInput): Promise<PaymentInitiation>;
74
+ verify(input: VerifyPaymentInput): Promise<VerifiedPayment>;
75
+ refund?(input: RefundPaymentInput): Promise<RefundResult>;
76
+ }