awesome-node-checkout 1.0.1 → 1.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.
- package/CHANGELOG.md +37 -0
- package/README.md +44 -10
- package/dist/abstract/base-payment-provider.abstract.d.ts +30 -0
- package/dist/abstract/base-payment-provider.abstract.d.ts.map +1 -0
- package/dist/abstract/base-payment-provider.abstract.js +28 -0
- package/dist/abstract/base-payment-provider.abstract.js.map +1 -0
- package/dist/adapters/express/index.d.ts +65 -0
- package/dist/adapters/express/index.d.ts.map +1 -0
- package/dist/adapters/express/index.js +176 -0
- package/dist/adapters/express/index.js.map +1 -0
- package/dist/checkout-configurator.d.ts +66 -0
- package/dist/checkout-configurator.d.ts.map +1 -0
- package/dist/checkout-configurator.js +148 -0
- package/dist/checkout-configurator.js.map +1 -0
- package/dist/events/checkout-event-bus.d.ts +29 -0
- package/dist/events/checkout-event-bus.d.ts.map +1 -0
- package/dist/events/checkout-event-bus.js +37 -0
- package/dist/events/checkout-event-bus.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/payment-provider.interface.d.ts +33 -0
- package/dist/interfaces/payment-provider.interface.d.ts.map +1 -0
- package/dist/interfaces/payment-provider.interface.js +3 -0
- package/dist/interfaces/payment-provider.interface.js.map +1 -0
- package/dist/interfaces/transaction-store.interface.d.ts +24 -0
- package/dist/interfaces/transaction-store.interface.d.ts.map +1 -0
- package/dist/interfaces/transaction-store.interface.js +3 -0
- package/dist/interfaces/transaction-store.interface.js.map +1 -0
- package/dist/models/checkout-config.model.d.ts +12 -0
- package/dist/models/checkout-config.model.d.ts.map +1 -0
- package/dist/models/checkout-config.model.js +3 -0
- package/dist/models/checkout-config.model.js.map +1 -0
- package/dist/models/errors.d.ts +7 -0
- package/dist/models/errors.d.ts.map +1 -0
- package/dist/models/errors.js +17 -0
- package/dist/models/errors.js.map +1 -0
- package/dist/models/payment-request.model.d.ts +21 -0
- package/dist/models/payment-request.model.d.ts.map +1 -0
- package/dist/models/payment-request.model.js +3 -0
- package/dist/models/payment-request.model.js.map +1 -0
- package/dist/models/payment-result.model.d.ts +23 -0
- package/dist/models/payment-result.model.d.ts.map +1 -0
- package/dist/models/payment-result.model.js +3 -0
- package/dist/models/payment-result.model.js.map +1 -0
- package/dist/providers/nexi/nexi.provider.d.ts +64 -0
- package/dist/providers/nexi/nexi.provider.d.ts.map +1 -0
- package/dist/providers/nexi/nexi.provider.js +227 -0
- package/dist/providers/nexi/nexi.provider.js.map +1 -0
- package/dist/providers/paypal/paypal.provider.d.ts +33 -0
- package/dist/providers/paypal/paypal.provider.d.ts.map +1 -0
- package/dist/providers/paypal/paypal.provider.js +167 -0
- package/dist/providers/paypal/paypal.provider.js.map +1 -0
- package/dist/providers/satispay/satispay.provider.d.ts +79 -0
- package/dist/providers/satispay/satispay.provider.d.ts.map +1 -0
- package/dist/providers/satispay/satispay.provider.js +301 -0
- package/dist/providers/satispay/satispay.provider.js.map +1 -0
- package/dist/stores/in-memory-transaction.store.d.ts +17 -0
- package/dist/stores/in-memory-transaction.store.d.ts.map +1 -0
- package/dist/stores/in-memory-transaction.store.js +30 -0
- package/dist/stores/in-memory-transaction.store.js.map +1 -0
- package/package.json +12 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.1.0] - 2026-09-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Security**: Added `buildPaymentRequest` hook in `createCheckoutRouter` options to build payment requests securely server-side (preventing clients from tampering with amounts and order parameters). Resolves #3.
|
|
12
|
+
- **Security**: Added mount-time warning when `createCheckoutRouter` is used without `buildPaymentRequest`.
|
|
13
|
+
- **Validation**: Added input validation on `POST /:provider` when `buildPaymentRequest` is not configured (requires finite positive number `amount` and non-empty `currency`).
|
|
14
|
+
- **Gating**: Gated `POST /:provider/refund` behind `options.refund` (returns 404 unless explicitly configured with optional middleware).
|
|
15
|
+
- **Hooks**: Added `onBeforeExecute` and dedicated `middleware` support in `options.execute` for `POST /:provider/execute`.
|
|
16
|
+
- **Examples**: Added reference Express implementation under `examples/express` demonstrating SQLite store, Handlebars UI, and full checkout flow.
|
|
17
|
+
|
|
18
|
+
## [1.0.2] - 2026-04-23
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Removed compiled `dist/` artifacts from git tracking.
|
|
22
|
+
- Added GitHub Actions publish workflow.
|
|
23
|
+
- Updated dependencies.
|
|
24
|
+
|
|
25
|
+
## [1.0.1] - 2026-04-13
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Satispay provider warning at construction time when `webhookPublicKey` is not configured.
|
|
29
|
+
- PayPal refund implementation and security improvements.
|
|
30
|
+
|
|
31
|
+
## [1.0.0] - 2026-03-30
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- Initial release of `awesome-node-checkout`.
|
|
35
|
+
- Framework-agnostic `CheckoutConfigurator`.
|
|
36
|
+
- PayPal, Nexi, and Satispay providers.
|
|
37
|
+
- Express adapter with `createCheckoutRouter`.
|
package/README.md
CHANGED
|
@@ -68,12 +68,40 @@ import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
|
68
68
|
const app = express();
|
|
69
69
|
app.use(express.json());
|
|
70
70
|
|
|
71
|
-
//
|
|
72
|
-
app.use(
|
|
71
|
+
// Mount checkout routes with secure server-side request builder
|
|
72
|
+
app.use(
|
|
73
|
+
'/checkout',
|
|
74
|
+
createCheckoutRouter(checkout, {
|
|
75
|
+
// Recommended: verify order and compute amount on the server
|
|
76
|
+
buildPaymentRequest: async (req) => {
|
|
77
|
+
const order = await myOrderService.getOrder(req.body.orderId);
|
|
78
|
+
return {
|
|
79
|
+
amount: order.totalAmount,
|
|
80
|
+
currency: order.currency, // e.g. 'EUR'
|
|
81
|
+
orderId: order.id,
|
|
82
|
+
returnUrl: 'https://myapp.com/payment/success',
|
|
83
|
+
cancelUrl: 'https://myapp.com/payment/cancel',
|
|
84
|
+
description: `Order #${order.id}`,
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
// Opt-in: mount POST /:provider/refund protected by admin middleware
|
|
88
|
+
refund: {
|
|
89
|
+
middleware: [requireAdminAuth],
|
|
90
|
+
},
|
|
91
|
+
// Optional: guard execution before capturing payment
|
|
92
|
+
execute: {
|
|
93
|
+
onBeforeExecute: async (req, paymentId) => {
|
|
94
|
+
// Verify payment belongs to current session or user
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
73
99
|
|
|
74
100
|
app.listen(3000);
|
|
75
101
|
```
|
|
76
102
|
|
|
103
|
+
> **Security Note**: Never trust client-sent amounts directly. Always define `buildPaymentRequest` so the payment amount and order parameters are derived server-side. If `buildPaymentRequest` is omitted, the router will issue a warning at mount time and validate that client bodies contain positive finite amounts and valid currencies.
|
|
104
|
+
|
|
77
105
|
### With Fastify (or any other framework)
|
|
78
106
|
|
|
79
107
|
Use the `CheckoutConfigurator` methods directly in your own routes:
|
|
@@ -92,16 +120,22 @@ fastify.post('/checkout/:provider/webhook', async (req, reply) => {
|
|
|
92
120
|
|
|
93
121
|
---
|
|
94
122
|
|
|
123
|
+
## Example Application
|
|
124
|
+
|
|
125
|
+
A full-featured reference implementation using Express 5, SQLite (`ITransactionStore`), and Handlebars is available in [examples/express](./examples/express).
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
95
129
|
## Routes (Express adapter)
|
|
96
130
|
|
|
97
|
-
| Method | Path | Description
|
|
98
|
-
|
|
99
|
-
| POST | `/:provider` | Create a payment
|
|
100
|
-
| POST | `/:provider/execute` | Execute/capture a payment
|
|
101
|
-
| GET | `/:provider/redirect` | Handle provider redirect callback|
|
|
102
|
-
| GET | `/:provider/:id` | Get payment details
|
|
103
|
-
| POST | `/:provider/refund` | Refund a payment
|
|
104
|
-
| POST | `/:provider/webhook` | Handle provider webhook
|
|
131
|
+
| Method | Path | Description |
|
|
132
|
+
|--------|-----------------------------|----------------------------------------------------------------|
|
|
133
|
+
| POST | `/:provider` | Create a payment (server-built or validated) |
|
|
134
|
+
| POST | `/:provider/execute` | Execute/capture a payment (supports `onBeforeExecute`) |
|
|
135
|
+
| GET | `/:provider/redirect` | Handle provider redirect callback |
|
|
136
|
+
| GET | `/:provider/:id` | Get payment details |
|
|
137
|
+
| POST | `/:provider/refund` | Refund a payment (*opt-in*: mounted only when `refund` option is set)|
|
|
138
|
+
| POST | `/:provider/webhook` | Handle provider webhook |
|
|
105
139
|
|
|
106
140
|
---
|
|
107
141
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { IPaymentProvider, PaymentFlow } from '../interfaces/payment-provider.interface';
|
|
2
|
+
import { PaymentRequest } from '../models/payment-request.model';
|
|
3
|
+
import { PaymentResult } from '../models/payment-result.model';
|
|
4
|
+
/**
|
|
5
|
+
* Convenience base class for building custom payment providers.
|
|
6
|
+
* Provides the `errorResult` helper to normalize error handling.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* export class MyProvider extends BasePaymentProvider {
|
|
11
|
+
* readonly name = 'myprovider';
|
|
12
|
+
* readonly flow = 'redirect' as const;
|
|
13
|
+
* // ... implement abstract methods
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare abstract class BasePaymentProvider implements IPaymentProvider {
|
|
18
|
+
abstract readonly name: string;
|
|
19
|
+
abstract readonly flow: PaymentFlow;
|
|
20
|
+
abstract createPayment(request: PaymentRequest): Promise<PaymentResult>;
|
|
21
|
+
abstract executePayment(paymentId: string, data?: Record<string, string>): Promise<PaymentResult>;
|
|
22
|
+
abstract getPaymentDetails(paymentId: string): Promise<PaymentResult>;
|
|
23
|
+
abstract refundPayment(paymentId: string, amount?: number): Promise<PaymentResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Helper to produce a normalized error PaymentResult.
|
|
26
|
+
* Extracts message from Error instances automatically.
|
|
27
|
+
*/
|
|
28
|
+
protected errorResult(error: unknown, fallback?: string): PaymentResult;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=base-payment-provider.abstract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-payment-provider.abstract.d.ts","sourceRoot":"","sources":["../../src/abstract/base-payment-provider.abstract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D;;;;;;;;;;;;GAYG;AACH,8BAAsB,mBAAoB,YAAW,gBAAgB;IACnE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAEpC,QAAQ,CAAC,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IACvE,QAAQ,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IACjG,QAAQ,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IACrE,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAElF;;;OAGG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAkB,GAAG,aAAa;CAIjF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BasePaymentProvider = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Convenience base class for building custom payment providers.
|
|
6
|
+
* Provides the `errorResult` helper to normalize error handling.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* export class MyProvider extends BasePaymentProvider {
|
|
11
|
+
* readonly name = 'myprovider';
|
|
12
|
+
* readonly flow = 'redirect' as const;
|
|
13
|
+
* // ... implement abstract methods
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
class BasePaymentProvider {
|
|
18
|
+
/**
|
|
19
|
+
* Helper to produce a normalized error PaymentResult.
|
|
20
|
+
* Extracts message from Error instances automatically.
|
|
21
|
+
*/
|
|
22
|
+
errorResult(error, fallback = 'Unknown error') {
|
|
23
|
+
const message = error instanceof Error ? error.message : fallback;
|
|
24
|
+
return { success: false, error: message };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.BasePaymentProvider = BasePaymentProvider;
|
|
28
|
+
//# sourceMappingURL=base-payment-provider.abstract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-payment-provider.abstract.js","sourceRoot":"","sources":["../../src/abstract/base-payment-provider.abstract.ts"],"names":[],"mappings":";;;AAIA;;;;;;;;;;;;GAYG;AACH,MAAsB,mBAAmB;IASvC;;;OAGG;IACO,WAAW,CAAC,KAAc,EAAE,QAAQ,GAAG,eAAe;QAC9D,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QAClE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC5C,CAAC;CACF;AAjBD,kDAiBC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Router, Request, RequestHandler } from 'express';
|
|
2
|
+
import { CheckoutConfigurator } from '../../checkout-configurator';
|
|
3
|
+
import { PaymentRequest } from '../../models/payment-request.model';
|
|
4
|
+
export interface ExpressCheckoutOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Middleware applied to ALL checkout routes (e.g. API key auth).
|
|
7
|
+
* Webhooks are typically public — use `publicPaths` to exclude them.
|
|
8
|
+
*/
|
|
9
|
+
middleware?: RequestHandler[];
|
|
10
|
+
/**
|
|
11
|
+
* Route suffixes that bypass `middleware` (e.g. [':provider/webhook']).
|
|
12
|
+
* Matched against the path suffix after the mount point.
|
|
13
|
+
*/
|
|
14
|
+
publicPaths?: string[];
|
|
15
|
+
/**
|
|
16
|
+
* Builds the PaymentRequest on the server (amount from your own order), ignoring the client body.
|
|
17
|
+
*/
|
|
18
|
+
buildPaymentRequest?: (req: Request) => PaymentRequest | Promise<PaymentRequest>;
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for POST /:provider/refund.
|
|
21
|
+
* If omitted, the refund route is NOT mounted (returns 404).
|
|
22
|
+
*/
|
|
23
|
+
refund?: {
|
|
24
|
+
middleware?: RequestHandler[];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Configuration for POST /:provider/execute.
|
|
28
|
+
*/
|
|
29
|
+
execute?: {
|
|
30
|
+
middleware?: RequestHandler[];
|
|
31
|
+
onBeforeExecute?: (req: Request, paymentId: string) => void | Promise<void>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates an Express Router with all checkout endpoints pre-wired.
|
|
36
|
+
*
|
|
37
|
+
* Routes mounted:
|
|
38
|
+
* ```
|
|
39
|
+
* POST /:provider → createPayment
|
|
40
|
+
* POST /:provider/execute → executePayment
|
|
41
|
+
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
42
|
+
* GET /:provider/:id → getPaymentDetails
|
|
43
|
+
* POST /:provider/refund → refundPayment (only if options.refund is configured)
|
|
44
|
+
* POST /:provider/webhook → handleWebhook
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* Usage:
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
50
|
+
* app.use('/checkout', createCheckoutRouter(checkout, {
|
|
51
|
+
* buildPaymentRequest: async (req) => {
|
|
52
|
+
* const order = await getOrder(req.body.orderId);
|
|
53
|
+
* return {
|
|
54
|
+
* amount: order.total,
|
|
55
|
+
* currency: 'EUR',
|
|
56
|
+
* orderId: order.id,
|
|
57
|
+
* returnUrl: 'https://example.com/success',
|
|
58
|
+
* cancelUrl: 'https://example.com/cancel',
|
|
59
|
+
* };
|
|
60
|
+
* },
|
|
61
|
+
* }));
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function createCheckoutRouter(checkout: CheckoutConfigurator, options?: ExpressCheckoutOptions): Router;
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAY,cAAc,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,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,CAAC,GAAG,EAAE,OAAO,KAAK,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAEjF;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAsIR"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCheckoutRouter = createCheckoutRouter;
|
|
4
|
+
const express_1 = require("express");
|
|
5
|
+
const errors_1 = require("../../models/errors");
|
|
6
|
+
/**
|
|
7
|
+
* Creates an Express Router with all checkout endpoints pre-wired.
|
|
8
|
+
*
|
|
9
|
+
* Routes mounted:
|
|
10
|
+
* ```
|
|
11
|
+
* POST /:provider → createPayment
|
|
12
|
+
* POST /:provider/execute → executePayment
|
|
13
|
+
* GET /:provider/redirect → handleRedirect (must come before /:provider/:id)
|
|
14
|
+
* GET /:provider/:id → getPaymentDetails
|
|
15
|
+
* POST /:provider/refund → refundPayment (only if options.refund is configured)
|
|
16
|
+
* POST /:provider/webhook → handleWebhook
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
22
|
+
* app.use('/checkout', createCheckoutRouter(checkout, {
|
|
23
|
+
* buildPaymentRequest: async (req) => {
|
|
24
|
+
* const order = await getOrder(req.body.orderId);
|
|
25
|
+
* return {
|
|
26
|
+
* amount: order.total,
|
|
27
|
+
* currency: 'EUR',
|
|
28
|
+
* orderId: order.id,
|
|
29
|
+
* returnUrl: 'https://example.com/success',
|
|
30
|
+
* cancelUrl: 'https://example.com/cancel',
|
|
31
|
+
* };
|
|
32
|
+
* },
|
|
33
|
+
* }));
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function createCheckoutRouter(checkout, options = {}) {
|
|
37
|
+
const router = (0, express_1.Router)();
|
|
38
|
+
if (!options.buildPaymentRequest) {
|
|
39
|
+
console.warn('[awesome-node-checkout] createCheckoutRouter: the client body decides the amount; set buildPaymentRequest');
|
|
40
|
+
}
|
|
41
|
+
// Apply shared middleware (skipping public paths)
|
|
42
|
+
if (options.middleware?.length) {
|
|
43
|
+
const publicPaths = options.publicPaths ?? [];
|
|
44
|
+
router.use((req, res, next) => {
|
|
45
|
+
const providerParam = String(req.params?.provider ?? '');
|
|
46
|
+
const isPublic = publicPaths.some((p) => req.path.endsWith(p) || req.path.includes(p.replace(':provider', providerParam)));
|
|
47
|
+
if (isPublic)
|
|
48
|
+
return next();
|
|
49
|
+
// Chain all middleware in sequence
|
|
50
|
+
const handlers = options.middleware;
|
|
51
|
+
const run = (i) => {
|
|
52
|
+
if (i >= handlers.length)
|
|
53
|
+
return next();
|
|
54
|
+
handlers[i](req, res, () => run(i + 1));
|
|
55
|
+
};
|
|
56
|
+
run(0);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// ---- POST /:provider — create payment ------------------------------------
|
|
60
|
+
router.post('/:provider', async (req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
let paymentRequest;
|
|
63
|
+
if (options.buildPaymentRequest) {
|
|
64
|
+
paymentRequest = await options.buildPaymentRequest(req);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const body = req.body;
|
|
68
|
+
const amount = body?.amount;
|
|
69
|
+
const currency = body?.currency;
|
|
70
|
+
if (typeof amount !== 'number' ||
|
|
71
|
+
!Number.isFinite(amount) ||
|
|
72
|
+
amount <= 0 ||
|
|
73
|
+
typeof currency !== 'string' ||
|
|
74
|
+
currency.trim() === '') {
|
|
75
|
+
return res.status(400).json({
|
|
76
|
+
success: false,
|
|
77
|
+
error: 'Invalid payment request: amount must be a positive number and currency is required',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
paymentRequest = body;
|
|
81
|
+
}
|
|
82
|
+
const result = await checkout.createPayment(String(req.params.provider), paymentRequest);
|
|
83
|
+
res.status(result.success ? 201 : 400).json(result);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
sendError(res, err);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// ---- POST /:provider/execute — execute payment ---------------------------
|
|
90
|
+
const executeHandlers = [];
|
|
91
|
+
if (options.execute?.middleware?.length) {
|
|
92
|
+
executeHandlers.push(...options.execute.middleware);
|
|
93
|
+
}
|
|
94
|
+
router.post('/:provider/execute', ...executeHandlers, async (req, res) => {
|
|
95
|
+
try {
|
|
96
|
+
const { paymentId, data } = req.body;
|
|
97
|
+
if (options.execute?.onBeforeExecute) {
|
|
98
|
+
await options.execute.onBeforeExecute(req, paymentId);
|
|
99
|
+
}
|
|
100
|
+
const result = await checkout.executePayment(String(req.params.provider), paymentId, data);
|
|
101
|
+
res.status(result.success ? 200 : 400).json(result);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
sendError(res, err);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// ---- POST /:provider/refund — refund payment (opt-in) --------------------
|
|
108
|
+
if (options.refund) {
|
|
109
|
+
const refundHandlers = [];
|
|
110
|
+
if (options.refund.middleware?.length) {
|
|
111
|
+
refundHandlers.push(...options.refund.middleware);
|
|
112
|
+
}
|
|
113
|
+
router.post('/:provider/refund', ...refundHandlers, async (req, res) => {
|
|
114
|
+
try {
|
|
115
|
+
const { paymentId, amount } = req.body;
|
|
116
|
+
const result = await checkout.refundPayment(String(req.params.provider), paymentId, amount);
|
|
117
|
+
res.status(result.success ? 200 : 400).json(result);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
sendError(res, err);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// ---- POST /:provider/webhook — handle webhook ----------------------------
|
|
125
|
+
router.post('/:provider/webhook', async (req, res) => {
|
|
126
|
+
try {
|
|
127
|
+
const result = await checkout.handleWebhook(String(req.params.provider), req.body, req.headers);
|
|
128
|
+
res.status(result.success ? 200 : 400).json(result);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
sendError(res, err);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
// ---- GET /:provider/redirect — handle redirect callback ------------------
|
|
135
|
+
// NOTE: must be defined BEFORE /:provider/:id to avoid "redirect" being
|
|
136
|
+
// treated as a paymentId.
|
|
137
|
+
router.get('/:provider/redirect', async (req, res) => {
|
|
138
|
+
try {
|
|
139
|
+
const result = await checkout.handleRedirect(String(req.params.provider), req.query);
|
|
140
|
+
res.status(result.success ? 200 : 400).json(result);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
sendError(res, err);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
// ---- GET /:provider/:id — get payment details ----------------------------
|
|
147
|
+
router.get('/:provider/:id', async (req, res) => {
|
|
148
|
+
try {
|
|
149
|
+
const result = await checkout.getPaymentDetails(String(req.params.provider), String(req.params.id));
|
|
150
|
+
res.status(result.success ? 200 : 404).json(result);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
sendError(res, err);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return router;
|
|
157
|
+
}
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Helpers
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
function sendError(res, err) {
|
|
162
|
+
if (err instanceof errors_1.CheckoutError) {
|
|
163
|
+
const statusMap = {
|
|
164
|
+
PROVIDER_NOT_FOUND: 404,
|
|
165
|
+
WEBHOOK_NOT_SUPPORTED: 422,
|
|
166
|
+
REDIRECT_NOT_SUPPORTED: 422,
|
|
167
|
+
};
|
|
168
|
+
const status = statusMap[err.code] ?? 400;
|
|
169
|
+
res.status(status).json({ success: false, error: err.message, code: err.code });
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const message = err instanceof Error ? err.message : 'Internal server error';
|
|
173
|
+
res.status(500).json({ success: false, error: message });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/express/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { IPaymentProvider } from './interfaces/payment-provider.interface';
|
|
2
|
+
import { PaymentRequest } from './models/payment-request.model';
|
|
3
|
+
import { PaymentResult, WebhookResult } from './models/payment-result.model';
|
|
4
|
+
import { CheckoutConfig } from './models/checkout-config.model';
|
|
5
|
+
import { CheckoutEventBus } from './events/checkout-event-bus';
|
|
6
|
+
/**
|
|
7
|
+
* Main entry point for awesome-node-checkout.
|
|
8
|
+
*
|
|
9
|
+
* Usage (framework-agnostic):
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const checkout = new CheckoutConfigurator();
|
|
12
|
+
* checkout
|
|
13
|
+
* .registerProvider(new PayPalProvider({ clientId, clientSecret }))
|
|
14
|
+
* .registerProvider(new NexiProvider({ merchantId, macKey }))
|
|
15
|
+
* .registerProvider(new SatispayProvider({ keyId, privateKey }));
|
|
16
|
+
*
|
|
17
|
+
* // Use directly in any framework
|
|
18
|
+
* const result = await checkout.createPayment('paypal', request);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Usage with Express adapter:
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
24
|
+
* app.use('/checkout', createCheckoutRouter(checkout));
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class CheckoutConfigurator {
|
|
28
|
+
private readonly config;
|
|
29
|
+
private readonly providers;
|
|
30
|
+
/** Event bus — subscribe to payment lifecycle events */
|
|
31
|
+
readonly events: CheckoutEventBus;
|
|
32
|
+
constructor(config?: CheckoutConfig);
|
|
33
|
+
/** Emits an event only when `config.emitEvents` is not explicitly `false`. */
|
|
34
|
+
private emit;
|
|
35
|
+
/**
|
|
36
|
+
* Register a payment provider. Chainable.
|
|
37
|
+
* @throws CheckoutError if a provider with the same name is already registered.
|
|
38
|
+
*/
|
|
39
|
+
registerProvider(provider: IPaymentProvider): this;
|
|
40
|
+
/**
|
|
41
|
+
* Retrieve a registered provider by name.
|
|
42
|
+
* @throws CheckoutError if the provider is not found.
|
|
43
|
+
*/
|
|
44
|
+
getProvider(name: string): IPaymentProvider;
|
|
45
|
+
/** Returns the names of all registered providers */
|
|
46
|
+
getRegisteredProviders(): string[];
|
|
47
|
+
/** Create a new payment via the specified provider */
|
|
48
|
+
createPayment(providerName: string, request: PaymentRequest): Promise<PaymentResult>;
|
|
49
|
+
/** Execute/capture a previously created payment */
|
|
50
|
+
executePayment(providerName: string, paymentId: string, data?: Record<string, string>): Promise<PaymentResult>;
|
|
51
|
+
/** Retrieve the current status and details of a payment */
|
|
52
|
+
getPaymentDetails(providerName: string, paymentId: string): Promise<PaymentResult>;
|
|
53
|
+
/** Issue a full or partial refund */
|
|
54
|
+
refundPayment(providerName: string, paymentId: string, amount?: number): Promise<PaymentResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Handle an incoming webhook from the provider.
|
|
57
|
+
* @throws CheckoutError if the provider does not support webhooks.
|
|
58
|
+
*/
|
|
59
|
+
handleWebhook(providerName: string, body: Record<string, unknown>, headers: Record<string, string>): Promise<WebhookResult>;
|
|
60
|
+
/**
|
|
61
|
+
* Handle the redirect callback from the provider (query params).
|
|
62
|
+
* @throws CheckoutError if the provider does not support redirect handling.
|
|
63
|
+
*/
|
|
64
|
+
handleRedirect(providerName: string, query: Record<string, string>): Promise<PaymentResult>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=checkout-configurator.d.ts.map
|
|
@@ -0,0 +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;IAMnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IAEtE,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;IAa1F,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;IAYzB,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;IAYzB;;;OAGG;IACG,aAAa,CACjB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,aAAa,CAAC;IAoBzB;;;OAGG;IACG,cAAc,CAClB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,aAAa,CAAC;CAkB1B"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CheckoutConfigurator = void 0;
|
|
4
|
+
const errors_1 = require("./models/errors");
|
|
5
|
+
const checkout_event_bus_1 = require("./events/checkout-event-bus");
|
|
6
|
+
/**
|
|
7
|
+
* Main entry point for awesome-node-checkout.
|
|
8
|
+
*
|
|
9
|
+
* Usage (framework-agnostic):
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const checkout = new CheckoutConfigurator();
|
|
12
|
+
* checkout
|
|
13
|
+
* .registerProvider(new PayPalProvider({ clientId, clientSecret }))
|
|
14
|
+
* .registerProvider(new NexiProvider({ merchantId, macKey }))
|
|
15
|
+
* .registerProvider(new SatispayProvider({ keyId, privateKey }));
|
|
16
|
+
*
|
|
17
|
+
* // Use directly in any framework
|
|
18
|
+
* const result = await checkout.createPayment('paypal', request);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Usage with Express adapter:
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import { createCheckoutRouter } from 'awesome-node-checkout/express';
|
|
24
|
+
* app.use('/checkout', createCheckoutRouter(checkout));
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
class CheckoutConfigurator {
|
|
28
|
+
constructor(config = {}) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
this.providers = new Map();
|
|
31
|
+
/** Event bus — subscribe to payment lifecycle events */
|
|
32
|
+
this.events = new checkout_event_bus_1.CheckoutEventBus();
|
|
33
|
+
}
|
|
34
|
+
/** Emits an event only when `config.emitEvents` is not explicitly `false`. */
|
|
35
|
+
async emit(event, data) {
|
|
36
|
+
if (this.config.emitEvents !== false) {
|
|
37
|
+
await this.events.emit(event, data);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Register a payment provider. Chainable.
|
|
42
|
+
* @throws CheckoutError if a provider with the same name is already registered.
|
|
43
|
+
*/
|
|
44
|
+
registerProvider(provider) {
|
|
45
|
+
const key = provider.name.toLowerCase();
|
|
46
|
+
if (this.providers.has(key)) {
|
|
47
|
+
throw new errors_1.CheckoutError(`Provider '${provider.name}' is already registered`, 'PROVIDER_ALREADY_REGISTERED', provider.name);
|
|
48
|
+
}
|
|
49
|
+
this.providers.set(key, provider);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Retrieve a registered provider by name.
|
|
54
|
+
* @throws CheckoutError if the provider is not found.
|
|
55
|
+
*/
|
|
56
|
+
getProvider(name) {
|
|
57
|
+
const provider = this.providers.get(name.toLowerCase());
|
|
58
|
+
if (!provider) {
|
|
59
|
+
throw new errors_1.CheckoutError(`Payment provider '${name}' not found. Did you call registerProvider()?`, 'PROVIDER_NOT_FOUND', name);
|
|
60
|
+
}
|
|
61
|
+
return provider;
|
|
62
|
+
}
|
|
63
|
+
/** Returns the names of all registered providers */
|
|
64
|
+
getRegisteredProviders() {
|
|
65
|
+
return Array.from(this.providers.keys());
|
|
66
|
+
}
|
|
67
|
+
/** Create a new payment via the specified provider */
|
|
68
|
+
async createPayment(providerName, request) {
|
|
69
|
+
const provider = this.getProvider(providerName);
|
|
70
|
+
const result = await provider.createPayment(request);
|
|
71
|
+
await this.emit(result.success ? 'payment.created' : 'payment.failed', {
|
|
72
|
+
provider: providerName,
|
|
73
|
+
paymentId: result.paymentId,
|
|
74
|
+
orderId: request.orderId,
|
|
75
|
+
status: result.status,
|
|
76
|
+
error: result.error,
|
|
77
|
+
});
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
/** Execute/capture a previously created payment */
|
|
81
|
+
async executePayment(providerName, paymentId, data) {
|
|
82
|
+
const provider = this.getProvider(providerName);
|
|
83
|
+
const result = await provider.executePayment(paymentId, data);
|
|
84
|
+
await this.emit(result.success ? 'payment.completed' : 'payment.failed', {
|
|
85
|
+
provider: providerName,
|
|
86
|
+
paymentId,
|
|
87
|
+
status: result.status,
|
|
88
|
+
error: result.error,
|
|
89
|
+
});
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
/** Retrieve the current status and details of a payment */
|
|
93
|
+
async getPaymentDetails(providerName, paymentId) {
|
|
94
|
+
const provider = this.getProvider(providerName);
|
|
95
|
+
return provider.getPaymentDetails(paymentId);
|
|
96
|
+
}
|
|
97
|
+
/** Issue a full or partial refund */
|
|
98
|
+
async refundPayment(providerName, paymentId, amount) {
|
|
99
|
+
const provider = this.getProvider(providerName);
|
|
100
|
+
const result = await provider.refundPayment(paymentId, amount);
|
|
101
|
+
await this.emit('payment.refunded', {
|
|
102
|
+
provider: providerName,
|
|
103
|
+
paymentId,
|
|
104
|
+
status: result.status,
|
|
105
|
+
error: result.error,
|
|
106
|
+
});
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Handle an incoming webhook from the provider.
|
|
111
|
+
* @throws CheckoutError if the provider does not support webhooks.
|
|
112
|
+
*/
|
|
113
|
+
async handleWebhook(providerName, body, headers) {
|
|
114
|
+
const provider = this.getProvider(providerName);
|
|
115
|
+
if (!provider.handleWebhook) {
|
|
116
|
+
throw new errors_1.CheckoutError(`Provider '${providerName}' does not support webhook handling`, 'WEBHOOK_NOT_SUPPORTED', providerName);
|
|
117
|
+
}
|
|
118
|
+
const result = await provider.handleWebhook(body, headers);
|
|
119
|
+
await this.emit('webhook.received', {
|
|
120
|
+
provider: providerName,
|
|
121
|
+
paymentId: result.paymentId,
|
|
122
|
+
status: result.status,
|
|
123
|
+
error: result.error,
|
|
124
|
+
data: body,
|
|
125
|
+
});
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Handle the redirect callback from the provider (query params).
|
|
130
|
+
* @throws CheckoutError if the provider does not support redirect handling.
|
|
131
|
+
*/
|
|
132
|
+
async handleRedirect(providerName, query) {
|
|
133
|
+
const provider = this.getProvider(providerName);
|
|
134
|
+
if (!provider.handleRedirect) {
|
|
135
|
+
throw new errors_1.CheckoutError(`Provider '${providerName}' does not support redirect handling`, 'REDIRECT_NOT_SUPPORTED', providerName);
|
|
136
|
+
}
|
|
137
|
+
const result = await provider.handleRedirect(query);
|
|
138
|
+
await this.emit(result.success ? 'payment.completed' : 'payment.failed', {
|
|
139
|
+
provider: providerName,
|
|
140
|
+
paymentId: result.paymentId,
|
|
141
|
+
status: result.status,
|
|
142
|
+
error: result.error,
|
|
143
|
+
});
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.CheckoutConfigurator = CheckoutConfigurator;
|
|
148
|
+
//# sourceMappingURL=checkout-configurator.js.map
|