awesome-node-checkout 1.1.0 → 1.2.1
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 +48 -0
- package/README.md +88 -28
- package/dist/adapters/express/index.d.ts +40 -9
- package/dist/adapters/express/index.d.ts.map +1 -1
- package/dist/adapters/express/index.js +85 -15
- package/dist/adapters/express/index.js.map +1 -1
- package/dist/checkout-configurator.d.ts +9 -1
- package/dist/checkout-configurator.d.ts.map +1 -1
- package/dist/checkout-configurator.js +52 -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 +8 -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 +11 -0
- package/dist/models/payment-result.model.d.ts.map +1 -1
- package/dist/providers/nexi/nexi.provider.d.ts +26 -8
- package/dist/providers/nexi/nexi.provider.d.ts.map +1 -1
- package/dist/providers/nexi/nexi.provider.js +180 -45
- 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 +28 -4
- package/dist/providers/paypal/paypal.provider.js.map +1 -1
- package/dist/providers/satispay/satispay.provider.d.ts +39 -5
- package/dist/providers/satispay/satispay.provider.d.ts.map +1 -1
- package/dist/providers/satispay/satispay.provider.js +130 -23
- package/dist/providers/satispay/satispay.provider.js.map +1 -1
- package/dist/utils/parsing.util.d.ts +17 -0
- package/dist/utils/parsing.util.d.ts.map +1 -0
- package/dist/utils/parsing.util.js +60 -0
- package/dist/utils/parsing.util.js.map +1 -0
- package/package.json +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,54 @@ 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.1] - 2026-09-28
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
- **Satispay**: Disallowed request body acting as headers during webhook signature verification. Resolves #16.
|
|
12
|
+
- **Satispay**: Enforced timestamp freshness window (`signatureMaxAgeMs`, default 5 minutes) on webhook signatures to prevent replay attacks. Resolves #16.
|
|
13
|
+
- **Satispay**: Strictly bound `Digest` header validation to the raw request body (`context.rawBody` or stringified body) verifying `SHA-256=<base64>`. Resolves #16.
|
|
14
|
+
- **Satispay**: Enforced `(request-target)` to be derived solely from trusted `context.method` and `context.path`. Resolves #16.
|
|
15
|
+
- **Satispay**: URL-encoded `payment_id` across API endpoints and strictly validated payment ID format (`/^[A-Za-z0-9_-]{1,64}$/`) rejecting directory traversal patterns. Resolves #16.
|
|
16
|
+
- **Nexi**: Enforced strict 40-character hexadecimal format validation on outcome MAC strings to prevent `RangeError` during timing-safe comparisons. Resolves #17.
|
|
17
|
+
- **Express Adapter**: Prevented sensitive internal provider payload data from being echoed in HTTP webhook JSON responses (strips `raw`). Resolves #17.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- **PayPal**: Payment `success` is now strictly derived from capture status (`capture.status === 'COMPLETED'`) in `executePayment` and `getPaymentDetails`. Payments with non-completed or missing captures evaluate to `success: false`. Resolves #17.
|
|
21
|
+
- **Nexi**: Removed `report[0]` fallback in `getPaymentDetails` when the requested transaction order ID is not present in the BackOffice report, returning `{ success: false, error: 'Report not found' }`. Resolves #17.
|
|
22
|
+
- **Nexi**: Mapped ISO-4217 numeric currency codes (e.g. `'978'` -> `'EUR'`, `'840'` -> `'USD'`) to uppercase alpha-3 codes, returning `undefined` for unrecognized numeric codes. Resolves #17.
|
|
23
|
+
- **Core**: Empty or whitespace-only amount strings are parsed to `undefined` rather than coercing to `0`. Resolves #17.
|
|
24
|
+
- **Core**: Bounded `paymentOrders` map capacity to 10,000 entries with FIFO eviction to prevent memory leaks in long-running processes. Resolves #17.
|
|
25
|
+
- **Express Adapter**: Handled `res.headersSent` when `buildPaymentRequest` sends a response directly via `ctx.res` and returns `null`, preventing `ERR_HTTP_HEADERS_SENT` / double response errors. Resolves #17.
|
|
26
|
+
- **Documentation**: Corrected `publicPaths` examples, clarified Satispay callback URL configuration, raw body middleware, and GET callback authentication. Resolves #17.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- **Core**: Added optional `orderId` to `PaymentResult` and `WebhookResult`. Resolves #17.
|
|
30
|
+
- **Core**: Added `rawBody` support to `context` parameter in `IPaymentProvider.handleWebhook`. Resolves #16.
|
|
31
|
+
|
|
32
|
+
## [1.2.0] - 2026-09-27
|
|
33
|
+
|
|
34
|
+
### Security
|
|
35
|
+
- **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.
|
|
36
|
+
- **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.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
- **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.
|
|
40
|
+
- **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.
|
|
41
|
+
- **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.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- **Nexi**: Added server-to-server outcome notification support (`urlpost`) configured via `PaymentRequest.notifyUrl` in `createPayment`, and handled via `NexiProvider.handleWebhook`. Resolves #8.
|
|
45
|
+
- **Core**: Exposed `amount` and `currency` in `PaymentResult` and `WebhookResult` across all providers (Nexi, PayPal, Satispay) to enable post-payment amount verification. Resolves #9.
|
|
46
|
+
- **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.
|
|
47
|
+
- **Express Adapter**: Added `options.redirect` for automatic 302 HTTP redirects to `onSuccess(result, req)` or `onFailure(result, req)` on browser return. Resolves #12.
|
|
48
|
+
- **Express Adapter**: Added `options.details` configuration to gate or disable `GET /:provider/:id` (`enabled: false` returns 404, or custom `middleware`). Resolves #12.
|
|
49
|
+
- **Express Adapter**: Added context argument `{ provider, res }` to `buildPaymentRequest(req, ctx)` and strict validation of returned `PaymentRequest`. Resolves #12.
|
|
50
|
+
- **Express Adapter**: Mounted both `GET` and `POST` routes for `/:provider/webhook`. Resolves #10.
|
|
51
|
+
- **Packaging**: Added `"types"` condition to `exports` in `package.json` for TypeScript package resolution under `node16`/`nodenext`. Resolves #12.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
- **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`.
|
|
55
|
+
|
|
8
56
|
## [1.1.0] - 2026-09-27
|
|
9
57
|
|
|
10
58
|
### Added
|
package/README.md
CHANGED
|
@@ -41,7 +41,8 @@ checkout
|
|
|
41
41
|
keyId: process.env.SATISPAY_KEY_ID!,
|
|
42
42
|
privateKey: fs.readFileSync('private.pem', 'utf8'),
|
|
43
43
|
environment: 'sandbox',
|
|
44
|
-
serverUrl: 'https://myapp.com',
|
|
44
|
+
serverUrl: 'https://myapp.com', // Base URL for callbacks: serverUrl + callbackPath
|
|
45
|
+
callbackPath: '/checkout/satispay/webhook', // Optional: defaults to /checkout/satispay/webhook
|
|
45
46
|
}));
|
|
46
47
|
|
|
47
48
|
// Use directly — no HTTP framework needed
|
|
@@ -66,24 +67,41 @@ import express from 'express';
|
|
|
66
67
|
import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
67
68
|
|
|
68
69
|
const app = express();
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
// Capture rawBody for webhook signature & digest verification (e.g. Satispay)
|
|
71
|
+
app.use(express.json({
|
|
72
|
+
verify: (req, _res, buf) => {
|
|
73
|
+
(req as any).rawBody = buf;
|
|
74
|
+
},
|
|
75
|
+
}));
|
|
76
|
+
// If using Nexi server notifications (urlpost), urlencoded parser is required:
|
|
77
|
+
app.use(express.urlencoded({ extended: false }));
|
|
78
|
+
|
|
79
|
+
// Mount checkout routes with secure server-side request builder and browser redirects
|
|
72
80
|
app.use(
|
|
73
81
|
'/checkout',
|
|
74
82
|
createCheckoutRouter(checkout, {
|
|
75
83
|
// Recommended: verify order and compute amount on the server
|
|
76
|
-
buildPaymentRequest: async (req) => {
|
|
84
|
+
buildPaymentRequest: async (req, { provider }) => {
|
|
77
85
|
const order = await myOrderService.getOrder(req.body.orderId);
|
|
78
86
|
return {
|
|
79
87
|
amount: order.totalAmount,
|
|
80
88
|
currency: order.currency, // e.g. 'EUR'
|
|
81
89
|
orderId: order.id,
|
|
82
|
-
returnUrl: 'https://myapp.com/
|
|
90
|
+
returnUrl: 'https://myapp.com/checkout/' + provider + '/redirect',
|
|
83
91
|
cancelUrl: 'https://myapp.com/payment/cancel',
|
|
92
|
+
notifyUrl: 'https://myapp.com/checkout/' + provider + '/webhook',
|
|
84
93
|
description: `Order #${order.id}`,
|
|
85
94
|
};
|
|
86
95
|
},
|
|
96
|
+
// Optional: automatic 302 redirects for browser-based flows
|
|
97
|
+
redirect: {
|
|
98
|
+
onSuccess: (result, req) => `/orders/${result.orderId}?status=success`,
|
|
99
|
+
onFailure: (result, req) => `/orders/${result.orderId}?status=failed&error=${encodeURIComponent(result.error || '')}`,
|
|
100
|
+
},
|
|
101
|
+
// Optional: gate or disable public access to GET /:provider/:id
|
|
102
|
+
details: {
|
|
103
|
+
enabled: false, // or provide middleware: [requireAuth]
|
|
104
|
+
},
|
|
87
105
|
// Opt-in: mount POST /:provider/refund protected by admin middleware
|
|
88
106
|
refund: {
|
|
89
107
|
middleware: [requireAdminAuth],
|
|
@@ -94,6 +112,11 @@ app.use(
|
|
|
94
112
|
// Verify payment belongs to current session or user
|
|
95
113
|
},
|
|
96
114
|
},
|
|
115
|
+
// Public paths matching segments safely without substring bypasses (accepts strings or segment arrays)
|
|
116
|
+
publicPaths: [
|
|
117
|
+
':provider/webhook',
|
|
118
|
+
':provider/redirect',
|
|
119
|
+
],
|
|
97
120
|
}),
|
|
98
121
|
);
|
|
99
122
|
|
|
@@ -112,9 +135,20 @@ fastify.post('/checkout/:provider', async (req, reply) => {
|
|
|
112
135
|
reply.status(result.success ? 201 : 400).send(result);
|
|
113
136
|
});
|
|
114
137
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
138
|
+
// Handle both POST and GET webhooks (e.g. Satispay GET callback vs Nexi/Satispay POST notification)
|
|
139
|
+
fastify.route({
|
|
140
|
+
method: ['GET', 'POST'],
|
|
141
|
+
url: '/checkout/:provider/webhook',
|
|
142
|
+
handler: async (req, reply) => {
|
|
143
|
+
const result = await checkout.handleWebhook(req.params.provider, req.body, req.headers, req.query, {
|
|
144
|
+
method: req.method,
|
|
145
|
+
path: req.url,
|
|
146
|
+
rawBody: (req as any).rawBody,
|
|
147
|
+
});
|
|
148
|
+
// Strip internal raw data before responding
|
|
149
|
+
const { raw, ...safeResult } = result;
|
|
150
|
+
reply.status(result.success ? 200 : 400).send(safeResult);
|
|
151
|
+
},
|
|
118
152
|
});
|
|
119
153
|
```
|
|
120
154
|
|
|
@@ -128,14 +162,40 @@ A full-featured reference implementation using Express 5, SQLite (`ITransactionS
|
|
|
128
162
|
|
|
129
163
|
## Routes (Express adapter)
|
|
130
164
|
|
|
131
|
-
| Method
|
|
132
|
-
|
|
133
|
-
| POST
|
|
134
|
-
| POST
|
|
135
|
-
| GET
|
|
136
|
-
| GET
|
|
137
|
-
| POST
|
|
138
|
-
| POST
|
|
165
|
+
| Method | Path | Description |
|
|
166
|
+
|-------------|-----------------------------|----------------------------------------------------------------|
|
|
167
|
+
| POST | `/:provider` | Create a payment (server-built or validated) |
|
|
168
|
+
| POST | `/:provider/execute` | Execute/capture a payment (supports `onBeforeExecute`) |
|
|
169
|
+
| GET | `/:provider/redirect` | Handle provider redirect callback (supports 302 via `redirect`)|
|
|
170
|
+
| GET | `/:provider/:id` | Get payment details (*configurable*: can disable or protect via `details`)|
|
|
171
|
+
| POST | `/:provider/refund` | Refund a payment (*opt-in*: mounted only when `refund` option is set)|
|
|
172
|
+
| GET / POST | `/:provider/webhook` | Handle provider webhook (POST for Nexi/Satispay, GET for Satispay)|
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Security: A Redirect Is Not Proof of Payment
|
|
177
|
+
|
|
178
|
+
Relying solely on front-end browser redirects (`GET /:provider/redirect`) to fulfill orders introduces significant risk:
|
|
179
|
+
- **Drop-off**: Shoppers may close the tab, lose connectivity, or navigate away before reaching your redirect URL.
|
|
180
|
+
- **Tampering**: Unless protected by cryptographic signatures (such as Nexi's mandatory outcome MAC), client-controlled URL parameters can be manipulated.
|
|
181
|
+
- **Amount Mismatch**: Always verify that the captured payment amount matches your order ledger before fulfilling goods or services.
|
|
182
|
+
|
|
183
|
+
To ensure safe fulfillment:
|
|
184
|
+
1. **Verify Amount & Currency**: `PaymentResult` and `WebhookResult` return `amount` and `currency` extracted directly from the provider. Compare amounts in minor units (cents) or with rounding to prevent floating-point discrepancies:
|
|
185
|
+
```typescript
|
|
186
|
+
const result = await checkout.handleRedirect(provider, req.query);
|
|
187
|
+
if (result.success && result.status === 'COMPLETED') {
|
|
188
|
+
const orderCents = Math.round(order.totalAmount * 100);
|
|
189
|
+
const resultCents = Math.round((result.amount ?? 0) * 100);
|
|
190
|
+
if (resultCents !== orderCents || result.currency !== order.currency) {
|
|
191
|
+
throw new Error('Authorized amount does not match order total');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
2. **Use Server Notifications / Webhooks**:
|
|
196
|
+
- 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 }))`.
|
|
197
|
+
- For **Satispay**: Set `serverUrl` (and optionally `callbackPath`, default `/checkout/satispay/webhook`). POST notifications verify RSA-SHA256 HTTP signatures against Satispay's RSA public key with `(request-target)` and SHA-256 `Digest` binding to `rawBody`. GET callbacks (`?payment_id=...`) are verified by server-side query to Satispay's API with merchant RSA authentication.
|
|
198
|
+
3. **Idempotency**: Because both the redirect callback and the webhook notification may arrive for the same order, ensure your fulfillment handler is idempotent.
|
|
139
199
|
|
|
140
200
|
---
|
|
141
201
|
|
|
@@ -144,7 +204,7 @@ A full-featured reference implementation using Express 5, SQLite (`ITransactionS
|
|
|
144
204
|
| Flow | Providers | Description |
|
|
145
205
|
|------------|------------------|----------------------------------------------------------|
|
|
146
206
|
| `redirect` | PayPal, Nexi | User is redirected to provider, returns with query params|
|
|
147
|
-
| `webhook` | Satispay
|
|
207
|
+
| `webhook` | Satispay, Nexi | Provider calls a webhook URL for async confirmation |
|
|
148
208
|
| `direct` | *(future)* | Synchronous processing (card tokenization, etc.) |
|
|
149
209
|
|
|
150
210
|
---
|
|
@@ -153,11 +213,11 @@ A full-featured reference implementation using Express 5, SQLite (`ITransactionS
|
|
|
153
213
|
|
|
154
214
|
```typescript
|
|
155
215
|
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 }) => { /* ... */ });
|
|
216
|
+
.on('payment.created', ({ provider, paymentId, orderId, raw }) => { /* ... */ })
|
|
217
|
+
.on('payment.completed', ({ provider, paymentId, orderId, raw }) => { /* ... */ })
|
|
218
|
+
.on('payment.failed', ({ provider, error, orderId, raw }) => { /* ... */ })
|
|
219
|
+
.on('payment.refunded', ({ provider, paymentId, orderId, raw }) => { /* ... */ })
|
|
220
|
+
.on('webhook.received', ({ provider, data, orderId, raw }) => { /* ... */ });
|
|
161
221
|
```
|
|
162
222
|
|
|
163
223
|
---
|
|
@@ -213,11 +273,11 @@ checkout.registerProvider(new StripeProvider({ secretKey: '...' }));
|
|
|
213
273
|
|
|
214
274
|
## Built-in Providers
|
|
215
275
|
|
|
216
|
-
| Provider | Flow
|
|
217
|
-
|
|
218
|
-
| PayPal | redirect
|
|
219
|
-
| Nexi | redirect | eCommerce DispatcherServlet + MAC SHA-1
|
|
220
|
-
| Satispay | webhook
|
|
276
|
+
| Provider | Flow | Notes |
|
|
277
|
+
|-----------|---------------------|---------------------------------------------------------------------------------------------|
|
|
278
|
+
| PayPal | redirect | Orders API v2 (`return=representation`, captures expose `amount` and `currency`) |
|
|
279
|
+
| Nexi | redirect + webhook | eCommerce DispatcherServlet + MAC SHA-1 (mandatory 7-field outcome MAC, `urlpost`, BackOffice `situazioneOrdine` API) |
|
|
280
|
+
| Satispay | webhook | Business API v1, RSA-SHA256 HTTP signature, GET and POST callbacks, configurable `callbackPath` |
|
|
221
281
|
|
|
222
282
|
---
|
|
223
283
|
|
|
@@ -1,21 +1,45 @@
|
|
|
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).
|
|
18
|
+
* Supports both path strings (':provider/webhook') and segment arrays ([':provider', 'webhook']).
|
|
13
19
|
*/
|
|
14
|
-
publicPaths?: string[];
|
|
20
|
+
publicPaths?: (string | string[])[];
|
|
15
21
|
/**
|
|
16
22
|
* Builds the PaymentRequest on the server (amount from your own order), ignoring the client body.
|
|
23
|
+
* If the hook sends an HTTP response directly (e.g. via `ctx.res.status(403)...`),
|
|
24
|
+
* returning `null` or `undefined` halts further adapter processing without error.
|
|
25
|
+
*/
|
|
26
|
+
buildPaymentRequest?: (req: Request, ctx: PaymentRequestContext) => PaymentRequest | null | undefined | Promise<PaymentRequest | null | undefined>;
|
|
27
|
+
/**
|
|
28
|
+
* When configured, GET /:provider/redirect responds with a 302 redirect
|
|
29
|
+
* to the URL returned by onSuccess or onFailure instead of JSON.
|
|
17
30
|
*/
|
|
18
|
-
|
|
31
|
+
redirect?: {
|
|
32
|
+
onSuccess: (result: PaymentResult, req: Request) => string;
|
|
33
|
+
onFailure: (result: PaymentResult, req: Request) => string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Configuration for GET /:provider/:id (getPaymentDetails).
|
|
37
|
+
* Default: enabled: true. Set enabled: false to return 404.
|
|
38
|
+
*/
|
|
39
|
+
details?: {
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
middleware?: RequestHandler[];
|
|
42
|
+
};
|
|
19
43
|
/**
|
|
20
44
|
* Configuration for POST /:provider/refund.
|
|
21
45
|
* If omitted, the refund route is NOT mounted (returns 404).
|
|
@@ -31,6 +55,12 @@ export interface ExpressCheckoutOptions {
|
|
|
31
55
|
onBeforeExecute?: (req: Request, paymentId: string) => void | Promise<void>;
|
|
32
56
|
};
|
|
33
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Matches a request path against public path patterns by segments.
|
|
60
|
+
* ':provider' matches any single non-empty segment.
|
|
61
|
+
* Accepts patterns formatted as string ('/:provider/webhook') or segment arrays ([':provider', 'webhook']).
|
|
62
|
+
*/
|
|
63
|
+
export declare function isPathPublic(reqPath: string, publicPaths: (string | string[])[]): boolean;
|
|
34
64
|
/**
|
|
35
65
|
* Creates an Express Router with all checkout endpoints pre-wired.
|
|
36
66
|
*
|
|
@@ -38,17 +68,18 @@ export interface ExpressCheckoutOptions {
|
|
|
38
68
|
* ```
|
|
39
69
|
* POST /:provider → createPayment
|
|
40
70
|
* POST /:provider/execute → executePayment
|
|
41
|
-
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
42
|
-
* GET /:provider/:id → getPaymentDetails
|
|
43
71
|
* POST /:provider/refund → refundPayment (only if options.refund is configured)
|
|
72
|
+
* GET /:provider/webhook → handleWebhook
|
|
44
73
|
* POST /:provider/webhook → handleWebhook
|
|
74
|
+
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
75
|
+
* GET /:provider/:id → getPaymentDetails
|
|
45
76
|
* ```
|
|
46
77
|
*
|
|
47
78
|
* Usage:
|
|
48
79
|
* ```typescript
|
|
49
80
|
* import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
50
81
|
* app.use('/checkout', createCheckoutRouter(checkout, {
|
|
51
|
-
* buildPaymentRequest: async (req) => {
|
|
82
|
+
* buildPaymentRequest: async (req, ctx) => {
|
|
52
83
|
* const order = await getOrder(req.body.orderId);
|
|
53
84
|
* return {
|
|
54
85
|
* 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;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC;IAEpC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,CACpB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,qBAAqB,KACvB,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAEpF;;;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;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAgBzF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAwLR"}
|
|
@@ -1,8 +1,31 @@
|
|
|
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
|
+
* Accepts patterns formatted as string ('/:provider/webhook') or segment arrays ([':provider', 'webhook']).
|
|
11
|
+
*/
|
|
12
|
+
function isPathPublic(reqPath, publicPaths) {
|
|
13
|
+
const reqSegments = reqPath.split('/').filter(Boolean);
|
|
14
|
+
return publicPaths.some((pattern) => {
|
|
15
|
+
const patternSegments = Array.isArray(pattern)
|
|
16
|
+
? pattern
|
|
17
|
+
: pattern.split('/').filter(Boolean);
|
|
18
|
+
if (reqSegments.length !== patternSegments.length) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return patternSegments.every((patSeg, i) => {
|
|
22
|
+
if (patSeg === ':provider' || patSeg.startsWith(':')) {
|
|
23
|
+
return reqSegments[i].length > 0;
|
|
24
|
+
}
|
|
25
|
+
return patSeg === reqSegments[i];
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
6
29
|
/**
|
|
7
30
|
* Creates an Express Router with all checkout endpoints pre-wired.
|
|
8
31
|
*
|
|
@@ -10,17 +33,18 @@ const errors_1 = require("../../models/errors");
|
|
|
10
33
|
* ```
|
|
11
34
|
* POST /:provider → createPayment
|
|
12
35
|
* POST /:provider/execute → executePayment
|
|
13
|
-
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
14
|
-
* GET /:provider/:id → getPaymentDetails
|
|
15
36
|
* POST /:provider/refund → refundPayment (only if options.refund is configured)
|
|
37
|
+
* GET /:provider/webhook → handleWebhook
|
|
16
38
|
* POST /:provider/webhook → handleWebhook
|
|
39
|
+
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
40
|
+
* GET /:provider/:id → getPaymentDetails
|
|
17
41
|
* ```
|
|
18
42
|
*
|
|
19
43
|
* Usage:
|
|
20
44
|
* ```typescript
|
|
21
45
|
* import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
22
46
|
* app.use('/checkout', createCheckoutRouter(checkout, {
|
|
23
|
-
* buildPaymentRequest: async (req) => {
|
|
47
|
+
* buildPaymentRequest: async (req, ctx) => {
|
|
24
48
|
* const order = await getOrder(req.body.orderId);
|
|
25
49
|
* return {
|
|
26
50
|
* amount: order.total,
|
|
@@ -38,13 +62,11 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
38
62
|
if (!options.buildPaymentRequest) {
|
|
39
63
|
console.warn('[awesome-node-checkout] createCheckoutRouter: the client body decides the amount; set buildPaymentRequest');
|
|
40
64
|
}
|
|
41
|
-
// Apply shared middleware (skipping public paths)
|
|
65
|
+
// Apply shared middleware (skipping public paths by segment match)
|
|
42
66
|
if (options.middleware?.length) {
|
|
43
67
|
const publicPaths = options.publicPaths ?? [];
|
|
44
68
|
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)
|
|
69
|
+
if (isPathPublic(req.path, publicPaths))
|
|
48
70
|
return next();
|
|
49
71
|
// Chain all middleware in sequence
|
|
50
72
|
const handlers = options.middleware;
|
|
@@ -59,9 +81,34 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
59
81
|
// ---- POST /:provider — create payment ------------------------------------
|
|
60
82
|
router.post('/:provider', async (req, res) => {
|
|
61
83
|
try {
|
|
84
|
+
const provider = String(req.params.provider);
|
|
62
85
|
let paymentRequest;
|
|
63
86
|
if (options.buildPaymentRequest) {
|
|
64
|
-
|
|
87
|
+
const customReq = await options.buildPaymentRequest(req, {
|
|
88
|
+
provider,
|
|
89
|
+
res,
|
|
90
|
+
});
|
|
91
|
+
if (res.headersSent) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!customReq ||
|
|
95
|
+
typeof customReq !== 'object' ||
|
|
96
|
+
typeof customReq.amount !== 'number' ||
|
|
97
|
+
!Number.isFinite(customReq.amount) ||
|
|
98
|
+
customReq.amount <= 0 ||
|
|
99
|
+
typeof customReq.currency !== 'string' ||
|
|
100
|
+
customReq.currency.trim() === '' ||
|
|
101
|
+
typeof customReq.returnUrl !== 'string' ||
|
|
102
|
+
customReq.returnUrl.trim() === '' ||
|
|
103
|
+
typeof customReq.cancelUrl !== 'string' ||
|
|
104
|
+
customReq.cancelUrl.trim() === '') {
|
|
105
|
+
return res.status(400).json({
|
|
106
|
+
success: false,
|
|
107
|
+
code: 'INVALID_PAYMENT_REQUEST',
|
|
108
|
+
error: 'Invalid payment request returned by buildPaymentRequest: amount must be a positive number, currency, returnUrl, and cancelUrl are required strings',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
paymentRequest = customReq;
|
|
65
112
|
}
|
|
66
113
|
else {
|
|
67
114
|
const body = req.body;
|
|
@@ -79,7 +126,7 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
79
126
|
}
|
|
80
127
|
paymentRequest = body;
|
|
81
128
|
}
|
|
82
|
-
const result = await checkout.createPayment(
|
|
129
|
+
const result = await checkout.createPayment(provider, paymentRequest);
|
|
83
130
|
res.status(result.success ? 201 : 400).json(result);
|
|
84
131
|
}
|
|
85
132
|
catch (err) {
|
|
@@ -121,22 +168,36 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
121
168
|
}
|
|
122
169
|
});
|
|
123
170
|
}
|
|
124
|
-
// ---- POST /:provider/webhook — handle webhook
|
|
125
|
-
|
|
171
|
+
// ---- GET & POST /:provider/webhook — handle webhook ----------------------
|
|
172
|
+
const webhookHandler = async (req, res) => {
|
|
126
173
|
try {
|
|
127
|
-
const result = await checkout.handleWebhook(String(req.params.provider), req.body, req.headers
|
|
128
|
-
|
|
174
|
+
const result = await checkout.handleWebhook(String(req.params.provider), req.body, req.headers, req.query, {
|
|
175
|
+
method: req.method,
|
|
176
|
+
path: req.originalUrl || req.url,
|
|
177
|
+
rawBody: req.rawBody ?? (Buffer.isBuffer(req.body) ? req.body : undefined),
|
|
178
|
+
});
|
|
179
|
+
// Sanitize: do not echo raw provider payload in webhook response
|
|
180
|
+
const { raw, ...safeResult } = result;
|
|
181
|
+
res.status(result.success ? 200 : 400).json(safeResult);
|
|
129
182
|
}
|
|
130
183
|
catch (err) {
|
|
131
184
|
sendError(res, err);
|
|
132
185
|
}
|
|
133
|
-
}
|
|
186
|
+
};
|
|
187
|
+
router.get('/:provider/webhook', webhookHandler);
|
|
188
|
+
router.post('/:provider/webhook', webhookHandler);
|
|
134
189
|
// ---- GET /:provider/redirect — handle redirect callback ------------------
|
|
135
190
|
// NOTE: must be defined BEFORE /:provider/:id to avoid "redirect" being
|
|
136
191
|
// treated as a paymentId.
|
|
137
192
|
router.get('/:provider/redirect', async (req, res) => {
|
|
138
193
|
try {
|
|
139
194
|
const result = await checkout.handleRedirect(String(req.params.provider), req.query);
|
|
195
|
+
if (options.redirect) {
|
|
196
|
+
const targetUrl = result.success
|
|
197
|
+
? options.redirect.onSuccess(result, req)
|
|
198
|
+
: options.redirect.onFailure(result, req);
|
|
199
|
+
return res.redirect(302, targetUrl);
|
|
200
|
+
}
|
|
140
201
|
res.status(result.success ? 200 : 400).json(result);
|
|
141
202
|
}
|
|
142
203
|
catch (err) {
|
|
@@ -144,7 +205,14 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
144
205
|
}
|
|
145
206
|
});
|
|
146
207
|
// ---- GET /:provider/:id — get payment details ----------------------------
|
|
147
|
-
|
|
208
|
+
const detailsHandlers = [];
|
|
209
|
+
if (options.details?.middleware?.length) {
|
|
210
|
+
detailsHandlers.push(...options.details.middleware);
|
|
211
|
+
}
|
|
212
|
+
router.get('/:provider/:id', ...detailsHandlers, async (req, res) => {
|
|
213
|
+
if (options.details?.enabled === false) {
|
|
214
|
+
return res.status(404).json({ success: false, error: 'Payment details route is disabled' });
|
|
215
|
+
}
|
|
148
216
|
try {
|
|
149
217
|
const result = await checkout.getPaymentDetails(String(req.params.provider), String(req.params.id));
|
|
150
218
|
res.status(result.success ? 200 : 404).json(result);
|
|
@@ -159,6 +227,8 @@ function createCheckoutRouter(checkout, options = {}) {
|
|
|
159
227
|
// Helpers
|
|
160
228
|
// ---------------------------------------------------------------------------
|
|
161
229
|
function sendError(res, err) {
|
|
230
|
+
if (res.headersSent)
|
|
231
|
+
return;
|
|
162
232
|
if (err instanceof errors_1.CheckoutError) {
|
|
163
233
|
const statusMap = {
|
|
164
234
|
PROVIDER_NOT_FOUND: 404,
|
|
@@ -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,9 +27,13 @@ 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 maxPaymentOrders;
|
|
31
|
+
private readonly paymentOrders;
|
|
30
32
|
/** Event bus — subscribe to payment lifecycle events */
|
|
31
33
|
readonly events: CheckoutEventBus;
|
|
32
34
|
constructor(config?: CheckoutConfig);
|
|
35
|
+
/** Stores a paymentId -> orderId mapping with bounded capacity to prevent memory leaks */
|
|
36
|
+
private setPaymentOrder;
|
|
33
37
|
/** Emits an event only when `config.emitEvents` is not explicitly `false`. */
|
|
34
38
|
private emit;
|
|
35
39
|
/**
|
|
@@ -56,7 +60,11 @@ export declare class CheckoutConfigurator {
|
|
|
56
60
|
* Handle an incoming webhook from the provider.
|
|
57
61
|
* @throws CheckoutError if the provider does not support webhooks.
|
|
58
62
|
*/
|
|
59
|
-
handleWebhook(providerName: string, body
|
|
63
|
+
handleWebhook(providerName: string, body?: Record<string, unknown>, headers?: Record<string, string>, query?: Record<string, string>, context?: {
|
|
64
|
+
method?: string;
|
|
65
|
+
path?: string;
|
|
66
|
+
rawBody?: Buffer | string;
|
|
67
|
+
}): Promise<WebhookResult>;
|
|
60
68
|
/**
|
|
61
69
|
* Handle the redirect callback from the provider (query params).
|
|
62
70
|
* @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;IAQnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IACtE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAEhE,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAA0B;gBAE9B,MAAM,GAAE,cAAmB;IAExD,0FAA0F;IAC1F,OAAO,CAAC,eAAe;IAUvB,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;IAkB1F,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;IAkBzB,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;IAkBzB;;;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,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GACtE,OAAO,CAAC,aAAa,CAAC;IAmCzB;;;OAGG;IACG,cAAc,CAClB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,aAAa,CAAC;CA6B1B"}
|