@opengis/pay 2.0.3 → 2.0.4
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/package.json +39 -39
- package/server/migrations/7_transactions.sql +2 -0
- package/server/plugins/payment/createPayment.js +3 -5
- package/server/plugins/payment/createTransaction.js +2 -1
- package/server/plugins/payment/liqpay.createTransaction.js +30 -10
- package/server/plugins/payment/monopay.createTransaction.js +18 -8
- package/server/plugins/payment/portmone.createTransaction.js +27 -8
package/package.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@opengis/pay",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "pay plugins for billing",
|
|
6
|
-
"main": "plugin.js",
|
|
7
|
-
"browser": "plugin.js",
|
|
8
|
-
"files": [
|
|
9
|
-
"server/*",
|
|
10
|
-
"module/*",
|
|
11
|
-
"plugin.js",
|
|
12
|
-
"config.js",
|
|
13
|
-
"utils.js"
|
|
14
|
-
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"patch": "npm version patch && git push && npm publish",
|
|
17
|
-
"test": "node --test",
|
|
18
|
-
"start": "node --watch-path=server server",
|
|
19
|
-
"admin": "set NODE_ENV=admin&& npm run start",
|
|
20
|
-
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
|
21
|
-
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"liqpayjs-sdk": "3.3.1",
|
|
24
|
-
"request-promise": "4.2.6"
|
|
25
|
-
},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"dotenv": "^17.2.2",
|
|
28
|
-
"eslint": "8.49.0",
|
|
29
|
-
"eslint-config-airbnb": "19.0.4",
|
|
30
|
-
"fastify": "5.3.3",
|
|
31
|
-
"fastify-plugin": "5.0.1",
|
|
32
|
-
"ioredis": "5.3.2",
|
|
33
|
-
"nodemailer": "6.5.0",
|
|
34
|
-
"pg": "8.11.3",
|
|
35
|
-
"vitest": "^3.2.4"
|
|
36
|
-
},
|
|
37
|
-
"author": "Softpro",
|
|
38
|
-
"license": "ISC"
|
|
39
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@opengis/pay",
|
|
3
|
+
"version": "2.0.4",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "pay plugins for billing",
|
|
6
|
+
"main": "plugin.js",
|
|
7
|
+
"browser": "plugin.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"server/*",
|
|
10
|
+
"module/*",
|
|
11
|
+
"plugin.js",
|
|
12
|
+
"config.js",
|
|
13
|
+
"utils.js"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"patch": "npm version patch && git push && npm publish",
|
|
17
|
+
"test": "node --test",
|
|
18
|
+
"start": "node --watch-path=server server",
|
|
19
|
+
"admin": "set NODE_ENV=admin&& npm run start",
|
|
20
|
+
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"liqpayjs-sdk": "3.3.1",
|
|
24
|
+
"request-promise": "4.2.6"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"dotenv": "^17.2.2",
|
|
28
|
+
"eslint": "8.49.0",
|
|
29
|
+
"eslint-config-airbnb": "19.0.4",
|
|
30
|
+
"fastify": "5.3.3",
|
|
31
|
+
"fastify-plugin": "5.0.1",
|
|
32
|
+
"ioredis": "5.3.2",
|
|
33
|
+
"nodemailer": "6.5.0",
|
|
34
|
+
"pg": "8.11.3",
|
|
35
|
+
"vitest": "^3.2.4"
|
|
36
|
+
},
|
|
37
|
+
"author": "Softpro",
|
|
38
|
+
"license": "ISC"
|
|
39
|
+
}
|
|
@@ -28,6 +28,7 @@ alter table pay.transactions add column if not exists paytypes text;
|
|
|
28
28
|
alter table pay.transactions add column if not exists paytype text;
|
|
29
29
|
alter table pay.transactions add column if not exists customer text;
|
|
30
30
|
alter table pay.transactions add column if not exists receipt text;
|
|
31
|
+
alter table pay.transactions add column if not exists redirect_url text;
|
|
31
32
|
|
|
32
33
|
COMMENT ON TABLE pay.transactions IS 'Транзакції';
|
|
33
34
|
|
|
@@ -52,6 +53,7 @@ COMMENT ON COLUMN pay.transactions.paytypes IS 'Дозволені типи оп
|
|
|
52
53
|
COMMENT ON COLUMN pay.transactions.paytype IS 'Використаний тип оплати';
|
|
53
54
|
COMMENT ON COLUMN pay.transactions.customer IS 'Унікальний ідентифікатор користувача на сайті мерчанта';
|
|
54
55
|
COMMENT ON COLUMN pay.transactions.receipt IS 'Посилання на квітанцію сервісу оплати';
|
|
56
|
+
COMMENT ON COLUMN pay.transactions.redirect_url IS 'Посилання на сторінку сервісу платежів';
|
|
55
57
|
|
|
56
58
|
CREATE INDEX if not exists pay_transactions_transactions_status_btree_idx ON pay.transactions USING btree (status COLLATE pg_catalog."default");
|
|
57
59
|
|
|
@@ -2,18 +2,16 @@ import config from '../../../config.js';
|
|
|
2
2
|
import pgClients from '../pg/pgClients.js';
|
|
3
3
|
import dataInsert from '../crud/dataInsert.js';
|
|
4
4
|
|
|
5
|
-
const {
|
|
5
|
+
const { table = 'pay.transactions' } = config.integrations?.pay || {};
|
|
6
6
|
|
|
7
7
|
export default async function createPayment({
|
|
8
|
-
|
|
8
|
+
orderId, amount, referer, uid, service, expiredDate, description, action = 'pay', currency = 'uah', language = 'uk', paytypes,
|
|
9
9
|
}, pg = pgClients.client) {
|
|
10
|
-
if (!paymentId) { throw new Error('not enough params: paymentId'); }
|
|
11
10
|
if (!uid) { throw new Error('not enough params: uid'); }
|
|
12
11
|
if (!service) { throw new Error('not enough params: service'); }
|
|
13
12
|
if (!amount) { throw new Error('not enough params: amount'); }
|
|
14
13
|
|
|
15
14
|
const result = await dataInsert({
|
|
16
|
-
id: paymentId,
|
|
17
15
|
pg,
|
|
18
16
|
table,
|
|
19
17
|
data: {
|
|
@@ -32,5 +30,5 @@ export default async function createPayment({
|
|
|
32
30
|
},
|
|
33
31
|
uid,
|
|
34
32
|
}).then(el => el.rows?.[0]);
|
|
35
|
-
return { id:
|
|
33
|
+
return { id: result?.[pg.pk?.[table]], ...result || {} };
|
|
36
34
|
}
|
|
@@ -13,7 +13,7 @@ const controller = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export default async function createTransaction({
|
|
16
|
-
orderId, amount, referer, uid, debug, service, expiredDate, description, action, language, paytypes, currency,
|
|
16
|
+
orderId, amount, referer, uid, debug, service, expiredDate, description, action, language, paytypes, currency, deliveryEmails,
|
|
17
17
|
}, pg = pgClients.client) {
|
|
18
18
|
if (!service) throw new Error('not enough params: service');
|
|
19
19
|
if (!controller[service]) { throw new Error('invalid params: service'); }
|
|
@@ -38,6 +38,7 @@ export default async function createTransaction({
|
|
|
38
38
|
description,
|
|
39
39
|
language,
|
|
40
40
|
paytypes,
|
|
41
|
+
deliveryEmails,
|
|
41
42
|
}, pg);
|
|
42
43
|
return result;
|
|
43
44
|
}
|
|
@@ -3,6 +3,7 @@ import LiqPay from 'liqpayjs-sdk';
|
|
|
3
3
|
|
|
4
4
|
import config from '../../../config.js';
|
|
5
5
|
import pgClients from '../pg/pgClients.js';
|
|
6
|
+
import dataUpdate from '../crud/dataUpdate.js';
|
|
6
7
|
|
|
7
8
|
import createPayment from './createPayment.js';
|
|
8
9
|
|
|
@@ -17,9 +18,9 @@ const {
|
|
|
17
18
|
const liqpay = new LiqPay(publicKey, privateKey);
|
|
18
19
|
|
|
19
20
|
export default async function liqpayCreateTransaction({
|
|
20
|
-
id, orderId, amount, referer, uid, debug, expiredDate, description, paytypes, action = 'pay', currency = 'uah', language = 'uk',
|
|
21
|
+
id, orderId, amount, referer, uid, debug, expiredDate, description, paytypes, action = 'pay', currency = 'uah', language = 'uk', deliveryEmails,
|
|
21
22
|
}, pg = pgClients.client) {
|
|
22
|
-
const backUrl = bUrl || referer
|
|
23
|
+
const backUrl = bUrl || referer?.slice?.(0, referer.indexOf('/', 7));
|
|
23
24
|
|
|
24
25
|
if (!publicKey || !privateKey) {
|
|
25
26
|
throw new Error('invalid liqpay integration settings');
|
|
@@ -41,13 +42,10 @@ export default async function liqpayCreateTransaction({
|
|
|
41
42
|
throw new Error('not enough params: uid required');
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
const paymentId = !id ? await pg.query('select next_id()').then(el => el.rows?.[0]?.next_id) : id;
|
|
45
|
-
|
|
46
45
|
const paymentData = id && pg.pk?.[table]
|
|
47
46
|
? await pg.query(`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]}=$1`, [id]).then(el => el.rows?.[0])
|
|
48
47
|
: await createPayment({
|
|
49
48
|
service: 'liqpay',
|
|
50
|
-
paymentId,
|
|
51
49
|
orderId,
|
|
52
50
|
amount,
|
|
53
51
|
referer,
|
|
@@ -61,20 +59,30 @@ export default async function liqpayCreateTransaction({
|
|
|
61
59
|
});
|
|
62
60
|
|
|
63
61
|
if (!paymentData?.id) {
|
|
64
|
-
throw new Error('
|
|
62
|
+
throw new Error('Payment not found / creation failed');
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
const paymentAmount = id ? paymentData.amount : amount;
|
|
68
66
|
|
|
69
67
|
if (!paymentAmount) {
|
|
70
|
-
throw new Error('
|
|
68
|
+
throw new Error('Invalid payment amount');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (id && paymentData?.expired_date
|
|
72
|
+
&& new Date(paymentData.expired_date)
|
|
73
|
+
&& new Date() > new Date(paymentData.expired_date)) {
|
|
74
|
+
throw new Error('Transaction date already expired');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (id && paymentData?.redirect_url) {
|
|
78
|
+
return paymentData.redirect_url;
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
const paymentOptions = {
|
|
74
82
|
action,
|
|
75
83
|
amount: paymentAmount,
|
|
76
84
|
currency: currency.toUpperCase(),
|
|
77
|
-
description: description ||
|
|
85
|
+
description: description || `Послуга - поповнення рахунку на суму ${paymentAmount} грн`,
|
|
78
86
|
order_id: paymentData.id,
|
|
79
87
|
version: '3',
|
|
80
88
|
language,
|
|
@@ -82,6 +90,9 @@ export default async function liqpayCreateTransaction({
|
|
|
82
90
|
expired_date: expiredDate && new Date(expiredDate) ? new Date(expiredDate).toISOString().split('.')[0].replace('T', ' ') : undefined,
|
|
83
91
|
// server_url: `${backUrl}${prefix}/liqpay/${paymentData.id}/success`, // update payment info - api url, does not work on sdk package out of the box
|
|
84
92
|
result_url: `${backUrl}${prefix}/liqpay/${paymentData.id}/success`, // redirect user after payment - page url
|
|
93
|
+
rro_info: Array.isArray(deliveryEmails) && deliveryEmails.length ? {
|
|
94
|
+
delivery_emails: deliveryEmails,
|
|
95
|
+
} : undefined,
|
|
85
96
|
};
|
|
86
97
|
|
|
87
98
|
// html interface for debug via browser only
|
|
@@ -110,9 +121,18 @@ export default async function liqpayCreateTransaction({
|
|
|
110
121
|
throw new Error(response.headers?.location);
|
|
111
122
|
}
|
|
112
123
|
|
|
113
|
-
|
|
124
|
+
await dataUpdate({
|
|
125
|
+
table,
|
|
126
|
+
id: paymentData.id,
|
|
127
|
+
data: {
|
|
128
|
+
redirect_url: response.headers.location,
|
|
129
|
+
},
|
|
130
|
+
uid,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// debug via unit test
|
|
114
134
|
if (process.env.NODE_ENV === 'test' && debug) {
|
|
115
|
-
return { url: response.headers.location
|
|
135
|
+
return { id: paymentData.id, url: response.headers.location };
|
|
116
136
|
}
|
|
117
137
|
|
|
118
138
|
return response.headers.location;
|
|
@@ -5,7 +5,7 @@ import dataUpdate from '../crud/dataUpdate.js';
|
|
|
5
5
|
import createPayment from './createPayment.js';
|
|
6
6
|
|
|
7
7
|
const { prefix = '/api' } = config;
|
|
8
|
-
const { table = 'pay.transactions', backUrl } = config.integrations?.pay || {};
|
|
8
|
+
const { table = 'pay.transactions', backUrl: bUrl } = config.integrations?.pay || {};
|
|
9
9
|
|
|
10
10
|
const {
|
|
11
11
|
host = 'https://api.monobank.ua',
|
|
@@ -24,6 +24,8 @@ const ccyObj = {
|
|
|
24
24
|
export default async function monopayCreateTransaction({
|
|
25
25
|
id, orderId, amount, referer, uid, debug, expiredDate, description, action, currency, language,
|
|
26
26
|
}, pg = pgClients.client) {
|
|
27
|
+
const backUrl = bUrl || referer?.slice?.(0, referer.indexOf('/', 7));
|
|
28
|
+
|
|
27
29
|
if (!host || !token || !backUrl) {
|
|
28
30
|
throw new Error('invalid monopay integration settings');
|
|
29
31
|
}
|
|
@@ -36,13 +38,10 @@ export default async function monopayCreateTransaction({
|
|
|
36
38
|
throw new Error('not enough params: uid required');
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
const paymentId = !id ? await pg.query('select next_id()').then(el => el.rows?.[0]?.next_id) : id;
|
|
40
|
-
|
|
41
41
|
const paymentData = id && pg.pk?.[table]
|
|
42
42
|
? await pg.query(`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]}=$1`, [id]).then(el => el.rows?.[0])
|
|
43
43
|
: await createPayment({
|
|
44
44
|
service: 'monopay',
|
|
45
|
-
paymentId,
|
|
46
45
|
orderId,
|
|
47
46
|
amount,
|
|
48
47
|
referer,
|
|
@@ -64,8 +63,18 @@ export default async function monopayCreateTransaction({
|
|
|
64
63
|
throw new Error('invalid payment amount');
|
|
65
64
|
}
|
|
66
65
|
|
|
66
|
+
if (id && paymentData?.expired_date
|
|
67
|
+
&& new Date(paymentData.expired_date)
|
|
68
|
+
&& new Date() > new Date(paymentData.expired_date)) {
|
|
69
|
+
throw new Error('Transaction date already expired');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (id && paymentData?.redirect_url) {
|
|
73
|
+
return paymentData.redirect_url;
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
// redirect to api to check payment status and redirect user to page afterwards
|
|
68
|
-
const serverUrl = `${backUrl}${prefix}/monopay/${
|
|
77
|
+
const serverUrl = `${backUrl}${prefix}/monopay/${paymentData.id}/success`;
|
|
69
78
|
|
|
70
79
|
const validity = expiredDate && new Date(expiredDate) ? Math.max(((new Date(expiredDate).setHours(0, 0, 0, 0) - new Date().setHours(0, 0, 0, 0)) / 1000 || 0), 86400) : 86400;
|
|
71
80
|
|
|
@@ -74,7 +83,7 @@ export default async function monopayCreateTransaction({
|
|
|
74
83
|
ccy: ccyObj[currency] || 980, // ISO 4217, 980 = UAH
|
|
75
84
|
merchantPaymInfo,
|
|
76
85
|
redirectUrl: serverUrl,
|
|
77
|
-
// redirectUrl: (paymentRedirect ? `${rootPageUrl}${paymentRedirect.replace(/{{id}}/g,
|
|
86
|
+
// redirectUrl: (paymentRedirect ? `${rootPageUrl}${paymentRedirect.replace(/{{id}}/g, paymentData.id)}` : headers?.referer) || rootPageUrl,
|
|
78
87
|
webHookUrl: serverUrl,
|
|
79
88
|
validity, // 1 day by default, then invalid
|
|
80
89
|
paymentType: 'debit',
|
|
@@ -97,11 +106,12 @@ export default async function monopayCreateTransaction({
|
|
|
97
106
|
// !save invoice ID to check status later
|
|
98
107
|
await dataUpdate({
|
|
99
108
|
table,
|
|
100
|
-
id:
|
|
109
|
+
id: paymentData.id,
|
|
101
110
|
data: {
|
|
102
111
|
invoice_id: response?.invoiceId,
|
|
103
112
|
data: resp.status === 200 ? undefined : { response },
|
|
104
113
|
status: resp.status === 200 ? 'inprogress' : 'error',
|
|
114
|
+
redirect_url: response?.pageUrl,
|
|
105
115
|
},
|
|
106
116
|
uid,
|
|
107
117
|
});
|
|
@@ -116,7 +126,7 @@ export default async function monopayCreateTransaction({
|
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
if (process.env.NODE_ENV === 'test' && debug) {
|
|
119
|
-
return { id:
|
|
129
|
+
return { id: paymentData.id, url: response.pageUrl };
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
return response.pageUrl;
|
|
@@ -2,18 +2,21 @@ import request from 'request-promise';
|
|
|
2
2
|
|
|
3
3
|
import config from '../../../config.js';
|
|
4
4
|
import pgClients from '../pg/pgClients.js';
|
|
5
|
+
import dataUpdate from '../crud/dataUpdate.js';
|
|
5
6
|
|
|
6
7
|
import createPayment from './createPayment.js';
|
|
7
8
|
|
|
8
9
|
const { prefix = '/api' } = config;
|
|
9
10
|
|
|
10
|
-
const { table = 'pay.transactions', backUrl } = config.integrations?.pay || {};
|
|
11
|
+
const { table = 'pay.transactions', backUrl: bUrl } = config.integrations?.pay || {};
|
|
11
12
|
const { host = 'https://www.portmone.com.ua', payeeId } = config.integrations?.portmone || {};
|
|
12
13
|
|
|
13
14
|
export default async function portmoneCreateTransaction({
|
|
14
15
|
id, orderId, amount, referer, uid, debug, expiredDate, description, action, currency = 'uah', language = 'uk', paytypes,
|
|
15
16
|
}, pg = pgClients.client) {
|
|
16
|
-
|
|
17
|
+
const backUrl = bUrl || referer?.slice?.(0, referer.indexOf('/', 7));
|
|
18
|
+
|
|
19
|
+
if (!payeeId || !backUrl) {
|
|
17
20
|
throw new Error('invalid portmone integration settings');
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -29,13 +32,10 @@ export default async function portmoneCreateTransaction({
|
|
|
29
32
|
throw new Error('not enough params: uid required');
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
const paymentId = !id ? await pg.query('select next_id()').then(el => el.rows?.[0]?.next_id) : id;
|
|
33
|
-
|
|
34
35
|
const paymentData = id && pg.pk?.[table]
|
|
35
36
|
? await pg.query(`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]}=$1`, [id]).then(el => el.rows?.[0])
|
|
36
37
|
: await createPayment({
|
|
37
38
|
service: 'portmone',
|
|
38
|
-
paymentId,
|
|
39
39
|
orderId,
|
|
40
40
|
amount,
|
|
41
41
|
referer,
|
|
@@ -49,13 +49,23 @@ export default async function portmoneCreateTransaction({
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
if (!paymentData?.id) {
|
|
52
|
-
throw new Error('
|
|
52
|
+
throw new Error('Payment not found / creation failed');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const paymentAmount = id ? paymentData.amount : amount;
|
|
56
56
|
|
|
57
57
|
if (!paymentAmount) {
|
|
58
|
-
throw new Error('
|
|
58
|
+
throw new Error('Invalid payment amount');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (id && paymentData?.expired_date
|
|
62
|
+
&& new Date(paymentData.expired_date)
|
|
63
|
+
&& new Date() > new Date(paymentData.expired_date)) {
|
|
64
|
+
throw new Error('Transaction date already expired');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (id && paymentData?.redirect_url) {
|
|
68
|
+
return paymentData.redirect_url;
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
const formData = {
|
|
@@ -90,8 +100,17 @@ export default async function portmoneCreateTransaction({
|
|
|
90
100
|
throw new Error(response);
|
|
91
101
|
}
|
|
92
102
|
|
|
103
|
+
await dataUpdate({
|
|
104
|
+
table,
|
|
105
|
+
id: paymentData.id,
|
|
106
|
+
data: {
|
|
107
|
+
redirect_url: response.headers.location,
|
|
108
|
+
},
|
|
109
|
+
uid,
|
|
110
|
+
});
|
|
111
|
+
|
|
93
112
|
if (process.env.NODE_ENV === 'test' && debug) {
|
|
94
|
-
return { id: paymentData
|
|
113
|
+
return { id: paymentData.id, url: response.headers.location };
|
|
95
114
|
}
|
|
96
115
|
|
|
97
116
|
return response.headers.location;
|