@smartsoft001/payu 1.1.90 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +13 -0
- package/README.md +25 -4
- package/jest.config.ts +27 -0
- package/package.json +3 -29
- package/project.json +35 -0
- package/src/index.ts +2 -0
- package/src/lib/payu.config.ts +14 -0
- package/src/lib/payu.service.spec.ts +483 -0
- package/src/lib/payu.service.ts +219 -0
- package/tsconfig.json +13 -0
- package/tsconfig.lib.json +11 -0
- package/tsconfig.spec.json +20 -0
- package/src/index.d.ts +0 -2
- package/src/index.js +0 -6
- package/src/index.js.map +0 -1
- package/src/lib/payu.config.d.ts +0 -12
- package/src/lib/payu.config.js +0 -11
- package/src/lib/payu.config.js.map +0 -1
- package/src/lib/payu.service.d.ts +0 -35
- package/src/lib/payu.service.js +0 -173
- package/src/lib/payu.service.js.map +0 -1
- package/test-setup.js +0 -1
- package/test-setup.js.map +0 -1
- /package/{test-setup.d.ts → test-setup.ts} +0 -0
package/.eslintrc
ADDED
package/README.md
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 📦 @smartsoft001/payu
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
 
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 🚀 Usage
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`npm i @smartsoft001/payu`
|
|
8
|
+
|
|
9
|
+
## 🛠️ Services & Methods
|
|
10
|
+
|
|
11
|
+
### PayuService
|
|
12
|
+
|
|
13
|
+
Methods:
|
|
14
|
+
|
|
15
|
+
<table>
|
|
16
|
+
<tr>
|
|
17
|
+
<td>create</td>
|
|
18
|
+
<td>Initiates a new payment order with an external payment API</td>
|
|
19
|
+
</tr>
|
|
20
|
+
<tr>
|
|
21
|
+
<td>getStatus</td>
|
|
22
|
+
<td>Retrieves the current status of an existing payment order</td>
|
|
23
|
+
</tr>
|
|
24
|
+
<tr>
|
|
25
|
+
<td>refund</td>
|
|
26
|
+
<td>Issues a refund for an existing payment order</td>
|
|
27
|
+
</tr>
|
|
28
|
+
</table>
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartsoft001/payu",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Utils for payu",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/emiljuchnikowski/smartsoft.git"
|
|
@@ -11,31 +11,5 @@
|
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/emiljuchnikowski/smartsoft/issues"
|
|
13
13
|
},
|
|
14
|
-
"homepage": "https://github.com/emiljuchnikowski/smartsoft#readme"
|
|
15
|
-
"dependencies": {
|
|
16
|
-
"@angular/common": "16.1.3",
|
|
17
|
-
"@angular/core": "16.1.3",
|
|
18
|
-
"@angular/platform-browser-dynamic": "16.1.3",
|
|
19
|
-
"@nestjs/axios": "3.0.0",
|
|
20
|
-
"@nestjs/common": "^10.0.5",
|
|
21
|
-
"@nestjs/core": "^10.0.5",
|
|
22
|
-
"flatted": "^3.2.4",
|
|
23
|
-
"guid-typescript": "1.0.9",
|
|
24
|
-
"lodash": "^4.17.20",
|
|
25
|
-
"md5": "2.3.0",
|
|
26
|
-
"reflect-metadata": "0.1.13",
|
|
27
|
-
"rxjs": "7.8.1",
|
|
28
|
-
"typeorm": "0.3.17"
|
|
29
|
-
},
|
|
30
|
-
"peerDependencies": {
|
|
31
|
-
"@smartsoft001/domain-core": "1.1.90",
|
|
32
|
-
"@smartsoft001/models": "1.1.90",
|
|
33
|
-
"@smartsoft001/trans-domain": "1.1.90",
|
|
34
|
-
"@smartsoft001/users": "1.1.90",
|
|
35
|
-
"@smartsoft001/utils": "1.1.90",
|
|
36
|
-
"util": "0.12.5",
|
|
37
|
-
"tslib": "2.5.3"
|
|
38
|
-
},
|
|
39
|
-
"main": "./src/index.js",
|
|
40
|
-
"types": "./src/index.d.ts"
|
|
14
|
+
"homepage": "https://github.com/emiljuchnikowski/smartsoft#readme"
|
|
41
15
|
}
|
package/project.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "shared-payu",
|
|
3
|
+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "packages/shared/payu/src",
|
|
5
|
+
"projectType": "library",
|
|
6
|
+
"generators": {},
|
|
7
|
+
"targets": {
|
|
8
|
+
"lint": {
|
|
9
|
+
"executor": "@nrwl/linter:eslint",
|
|
10
|
+
"options": {
|
|
11
|
+
"lintFilePatterns": ["packages/shared/payu/**/*.ts"]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"test": {
|
|
15
|
+
"executor": "@nx/jest:jest",
|
|
16
|
+
"options": {
|
|
17
|
+
"jestConfig": "packages/shared/payu/jest.config.ts",
|
|
18
|
+
"passWithNoTests": true
|
|
19
|
+
},
|
|
20
|
+
"outputs": ["{workspaceRoot}/coverage/packages/shared/payu"]
|
|
21
|
+
},
|
|
22
|
+
"build": {
|
|
23
|
+
"executor": "@nx/esbuild:esbuild",
|
|
24
|
+
"options": {
|
|
25
|
+
"outputPath": "dist/packages/shared/payu",
|
|
26
|
+
"tsConfig": "packages/shared/payu/tsconfig.lib.json",
|
|
27
|
+
"packageJson": "packages/shared/payu/package.json",
|
|
28
|
+
"main": "packages/shared/payu/src/index.ts",
|
|
29
|
+
"assets": ["packages/shared/payu/*.md"]
|
|
30
|
+
},
|
|
31
|
+
"outputs": ["{options.outputPath}"]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"tags": ["scope:shared", "type:util"]
|
|
35
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
2
|
+
import { HttpService } from '@nestjs/axios';
|
|
3
|
+
import { ModuleRef } from '@nestjs/core';
|
|
4
|
+
import { of, throwError } from 'rxjs';
|
|
5
|
+
|
|
6
|
+
import { PayuService } from './payu.service';
|
|
7
|
+
import { PayuConfig } from './payu.config';
|
|
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
|
+
});
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { Inject, Injectable, Logger, Optional } from '@nestjs/common';
|
|
2
|
+
import { ModuleRef } from '@nestjs/core';
|
|
3
|
+
import { HttpService } from '@nestjs/axios';
|
|
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
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
}
|
package/src/index.d.ts
DELETED
package/src/index.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
tslib_1.__exportStar(require("./lib/payu.service"), exports);
|
|
5
|
-
tslib_1.__exportStar(require("./lib/payu.config"), exports);
|
|
6
|
-
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/shared/payu/src/index.ts"],"names":[],"mappings":";;;AAAA,6DAAmC;AACnC,4DAAkC"}
|
package/src/lib/payu.config.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
}
|
package/src/lib/payu.config.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.IPayuConfigProvider = exports.PAYU_CONFIG_PROVIDER = exports.PayuConfig = void 0;
|
|
4
|
-
class PayuConfig {
|
|
5
|
-
}
|
|
6
|
-
exports.PayuConfig = PayuConfig;
|
|
7
|
-
exports.PAYU_CONFIG_PROVIDER = "PAYU_CONFIG_PROVIDER";
|
|
8
|
-
class IPayuConfigProvider {
|
|
9
|
-
}
|
|
10
|
-
exports.IPayuConfigProvider = IPayuConfigProvider;
|
|
11
|
-
//# sourceMappingURL=payu.config.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"payu.config.js","sourceRoot":"","sources":["../../../../../../libs/shared/payu/src/lib/payu.config.ts"],"names":[],"mappings":";;;AAAA,MAAa,UAAU;CAOtB;AAPD,gCAOC;AAEY,QAAA,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D,MAAsB,mBAAmB;CAExC;AAFD,kDAEC"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { ModuleRef } from "@nestjs/core";
|
|
2
|
-
import { HttpService } from "@nestjs/axios";
|
|
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/src/lib/payu.service.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var PayuService_1;
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.PayuService = void 0;
|
|
5
|
-
const tslib_1 = require("tslib");
|
|
6
|
-
const common_1 = require("@nestjs/common");
|
|
7
|
-
const core_1 = require("@nestjs/core");
|
|
8
|
-
const axios_1 = require("@nestjs/axios");
|
|
9
|
-
const payu_config_1 = require("./payu.config");
|
|
10
|
-
let PayuService = exports.PayuService = PayuService_1 = class PayuService {
|
|
11
|
-
constructor(httpService, config, moduleRef) {
|
|
12
|
-
this.httpService = httpService;
|
|
13
|
-
this.config = config;
|
|
14
|
-
this.moduleRef = moduleRef;
|
|
15
|
-
}
|
|
16
|
-
async create(obj) {
|
|
17
|
-
const config = await this.getConfig(obj.data);
|
|
18
|
-
const token = await this.getToken(config);
|
|
19
|
-
const data = {
|
|
20
|
-
customerIp: obj.clientIp,
|
|
21
|
-
extOrderId: obj.id,
|
|
22
|
-
merchantPosId: config.posId,
|
|
23
|
-
description: obj.name,
|
|
24
|
-
currencyCode: "PLN",
|
|
25
|
-
totalAmount: obj.amount,
|
|
26
|
-
notifyUrl: config.notifyUrl,
|
|
27
|
-
continueUrl: config.continueUrl,
|
|
28
|
-
products: [
|
|
29
|
-
{
|
|
30
|
-
name: obj.name,
|
|
31
|
-
unitPrice: obj.amount,
|
|
32
|
-
quantity: "1",
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
};
|
|
36
|
-
if (obj.options && obj.options["payMethod"]) {
|
|
37
|
-
data["payMethods"] = {
|
|
38
|
-
payMethod: obj.options["payMethod"],
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
if (obj.contactPhone || obj.email || obj.firstName || obj.lastName) {
|
|
42
|
-
data["buyer"] = {
|
|
43
|
-
email: obj.email,
|
|
44
|
-
phone: obj.contactPhone,
|
|
45
|
-
firstName: obj.firstName,
|
|
46
|
-
lastName: obj.lastName,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
await this.httpService
|
|
51
|
-
.post(this.getBaseUrl(config) + "/api/v2_1/orders", data, {
|
|
52
|
-
headers: {
|
|
53
|
-
"Content-Type": "application/json",
|
|
54
|
-
Authorization: "Bearer " + token,
|
|
55
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
56
|
-
},
|
|
57
|
-
maxRedirects: 0,
|
|
58
|
-
})
|
|
59
|
-
.toPromise();
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
if (e.response && e.response.status === 302) {
|
|
64
|
-
return {
|
|
65
|
-
redirectUrl: e.response.data.redirectUri,
|
|
66
|
-
orderId: e.response.data.orderId,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
console.error(e);
|
|
70
|
-
throw e;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async getStatus(trans) {
|
|
74
|
-
const orderId = this.getOrderId(trans);
|
|
75
|
-
const config = await this.getConfig(trans.data);
|
|
76
|
-
const token = await this.getToken(config);
|
|
77
|
-
const response = await this.httpService
|
|
78
|
-
.get(this.getBaseUrl(config) + "/api/v2_1/orders/" + orderId, {
|
|
79
|
-
headers: {
|
|
80
|
-
"Content-Type": "application/json",
|
|
81
|
-
Authorization: "Bearer " + token,
|
|
82
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
83
|
-
},
|
|
84
|
-
maxRedirects: 0,
|
|
85
|
-
})
|
|
86
|
-
.toPromise();
|
|
87
|
-
if (!response.data.orders)
|
|
88
|
-
return null;
|
|
89
|
-
const order = response.data.orders[0];
|
|
90
|
-
return {
|
|
91
|
-
status: this.getStatusFromExternal(order.status),
|
|
92
|
-
data: order,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
async refund(trans, comment) {
|
|
96
|
-
const orderId = this.getOrderId(trans);
|
|
97
|
-
const config = await this.getConfig(trans.data);
|
|
98
|
-
const token = await this.getToken(config);
|
|
99
|
-
const response = await this.httpService
|
|
100
|
-
.post(this.getBaseUrl(config) + "/api/v2_1/orders/" + orderId, {
|
|
101
|
-
refund: {
|
|
102
|
-
description: comment,
|
|
103
|
-
},
|
|
104
|
-
}, {
|
|
105
|
-
headers: {
|
|
106
|
-
"Content-Type": "application/json",
|
|
107
|
-
Authorization: "Bearer " + token,
|
|
108
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
109
|
-
},
|
|
110
|
-
maxRedirects: 0,
|
|
111
|
-
})
|
|
112
|
-
.toPromise();
|
|
113
|
-
return response.data;
|
|
114
|
-
}
|
|
115
|
-
getOrderId(trans) {
|
|
116
|
-
const historyItem = trans.history.find((x) => x.status === "started");
|
|
117
|
-
if (!historyItem) {
|
|
118
|
-
console.warn("Transaction without start status");
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
return historyItem.data.orderId;
|
|
122
|
-
}
|
|
123
|
-
async getToken(config) {
|
|
124
|
-
try {
|
|
125
|
-
const response = await this.httpService
|
|
126
|
-
.post(this.getBaseUrl(config) + "/pl/standard/user/oauth/authorize", `grant_type=client_credentials&client_id=${config.clientId}&client_secret=${config.clientSecret}`)
|
|
127
|
-
.toPromise();
|
|
128
|
-
return response.data["access_token"];
|
|
129
|
-
}
|
|
130
|
-
catch (e) {
|
|
131
|
-
console.error({
|
|
132
|
-
url: this.getBaseUrl(config) + "/pl/standard/user/oauth/authorize",
|
|
133
|
-
data: `grant_type=client_credentials&client_id=${config.clientId}&client_secret=${config.clientSecret}`,
|
|
134
|
-
ex: e,
|
|
135
|
-
});
|
|
136
|
-
throw e;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
async getConfig(data) {
|
|
140
|
-
try {
|
|
141
|
-
const provider = this.moduleRef.get(payu_config_1.PAYU_CONFIG_PROVIDER, { strict: false });
|
|
142
|
-
return await provider.get(data);
|
|
143
|
-
}
|
|
144
|
-
catch (e) {
|
|
145
|
-
common_1.Logger.warn("PayPal config provider not found", PayuService_1.name);
|
|
146
|
-
}
|
|
147
|
-
return this.config;
|
|
148
|
-
}
|
|
149
|
-
getBaseUrl(config) {
|
|
150
|
-
if (config.test)
|
|
151
|
-
return "https://secure.snd.payu.com";
|
|
152
|
-
return "https://secure.payu.com";
|
|
153
|
-
}
|
|
154
|
-
getStatusFromExternal(status) {
|
|
155
|
-
switch (status) {
|
|
156
|
-
case "COMPLETED":
|
|
157
|
-
return "completed";
|
|
158
|
-
case "CANCELED":
|
|
159
|
-
return "canceled";
|
|
160
|
-
case "PENDING":
|
|
161
|
-
return "pending";
|
|
162
|
-
default:
|
|
163
|
-
return status;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
exports.PayuService = PayuService = PayuService_1 = tslib_1.__decorate([
|
|
168
|
-
(0, common_1.Injectable)(),
|
|
169
|
-
tslib_1.__metadata("design:paramtypes", [axios_1.HttpService,
|
|
170
|
-
payu_config_1.PayuConfig,
|
|
171
|
-
core_1.ModuleRef])
|
|
172
|
-
], PayuService);
|
|
173
|
-
//# sourceMappingURL=payu.service.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"payu.service.js","sourceRoot":"","sources":["../../../../../../libs/shared/payu/src/lib/payu.service.ts"],"names":[],"mappings":"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"}
|
package/test-setup.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=test-setup.js.map
|
package/test-setup.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-setup.js","sourceRoot":"","sources":["../../../../libs/shared/payu/test-setup.ts"],"names":[],"mappings":""}
|
|
File without changes
|