awesome-node-checkout 1.1.0 → 1.2.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.
- package/CHANGELOG.md +24 -0
- package/README.md +67 -23
- package/dist/adapters/express/index.d.ts +35 -8
- package/dist/adapters/express/index.d.ts.map +1 -1
- package/dist/adapters/express/index.js +69 -14
- package/dist/adapters/express/index.js.map +1 -1
- package/dist/checkout-configurator.d.ts +5 -1
- package/dist/checkout-configurator.d.ts.map +1 -1
- package/dist/checkout-configurator.js +28 -3
- package/dist/checkout-configurator.js.map +1 -1
- package/dist/events/checkout-event-bus.d.ts +1 -0
- package/dist/events/checkout-event-bus.d.ts.map +1 -1
- package/dist/events/checkout-event-bus.js.map +1 -1
- package/dist/interfaces/payment-provider.interface.d.ts +7 -3
- package/dist/interfaces/payment-provider.interface.d.ts.map +1 -1
- package/dist/models/payment-request.model.d.ts +2 -0
- package/dist/models/payment-request.model.d.ts.map +1 -1
- package/dist/models/payment-result.model.d.ts +8 -0
- package/dist/models/payment-result.model.d.ts.map +1 -1
- package/dist/providers/nexi/nexi.provider.d.ts +21 -8
- package/dist/providers/nexi/nexi.provider.d.ts.map +1 -1
- package/dist/providers/nexi/nexi.provider.js +160 -44
- package/dist/providers/nexi/nexi.provider.js.map +1 -1
- package/dist/providers/paypal/paypal.provider.d.ts.map +1 -1
- package/dist/providers/paypal/paypal.provider.js +17 -0
- package/dist/providers/paypal/paypal.provider.js.map +1 -1
- package/dist/providers/satispay/satispay.provider.d.ts +15 -5
- package/dist/providers/satispay/satispay.provider.d.ts.map +1 -1
- package/dist/providers/satispay/satispay.provider.js +54 -13
- package/dist/providers/satispay/satispay.provider.js.map +1 -1
- package/package.json +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.0] - 2026-09-27
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
- **Nexi**: Fail-closed outcome MAC verification on redirect and webhook. Missing or invalid `mac` parameter rejects payment with status `FAILED` and error `'MAC missing'`. Verifies 7-field formula (`codTrans`, `esito`, `importo`, `divisa`, `data`, `orario`, `codAut`, `macKey`) using constant-time comparison `crypto.timingSafeEqual`. Resolves #6.
|
|
12
|
+
- **Express Adapter**: `publicPaths` now uses segment-based path matching rather than substring matching, preventing subpath bypasses such as `/webhook/execute` or `/redirect/execute` when `/webhook` or `/redirect` is marked public. Resolves #13.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Nexi**: Updated `getPaymentDetails` to query the official BackOffice endpoint `POST /ecomm/api/bo/situazioneOrdine` with JSON payload and response MAC verification instead of the non-existent IGFS endpoint. Resolves #7.
|
|
16
|
+
- **Satispay**: Fixed webhook handling for GET callbacks containing `payment_id` query parameter, ensuring callbacks routed to `handleWebhook` correctly extract payment details and verify signatures. Added configurable `callbackPath` (defaulting to `/checkout/satispay/webhook`). Resolves #10.
|
|
17
|
+
- **Satispay**: Fixed HTTP signature verification to support `(request-target)` pseudo-header via request context, signature header without `(request-target)`, flexible header key order, and body digest verification. Resolves #11.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- **Nexi**: Added server-to-server outcome notification support (`urlpost`) configured via `PaymentRequest.notifyUrl` in `createPayment`, and handled via `NexiProvider.handleWebhook`. Resolves #8.
|
|
21
|
+
- **Core**: Exposed `amount` and `currency` in `PaymentResult` and `WebhookResult` across all providers (Nexi, PayPal, Satispay) to enable post-payment amount verification. Resolves #9.
|
|
22
|
+
- **Core**: Propagated `orderId` and `raw` provider response data through `CheckoutConfigurator` event bus payloads (`payment.created`, `payment.completed`, `payment.failed`, `payment.refunded`, `webhook.received`). Resolves #12.
|
|
23
|
+
- **Express Adapter**: Added `options.redirect` for automatic 302 HTTP redirects to `onSuccess(result, req)` or `onFailure(result, req)` on browser return. Resolves #12.
|
|
24
|
+
- **Express Adapter**: Added `options.details` configuration to gate or disable `GET /:provider/:id` (`enabled: false` returns 404, or custom `middleware`). Resolves #12.
|
|
25
|
+
- **Express Adapter**: Added context argument `{ provider, res }` to `buildPaymentRequest(req, ctx)` and strict validation of returned `PaymentRequest`. Resolves #12.
|
|
26
|
+
- **Express Adapter**: Mounted both `GET` and `POST` routes for `/:provider/webhook`. Resolves #10.
|
|
27
|
+
- **Packaging**: Added `"types"` condition to `exports` in `package.json` for TypeScript package resolution under `node16`/`nodenext`. Resolves #12.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- **Nexi**: Breaking change in outcome verification: previously `handleRedirect` accepted outcomes without a `mac` field. It is now strictly required (fail-closed). Any integration testing Nexi callbacks must supply a valid `mac`.
|
|
31
|
+
|
|
8
32
|
## [1.1.0] - 2026-09-27
|
|
9
33
|
|
|
10
34
|
### Added
|
package/README.md
CHANGED
|
@@ -67,23 +67,35 @@ import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
|
67
67
|
|
|
68
68
|
const app = express();
|
|
69
69
|
app.use(express.json());
|
|
70
|
+
// If using Nexi server notifications (urlpost), urlencoded parser is required:
|
|
71
|
+
app.use(express.urlencoded({ extended: false }));
|
|
70
72
|
|
|
71
|
-
// Mount checkout routes with secure server-side request builder
|
|
73
|
+
// Mount checkout routes with secure server-side request builder and browser redirects
|
|
72
74
|
app.use(
|
|
73
75
|
'/checkout',
|
|
74
76
|
createCheckoutRouter(checkout, {
|
|
75
77
|
// Recommended: verify order and compute amount on the server
|
|
76
|
-
buildPaymentRequest: async (req) => {
|
|
78
|
+
buildPaymentRequest: async (req, { provider }) => {
|
|
77
79
|
const order = await myOrderService.getOrder(req.body.orderId);
|
|
78
80
|
return {
|
|
79
81
|
amount: order.totalAmount,
|
|
80
82
|
currency: order.currency, // e.g. 'EUR'
|
|
81
83
|
orderId: order.id,
|
|
82
|
-
returnUrl: 'https://myapp.com/
|
|
84
|
+
returnUrl: 'https://myapp.com/checkout/' + provider + '/redirect',
|
|
83
85
|
cancelUrl: 'https://myapp.com/payment/cancel',
|
|
86
|
+
notifyUrl: 'https://myapp.com/checkout/' + provider + '/webhook',
|
|
84
87
|
description: `Order #${order.id}`,
|
|
85
88
|
};
|
|
86
89
|
},
|
|
90
|
+
// Optional: automatic 302 redirects for browser-based flows
|
|
91
|
+
redirect: {
|
|
92
|
+
onSuccess: (result, req) => `/orders/${result.orderId}?status=success`,
|
|
93
|
+
onFailure: (result, req) => `/orders/${result.orderId}?status=failed&error=${encodeURIComponent(result.error || '')}`,
|
|
94
|
+
},
|
|
95
|
+
// Optional: gate or disable public access to GET /:provider/:id
|
|
96
|
+
details: {
|
|
97
|
+
enabled: false, // or provide middleware: [requireAuth]
|
|
98
|
+
},
|
|
87
99
|
// Opt-in: mount POST /:provider/refund protected by admin middleware
|
|
88
100
|
refund: {
|
|
89
101
|
middleware: [requireAdminAuth],
|
|
@@ -94,6 +106,11 @@ app.use(
|
|
|
94
106
|
// Verify payment belongs to current session or user
|
|
95
107
|
},
|
|
96
108
|
},
|
|
109
|
+
// Public paths matching segments safely without substring bypasses
|
|
110
|
+
publicPaths: [
|
|
111
|
+
[':provider', 'webhook'],
|
|
112
|
+
[':provider', 'redirect'],
|
|
113
|
+
],
|
|
97
114
|
}),
|
|
98
115
|
);
|
|
99
116
|
|
|
@@ -113,7 +130,10 @@ fastify.post('/checkout/:provider', async (req, reply) => {
|
|
|
113
130
|
});
|
|
114
131
|
|
|
115
132
|
fastify.post('/checkout/:provider/webhook', async (req, reply) => {
|
|
116
|
-
const result = await checkout.handleWebhook(req.params.provider, req.body, req.headers
|
|
133
|
+
const result = await checkout.handleWebhook(req.params.provider, req.body, req.headers, req.query, {
|
|
134
|
+
method: req.method,
|
|
135
|
+
path: req.url,
|
|
136
|
+
});
|
|
117
137
|
reply.send(result);
|
|
118
138
|
});
|
|
119
139
|
```
|
|
@@ -128,14 +148,38 @@ A full-featured reference implementation using Express 5, SQLite (`ITransactionS
|
|
|
128
148
|
|
|
129
149
|
## Routes (Express adapter)
|
|
130
150
|
|
|
131
|
-
| Method
|
|
132
|
-
|
|
133
|
-
| POST
|
|
134
|
-
| POST
|
|
135
|
-
| GET
|
|
136
|
-
| GET
|
|
137
|
-
| POST
|
|
138
|
-
| POST
|
|
151
|
+
| Method | Path | Description |
|
|
152
|
+
|-------------|-----------------------------|----------------------------------------------------------------|
|
|
153
|
+
| POST | `/:provider` | Create a payment (server-built or validated) |
|
|
154
|
+
| POST | `/:provider/execute` | Execute/capture a payment (supports `onBeforeExecute`) |
|
|
155
|
+
| GET | `/:provider/redirect` | Handle provider redirect callback (supports 302 via `redirect`)|
|
|
156
|
+
| GET | `/:provider/:id` | Get payment details (*configurable*: can disable or protect via `details`)|
|
|
157
|
+
| POST | `/:provider/refund` | Refund a payment (*opt-in*: mounted only when `refund` option is set)|
|
|
158
|
+
| GET / POST | `/:provider/webhook` | Handle provider webhook (POST for Nexi/Satispay, GET for Satispay)|
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Security: A Redirect Is Not Proof of Payment
|
|
163
|
+
|
|
164
|
+
Relying solely on front-end browser redirects (`GET /:provider/redirect`) to fulfill orders introduces significant risk:
|
|
165
|
+
- **Drop-off**: Shoppers may close the tab, lose connectivity, or navigate away before reaching your redirect URL.
|
|
166
|
+
- **Tampering**: Unless protected by cryptographic signatures (such as Nexi's mandatory outcome MAC), client-controlled URL parameters can be manipulated.
|
|
167
|
+
- **Amount Mismatch**: Always verify that the captured payment amount matches your order ledger before fulfilling goods or services.
|
|
168
|
+
|
|
169
|
+
To ensure safe fulfillment:
|
|
170
|
+
1. **Verify Amount & Currency**: `PaymentResult` and `WebhookResult` return `amount` and `currency` extracted directly from the provider.
|
|
171
|
+
```typescript
|
|
172
|
+
const result = await checkout.handleRedirect(provider, req.query);
|
|
173
|
+
if (result.success && result.status === 'COMPLETED') {
|
|
174
|
+
if (result.amount !== order.totalAmount || result.currency !== order.currency) {
|
|
175
|
+
throw new Error('Authorized amount does not match order total');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
2. **Use Server Notifications / Webhooks**:
|
|
180
|
+
- For **Nexi**: Pass `notifyUrl` in `PaymentRequest` (e.g. `https://myapp.com/checkout/nexi/webhook`). Nexi sends a server-to-server POST notification (`urlpost`) with the 7-field outcome MAC. Ensure your app includes `app.use(express.urlencoded({ extended: false }))`.
|
|
181
|
+
- For **Satispay**: Use `callbackPath` or webhook callbacks; signatures are verified against Satispay's RSA public key with `(request-target)` HTTP signature support.
|
|
182
|
+
3. **Idempotency**: Because both the redirect callback and the webhook notification may arrive for the same order, ensure your fulfillment handler is idempotent.
|
|
139
183
|
|
|
140
184
|
---
|
|
141
185
|
|
|
@@ -144,7 +188,7 @@ A full-featured reference implementation using Express 5, SQLite (`ITransactionS
|
|
|
144
188
|
| Flow | Providers | Description |
|
|
145
189
|
|------------|------------------|----------------------------------------------------------|
|
|
146
190
|
| `redirect` | PayPal, Nexi | User is redirected to provider, returns with query params|
|
|
147
|
-
| `webhook` | Satispay
|
|
191
|
+
| `webhook` | Satispay, Nexi | Provider calls a webhook URL for async confirmation |
|
|
148
192
|
| `direct` | *(future)* | Synchronous processing (card tokenization, etc.) |
|
|
149
193
|
|
|
150
194
|
---
|
|
@@ -153,11 +197,11 @@ A full-featured reference implementation using Express 5, SQLite (`ITransactionS
|
|
|
153
197
|
|
|
154
198
|
```typescript
|
|
155
199
|
checkout.events
|
|
156
|
-
.on('payment.created', ({ provider, paymentId }) => { /* ... */ })
|
|
157
|
-
.on('payment.completed', ({ provider, paymentId }) => { /* ... */ })
|
|
158
|
-
.on('payment.failed', ({ provider, error }) => { /* ... */ })
|
|
159
|
-
.on('payment.refunded', ({ provider, paymentId }) => { /* ... */ })
|
|
160
|
-
.on('webhook.received', ({ provider, data }) => { /* ... */ });
|
|
200
|
+
.on('payment.created', ({ provider, paymentId, orderId, raw }) => { /* ... */ })
|
|
201
|
+
.on('payment.completed', ({ provider, paymentId, orderId, raw }) => { /* ... */ })
|
|
202
|
+
.on('payment.failed', ({ provider, error, orderId, raw }) => { /* ... */ })
|
|
203
|
+
.on('payment.refunded', ({ provider, paymentId, orderId, raw }) => { /* ... */ })
|
|
204
|
+
.on('webhook.received', ({ provider, data, orderId, raw }) => { /* ... */ });
|
|
161
205
|
```
|
|
162
206
|
|
|
163
207
|
---
|
|
@@ -213,11 +257,11 @@ checkout.registerProvider(new StripeProvider({ secretKey: '...' }));
|
|
|
213
257
|
|
|
214
258
|
## Built-in Providers
|
|
215
259
|
|
|
216
|
-
| Provider | Flow
|
|
217
|
-
|
|
218
|
-
| PayPal | redirect
|
|
219
|
-
| Nexi | redirect | eCommerce DispatcherServlet + MAC SHA-1
|
|
220
|
-
| Satispay | webhook
|
|
260
|
+
| Provider | Flow | Notes |
|
|
261
|
+
|-----------|---------------------|---------------------------------------------------------------------------------------------|
|
|
262
|
+
| PayPal | redirect | Orders API v2 (`return=representation`, captures expose `amount` and `currency`) |
|
|
263
|
+
| Nexi | redirect + webhook | eCommerce DispatcherServlet + MAC SHA-1 (mandatory 7-field outcome MAC, `urlpost`, BackOffice `situazioneOrdine` API) |
|
|
264
|
+
| Satispay | webhook | Business API v1, RSA-SHA256 HTTP signature, GET and POST callbacks, configurable `callbackPath` |
|
|
221
265
|
|
|
222
266
|
---
|
|
223
267
|
|
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
import { Router, Request, RequestHandler } from 'express';
|
|
1
|
+
import { Router, Request, Response, RequestHandler } from 'express';
|
|
2
2
|
import { CheckoutConfigurator } from '../../checkout-configurator';
|
|
3
3
|
import { PaymentRequest } from '../../models/payment-request.model';
|
|
4
|
+
import { PaymentResult } from '../../models/payment-result.model';
|
|
5
|
+
export interface PaymentRequestContext {
|
|
6
|
+
provider: string;
|
|
7
|
+
res: Response;
|
|
8
|
+
}
|
|
4
9
|
export interface ExpressCheckoutOptions {
|
|
5
10
|
/**
|
|
6
11
|
* Middleware applied to ALL checkout routes (e.g. API key auth).
|
|
7
|
-
* Webhooks are typically public — use `publicPaths` to exclude them.
|
|
12
|
+
* Webhooks and redirects are typically public — use `publicPaths` to exclude them.
|
|
8
13
|
*/
|
|
9
14
|
middleware?: RequestHandler[];
|
|
10
15
|
/**
|
|
11
|
-
* Route
|
|
12
|
-
* Matched
|
|
16
|
+
* Route patterns that bypass `middleware` (e.g. [':provider/webhook', ':provider/redirect']).
|
|
17
|
+
* Matched by path segments after the mount point (:provider matches any single segment).
|
|
13
18
|
*/
|
|
14
19
|
publicPaths?: string[];
|
|
15
20
|
/**
|
|
16
21
|
* Builds the PaymentRequest on the server (amount from your own order), ignoring the client body.
|
|
17
22
|
*/
|
|
18
|
-
buildPaymentRequest?: (req: Request) => PaymentRequest | Promise<PaymentRequest>;
|
|
23
|
+
buildPaymentRequest?: (req: Request, ctx: PaymentRequestContext) => PaymentRequest | Promise<PaymentRequest>;
|
|
24
|
+
/**
|
|
25
|
+
* When configured, GET /:provider/redirect responds with a 302 redirect
|
|
26
|
+
* to the URL returned by onSuccess or onFailure instead of JSON.
|
|
27
|
+
*/
|
|
28
|
+
redirect?: {
|
|
29
|
+
onSuccess: (result: PaymentResult, req: Request) => string;
|
|
30
|
+
onFailure: (result: PaymentResult, req: Request) => string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Configuration for GET /:provider/:id (getPaymentDetails).
|
|
34
|
+
* Default: enabled: true. Set enabled: false to return 404.
|
|
35
|
+
*/
|
|
36
|
+
details?: {
|
|
37
|
+
enabled?: boolean;
|
|
38
|
+
middleware?: RequestHandler[];
|
|
39
|
+
};
|
|
19
40
|
/**
|
|
20
41
|
* Configuration for POST /:provider/refund.
|
|
21
42
|
* If omitted, the refund route is NOT mounted (returns 404).
|
|
@@ -31,6 +52,11 @@ export interface ExpressCheckoutOptions {
|
|
|
31
52
|
onBeforeExecute?: (req: Request, paymentId: string) => void | Promise<void>;
|
|
32
53
|
};
|
|
33
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Matches a request path against public path patterns by segments.
|
|
57
|
+
* ':provider' matches any single non-empty segment.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isPathPublic(reqPath: string, publicPaths: string[]): boolean;
|
|
34
60
|
/**
|
|
35
61
|
* Creates an Express Router with all checkout endpoints pre-wired.
|
|
36
62
|
*
|
|
@@ -38,17 +64,18 @@ export interface ExpressCheckoutOptions {
|
|
|
38
64
|
* ```
|
|
39
65
|
* POST /:provider → createPayment
|
|
40
66
|
* POST /:provider/execute → executePayment
|
|
41
|
-
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
42
|
-
* GET /:provider/:id → getPaymentDetails
|
|
43
67
|
* POST /:provider/refund → refundPayment (only if options.refund is configured)
|
|
68
|
+
* GET /:provider/webhook → handleWebhook
|
|
44
69
|
* POST /:provider/webhook → handleWebhook
|
|
70
|
+
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
71
|
+
* GET /:provider/:id → getPaymentDetails
|
|
45
72
|
* ```
|
|
46
73
|
*
|
|
47
74
|
* Usage:
|
|
48
75
|
* ```typescript
|
|
49
76
|
* import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
50
77
|
* app.use('/checkout', createCheckoutRouter(checkout, {
|
|
51
|
-
* buildPaymentRequest: async (req) => {
|
|
78
|
+
* buildPaymentRequest: async (req, ctx) => {
|
|
52
79
|
* const order = await getOrder(req.body.orderId);
|
|
53
80
|
* return {
|
|
54
81
|
* amount: order.total,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAElE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAE9B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB;;OAEG;IACH,mBAAmB,CAAC,EAAE,CACpB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,qBAAqB,KACvB,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAE9C;;;OAGG;IACH,QAAQ,CAAC,EAAE;QACT,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;QAC3D,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;KAC5D,CAAC;IAEF;;;OAGG;IACH,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;KAC/B,CAAC;IAEF;;;OAGG;IACH,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;KAC/B,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;QAC9B,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC7E,CAAC;CACH;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAc5E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,GAAE,sBAA2B,GACnC,MAAM,CA6KR"}
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPathPublic = isPathPublic;
|
|
3
4
|
exports.createCheckoutRouter = createCheckoutRouter;
|
|
4
5
|
const express_1 = require("express");
|
|
5
6
|
const errors_1 = require("../../models/errors");
|
|
7
|
+
/**
|
|
8
|
+
* Matches a request path against public path patterns by segments.
|
|
9
|
+
* ':provider' matches any single non-empty segment.
|
|
10
|
+
*/
|
|
11
|
+
function isPathPublic(reqPath, publicPaths) {
|
|
12
|
+
const reqSegments = reqPath.split('/').filter(Boolean);
|
|
13
|
+
return publicPaths.some((pattern) => {
|
|
14
|
+
const patternSegments = pattern.split('/').filter(Boolean);
|
|
15
|
+
if (reqSegments.length !== patternSegments.length) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return patternSegments.every((patSeg, i) => {
|
|
19
|
+
if (patSeg === ':provider') {
|
|
20
|
+
return reqSegments[i].length > 0;
|
|
21
|
+
}
|
|
22
|
+
return patSeg === reqSegments[i];
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
6
26
|
/**
|
|
7
27
|
* Creates an Express Router with all checkout endpoints pre-wired.
|
|
8
28
|
*
|
|
@@ -10,17 +30,18 @@ const errors_1 = require("../../models/errors");
|
|
|
10
30
|
* ```
|
|
11
31
|
* POST /:provider → createPayment
|
|
12
32
|
* POST /:provider/execute → executePayment
|
|
13
|
-
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
14
|
-
* GET /:provider/:id → getPaymentDetails
|
|
15
33
|
* POST /:provider/refund → refundPayment (only if options.refund is configured)
|
|
34
|
+
* GET /:provider/webhook → handleWebhook
|
|
16
35
|
* POST /:provider/webhook → handleWebhook
|
|
36
|
+
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
37
|
+
* GET /:provider/:id → getPaymentDetails
|
|
17
38
|
* ```
|
|
18
39
|
*
|
|
19
40
|
* Usage:
|
|
20
41
|
* ```typescript
|
|
21
42
|
* import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
22
43
|
* app.use('/checkout', createCheckoutRouter(checkout, {
|
|
23
|
-
* buildPaymentRequest: async (req) => {
|
|
44
|
+
* buildPaymentRequest: async (req, ctx) => {
|
|
24
45
|
* const order = await getOrder(req.body.orderId);
|
|
25
46
|
* return {
|
|
26
47
|
* amount: order.total,
|
|
@@ -38,13 +59,11 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
38
59
|
if (!options.buildPaymentRequest) {
|
|
39
60
|
console.warn('[awesome-node-checkout] createCheckoutRouter: the client body decides the amount; set buildPaymentRequest');
|
|
40
61
|
}
|
|
41
|
-
// Apply shared middleware (skipping public paths)
|
|
62
|
+
// Apply shared middleware (skipping public paths by segment match)
|
|
42
63
|
if (options.middleware?.length) {
|
|
43
64
|
const publicPaths = options.publicPaths ?? [];
|
|
44
65
|
router.use((req, res, next) => {
|
|
45
|
-
|
|
46
|
-
const isPublic = publicPaths.some((p) => req.path.endsWith(p) || req.path.includes(p.replace(':provider', providerParam)));
|
|
47
|
-
if (isPublic)
|
|
66
|
+
if (isPathPublic(req.path, publicPaths))
|
|
48
67
|
return next();
|
|
49
68
|
// Chain all middleware in sequence
|
|
50
69
|
const handlers = options.middleware;
|
|
@@ -59,9 +78,30 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
59
78
|
// ---- POST /:provider — create payment ------------------------------------
|
|
60
79
|
router.post('/:provider', async (req, res) => {
|
|
61
80
|
try {
|
|
81
|
+
const provider = String(req.params.provider);
|
|
62
82
|
let paymentRequest;
|
|
63
83
|
if (options.buildPaymentRequest) {
|
|
64
|
-
paymentRequest = await options.buildPaymentRequest(req
|
|
84
|
+
paymentRequest = await options.buildPaymentRequest(req, {
|
|
85
|
+
provider,
|
|
86
|
+
res,
|
|
87
|
+
});
|
|
88
|
+
if (!paymentRequest ||
|
|
89
|
+
typeof paymentRequest !== 'object' ||
|
|
90
|
+
typeof paymentRequest.amount !== 'number' ||
|
|
91
|
+
!Number.isFinite(paymentRequest.amount) ||
|
|
92
|
+
paymentRequest.amount <= 0 ||
|
|
93
|
+
typeof paymentRequest.currency !== 'string' ||
|
|
94
|
+
paymentRequest.currency.trim() === '' ||
|
|
95
|
+
typeof paymentRequest.returnUrl !== 'string' ||
|
|
96
|
+
paymentRequest.returnUrl.trim() === '' ||
|
|
97
|
+
typeof paymentRequest.cancelUrl !== 'string' ||
|
|
98
|
+
paymentRequest.cancelUrl.trim() === '') {
|
|
99
|
+
return res.status(400).json({
|
|
100
|
+
success: false,
|
|
101
|
+
code: 'INVALID_PAYMENT_REQUEST',
|
|
102
|
+
error: 'Invalid payment request returned by buildPaymentRequest: amount must be a positive number, currency, returnUrl, and cancelUrl are required strings',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
65
105
|
}
|
|
66
106
|
else {
|
|
67
107
|
const body = req.body;
|
|
@@ -79,7 +119,7 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
79
119
|
}
|
|
80
120
|
paymentRequest = body;
|
|
81
121
|
}
|
|
82
|
-
const result = await checkout.createPayment(
|
|
122
|
+
const result = await checkout.createPayment(provider, paymentRequest);
|
|
83
123
|
res.status(result.success ? 201 : 400).json(result);
|
|
84
124
|
}
|
|
85
125
|
catch (err) {
|
|
@@ -121,22 +161,30 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
121
161
|
}
|
|
122
162
|
});
|
|
123
163
|
}
|
|
124
|
-
// ---- POST /:provider/webhook — handle webhook
|
|
125
|
-
|
|
164
|
+
// ---- GET & POST /:provider/webhook — handle webhook ----------------------
|
|
165
|
+
const webhookHandler = async (req, res) => {
|
|
126
166
|
try {
|
|
127
|
-
const result = await checkout.handleWebhook(String(req.params.provider), req.body, req.headers);
|
|
167
|
+
const result = await checkout.handleWebhook(String(req.params.provider), req.body, req.headers, req.query, { method: req.method, path: req.originalUrl || req.url });
|
|
128
168
|
res.status(result.success ? 200 : 400).json(result);
|
|
129
169
|
}
|
|
130
170
|
catch (err) {
|
|
131
171
|
sendError(res, err);
|
|
132
172
|
}
|
|
133
|
-
}
|
|
173
|
+
};
|
|
174
|
+
router.get('/:provider/webhook', webhookHandler);
|
|
175
|
+
router.post('/:provider/webhook', webhookHandler);
|
|
134
176
|
// ---- GET /:provider/redirect — handle redirect callback ------------------
|
|
135
177
|
// NOTE: must be defined BEFORE /:provider/:id to avoid "redirect" being
|
|
136
178
|
// treated as a paymentId.
|
|
137
179
|
router.get('/:provider/redirect', async (req, res) => {
|
|
138
180
|
try {
|
|
139
181
|
const result = await checkout.handleRedirect(String(req.params.provider), req.query);
|
|
182
|
+
if (options.redirect) {
|
|
183
|
+
const targetUrl = result.success
|
|
184
|
+
? options.redirect.onSuccess(result, req)
|
|
185
|
+
: options.redirect.onFailure(result, req);
|
|
186
|
+
return res.redirect(302, targetUrl);
|
|
187
|
+
}
|
|
140
188
|
res.status(result.success ? 200 : 400).json(result);
|
|
141
189
|
}
|
|
142
190
|
catch (err) {
|
|
@@ -144,7 +192,14 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
144
192
|
}
|
|
145
193
|
});
|
|
146
194
|
// ---- GET /:provider/:id — get payment details ----------------------------
|
|
147
|
-
|
|
195
|
+
const detailsHandlers = [];
|
|
196
|
+
if (options.details?.middleware?.length) {
|
|
197
|
+
detailsHandlers.push(...options.details.middleware);
|
|
198
|
+
}
|
|
199
|
+
router.get('/:provider/:id', ...detailsHandlers, async (req, res) => {
|
|
200
|
+
if (options.details?.enabled === false) {
|
|
201
|
+
return res.status(404).json({ success: false, error: 'Payment details route is disabled' });
|
|
202
|
+
}
|
|
148
203
|
try {
|
|
149
204
|
const result = await checkout.getPaymentDetails(String(req.params.provider), String(req.params.id));
|
|
150
205
|
res.status(result.success ? 200 : 404).json(result);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/express/index.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/express/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,7 @@ import { CheckoutEventBus } from './events/checkout-event-bus';
|
|
|
27
27
|
export declare class CheckoutConfigurator {
|
|
28
28
|
private readonly config;
|
|
29
29
|
private readonly providers;
|
|
30
|
+
private readonly paymentOrders;
|
|
30
31
|
/** Event bus — subscribe to payment lifecycle events */
|
|
31
32
|
readonly events: CheckoutEventBus;
|
|
32
33
|
constructor(config?: CheckoutConfig);
|
|
@@ -56,7 +57,10 @@ export declare class CheckoutConfigurator {
|
|
|
56
57
|
* Handle an incoming webhook from the provider.
|
|
57
58
|
* @throws CheckoutError if the provider does not support webhooks.
|
|
58
59
|
*/
|
|
59
|
-
handleWebhook(providerName: string, body
|
|
60
|
+
handleWebhook(providerName: string, body?: Record<string, unknown>, headers?: Record<string, string>, query?: Record<string, string>, context?: {
|
|
61
|
+
method?: string;
|
|
62
|
+
path?: string;
|
|
63
|
+
}): Promise<WebhookResult>;
|
|
60
64
|
/**
|
|
61
65
|
* Handle the redirect callback from the provider (query params).
|
|
62
66
|
* @throws CheckoutError if the provider does not support redirect handling.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout-configurator.d.ts","sourceRoot":"","sources":["../src/checkout-configurator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAA2C,MAAM,6BAA6B,CAAC;AAExG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,oBAAoB;
|
|
1
|
+
{"version":3,"file":"checkout-configurator.d.ts","sourceRoot":"","sources":["../src/checkout-configurator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAA2C,MAAM,6BAA6B,CAAC;AAExG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,oBAAoB;IAOnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IACtE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAEhE,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAA0B;gBAE9B,MAAM,GAAE,cAAmB;IAExD,8EAA8E;YAChE,IAAI;IASlB;;;OAGG;IACH,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAalD;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB;IAY3C,oDAAoD;IACpD,sBAAsB,IAAI,MAAM,EAAE;IAIlC,sDAAsD;IAChD,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAiB1F,mDAAmD;IAC7C,cAAc,CAClB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,aAAa,CAAC;IAezB,2DAA2D;IACrD,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAKxF,qCAAqC;IAC/B,aAAa,CACjB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC;IAezB;;;OAGG;IACG,aAAa,CACjB,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACpC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,OAAO,CAAC,aAAa,CAAC;IA+BzB;;;OAGG;IACG,cAAc,CAClB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,aAAa,CAAC;CAyB1B"}
|
|
@@ -28,6 +28,7 @@ class CheckoutConfigurator {
|
|
|
28
28
|
constructor(config = {}) {
|
|
29
29
|
this.config = config;
|
|
30
30
|
this.providers = new Map();
|
|
31
|
+
this.paymentOrders = new Map();
|
|
31
32
|
/** Event bus — subscribe to payment lifecycle events */
|
|
32
33
|
this.events = new checkout_event_bus_1.CheckoutEventBus();
|
|
33
34
|
}
|
|
@@ -68,12 +69,16 @@ class CheckoutConfigurator {
|
|
|
68
69
|
async createPayment(providerName, request) {
|
|
69
70
|
const provider = this.getProvider(providerName);
|
|
70
71
|
const result = await provider.createPayment(request);
|
|
72
|
+
if (result.paymentId && request.orderId) {
|
|
73
|
+
this.paymentOrders.set(result.paymentId, request.orderId);
|
|
74
|
+
}
|
|
71
75
|
await this.emit(result.success ? 'payment.created' : 'payment.failed', {
|
|
72
76
|
provider: providerName,
|
|
73
77
|
paymentId: result.paymentId,
|
|
74
78
|
orderId: request.orderId,
|
|
75
79
|
status: result.status,
|
|
76
80
|
error: result.error,
|
|
81
|
+
raw: result.raw,
|
|
77
82
|
});
|
|
78
83
|
return result;
|
|
79
84
|
}
|
|
@@ -81,11 +86,14 @@ class CheckoutConfigurator {
|
|
|
81
86
|
async executePayment(providerName, paymentId, data) {
|
|
82
87
|
const provider = this.getProvider(providerName);
|
|
83
88
|
const result = await provider.executePayment(paymentId, data);
|
|
89
|
+
const orderId = this.paymentOrders.get(paymentId);
|
|
84
90
|
await this.emit(result.success ? 'payment.completed' : 'payment.failed', {
|
|
85
91
|
provider: providerName,
|
|
86
92
|
paymentId,
|
|
93
|
+
orderId,
|
|
87
94
|
status: result.status,
|
|
88
95
|
error: result.error,
|
|
96
|
+
raw: result.raw,
|
|
89
97
|
});
|
|
90
98
|
return result;
|
|
91
99
|
}
|
|
@@ -98,11 +106,14 @@ class CheckoutConfigurator {
|
|
|
98
106
|
async refundPayment(providerName, paymentId, amount) {
|
|
99
107
|
const provider = this.getProvider(providerName);
|
|
100
108
|
const result = await provider.refundPayment(paymentId, amount);
|
|
109
|
+
const orderId = this.paymentOrders.get(paymentId);
|
|
101
110
|
await this.emit('payment.refunded', {
|
|
102
111
|
provider: providerName,
|
|
103
112
|
paymentId,
|
|
113
|
+
orderId,
|
|
104
114
|
status: result.status,
|
|
105
115
|
error: result.error,
|
|
116
|
+
raw: result.raw,
|
|
106
117
|
});
|
|
107
118
|
return result;
|
|
108
119
|
}
|
|
@@ -110,18 +121,27 @@ class CheckoutConfigurator {
|
|
|
110
121
|
* Handle an incoming webhook from the provider.
|
|
111
122
|
* @throws CheckoutError if the provider does not support webhooks.
|
|
112
123
|
*/
|
|
113
|
-
async handleWebhook(providerName, body, headers) {
|
|
124
|
+
async handleWebhook(providerName, body, headers = {}, query, context) {
|
|
114
125
|
const provider = this.getProvider(providerName);
|
|
115
126
|
if (!provider.handleWebhook) {
|
|
116
127
|
throw new errors_1.CheckoutError(`Provider '${providerName}' does not support webhook handling`, 'WEBHOOK_NOT_SUPPORTED', providerName);
|
|
117
128
|
}
|
|
118
|
-
const result = await provider.handleWebhook(body, headers);
|
|
129
|
+
const result = await provider.handleWebhook(body, headers, query, context);
|
|
130
|
+
const rawAny = result.raw;
|
|
131
|
+
const orderId = rawAny?.orderId ??
|
|
132
|
+
rawAny?.order_id ??
|
|
133
|
+
rawAny?.external_code ??
|
|
134
|
+
rawAny?.codTrans ??
|
|
135
|
+
query?.order_id ??
|
|
136
|
+
(result.paymentId ? this.paymentOrders.get(result.paymentId) : undefined);
|
|
119
137
|
await this.emit('webhook.received', {
|
|
120
138
|
provider: providerName,
|
|
121
139
|
paymentId: result.paymentId,
|
|
140
|
+
orderId,
|
|
122
141
|
status: result.status,
|
|
123
142
|
error: result.error,
|
|
124
|
-
data: body,
|
|
143
|
+
data: body ?? query,
|
|
144
|
+
raw: result.raw,
|
|
125
145
|
});
|
|
126
146
|
return result;
|
|
127
147
|
}
|
|
@@ -135,11 +155,16 @@ class CheckoutConfigurator {
|
|
|
135
155
|
throw new errors_1.CheckoutError(`Provider '${providerName}' does not support redirect handling`, 'REDIRECT_NOT_SUPPORTED', providerName);
|
|
136
156
|
}
|
|
137
157
|
const result = await provider.handleRedirect(query);
|
|
158
|
+
const orderId = (result.paymentId ? this.paymentOrders.get(result.paymentId) : undefined) ??
|
|
159
|
+
query?.order_id ??
|
|
160
|
+
query?.codTrans;
|
|
138
161
|
await this.emit(result.success ? 'payment.completed' : 'payment.failed', {
|
|
139
162
|
provider: providerName,
|
|
140
163
|
paymentId: result.paymentId,
|
|
164
|
+
orderId,
|
|
141
165
|
status: result.status,
|
|
142
166
|
error: result.error,
|
|
167
|
+
raw: result.raw,
|
|
143
168
|
});
|
|
144
169
|
return result;
|
|
145
170
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout-configurator.js","sourceRoot":"","sources":["../src/checkout-configurator.ts"],"names":[],"mappings":";;;AAIA,4CAAgD;AAChD,oEAAwG;AAExG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAa,oBAAoB;
|
|
1
|
+
{"version":3,"file":"checkout-configurator.js","sourceRoot":"","sources":["../src/checkout-configurator.ts"],"names":[],"mappings":"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"}
|