@smartsoft001/payu 2.76.0 → 2.80.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/index.js ADDED
@@ -0,0 +1,188 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+
13
+ // packages/shared/payu/src/lib/payu.service.ts
14
+ import { Injectable, Logger } from "@nestjs/common";
15
+
16
+ // packages/shared/payu/src/lib/payu.config.ts
17
+ var PayuConfig = class {
18
+ };
19
+ var PAYU_CONFIG_PROVIDER = "PAYU_CONFIG_PROVIDER";
20
+ var IPayuConfigProvider = class {
21
+ };
22
+
23
+ // packages/shared/payu/src/lib/payu.service.ts
24
+ var PayuService = class {
25
+ constructor(httpService, config, moduleRef) {
26
+ this.httpService = httpService;
27
+ this.config = config;
28
+ this.moduleRef = moduleRef;
29
+ }
30
+ async create(obj) {
31
+ const config = await this.getConfig(obj.data);
32
+ const token = await this.getToken(config);
33
+ const data = {
34
+ customerIp: obj.clientIp,
35
+ extOrderId: obj.id,
36
+ merchantPosId: config.posId,
37
+ description: obj.name,
38
+ currencyCode: "PLN",
39
+ totalAmount: obj.amount,
40
+ notifyUrl: config.notifyUrl,
41
+ continueUrl: config.continueUrl,
42
+ products: [
43
+ {
44
+ name: obj.name,
45
+ unitPrice: obj.amount,
46
+ quantity: "1"
47
+ }
48
+ ]
49
+ };
50
+ if (obj.options && obj.options["payMethod"]) {
51
+ data["payMethods"] = {
52
+ payMethod: obj.options["payMethod"]
53
+ };
54
+ }
55
+ if (obj.contactPhone || obj.email || obj.firstName || obj.lastName) {
56
+ data["buyer"] = {
57
+ email: obj.email,
58
+ phone: obj.contactPhone,
59
+ firstName: obj.firstName,
60
+ lastName: obj.lastName
61
+ };
62
+ }
63
+ try {
64
+ await this.httpService.post(this.getBaseUrl(config) + "/api/v2_1/orders", data, {
65
+ headers: {
66
+ "Content-Type": "application/json",
67
+ Authorization: "Bearer " + token,
68
+ "X-Requested-With": "XMLHttpRequest"
69
+ },
70
+ maxRedirects: 0
71
+ }).toPromise();
72
+ return null;
73
+ } catch (e) {
74
+ if (e.response && e.response.status === 302) {
75
+ return {
76
+ redirectUrl: e.response.data.redirectUri,
77
+ orderId: e.response.data.orderId
78
+ };
79
+ }
80
+ console.error(e);
81
+ throw e;
82
+ }
83
+ }
84
+ async getStatus(trans) {
85
+ const orderId = this.getOrderId(trans);
86
+ const config = await this.getConfig(trans.data);
87
+ const token = await this.getToken(config);
88
+ const response = await this.httpService.get(this.getBaseUrl(config) + "/api/v2_1/orders/" + orderId, {
89
+ headers: {
90
+ "Content-Type": "application/json",
91
+ Authorization: "Bearer " + token,
92
+ "X-Requested-With": "XMLHttpRequest"
93
+ },
94
+ maxRedirects: 0
95
+ }).toPromise();
96
+ if (!response.data.orders)
97
+ return null;
98
+ const order = response.data.orders[0];
99
+ return {
100
+ status: this.getStatusFromExternal(order.status),
101
+ data: order
102
+ };
103
+ }
104
+ async refund(trans, comment) {
105
+ const orderId = this.getOrderId(trans);
106
+ const config = await this.getConfig(trans.data);
107
+ const token = await this.getToken(config);
108
+ const response = await this.httpService.post(
109
+ this.getBaseUrl(config) + "/api/v2_1/orders/" + orderId,
110
+ {
111
+ refund: {
112
+ description: comment
113
+ }
114
+ },
115
+ {
116
+ headers: {
117
+ "Content-Type": "application/json",
118
+ Authorization: "Bearer " + token,
119
+ "X-Requested-With": "XMLHttpRequest"
120
+ },
121
+ maxRedirects: 0
122
+ }
123
+ ).toPromise();
124
+ return response.data;
125
+ }
126
+ getOrderId(trans) {
127
+ const historyItem = trans.history.find((x) => x.status === "started");
128
+ if (!historyItem) {
129
+ console.warn("Transaction without start status");
130
+ return null;
131
+ }
132
+ return historyItem.data.orderId;
133
+ }
134
+ async getToken(config) {
135
+ try {
136
+ const response = await this.httpService.post(
137
+ this.getBaseUrl(config) + "/pl/standard/user/oauth/authorize",
138
+ `grant_type=client_credentials&client_id=${config.clientId}&client_secret=${config.clientSecret}`
139
+ ).toPromise();
140
+ return response.data["access_token"];
141
+ } catch (e) {
142
+ console.error({
143
+ url: this.getBaseUrl(config) + "/pl/standard/user/oauth/authorize",
144
+ data: `grant_type=client_credentials&client_id=${config.clientId}&client_secret=${config.clientSecret}`,
145
+ ex: e
146
+ });
147
+ throw e;
148
+ }
149
+ }
150
+ async getConfig(data) {
151
+ try {
152
+ const provider = this.moduleRef.get(
153
+ PAYU_CONFIG_PROVIDER,
154
+ { strict: false }
155
+ );
156
+ return await provider.get(data);
157
+ } catch (e) {
158
+ Logger.warn("PayPal config provider not found", PayuService.name);
159
+ }
160
+ return this.config;
161
+ }
162
+ getBaseUrl(config) {
163
+ if (config.test)
164
+ return "https://secure.snd.payu.com";
165
+ return "https://secure.payu.com";
166
+ }
167
+ getStatusFromExternal(status) {
168
+ switch (status) {
169
+ case "COMPLETED":
170
+ return "completed";
171
+ case "CANCELED":
172
+ return "canceled";
173
+ case "PENDING":
174
+ return "pending";
175
+ default:
176
+ return status;
177
+ }
178
+ }
179
+ };
180
+ PayuService = __decorateClass([
181
+ Injectable()
182
+ ], PayuService);
183
+ export {
184
+ IPayuConfigProvider,
185
+ PAYU_CONFIG_PROVIDER,
186
+ PayuConfig,
187
+ PayuService
188
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartsoft001/payu",
3
- "version": "2.76.0",
3
+ "version": "2.80.0",
4
4
  "description": "Utils for payu",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,12 @@
1
+ export declare class PayuConfig {
2
+ test?: boolean;
3
+ clientId: string;
4
+ clientSecret: string;
5
+ notifyUrl: string;
6
+ continueUrl: string;
7
+ posId: string;
8
+ }
9
+ export declare const PAYU_CONFIG_PROVIDER = "PAYU_CONFIG_PROVIDER";
10
+ export declare abstract class IPayuConfigProvider {
11
+ abstract get(data: any): Promise<PayuConfig>;
12
+ }
@@ -0,0 +1,35 @@
1
+ import { HttpService } from '@nestjs/axios';
2
+ import { ModuleRef } from '@nestjs/core';
3
+ import { ITransPaymentSingleService, Trans, TransStatus } from '@smartsoft001/trans-domain';
4
+ import { PayuConfig } from './payu.config';
5
+ export declare class PayuService implements ITransPaymentSingleService {
6
+ private readonly httpService;
7
+ private config;
8
+ private moduleRef;
9
+ constructor(httpService: HttpService, config: PayuConfig, moduleRef: ModuleRef);
10
+ create(obj: {
11
+ id: string;
12
+ name: string;
13
+ amount: number;
14
+ firstName?: string;
15
+ lastName?: string;
16
+ email?: string;
17
+ contactPhone?: string;
18
+ clientIp: string;
19
+ data: any;
20
+ options?: any;
21
+ }): Promise<{
22
+ orderId: string;
23
+ redirectUrl: string;
24
+ }>;
25
+ getStatus<T>(trans: Trans<T>): Promise<{
26
+ status: TransStatus;
27
+ data: any;
28
+ }>;
29
+ refund(trans: Trans<any>, comment: string): Promise<any>;
30
+ private getOrderId;
31
+ private getToken;
32
+ private getConfig;
33
+ private getBaseUrl;
34
+ private getStatusFromExternal;
35
+ }
package/.eslintrc DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "../../../.eslintrc.json",
3
- "ignorePatterns": ["!**/*"],
4
- "rules": {
5
-
6
- },
7
- "overrides": [
8
- {
9
- "files": ["*.json"],
10
- "parser": "jsonc-eslint-parser"
11
- }
12
- ]
13
- }
package/jest.config.ts DELETED
@@ -1,27 +0,0 @@
1
- /* eslint-disable */
2
- export default {
3
- displayName: 'shared-payu',
4
- globals: {},
5
- testEnvironment: 'node',
6
- transform: {
7
- '^.+\\.[tj]sx?$': [
8
- 'ts-jest',
9
- {
10
- tsConfig: '<rootDir>/tsconfig.spec.json',
11
- },
12
- ],
13
- },
14
- moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
15
- coverageDirectory: '../../../coverage/libs/shared/payu',
16
- preset: '../../../jest.preset.js',
17
- /* TODO: Update to latest Jest snapshotFormat
18
- * By default Nx has kept the older style of Jest Snapshot formats
19
- * to prevent breaking of any existing tests with snapshots.
20
- * It's recommend you update to the latest format.
21
- * You can do this by removing snapshotFormat property
22
- * and running tests with --update-snapshot flag.
23
- * Example: From within the project directory, run "nx test --update-snapshot"
24
- * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
25
- */
26
- snapshotFormat: { escapeString: true, printBasicPrototype: true },
27
- };
package/project.json DELETED
@@ -1,37 +0,0 @@
1
- {
2
- "name": "shared-payu",
3
- "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4
- "sourceRoot": "packages/shared/payu/src",
5
- "projectType": "library",
6
- "tags": ["scope:shared", "type:util"],
7
- "generators": {},
8
- "targets": {
9
- "lint": {
10
- "executor": "@nx/eslint:lint",
11
- "options": {
12
- "lintFilePatterns": [
13
- "packages/shared/payu/**/*.{ts,tsx,js,jsx}",
14
- "packages/shared/payu/package.json"
15
- ]
16
- }
17
- },
18
- "test": {
19
- "executor": "@nx/jest:jest",
20
- "options": {
21
- "jestConfig": "packages/shared/payu/jest.config.ts"
22
- },
23
- "outputs": ["{workspaceRoot}/coverage/packages/shared/payu"]
24
- },
25
- "build": {
26
- "executor": "@nx/esbuild:esbuild",
27
- "options": {
28
- "outputPath": "dist/packages/shared/payu",
29
- "tsConfig": "packages/shared/payu/tsconfig.lib.json",
30
- "packageJson": "packages/shared/payu/package.json",
31
- "main": "packages/shared/payu/src/index.ts",
32
- "assets": ["packages/shared/payu/*.md"]
33
- },
34
- "outputs": ["{options.outputPath}"]
35
- }
36
- }
37
- }
@@ -1,14 +0,0 @@
1
- export class PayuConfig {
2
- test?: boolean;
3
- clientId: string;
4
- clientSecret: string;
5
- notifyUrl: string;
6
- continueUrl: string;
7
- posId: string;
8
- }
9
-
10
- export const PAYU_CONFIG_PROVIDER = 'PAYU_CONFIG_PROVIDER';
11
-
12
- export abstract class IPayuConfigProvider {
13
- abstract get(data: any): Promise<PayuConfig>;
14
- }
@@ -1,483 +0,0 @@
1
- import { HttpService } from '@nestjs/axios';
2
- import { ModuleRef } from '@nestjs/core';
3
- import { Test, TestingModule } from '@nestjs/testing';
4
- import { of, throwError } from 'rxjs';
5
-
6
- import { PayuConfig } from './payu.config';
7
- import { PayuService } from './payu.service';
8
-
9
- const mockHttpService = {
10
- post: jest.fn(),
11
- get: jest.fn(),
12
- };
13
-
14
- const mockModuleRef = {
15
- get: jest.fn(),
16
- };
17
-
18
- const mockPayuConfig = {
19
- posId: 'mock-pos-id',
20
- clientId: 'mock-client-id',
21
- clientSecret: 'mock-client-secret',
22
- notifyUrl: 'https://mock.notify.url',
23
- continueUrl: 'https://mock.continue.url',
24
- test: true,
25
- };
26
-
27
- describe('payu: PayuService', () => {
28
- let service: PayuService;
29
-
30
- beforeEach(async () => {
31
- const module: TestingModule = await Test.createTestingModule({
32
- providers: [
33
- PayuService,
34
- { provide: PayuConfig, useValue: mockPayuConfig },
35
- { provide: HttpService, useValue: mockHttpService },
36
- { provide: ModuleRef, useValue: mockModuleRef },
37
- ],
38
- }).compile();
39
-
40
- service = module.get<PayuService>(PayuService);
41
- });
42
-
43
- afterEach(() => {
44
- jest.clearAllMocks();
45
- });
46
-
47
- describe('create', () => {
48
- it('should obtain OAuth token before creating payment', async () => {
49
- const mockTokenResponse = {
50
- data: {
51
- access_token: 'mock-token',
52
- },
53
- };
54
- const mockErrorResponse = {
55
- response: {
56
- status: 302,
57
- data: {
58
- redirectUri: 'https://redirect.url',
59
- orderId: 'test-order-id',
60
- },
61
- },
62
- };
63
-
64
- mockHttpService.post
65
- .mockReturnValueOnce(of(mockTokenResponse))
66
- .mockReturnValueOnce(throwError(() => mockErrorResponse));
67
-
68
- await service.create({
69
- id: 'test-id',
70
- name: 'test-name',
71
- amount: 100,
72
- email: 'test@example.com',
73
- clientIp: '127.0.0.1',
74
- data: {},
75
- });
76
-
77
- const firstCall = mockHttpService.post.mock.calls[0];
78
- expect(firstCall[0]).toBe(
79
- 'https://secure.snd.payu.com/pl/standard/user/oauth/authorize',
80
- );
81
- expect(firstCall[1]).toBe(
82
- 'grant_type=client_credentials&client_id=mock-client-id&client_secret=mock-client-secret',
83
- );
84
- });
85
-
86
- it('should create payment with correct request body', async () => {
87
- const mockTokenResponse = {
88
- data: {
89
- access_token: 'mock-token',
90
- },
91
- };
92
- const mockErrorResponse = {
93
- response: {
94
- status: 302,
95
- data: {
96
- redirectUri: 'https://redirect.url',
97
- orderId: 'test-order-id',
98
- },
99
- },
100
- };
101
-
102
- mockHttpService.post
103
- .mockReturnValueOnce(of(mockTokenResponse))
104
- .mockReturnValueOnce(throwError(() => mockErrorResponse));
105
-
106
- await service.create({
107
- id: 'test-id',
108
- name: 'test-name',
109
- amount: 100,
110
- email: 'test@example.com',
111
- clientIp: '127.0.0.1',
112
- data: {},
113
- });
114
-
115
- expect(mockHttpService.post.mock.calls[1][1]).toEqual({
116
- customerIp: '127.0.0.1',
117
- extOrderId: 'test-id',
118
- merchantPosId: 'mock-pos-id',
119
- description: 'test-name',
120
- currencyCode: 'PLN',
121
- totalAmount: 100,
122
- notifyUrl: 'https://mock.notify.url',
123
- continueUrl: 'https://mock.continue.url',
124
- products: [
125
- {
126
- name: 'test-name',
127
- unitPrice: 100,
128
- quantity: '1',
129
- },
130
- ],
131
- buyer: {
132
- email: 'test@example.com',
133
- },
134
- });
135
- });
136
-
137
- it('should create payment with correct headers', async () => {
138
- const mockTokenResponse = {
139
- data: {
140
- access_token: 'mock-token',
141
- },
142
- };
143
- const mockErrorResponse = {
144
- response: {
145
- status: 302,
146
- data: {
147
- redirectUri: 'https://redirect.url',
148
- orderId: 'test-order-id',
149
- },
150
- },
151
- };
152
-
153
- mockHttpService.post
154
- .mockReturnValueOnce(of(mockTokenResponse))
155
- .mockReturnValueOnce(throwError(() => mockErrorResponse));
156
-
157
- await service.create({
158
- id: 'test-id',
159
- name: 'test-name',
160
- amount: 100,
161
- email: 'test@example.com',
162
- clientIp: '127.0.0.1',
163
- data: {},
164
- });
165
-
166
- expect(mockHttpService.post.mock.calls[1][2]).toEqual({
167
- headers: {
168
- Authorization: 'Bearer mock-token',
169
- 'Content-Type': 'application/json',
170
- 'X-Requested-With': 'XMLHttpRequest',
171
- },
172
- maxRedirects: 0,
173
- });
174
- });
175
-
176
- it('should return redirect URL and order ID on successful payment creation', async () => {
177
- const mockTokenResponse = {
178
- data: {
179
- access_token: 'mock-token',
180
- },
181
- };
182
- const mockErrorResponse = {
183
- response: {
184
- status: 302,
185
- data: {
186
- redirectUri: 'https://redirect.url',
187
- orderId: 'test-order-id',
188
- },
189
- },
190
- };
191
-
192
- mockHttpService.post
193
- .mockReturnValueOnce(of(mockTokenResponse))
194
- .mockReturnValueOnce(throwError(() => mockErrorResponse));
195
-
196
- const result = await service.create({
197
- id: 'test-id',
198
- name: 'test-name',
199
- amount: 100,
200
- email: 'test@example.com',
201
- clientIp: '127.0.0.1',
202
- data: {},
203
- });
204
-
205
- expect(result).toEqual({
206
- orderId: 'test-order-id',
207
- redirectUrl: 'https://redirect.url',
208
- });
209
- });
210
- });
211
-
212
- describe('getStatus', () => {
213
- it('should obtain OAuth token before checking status', async () => {
214
- const mockTokenResponse = {
215
- data: {
216
- access_token: 'mock-token',
217
- },
218
- };
219
- const mockStatusResponse = {
220
- data: {
221
- orders: [
222
- {
223
- status: 'COMPLETED',
224
- orderId: 'test-order-id',
225
- },
226
- ],
227
- },
228
- };
229
-
230
- mockHttpService.post.mockReturnValueOnce(of(mockTokenResponse));
231
- mockHttpService.get.mockReturnValueOnce(of(mockStatusResponse));
232
-
233
- await service.getStatus({
234
- data: {},
235
- history: [
236
- {
237
- status: 'started',
238
- data: { orderId: 'test-order-id' },
239
- },
240
- ],
241
- } as any);
242
-
243
- const firstCall = mockHttpService.post.mock.calls[0];
244
- expect(firstCall[0]).toBe(
245
- 'https://secure.snd.payu.com/pl/standard/user/oauth/authorize',
246
- );
247
- expect(firstCall[1]).toBe(
248
- 'grant_type=client_credentials&client_id=mock-client-id&client_secret=mock-client-secret',
249
- );
250
- });
251
-
252
- it('should check status with correct headers', async () => {
253
- const mockTokenResponse = {
254
- data: {
255
- access_token: 'mock-token',
256
- },
257
- };
258
- const mockStatusResponse = {
259
- data: {
260
- orders: [
261
- {
262
- status: 'COMPLETED',
263
- orderId: 'test-order-id',
264
- },
265
- ],
266
- },
267
- };
268
-
269
- mockHttpService.post.mockReturnValueOnce(of(mockTokenResponse));
270
- mockHttpService.get.mockReturnValueOnce(of(mockStatusResponse));
271
-
272
- await service.getStatus({
273
- data: {},
274
- history: [
275
- {
276
- status: 'started',
277
- data: { orderId: 'test-order-id' },
278
- },
279
- ],
280
- } as any);
281
-
282
- const getCall = mockHttpService.get.mock.calls[0];
283
- expect(getCall[1]).toEqual({
284
- headers: {
285
- Authorization: 'Bearer mock-token',
286
- 'Content-Type': 'application/json',
287
- 'X-Requested-With': 'XMLHttpRequest',
288
- },
289
- maxRedirects: 0,
290
- });
291
- });
292
-
293
- it('should return correct status and data', async () => {
294
- const mockTokenResponse = {
295
- data: {
296
- access_token: 'mock-token',
297
- },
298
- };
299
- const mockStatusResponse = {
300
- data: {
301
- orders: [
302
- {
303
- status: 'COMPLETED',
304
- orderId: 'test-order-id',
305
- },
306
- ],
307
- },
308
- };
309
-
310
- mockHttpService.post.mockReturnValueOnce(of(mockTokenResponse));
311
- mockHttpService.get.mockReturnValueOnce(of(mockStatusResponse));
312
-
313
- const result = await service.getStatus({
314
- data: {},
315
- history: [
316
- {
317
- status: 'started',
318
- data: { orderId: 'test-order-id' },
319
- },
320
- ],
321
- } as any);
322
-
323
- expect(result).toEqual({
324
- status: 'completed',
325
- data: {
326
- status: 'COMPLETED',
327
- orderId: 'test-order-id',
328
- },
329
- });
330
- });
331
- });
332
-
333
- describe('refund', () => {
334
- it('should obtain OAuth token before processing refund', async () => {
335
- const mockTokenResponse = {
336
- data: {
337
- access_token: 'mock-token',
338
- },
339
- };
340
- const mockRefundResponse = {
341
- data: {
342
- refundId: 'test-refund-id',
343
- },
344
- };
345
-
346
- mockHttpService.post
347
- .mockReturnValueOnce(of(mockTokenResponse))
348
- .mockReturnValueOnce(of(mockRefundResponse));
349
-
350
- await service.refund(
351
- {
352
- data: {},
353
- history: [
354
- {
355
- status: 'started',
356
- data: { orderId: 'test-order-id' },
357
- },
358
- ],
359
- } as any,
360
- 'test comment',
361
- );
362
-
363
- const firstCall = mockHttpService.post.mock.calls[0];
364
- expect(firstCall[0]).toBe(
365
- 'https://secure.snd.payu.com/pl/standard/user/oauth/authorize',
366
- );
367
- expect(firstCall[1]).toBe(
368
- 'grant_type=client_credentials&client_id=mock-client-id&client_secret=mock-client-secret',
369
- );
370
- });
371
-
372
- it('should process refund with correct request body', async () => {
373
- const mockTokenResponse = {
374
- data: {
375
- access_token: 'mock-token',
376
- },
377
- };
378
- const mockRefundResponse = {
379
- data: {
380
- refundId: 'test-refund-id',
381
- },
382
- };
383
-
384
- mockHttpService.post
385
- .mockReturnValueOnce(of(mockTokenResponse))
386
- .mockReturnValueOnce(of(mockRefundResponse));
387
-
388
- await service.refund(
389
- {
390
- data: {},
391
- history: [
392
- {
393
- status: 'started',
394
- data: { orderId: 'test-order-id' },
395
- },
396
- ],
397
- } as any,
398
- 'test comment',
399
- );
400
-
401
- const secondCall = mockHttpService.post.mock.calls[1];
402
- expect(secondCall[1]).toEqual({
403
- refund: {
404
- description: 'test comment',
405
- },
406
- });
407
- });
408
-
409
- it('should process refund with correct headers', async () => {
410
- const mockTokenResponse = {
411
- data: {
412
- access_token: 'mock-token',
413
- },
414
- };
415
- const mockRefundResponse = {
416
- data: {
417
- refundId: 'test-refund-id',
418
- },
419
- };
420
-
421
- mockHttpService.post
422
- .mockReturnValueOnce(of(mockTokenResponse))
423
- .mockReturnValueOnce(of(mockRefundResponse));
424
-
425
- await service.refund(
426
- {
427
- data: {},
428
- history: [
429
- {
430
- status: 'started',
431
- data: { orderId: 'test-order-id' },
432
- },
433
- ],
434
- } as any,
435
- 'test comment',
436
- );
437
-
438
- const secondCall = mockHttpService.post.mock.calls[1];
439
- expect(secondCall[2]).toEqual({
440
- headers: {
441
- Authorization: 'Bearer mock-token',
442
- 'Content-Type': 'application/json',
443
- 'X-Requested-With': 'XMLHttpRequest',
444
- },
445
- maxRedirects: 0,
446
- });
447
- });
448
-
449
- it('should return refund response data', async () => {
450
- const mockTokenResponse = {
451
- data: {
452
- access_token: 'mock-token',
453
- },
454
- };
455
- const mockRefundResponse = {
456
- data: {
457
- refundId: 'test-refund-id',
458
- },
459
- };
460
-
461
- mockHttpService.post
462
- .mockReturnValueOnce(of(mockTokenResponse))
463
- .mockReturnValueOnce(of(mockRefundResponse));
464
-
465
- const result = await service.refund(
466
- {
467
- data: {},
468
- history: [
469
- {
470
- status: 'started',
471
- data: { orderId: 'test-order-id' },
472
- },
473
- ],
474
- } as any,
475
- 'test comment',
476
- );
477
-
478
- expect(result).toEqual({
479
- refundId: 'test-refund-id',
480
- });
481
- });
482
- });
483
- });
@@ -1,219 +0,0 @@
1
- import { HttpService } from '@nestjs/axios';
2
- import { Inject, Injectable, Logger, Optional } from '@nestjs/common';
3
- import { ModuleRef } from '@nestjs/core';
4
-
5
- import {
6
- ITransPaymentSingleService,
7
- Trans,
8
- TransStatus,
9
- } from '@smartsoft001/trans-domain';
10
-
11
- import {
12
- IPayuConfigProvider,
13
- PAYU_CONFIG_PROVIDER,
14
- PayuConfig,
15
- } from './payu.config';
16
-
17
- @Injectable()
18
- export class PayuService implements ITransPaymentSingleService {
19
- constructor(
20
- private readonly httpService: HttpService,
21
- private config: PayuConfig,
22
- private moduleRef: ModuleRef,
23
- ) {}
24
-
25
- async create(obj: {
26
- id: string;
27
- name: string;
28
- amount: number;
29
- firstName?: string;
30
- lastName?: string;
31
- email?: string;
32
- contactPhone?: string;
33
- clientIp: string;
34
- data: any;
35
- options?: any;
36
- }): Promise<{ orderId: string; redirectUrl: string }> {
37
- const config = await this.getConfig(obj.data);
38
- const token = await this.getToken(config);
39
-
40
- const data = {
41
- customerIp: obj.clientIp,
42
- extOrderId: obj.id,
43
- merchantPosId: config.posId,
44
- description: obj.name,
45
- currencyCode: 'PLN',
46
- totalAmount: obj.amount,
47
- notifyUrl: config.notifyUrl,
48
- continueUrl: config.continueUrl,
49
- products: [
50
- {
51
- name: obj.name,
52
- unitPrice: obj.amount,
53
- quantity: '1',
54
- },
55
- ],
56
- };
57
-
58
- if (obj.options && obj.options['payMethod']) {
59
- data['payMethods'] = {
60
- payMethod: obj.options['payMethod'],
61
- };
62
- }
63
-
64
- if (obj.contactPhone || obj.email || obj.firstName || obj.lastName) {
65
- data['buyer'] = {
66
- email: obj.email,
67
- phone: obj.contactPhone,
68
- firstName: obj.firstName,
69
- lastName: obj.lastName,
70
- };
71
- }
72
-
73
- try {
74
- await this.httpService
75
- .post(this.getBaseUrl(config) + '/api/v2_1/orders', data, {
76
- headers: {
77
- 'Content-Type': 'application/json',
78
- Authorization: 'Bearer ' + token,
79
- 'X-Requested-With': 'XMLHttpRequest',
80
- },
81
- maxRedirects: 0,
82
- })
83
- .toPromise();
84
-
85
- return null;
86
- } catch (e) {
87
- if (e.response && e.response.status === 302) {
88
- return {
89
- redirectUrl: e.response.data.redirectUri,
90
- orderId: e.response.data.orderId,
91
- };
92
- }
93
- console.error(e);
94
- throw e;
95
- }
96
- }
97
-
98
- async getStatus<T>(
99
- trans: Trans<T>,
100
- ): Promise<{ status: TransStatus; data: any }> {
101
- const orderId = this.getOrderId(trans);
102
- const config = await this.getConfig(trans.data);
103
-
104
- const token = await this.getToken(config);
105
-
106
- const response = await this.httpService
107
- .get(this.getBaseUrl(config) + '/api/v2_1/orders/' + orderId, {
108
- headers: {
109
- 'Content-Type': 'application/json',
110
- Authorization: 'Bearer ' + token,
111
- 'X-Requested-With': 'XMLHttpRequest',
112
- },
113
- maxRedirects: 0,
114
- })
115
- .toPromise();
116
-
117
- if (!response.data.orders) return null;
118
-
119
- const order = response.data.orders[0];
120
-
121
- return {
122
- status: this.getStatusFromExternal(order.status),
123
- data: order,
124
- };
125
- }
126
-
127
- async refund(trans: Trans<any>, comment: string): Promise<any> {
128
- const orderId = this.getOrderId(trans);
129
- const config = await this.getConfig(trans.data);
130
-
131
- const token = await this.getToken(config);
132
-
133
- const response = await this.httpService
134
- .post(
135
- this.getBaseUrl(config) + '/api/v2_1/orders/' + orderId,
136
- {
137
- refund: {
138
- description: comment,
139
- },
140
- },
141
- {
142
- headers: {
143
- 'Content-Type': 'application/json',
144
- Authorization: 'Bearer ' + token,
145
- 'X-Requested-With': 'XMLHttpRequest',
146
- },
147
- maxRedirects: 0,
148
- },
149
- )
150
- .toPromise();
151
-
152
- return response.data;
153
- }
154
-
155
- private getOrderId(trans: Trans<any>): string {
156
- const historyItem = trans.history.find((x) => x.status === 'started');
157
-
158
- if (!historyItem) {
159
- console.warn('Transaction without start status');
160
- return null;
161
- }
162
-
163
- return historyItem.data.orderId;
164
- }
165
-
166
- private async getToken(config: PayuConfig): Promise<string> {
167
- try {
168
- const response = await this.httpService
169
- .post(
170
- this.getBaseUrl(config) + '/pl/standard/user/oauth/authorize',
171
- `grant_type=client_credentials&client_id=${config.clientId}&client_secret=${config.clientSecret}`,
172
- )
173
- .toPromise();
174
-
175
- return response.data['access_token'];
176
- } catch (e) {
177
- console.error({
178
- url: this.getBaseUrl(config) + '/pl/standard/user/oauth/authorize',
179
- data: `grant_type=client_credentials&client_id=${config.clientId}&client_secret=${config.clientSecret}`,
180
- ex: e,
181
- });
182
-
183
- throw e;
184
- }
185
- }
186
-
187
- private async getConfig(data: any): Promise<PayuConfig> {
188
- try {
189
- const provider: IPayuConfigProvider = this.moduleRef.get(
190
- PAYU_CONFIG_PROVIDER,
191
- { strict: false },
192
- );
193
- return await provider.get(data);
194
- } catch (e) {
195
- Logger.warn('PayPal config provider not found', PayuService.name);
196
- }
197
-
198
- return this.config;
199
- }
200
-
201
- private getBaseUrl(config: PayuConfig): string {
202
- if (config.test) return 'https://secure.snd.payu.com';
203
-
204
- return 'https://secure.payu.com';
205
- }
206
-
207
- private getStatusFromExternal(status: string): any {
208
- switch (status) {
209
- case 'COMPLETED':
210
- return 'completed';
211
- case 'CANCELED':
212
- return 'canceled';
213
- case 'PENDING':
214
- return 'pending';
215
- default:
216
- return status;
217
- }
218
- }
219
- }
package/tsconfig.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.base.json",
3
- "files": [],
4
- "include": [],
5
- "references": [
6
- {
7
- "path": "./tsconfig.lib.json"
8
- },
9
- {
10
- "path": "./tsconfig.spec.json"
11
- }
12
- ]
13
- }
package/tsconfig.lib.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "module": "commonjs",
5
- "outDir": "../../../dist/out-tsc",
6
- "declaration": true,
7
- "types": ["node"]
8
- },
9
- "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"],
10
- "include": ["**/*.ts"]
11
- }
@@ -1,20 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../../dist/out-tsc",
5
- "module": "commonjs",
6
- "types": ["jest", "node"]
7
- },
8
- "include": [
9
- "**/*.spec.ts",
10
- "**/*.test.ts",
11
- "**/*.spec.tsx",
12
- "**/*.test.tsx",
13
- "**/*.spec.js",
14
- "**/*.test.js",
15
- "**/*.spec.jsx",
16
- "**/*.test.jsx",
17
- "**/*.d.ts",
18
- "jest.config.ts"
19
- ]
20
- }
File without changes
File without changes