@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Planned
10
+
11
+ - End-to-end tests against provider sandboxes and a real Vendure test server
12
+ - Fonepay dynamic QR support after official merchant API documentation is available
13
+ - Channel-specific provider credentials
14
+ - Admin Dashboard payment-attempt and reconciliation views
15
+
16
+ ## [0.1.0] - 2026-07-13
17
+
18
+ ### Added
19
+
20
+ - Vendure 3.7 plugin package with MIT licensing
21
+ - Khalti KPG-2 payment initiation, lookup verification, and wallet refunds, with bank-refund limitations documented
22
+ - eSewa ePay v2 signed form generation, callback validation, and status verification
23
+ - Persistent and idempotent payment-attempt records
24
+ - Authenticated settlement proofs bound to a Vendure order
25
+ - GET and POST provider callback routes
26
+ - Scheduled reconciliation for pending payments
27
+ - NPR-safe integer amount conversion
28
+ - Storefront GraphQL API and integration documentation
29
+ - Unit tests for configuration, signing, amount conversion, Khalti, and eSewa
30
+
31
+ [Unreleased]: https://github.com/pracharya2601/NPP/compare/v0.1.0...HEAD
32
+ [0.1.0]: https://github.com/pracharya2601/NPP/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TIAT
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE.md ADDED
@@ -0,0 +1,9 @@
1
+ # Notices
2
+
3
+ This project is an independent community plugin and is not affiliated with, endorsed by, or sponsored by Vendure, Khalti by IME, eSewa, Fonepay, or Nepal Rastra Bank.
4
+
5
+ Vendure is licensed separately under GPLv3. Vendure's official plugin-publishing documentation states that its plugin exception permits independently distributed plugins to use a different license. This plugin's own source code is distributed under the MIT License in [`LICENSE`](./LICENSE).
6
+
7
+ Vendure, Khalti, IME Pay, eSewa, Fonepay, and other names or logos are trademarks of their respective owners. They are used only to identify interoperability targets.
8
+
9
+ No payment-provider SDK source code, credentials, logos, or private merchant documentation are included in this package.
package/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # Vendure Nepal Payments
2
+
3
+ [![CI](https://github.com/pracharya2601/NPP/actions/workflows/ci.yml/badge.svg)](https://github.com/pracharya2601/NPP/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
5
+
6
+ An open-source Vendure 3.7 plugin for Nepalese payment providers, licensed under MIT. The initial release implements Khalti KPG-2 and eSewa ePay v2 behind a shared payment-attempt and provider interface. Fonepay is reserved in the API, but intentionally disabled until current official merchant documentation is available.
7
+
8
+ > **Pre-release status:** the code is suitable for integration and sandbox testing, but version 0.1.0 has not yet been validated with real merchant sandbox credentials or published to npm. Do not process production payments until the production checklist and provider certification/onboarding steps are complete.
9
+
10
+ ## Documentation
11
+
12
+ - [Install in an existing Vendure project](./docs/INSTALLATION.md)
13
+ - [Configuration reference](./docs/CONFIGURATION.md)
14
+ - [Storefront integration](./docs/STOREFRONT.md)
15
+ - [Provider support](./docs/PROVIDERS.md)
16
+ - [Architecture](./docs/ARCHITECTURE.md)
17
+ - [Production checklist](./docs/PRODUCTION.md)
18
+ - [Data handling and privacy](./docs/DATA_HANDLING.md)
19
+ - [Troubleshooting](./docs/TROUBLESHOOTING.md)
20
+ - [Complete documentation index](./docs/README.md)
21
+
22
+ ## What is implemented
23
+
24
+ - Khalti server-side initiation, redirect, lookup verification, and wallet refund calls; some bank-funded refunds require manual provider handling
25
+ - eSewa signed ePay form generation, signed callback validation, and status verification
26
+ - Separate Vendure payment handlers: `nepal-khalti` and `nepal-esewa`
27
+ - Persistent, idempotent `NepalPaymentAttempt` records
28
+ - Signed internal settlement proofs that bind a provider transaction to its Vendure order
29
+ - GET and POST callback routes
30
+ - A worker-safe scheduled task that retries pending payments every five minutes
31
+ - Integer-only NPR conversion and provider contract tests
32
+
33
+ Only a server-to-server provider lookup can settle an order. Browser callback fields are never accepted as proof of payment.
34
+
35
+ ## Install
36
+
37
+ This repository is currently the package itself. In a Vendure application, install the package and ensure Vendure's recommended `DefaultSchedulerPlugin` is enabled.
38
+
39
+ ```bash
40
+ # Available after the first npm publication
41
+ npm install @prakashacharya/vendure-plugin-nepal-payments
42
+ ```
43
+
44
+ For installation from a local clone before publication, build an npm tarball and install it into the Vendure server project. See the [complete installation guide](./docs/INSTALLATION.md#1-install-the-package).
45
+
46
+ Configure it in `vendure-config.ts`:
47
+
48
+ ```ts
49
+ import { DefaultSchedulerPlugin, VendureConfig } from '@vendure/core';
50
+ import { NepalPaymentsPlugin } from '@prakashacharya/vendure-plugin-nepal-payments';
51
+
52
+ export const config: VendureConfig = {
53
+ // ...
54
+ plugins: [
55
+ DefaultSchedulerPlugin.init(),
56
+ NepalPaymentsPlugin.init({
57
+ publicServerUrl: process.env.PUBLIC_SERVER_URL!,
58
+ storefrontResultUrl: process.env.STOREFRONT_PAYMENT_RESULT_URL!,
59
+ internalSigningSecret: process.env.NEPAL_PAYMENTS_INTERNAL_SECRET!,
60
+ khalti: {
61
+ environment: process.env.KHALTI_ENVIRONMENT as 'sandbox' | 'production',
62
+ secretKey: process.env.KHALTI_SECRET_KEY!,
63
+ websiteUrl: process.env.KHALTI_WEBSITE_URL!,
64
+ paymentMethodCode: 'khalti',
65
+ },
66
+ esewa: {
67
+ environment: process.env.ESEWA_ENVIRONMENT as 'sandbox' | 'production',
68
+ productCode: process.env.ESEWA_PRODUCT_CODE!,
69
+ secretKey: process.env.ESEWA_SECRET_KEY!,
70
+ paymentMethodCode: 'esewa',
71
+ },
72
+ reconciliationSchedule: '*/5 * * * *',
73
+ }),
74
+ ],
75
+ };
76
+ ```
77
+
78
+ `publicServerUrl` must be the externally reachable Vendure origin. Production values must use HTTPS. Generate `internalSigningSecret` independently from gateway credentials, with at least 32 random characters.
79
+
80
+ ## Database migration
81
+
82
+ The plugin adds the `NepalPaymentAttempt` entity. Generate and run a Vendure migration from the host application before deploying:
83
+
84
+ ```bash
85
+ npx vendure migrate
86
+ ```
87
+
88
+ Do not enable schema synchronization in production.
89
+
90
+ ## Create payment methods
91
+
92
+ In the Vendure Dashboard, create and assign these payment methods to the NPR channel:
93
+
94
+ | Payment method code | Handler |
95
+ | --- | --- |
96
+ | `khalti` | `Khalti by IME` / `nepal-khalti` |
97
+ | `esewa` | `eSewa` / `nepal-esewa` |
98
+
99
+ If a different payment method code is used, set the matching `paymentMethodCode` in plugin configuration. Keep the methods unavailable outside NPR channels using channel assignment or an eligibility checker.
100
+
101
+ ## Storefront checkout
102
+
103
+ First transition the active order to `ArrangingPayment`, then initiate a provider payment:
104
+
105
+ ```graphql
106
+ mutation InitiateNepalPayment($provider: NepalPaymentProviderCode!) {
107
+ initiateNepalPayment(provider: $provider) {
108
+ attemptId
109
+ provider
110
+ status
111
+ redirectUrl
112
+ expiresAt
113
+ form {
114
+ action
115
+ fields { name value }
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ For Khalti, redirect the browser to `redirectUrl`.
122
+
123
+ For eSewa, submit the returned fields as an HTML POST without changing any value:
124
+
125
+ ```ts
126
+ function submitProviderForm(form: { action: string; fields: Array<{ name: string; value: string }> }) {
127
+ const element = document.createElement('form');
128
+ element.method = 'POST';
129
+ element.action = form.action;
130
+ for (const field of form.fields) {
131
+ const input = document.createElement('input');
132
+ input.type = 'hidden';
133
+ input.name = field.name;
134
+ input.value = field.value;
135
+ element.appendChild(input);
136
+ }
137
+ document.body.appendChild(element);
138
+ element.submit();
139
+ }
140
+ ```
141
+
142
+ After the provider returns, the plugin redirects to `storefrontResultUrl` with `attemptId`, `provider`, and `status` query parameters. The storefront should then query the active order/order state from Vendure; it must not treat the redirect query string itself as proof of fulfillment.
143
+
144
+ ## Callback routes
145
+
146
+ The plugin creates both GET and POST routes:
147
+
148
+ ```text
149
+ /nepal-payments/callback/khalti/:attemptId
150
+ /nepal-payments/callback/esewa/:attemptId
151
+ ```
152
+
153
+ These URLs are generated automatically. They must be reachable from the public internet in production.
154
+
155
+ ## Payment lifecycle
156
+
157
+ 1. A UUID payment attempt is stored against the active Vendure order.
158
+ 2. The plugin creates a Khalti payment or signed eSewa form.
159
+ 3. The provider returns the customer to the callback route.
160
+ 4. The plugin claims the attempt atomically and calls the provider status endpoint.
161
+ 5. It verifies the exact amount and successful provider state.
162
+ 6. It generates a short-lived internal settlement proof bound to provider, attempt, order, amount, and provider reference.
163
+ 7. The Vendure payment handler verifies that proof and independently checks the provider again.
164
+ 8. Vendure creates a `Settled` payment and transitions the order normally.
165
+ 9. The scheduled reconciliation task repeats verification for delayed or interrupted callbacks.
166
+
167
+ ## Fonepay
168
+
169
+ Fonepay dynamic QR requires merchant-specific onboarding documentation and credentials. The shared adapter boundary and GraphQL enum are present, but initiating `FONEPAY` currently fails explicitly. Do not implement signing or callback rules from unofficial copies of its API document. Add the provider only after obtaining:
170
+
171
+ - sandbox and production base URLs
172
+ - authentication and signing specification
173
+ - dynamic QR request and response schema
174
+ - transaction enquiry endpoint
175
+ - refund capability
176
+ - callback authentication or source requirements
177
+
178
+ IME Pay is not implemented separately because it has merged into Khalti by IME.
179
+
180
+ ## Development
181
+
182
+ ```bash
183
+ npm install
184
+ npm run typecheck
185
+ npm test
186
+ npm run build
187
+ ```
188
+
189
+ The unit tests mock provider HTTP requests and do not contact live gateways.
190
+
191
+ ## Compatibility
192
+
193
+ | Component | Supported |
194
+ | --- | --- |
195
+ | Vendure | `>=3.7.0 <4.0.0` |
196
+ | Node.js | 22 and 24 |
197
+ | Currency | NPR |
198
+ | Databases | Vendure-supported PostgreSQL, MySQL/MariaDB, and SQLite; end-to-end database tests are planned before 1.0 |
199
+
200
+ ## Contributing and support
201
+
202
+ Contributions are welcome. Read [CONTRIBUTING.md](./CONTRIBUTING.md), the [Code of Conduct](./CODE_OF_CONDUCT.md), and [SECURITY.md](./SECURITY.md) before participating. General support expectations are in [SUPPORT.md](./SUPPORT.md), and planned work is tracked in [ROADMAP.md](./ROADMAP.md).
203
+
204
+ Do not open public issues containing credentials, customer information, private merchant documents, or security vulnerabilities.
205
+
206
+ ## License
207
+
208
+ The plugin's original code is available under the [MIT License](./LICENSE). Vendure's official plugin-publishing guide states that its GPLv3 plugin exception permits independently distributed plugins to use another license. See [licensing and trademark details](./docs/LICENSING.md) and [NOTICE.md](./NOTICE.md).
209
+
210
+ This community project is not affiliated with or endorsed by Vendure or any payment provider.
211
+
212
+ ## Production checklist
213
+
214
+ - Complete merchant onboarding separately with Khalti and eSewa.
215
+ - Use distinct sandbox and production credentials.
216
+ - Keep all secrets in a secret manager, not source control or Dashboard descriptions.
217
+ - Run the generated database migration before enabling payment methods.
218
+ - Run Vendure workers and `DefaultSchedulerPlugin` for reconciliation.
219
+ - Test duplicate, delayed, cancelled, expired, altered-amount, pending, timeout, and refund flows.
220
+ - Alert on attempts that remain pending or unknown beyond an agreed threshold.
221
+ - Confirm provider refund rules and transaction limits in the signed merchant agreements.
222
+
223
+ Official references: [Vendure payments](https://docs.vendure.io/current/core/core-concepts/payment), [publishing a Vendure plugin](https://docs.vendure.io/current/core/how-to/publish-plugin), [Khalti KPG-2](https://docs.khalti.com/khalti-epayment/), [Khalti refunds](https://docs.khalti.com/api/refund/), and [eSewa ePay](https://developer.esewa.com.np/pages/Epay).
@@ -0,0 +1 @@
1
+ export declare const shopApiExtensions: import("graphql").DocumentNode;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shopApiExtensions = void 0;
4
+ const graphql_tag_1 = require("graphql-tag");
5
+ exports.shopApiExtensions = (0, graphql_tag_1.gql) `
6
+ enum NepalPaymentProviderCode {
7
+ KHALTI
8
+ ESEWA
9
+ FONEPAY
10
+ }
11
+
12
+ type NepalPaymentFormField {
13
+ name: String!
14
+ value: String!
15
+ }
16
+
17
+ type NepalPaymentForm {
18
+ action: String!
19
+ fields: [NepalPaymentFormField!]!
20
+ }
21
+
22
+ type NepalPaymentInitiation {
23
+ attemptId: ID!
24
+ provider: NepalPaymentProviderCode!
25
+ status: String!
26
+ redirectUrl: String
27
+ qrPayload: String
28
+ expiresAt: DateTime
29
+ form: NepalPaymentForm
30
+ }
31
+
32
+ extend type Mutation {
33
+ initiateNepalPayment(provider: NepalPaymentProviderCode!): NepalPaymentInitiation!
34
+ }
35
+ `;
36
+ //# sourceMappingURL=api-extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-extensions.js","sourceRoot":"","sources":["../../src/api/api-extensions.ts"],"names":[],"mappings":";;;AAAA,6CAAkC;AAErB,QAAA,iBAAiB,GAAG,IAAA,iBAAG,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BnC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Response } from 'express';
2
+ import { PaymentOrchestratorService } from '../services/payment-orchestrator.service';
3
+ export declare class PaymentCallbackController {
4
+ private readonly orchestrator;
5
+ constructor(orchestrator: PaymentOrchestratorService);
6
+ getCallback(provider: string, attemptId: string, query: Record<string, string | string[]>, response: Response): Promise<void>;
7
+ postCallback(provider: string, attemptId: string, body: Record<string, unknown>, response: Response): Promise<void>;
8
+ private handle;
9
+ }
@@ -0,0 +1,83 @@
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
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.PaymentCallbackController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const config_1 = require("../config");
18
+ const payment_orchestrator_service_1 = require("../services/payment-orchestrator.service");
19
+ const types_1 = require("../types");
20
+ let PaymentCallbackController = class PaymentCallbackController {
21
+ constructor(orchestrator) {
22
+ this.orchestrator = orchestrator;
23
+ }
24
+ async getCallback(provider, attemptId, query, response) {
25
+ return this.handle(provider, attemptId, flatten(query), response);
26
+ }
27
+ async postCallback(provider, attemptId, body, response) {
28
+ return this.handle(provider, attemptId, body, response);
29
+ }
30
+ async handle(providerValue, attemptId, payload, response) {
31
+ const provider = providerValue.toLowerCase();
32
+ let status = 'error';
33
+ let message;
34
+ if (!types_1.NEPAL_PAYMENT_PROVIDERS.includes(provider)) {
35
+ message = 'Unsupported payment provider';
36
+ }
37
+ else {
38
+ try {
39
+ const attempt = await this.orchestrator.processCallback(provider, attemptId, payload);
40
+ status = attempt.status;
41
+ }
42
+ catch (error) {
43
+ message = 'Payment verification could not be completed';
44
+ }
45
+ }
46
+ const target = new URL((0, config_1.getPluginOptions)().storefrontResultUrl);
47
+ target.searchParams.set('attemptId', attemptId);
48
+ target.searchParams.set('provider', providerValue);
49
+ target.searchParams.set('status', status);
50
+ if (message)
51
+ target.searchParams.set('message', message.slice(0, 200));
52
+ return response.redirect(303, target.toString());
53
+ }
54
+ };
55
+ exports.PaymentCallbackController = PaymentCallbackController;
56
+ __decorate([
57
+ (0, common_1.Get)('callback/:provider/:attemptId'),
58
+ __param(0, (0, common_1.Param)('provider')),
59
+ __param(1, (0, common_1.Param)('attemptId')),
60
+ __param(2, (0, common_1.Query)()),
61
+ __param(3, (0, common_1.Res)()),
62
+ __metadata("design:type", Function),
63
+ __metadata("design:paramtypes", [String, String, Object, Object]),
64
+ __metadata("design:returntype", Promise)
65
+ ], PaymentCallbackController.prototype, "getCallback", null);
66
+ __decorate([
67
+ (0, common_1.Post)('callback/:provider/:attemptId'),
68
+ __param(0, (0, common_1.Param)('provider')),
69
+ __param(1, (0, common_1.Param)('attemptId')),
70
+ __param(2, (0, common_1.Body)()),
71
+ __param(3, (0, common_1.Res)()),
72
+ __metadata("design:type", Function),
73
+ __metadata("design:paramtypes", [String, String, Object, Object]),
74
+ __metadata("design:returntype", Promise)
75
+ ], PaymentCallbackController.prototype, "postCallback", null);
76
+ exports.PaymentCallbackController = PaymentCallbackController = __decorate([
77
+ (0, common_1.Controller)('nepal-payments'),
78
+ __metadata("design:paramtypes", [payment_orchestrator_service_1.PaymentOrchestratorService])
79
+ ], PaymentCallbackController);
80
+ function flatten(input) {
81
+ return Object.fromEntries(Object.entries(input).map(([key, value]) => [key, Array.isArray(value) ? value[0] : value]));
82
+ }
83
+ //# sourceMappingURL=payment-callback.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment-callback.controller.js","sourceRoot":"","sources":["../../src/api/payment-callback.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAgF;AAEhF,sCAA6C;AAC7C,2FAAsF;AACtF,oCAAkF;AAG3E,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;IACpC,YAA6B,YAAwC;QAAxC,iBAAY,GAAZ,YAAY,CAA4B;IAAG,CAAC;IAGnE,AAAN,KAAK,CAAC,WAAW,CACI,QAAgB,EACf,SAAiB,EAC5B,KAAwC,EAC1C,QAAkB;QAEzB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;IACpE,CAAC;IAGK,AAAN,KAAK,CAAC,YAAY,CACG,QAAgB,EACf,SAAiB,EAC7B,IAA6B,EAC9B,QAAkB;QAEzB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC1D,CAAC;IAEO,KAAK,CAAC,MAAM,CAClB,aAAqB,EACrB,SAAiB,EACjB,OAAgC,EAChC,QAAkB;QAElB,MAAM,QAAQ,GAAG,aAAa,CAAC,WAAW,EAA8B,CAAC;QACzE,IAAI,MAAM,GAAG,OAAO,CAAC;QACrB,IAAI,OAA2B,CAAC;QAChC,IAAI,CAAC,+BAAuB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChD,OAAO,GAAG,8BAA8B,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;gBACtF,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC1B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,GAAG,6CAA6C,CAAC;YAC1D,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAA,yBAAgB,GAAE,CAAC,mBAAmB,CAAC,CAAC;QAC/D,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAChD,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QACnD,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC1C,IAAI,OAAO;YAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACvE,OAAO,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;CACF,CAAA;AAjDY,8DAAyB;AAI9B;IADL,IAAA,YAAG,EAAC,+BAA+B,CAAC;IAElC,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;IACjB,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;IAClB,WAAA,IAAA,cAAK,GAAE,CAAA;IACP,WAAA,IAAA,YAAG,GAAE,CAAA;;;;4DAGP;AAGK;IADL,IAAA,aAAI,EAAC,+BAA+B,CAAC;IAEnC,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;IACjB,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;IAClB,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,YAAG,GAAE,CAAA;;;;6DAGP;oCArBU,yBAAyB;IADrC,IAAA,mBAAU,EAAC,gBAAgB,CAAC;qCAEgB,yDAA0B;GAD1D,yBAAyB,CAiDrC;AAED,SAAS,OAAO,CAAC,KAAwC;IACvD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACzH,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { RequestContext } from '@vendure/core';
2
+ import { PaymentOrchestratorService } from '../services/payment-orchestrator.service';
3
+ export declare class NepalPaymentsShopResolver {
4
+ private readonly orchestrator;
5
+ constructor(orchestrator: PaymentOrchestratorService);
6
+ initiateNepalPayment(ctx: RequestContext, args: {
7
+ provider: 'KHALTI' | 'ESEWA' | 'FONEPAY';
8
+ }): Promise<import("../services/payment-orchestrator.service").InitiatedPayment>;
9
+ }
@@ -0,0 +1,41 @@
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
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.NepalPaymentsShopResolver = void 0;
16
+ const graphql_1 = require("@nestjs/graphql");
17
+ const core_1 = require("@vendure/core");
18
+ const payment_orchestrator_service_1 = require("../services/payment-orchestrator.service");
19
+ let NepalPaymentsShopResolver = class NepalPaymentsShopResolver {
20
+ constructor(orchestrator) {
21
+ this.orchestrator = orchestrator;
22
+ }
23
+ initiateNepalPayment(ctx, args) {
24
+ return this.orchestrator.initiate(ctx, args.provider.toLowerCase());
25
+ }
26
+ };
27
+ exports.NepalPaymentsShopResolver = NepalPaymentsShopResolver;
28
+ __decorate([
29
+ (0, graphql_1.Mutation)(),
30
+ (0, core_1.Allow)(core_1.Permission.Owner),
31
+ __param(0, (0, core_1.Ctx)()),
32
+ __param(1, (0, graphql_1.Args)()),
33
+ __metadata("design:type", Function),
34
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
35
+ __metadata("design:returntype", void 0)
36
+ ], NepalPaymentsShopResolver.prototype, "initiateNepalPayment", null);
37
+ exports.NepalPaymentsShopResolver = NepalPaymentsShopResolver = __decorate([
38
+ (0, graphql_1.Resolver)(),
39
+ __metadata("design:paramtypes", [payment_orchestrator_service_1.PaymentOrchestratorService])
40
+ ], NepalPaymentsShopResolver);
41
+ //# sourceMappingURL=shop.resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shop.resolver.js","sourceRoot":"","sources":["../../src/api/shop.resolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6CAA2D;AAC3D,wCAAuE;AACvE,2FAAsF;AAI/E,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;IACpC,YAA6B,YAAwC;QAAxC,iBAAY,GAAZ,YAAY,CAA4B;IAAG,CAAC;IAIzE,oBAAoB,CACX,GAAmB,EAClB,IAAkD;QAE1D,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAA8B,CAAC,CAAC;IAClG,CAAC;CACF,CAAA;AAXY,8DAAyB;AAKpC;IAFC,IAAA,kBAAQ,GAAE;IACV,IAAA,YAAK,EAAC,iBAAU,CAAC,KAAK,CAAC;IAErB,WAAA,IAAA,UAAG,GAAE,CAAA;IACL,WAAA,IAAA,cAAI,GAAE,CAAA;;qCADK,qBAAc;;qEAI3B;oCAVU,yBAAyB;IADrC,IAAA,kBAAQ,GAAE;qCAEkC,yDAA0B;GAD1D,yBAAyB,CAWrC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Configuration for the Khalti KPG-2 integration.
3
+ *
4
+ * @category Configuration
5
+ */
6
+ export interface KhaltiPluginOptions {
7
+ secretKey: string;
8
+ websiteUrl: string;
9
+ paymentMethodCode?: string;
10
+ environment?: 'sandbox' | 'production';
11
+ }
12
+ /**
13
+ * Configuration for the eSewa ePay v2 integration.
14
+ *
15
+ * @category Configuration
16
+ */
17
+ export interface EsewaPluginOptions {
18
+ productCode: string;
19
+ secretKey: string;
20
+ paymentMethodCode?: string;
21
+ environment?: 'sandbox' | 'production';
22
+ }
23
+ /**
24
+ * Reserved configuration shape for a future official Fonepay integration.
25
+ * Fonepay is not implemented in the current release.
26
+ *
27
+ * @category Configuration
28
+ */
29
+ export interface FonepayPluginOptions {
30
+ paymentMethodCode?: string;
31
+ }
32
+ /**
33
+ * Options passed to {@link NepalPaymentsPlugin.init}.
34
+ *
35
+ * @category Configuration
36
+ */
37
+ export interface NepalPaymentsPluginOptions {
38
+ /** Public origin of the Vendure server, without a trailing slash. */
39
+ publicServerUrl: string;
40
+ /** Storefront page to receive the final attempt status. */
41
+ storefrontResultUrl: string;
42
+ /** At least 32 random characters, used to authenticate internal settlement metadata. */
43
+ internalSigningSecret: string;
44
+ /** Cron expression for pending-payment reconciliation, or false to disable. Defaults to every 5 minutes. */
45
+ reconciliationSchedule?: string | false;
46
+ khalti?: KhaltiPluginOptions;
47
+ esewa?: EsewaPluginOptions;
48
+ fonepay?: FonepayPluginOptions;
49
+ }
50
+ export declare const defaultPaymentMethodCodes: {
51
+ readonly khalti: "khalti";
52
+ readonly esewa: "esewa";
53
+ readonly fonepay: "fonepay";
54
+ };
55
+ export declare function setPluginOptions(value: NepalPaymentsPluginOptions): void;
56
+ export declare function getPluginOptions(): NepalPaymentsPluginOptions;
package/dist/config.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultPaymentMethodCodes = void 0;
4
+ exports.setPluginOptions = setPluginOptions;
5
+ exports.getPluginOptions = getPluginOptions;
6
+ exports.defaultPaymentMethodCodes = {
7
+ khalti: 'khalti',
8
+ esewa: 'esewa',
9
+ fonepay: 'fonepay',
10
+ };
11
+ let options;
12
+ function setPluginOptions(value) {
13
+ assertPublicUrl(value.publicServerUrl, 'publicServerUrl');
14
+ assertPublicUrl(value.storefrontResultUrl, 'storefrontResultUrl');
15
+ if (value.internalSigningSecret.length < 32) {
16
+ throw new Error('internalSigningSecret must contain at least 32 characters');
17
+ }
18
+ if (!value.khalti && !value.esewa) {
19
+ throw new Error('At least one implemented payment provider must be configured');
20
+ }
21
+ if (value.khalti && (!value.khalti.secretKey || !value.khalti.websiteUrl)) {
22
+ throw new Error('Khalti secretKey and websiteUrl are required');
23
+ }
24
+ if (value.khalti)
25
+ assertPublicUrl(value.khalti.websiteUrl, 'Khalti websiteUrl');
26
+ if (value.esewa && (!value.esewa.secretKey || !value.esewa.productCode)) {
27
+ throw new Error('eSewa secretKey and productCode are required');
28
+ }
29
+ options = value;
30
+ }
31
+ function getPluginOptions() {
32
+ if (!options) {
33
+ throw new Error('NepalPaymentsPlugin.init() must be called before the plugin is used');
34
+ }
35
+ return options;
36
+ }
37
+ function assertPublicUrl(value, name) {
38
+ let url;
39
+ try {
40
+ url = new URL(value);
41
+ }
42
+ catch {
43
+ throw new Error(`${name} must be an absolute URL`);
44
+ }
45
+ const local = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1';
46
+ if (url.protocol !== 'https:' && !(local && url.protocol === 'http:')) {
47
+ throw new Error(`${name} must use HTTPS outside localhost`);
48
+ }
49
+ if (url.username || url.password)
50
+ throw new Error(`${name} must not include credentials`);
51
+ }
52
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AA6DA,4CAiBC;AAED,4CAKC;AAhCY,QAAA,yBAAyB,GAAG;IACvC,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;CACV,CAAC;AAEX,IAAI,OAA+C,CAAC;AAEpD,SAAgB,gBAAgB,CAAC,KAAiC;IAChE,eAAe,CAAC,KAAK,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;IAC1D,eAAe,CAAC,KAAK,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;IAClE,IAAI,KAAK,CAAC,qBAAqB,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,KAAK,CAAC,MAAM;QAAE,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC;IAChF,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,GAAG,KAAK,CAAC;AAClB,CAAC;AAED,SAAgB,gBAAgB;IAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,eAAe,CAAC,KAAa,EAAE,IAAY;IAClD,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,0BAA0B,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK,CAAC;IACrG,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,mCAAmC,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,+BAA+B,CAAC,CAAC;AAC5F,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { DeepPartial, VendureEntity } from '@vendure/core';
2
+ import type { NepalPaymentProviderCode, PaymentAttemptStatus } from '../types';
3
+ /**
4
+ * Stores the asynchronous provider-side lifecycle before a verified Vendure
5
+ * {@link Payment} is created. Applications should treat these records as
6
+ * operational data and must not manually mark them settled.
7
+ *
8
+ * @category Entity
9
+ */
10
+ export declare class NepalPaymentAttempt extends VendureEntity {
11
+ constructor(input?: DeepPartial<NepalPaymentAttempt>);
12
+ orderId: string;
13
+ orderCode: string;
14
+ channelId: string;
15
+ channelToken: string;
16
+ provider: NepalPaymentProviderCode;
17
+ paymentMethodCode: string;
18
+ merchantReference: string;
19
+ providerReference: string | null;
20
+ amount: number;
21
+ currencyCode: string;
22
+ status: PaymentAttemptStatus | 'verifying';
23
+ idempotencyKey: string;
24
+ expiresAt?: Date;
25
+ verifiedAt?: Date;
26
+ providerTransactionId: string | null;
27
+ vendurePaymentId: string | null;
28
+ providerResponse: Record<string, unknown> | null;
29
+ }