liqpay-nestjs 0.2.16 → 0.2.17
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/README.md +338 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,347 @@
|
|
|
1
1
|
# liqpay-nestjs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
NestJS module for LiqPay payments with DI-friendly configuration, signed checkout helpers, typed request and response models, and webhook callback parsing.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- NestJS module with `forRoot` and `forRootAsync`
|
|
8
|
+
- `LiqpayService` with payment and webhook helpers
|
|
9
|
+
- Signed checkout URL and checkout form generation
|
|
10
|
+
- Typed payment status requests
|
|
11
|
+
- Callback signature validation and payload parsing
|
|
12
|
+
- Exported Zod schemas and TypeScript types from the package root
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- Node.js `>= 18`
|
|
17
|
+
- `@nestjs/common` `^10 || ^11`
|
|
18
|
+
- `@nestjs/core` `^10 || ^11`
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
4
21
|
|
|
5
22
|
```bash
|
|
6
|
-
|
|
23
|
+
npm install liqpay-nestjs
|
|
7
24
|
```
|
|
8
25
|
|
|
9
|
-
|
|
26
|
+
## Quick Start
|
|
10
27
|
|
|
11
|
-
|
|
12
|
-
|
|
28
|
+
### Register the module
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { Module } from '@nestjs/common'
|
|
32
|
+
import { LiqPayModule } from 'liqpay-nestjs'
|
|
33
|
+
|
|
34
|
+
@Module({
|
|
35
|
+
imports: [
|
|
36
|
+
LiqPayModule.forRoot({
|
|
37
|
+
publicKey: process.env.LIQPAY_PUBLIC_KEY!,
|
|
38
|
+
privateKey: process.env.LIQPAY_PRIVATE_KEY!,
|
|
39
|
+
resultUrl: 'https://example.com/payments/result',
|
|
40
|
+
serverUrl: 'https://example.com/payments/webhook',
|
|
41
|
+
isGlobal: true,
|
|
42
|
+
}),
|
|
43
|
+
],
|
|
44
|
+
})
|
|
45
|
+
export class AppModule {}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Register the module asynchronously
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { Module } from '@nestjs/common'
|
|
52
|
+
import { ConfigModule, ConfigService } from '@nestjs/config'
|
|
53
|
+
import { LiqPayModule } from 'liqpay-nestjs'
|
|
54
|
+
|
|
55
|
+
@Module({
|
|
56
|
+
imports: [
|
|
57
|
+
ConfigModule.forRoot(),
|
|
58
|
+
LiqPayModule.forRootAsync({
|
|
59
|
+
imports: [ConfigModule],
|
|
60
|
+
inject: [ConfigService],
|
|
61
|
+
useFactory: (config: ConfigService) => ({
|
|
62
|
+
publicKey: config.getOrThrow<string>('LIQPAY_PUBLIC_KEY'),
|
|
63
|
+
privateKey: config.getOrThrow<string>('LIQPAY_PRIVATE_KEY'),
|
|
64
|
+
resultUrl: config.get<string>('LIQPAY_RESULT_URL'),
|
|
65
|
+
serverUrl: config.get<string>('LIQPAY_SERVER_URL'),
|
|
66
|
+
isGlobal: true,
|
|
67
|
+
}),
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
})
|
|
71
|
+
export class AppModule {}
|
|
13
72
|
```
|
|
14
73
|
|
|
15
|
-
|
|
74
|
+
### Inject and use `LiqpayService`
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { Controller, Get, Query } from '@nestjs/common'
|
|
78
|
+
import { CheckoutRequest, LiqpayService } from 'liqpay-nestjs'
|
|
79
|
+
|
|
80
|
+
@Controller('payments')
|
|
81
|
+
export class PaymentsController {
|
|
82
|
+
constructor(private readonly liqpay: LiqpayService) {}
|
|
83
|
+
|
|
84
|
+
@Get('checkout-url')
|
|
85
|
+
getCheckoutUrl(@Query('orderId') orderId: string) {
|
|
86
|
+
const payload: CheckoutRequest = {
|
|
87
|
+
action: 'pay',
|
|
88
|
+
amount: 199,
|
|
89
|
+
currency: 'UAH',
|
|
90
|
+
description: 'Order payment',
|
|
91
|
+
orderId,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
url: this.liqpay.payments.getCheckoutUrl(payload),
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Typical Flow
|
|
102
|
+
|
|
103
|
+
1. Register `LiqPayModule` with your public and private keys.
|
|
104
|
+
2. Create a checkout URL or checkout form from a `CheckoutRequest`.
|
|
105
|
+
3. Point `serverUrl` to a Nest endpoint that receives the LiqPay callback envelope.
|
|
106
|
+
4. Parse the callback with `liqpay.webhooks.parseCheckoutCallback(...)`.
|
|
107
|
+
5. Optionally confirm the final state with `liqpay.payments.getPaymentStatus(orderId)`.
|
|
108
|
+
|
|
109
|
+
## Configuration
|
|
110
|
+
|
|
111
|
+
Both `LiqPayModule.forRoot(...)` and `LiqPayModule.forRootAsync(...)` resolve to the same options shape.
|
|
112
|
+
|
|
113
|
+
| Option | Type | Required | Description |
|
|
114
|
+
| ------------ | --------- | -------- | ------------------------------------------------------------------------------------ |
|
|
115
|
+
| `publicKey` | `string` | Yes | LiqPay public key. |
|
|
116
|
+
| `privateKey` | `string` | Yes | LiqPay private key used for request signing. |
|
|
117
|
+
| `resultUrl` | `string` | No | Default redirect URL after checkout. Can be overridden per request. |
|
|
118
|
+
| `serverUrl` | `string` | No | Default callback URL for LiqPay server notifications. Can be overridden per request. |
|
|
119
|
+
| `isGlobal` | `boolean` | No | Registers the Nest module as global when set to `true`. Defaults to `false`. |
|
|
120
|
+
|
|
121
|
+
The async registration path validates the resolved options at runtime and throws if `publicKey` or `privateKey` are missing or not strings.
|
|
122
|
+
|
|
123
|
+
## LiqpayService API
|
|
124
|
+
|
|
125
|
+
`LiqpayService` exposes two sub-services:
|
|
126
|
+
|
|
127
|
+
- `payments`
|
|
128
|
+
- `webhooks`
|
|
129
|
+
|
|
130
|
+
### `payments.getCheckoutUrl(payload)`
|
|
131
|
+
|
|
132
|
+
Builds a signed LiqPay checkout URL.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const url = liqpay.payments.getCheckoutUrl({
|
|
136
|
+
action: 'pay',
|
|
137
|
+
amount: 100,
|
|
138
|
+
currency: 'UAH',
|
|
139
|
+
description: 'Order #123',
|
|
140
|
+
orderId: 'order-123',
|
|
141
|
+
})
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Notes:
|
|
145
|
+
|
|
146
|
+
- This is a pure helper and does not perform an HTTP request.
|
|
147
|
+
- The library signs the payload with the configured private key.
|
|
148
|
+
- `version` is always sent as `7`.
|
|
149
|
+
- `resultUrl` and `serverUrl` fall back to module-level defaults if omitted from the payload.
|
|
150
|
+
|
|
151
|
+
### `payments.getCheckoutForm(payload, buttonText?, buttonColor?)`
|
|
152
|
+
|
|
153
|
+
Returns an HTML form string with LiqPay's `sdk-button` widget.
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const html = liqpay.payments.getCheckoutForm(
|
|
157
|
+
{
|
|
158
|
+
action: 'pay',
|
|
159
|
+
amount: 100,
|
|
160
|
+
currency: 'UAH',
|
|
161
|
+
description: 'Order #123',
|
|
162
|
+
orderId: 'order-123',
|
|
163
|
+
},
|
|
164
|
+
'Pay now',
|
|
165
|
+
'#1f9d55',
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Defaults:
|
|
170
|
+
|
|
171
|
+
- `buttonText`: `Pay`
|
|
172
|
+
- `buttonColor`: `#77CC5D`
|
|
173
|
+
|
|
174
|
+
### `payments.create(payload)`
|
|
175
|
+
|
|
176
|
+
Returns the normalized checkout payload together with the generated `checkoutUrl`.
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const checkout = liqpay.payments.create({
|
|
180
|
+
action: 'pay',
|
|
181
|
+
amount: 100,
|
|
182
|
+
currency: 'UAH',
|
|
183
|
+
description: 'Order #123',
|
|
184
|
+
orderId: 'order-123',
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
console.log(checkout.checkoutUrl)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Use this when you want both the prepared payload and the final redirect URL without making a network call.
|
|
191
|
+
|
|
192
|
+
### `payments.getPaymentStatus(orderId)`
|
|
193
|
+
|
|
194
|
+
Calls LiqPay's status API and returns `Promise<Result<PaymentStatusResponse>>`.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
const result = await liqpay.payments.getPaymentStatus('order-123')
|
|
198
|
+
|
|
199
|
+
if (result.error) {
|
|
200
|
+
console.error(result.error.code, result.error.description)
|
|
201
|
+
} else {
|
|
202
|
+
console.log(result.data.status)
|
|
203
|
+
console.log(result.data.amount)
|
|
204
|
+
console.log(result.data.orderId)
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
You only pass `orderId`; the library builds the `status` request internally.
|
|
209
|
+
|
|
210
|
+
### `webhooks.parseCheckoutCallback(envelope)`
|
|
211
|
+
|
|
212
|
+
Validates the callback signature, decodes the Base64 payload, and parses it into `Promise<Result<CheckoutCallback>>`.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { Body, Controller, Post } from '@nestjs/common'
|
|
216
|
+
import { LiqPayEnvelope, LiqpayService } from 'liqpay-nestjs'
|
|
217
|
+
|
|
218
|
+
@Controller('payments')
|
|
219
|
+
export class WebhookController {
|
|
220
|
+
constructor(private readonly liqpay: LiqpayService) {}
|
|
221
|
+
|
|
222
|
+
@Post('webhook')
|
|
223
|
+
async handleWebhook(@Body() envelope: LiqPayEnvelope) {
|
|
224
|
+
const result = await this.liqpay.webhooks.parseCheckoutCallback(envelope)
|
|
225
|
+
|
|
226
|
+
if (result.error) {
|
|
227
|
+
return { ok: false, error: result.error }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return { ok: true, callback: result.data }
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The expected envelope shape is:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
type LiqPayEnvelope = {
|
|
239
|
+
data: string
|
|
240
|
+
signature: string
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Request and Response Normalization
|
|
245
|
+
|
|
246
|
+
The library normalizes the most common application-facing models and handles LiqPay's wire format for you.
|
|
247
|
+
|
|
248
|
+
- Top-level checkout and status request fields use the expected TypeScript property names, for example `orderId`, `resultUrl`, `serverUrl`, and `senderFirstName`.
|
|
249
|
+
- Some nested LiqPay-specific helper objects keep the exact field names defined by their exported schemas. When in doubt, validate against the schema you are using.
|
|
250
|
+
- Outgoing requests are serialized into the format LiqPay expects.
|
|
251
|
+
- Callback and status responses are transformed back to camelCase.
|
|
252
|
+
- Dates, booleans, and several enum-backed fields are normalized where possible.
|
|
253
|
+
- Checkout signing always uses the module's configured public and private keys.
|
|
254
|
+
|
|
255
|
+
## Minimal Checkout Payload
|
|
256
|
+
|
|
257
|
+
The smallest useful `CheckoutRequest` usually looks like this:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { CheckoutRequest } from 'liqpay-nestjs'
|
|
261
|
+
|
|
262
|
+
const payload: CheckoutRequest = {
|
|
263
|
+
action: 'pay',
|
|
264
|
+
amount: 100,
|
|
265
|
+
currency: 'UAH',
|
|
266
|
+
description: 'Order #123',
|
|
267
|
+
orderId: 'order-123',
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
`CheckoutRequest` also supports many LiqPay-specific fields for advanced scenarios, including:
|
|
272
|
+
|
|
273
|
+
- recurring payments: `subscribe`, `subscribeDateStart`, `subscribePeriodicity`
|
|
274
|
+
- one-click and tokenized flows: `cardToken`, `customer`, `customerUserId`, `recurringbytoken`
|
|
275
|
+
- fiscalization: `rroInfo`, `dae`
|
|
276
|
+
- split payments: `splitRules`
|
|
277
|
+
- customer and sender metadata: `ip`, `senderFirstName`, `senderLastName`, `senderAddress`, and related fields
|
|
278
|
+
|
|
279
|
+
## Schemas and Types
|
|
280
|
+
|
|
281
|
+
All public schemas and types are exported from the package root, so you can import them directly from `liqpay-nestjs`.
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
import {
|
|
285
|
+
CheckoutCallback,
|
|
286
|
+
CheckoutCallbackSchema,
|
|
287
|
+
CheckoutRequest,
|
|
288
|
+
CheckoutRequestSchema,
|
|
289
|
+
PaymentStatusResponse,
|
|
290
|
+
PaymentStatusResponseSchema,
|
|
291
|
+
Result,
|
|
292
|
+
} from 'liqpay-nestjs'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Main export groups:
|
|
296
|
+
|
|
297
|
+
- base: `LiqPayEnvelope`, `LiqPayEnvelopeSchema`, `Result<T>`, request and response base unions
|
|
298
|
+
- checkout: `CheckoutRequest`, `CheckoutRequestSchema`, `CheckoutCallback`, `CheckoutCallbackSchema`
|
|
299
|
+
- payment status: `PaymentStatusRequest`, `PaymentStatusRequestSchema`, `PaymentStatusResponse`, `PaymentStatusResponseSchema`
|
|
300
|
+
- common: `DetailAddenda`, `FiscalData`, `SplitRule` and their schemas
|
|
301
|
+
- enums: action, currency, language, paytype, payment status, version, and related schema exports
|
|
302
|
+
- error: LiqPay error response and error code schemas and types
|
|
303
|
+
- nest: `LiqPayModule`, `LiqpayService`, `LiqPayOptions`, `LiqPayAsyncOptions`, `LIQPAY_OPTIONS`
|
|
304
|
+
|
|
305
|
+
### Validating your own DTOs with the exported schemas
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
import { CheckoutRequestSchema, LiqpayService } from 'liqpay-nestjs'
|
|
309
|
+
|
|
310
|
+
const payload = CheckoutRequestSchema.parse(input)
|
|
311
|
+
const checkout = liqpay.payments.create(payload)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
This is useful if you want to validate incoming controller data before passing it to the service.
|
|
315
|
+
|
|
316
|
+
## Result Contract and Error Handling
|
|
317
|
+
|
|
318
|
+
Methods that parse LiqPay responses use this result shape:
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
type Result<T> =
|
|
322
|
+
| { data: T; error?: null }
|
|
323
|
+
| { data: null; error: { code: string; description: string } }
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The library can return:
|
|
327
|
+
|
|
328
|
+
- LiqPay provider errors parsed from `err_code` and `err_description`
|
|
329
|
+
- internal parsing or transport errors such as `invalid_signature`, `decode_error`, `validation_error`, `invalid_response`, and `http_error`
|
|
330
|
+
|
|
331
|
+
Always check `result.error` before using `result.data`.
|
|
332
|
+
|
|
333
|
+
## Advanced Nest Exports
|
|
334
|
+
|
|
335
|
+
The package root also exports:
|
|
336
|
+
|
|
337
|
+
- `LIQPAY_OPTIONS`
|
|
338
|
+
- `createLiqpayOptionsProvider(...)`
|
|
339
|
+
- `createLiqpayAsyncOptionsProvider(...)`
|
|
340
|
+
|
|
341
|
+
These helpers are useful if you want to compose your own providers around the package token instead of using `LiqPayModule` directly.
|
|
342
|
+
|
|
343
|
+
## Build
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
npm run build
|
|
347
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liqpay-nestjs",
|
|
3
3
|
"description": "LiqPay integration module for NestJS with support for payments, callbacks, and configuration via DI.",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.17",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"main": "dist/index.js",
|