@opengis/pay 2.0.1 → 2.0.3
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 +25 -2
- package/server/plugins/payment/createPayment.js +11 -4
- package/server/plugins/payment/createTransaction.js +20 -3
- package/server/plugins/payment/liqpay.checkStatus.js +23 -1
- package/server/plugins/payment/liqpay.createTransaction.js +30 -14
- package/server/plugins/payment/monopay.checkStatus.js +10 -2
- package/server/plugins/payment/monopay.createTransaction.js +28 -21
- package/server/plugins/payment/paymentInfo.js +3 -1
- package/server/plugins/payment/portmone.checkStatus.js +18 -3
- package/server/plugins/payment/portmone.createTransaction.js +23 -19
- package/server/plugins/payment/processTransaction.js +10 -4
- package/server/routes/core/controllers/payment.info.js +2 -2
- package/server/routes/liqpay/controllers/liqpay.redirect.js +5 -10
- package/server/routes/liqpay/controllers/liqpay.status.js +1 -0
- package/server/routes/monopay/controllers/monopay.redirect.js +6 -11
- package/server/routes/monopay/index.mjs +1 -1
- package/server/routes/portmone/controllers/portmone.redirect.js +6 -11
- package/server/routes/portmone/controllers/portmone.status.js +1 -1
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.3",
|
|
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
|
+
}
|
|
@@ -17,8 +17,20 @@ alter table pay.transactions add column if not exists created_by text NOT NULL d
|
|
|
17
17
|
alter table pay.transactions add column if not exists updated_by text;
|
|
18
18
|
alter table pay.transactions add column if not exists updated_at timestamp without time zone;
|
|
19
19
|
alter table pay.transactions add column if not exists invoice_id text;
|
|
20
|
+
alter table pay.transactions add column if not exists payment_id text;
|
|
21
|
+
alter table pay.transactions add column if not exists order_id text;
|
|
22
|
+
alter table pay.transactions add column if not exists description text;
|
|
23
|
+
alter table pay.transactions add column if not exists action text;
|
|
24
|
+
alter table pay.transactions add column if not exists currency text;
|
|
25
|
+
alter table pay.transactions add column if not exists expired_date date;
|
|
26
|
+
alter table pay.transactions add column if not exists language text;
|
|
27
|
+
alter table pay.transactions add column if not exists paytypes text;
|
|
28
|
+
alter table pay.transactions add column if not exists paytype text;
|
|
29
|
+
alter table pay.transactions add column if not exists customer text;
|
|
30
|
+
alter table pay.transactions add column if not exists receipt text;
|
|
31
|
+
|
|
32
|
+
COMMENT ON TABLE pay.transactions IS 'Транзакції';
|
|
20
33
|
|
|
21
|
-
COMMENT ON TABLE pay.transactions IS 'Платежі';
|
|
22
34
|
COMMENT ON COLUMN pay.transactions.transaction_id IS 'ID';
|
|
23
35
|
COMMENT ON COLUMN pay.transactions.user_id IS 'ID користувача';
|
|
24
36
|
COMMENT ON COLUMN pay.transactions.status IS 'Статус транзації';
|
|
@@ -28,7 +40,18 @@ COMMENT ON COLUMN pay.transactions.service IS 'Сервіс оплати (portmo
|
|
|
28
40
|
COMMENT ON COLUMN pay.transactions.amount IS 'Сума поповнення рахунку';
|
|
29
41
|
COMMENT ON COLUMN pay.transactions.balance IS 'Поточний баланс';
|
|
30
42
|
COMMENT ON COLUMN pay.transactions.referer_url IS 'Посилання';
|
|
31
|
-
COMMENT ON COLUMN pay.transactions.invoice_id IS 'ID
|
|
43
|
+
COMMENT ON COLUMN pay.transactions.invoice_id IS 'ID рахунку сервісу оплати';
|
|
44
|
+
COMMENT ON COLUMN pay.transactions.invoice_id IS 'ID платежу сервісу оплати';
|
|
45
|
+
COMMENT ON COLUMN pay.transactions.order_id IS 'Внутрішній ID замовлення';
|
|
46
|
+
COMMENT ON COLUMN pay.transactions.description IS 'Опис';
|
|
47
|
+
COMMENT ON COLUMN pay.transactions.action IS 'Дія';
|
|
48
|
+
COMMENT ON COLUMN pay.transactions.currency IS 'Валюта';
|
|
49
|
+
COMMENT ON COLUMN pay.transactions.expired_date IS 'Термін дії';
|
|
50
|
+
COMMENT ON COLUMN pay.transactions.language IS 'Мова';
|
|
51
|
+
COMMENT ON COLUMN pay.transactions.paytypes IS 'Дозволені типи оплати';
|
|
52
|
+
COMMENT ON COLUMN pay.transactions.paytype IS 'Використаний тип оплати';
|
|
53
|
+
COMMENT ON COLUMN pay.transactions.customer IS 'Унікальний ідентифікатор користувача на сайті мерчанта';
|
|
54
|
+
COMMENT ON COLUMN pay.transactions.receipt IS 'Посилання на квітанцію сервісу оплати';
|
|
32
55
|
|
|
33
56
|
CREATE INDEX if not exists pay_transactions_transactions_status_btree_idx ON pay.transactions USING btree (status COLLATE pg_catalog."default");
|
|
34
57
|
|
|
@@ -5,23 +5,30 @@ import dataInsert from '../crud/dataInsert.js';
|
|
|
5
5
|
const { paymentRedirect, table = 'pay.transactions' } = config.integrations?.pay || {};
|
|
6
6
|
|
|
7
7
|
export default async function createPayment({
|
|
8
|
-
paymentId,
|
|
8
|
+
paymentId, orderId, amount, referer, uid, service, expiredDate, description, action = 'pay', currency = 'uah', language = 'uk', paytypes,
|
|
9
9
|
}, pg = pgClients.client) {
|
|
10
10
|
if (!paymentId) { throw new Error('not enough params: paymentId'); }
|
|
11
11
|
if (!uid) { throw new Error('not enough params: uid'); }
|
|
12
12
|
if (!service) { throw new Error('not enough params: service'); }
|
|
13
|
-
if (!
|
|
13
|
+
if (!amount) { throw new Error('not enough params: amount'); }
|
|
14
14
|
|
|
15
15
|
const result = await dataInsert({
|
|
16
16
|
id: paymentId,
|
|
17
17
|
pg,
|
|
18
18
|
table,
|
|
19
19
|
data: {
|
|
20
|
-
referer_url:
|
|
21
|
-
amount
|
|
20
|
+
referer_url: referer,
|
|
21
|
+
amount,
|
|
22
22
|
user_id: uid,
|
|
23
|
+
order_id: orderId,
|
|
23
24
|
service,
|
|
24
25
|
status: 'inprogress',
|
|
26
|
+
action,
|
|
27
|
+
expired_date: expiredDate,
|
|
28
|
+
description,
|
|
29
|
+
language,
|
|
30
|
+
currency,
|
|
31
|
+
paytypes,
|
|
25
32
|
},
|
|
26
33
|
uid,
|
|
27
34
|
}).then(el => el.rows?.[0]);
|
|
@@ -13,14 +13,31 @@ const controller = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export default async function createTransaction({
|
|
16
|
-
|
|
16
|
+
orderId, amount, referer, uid, debug, service, expiredDate, description, action, language, paytypes, currency,
|
|
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'); }
|
|
20
|
-
if (
|
|
20
|
+
if (amount && !uid) throw new Error('not enough params: uid');
|
|
21
|
+
|
|
22
|
+
if (currency && !['uah', 'usd', 'eur'].includes(currency)) {
|
|
23
|
+
throw new Error('invalid params: currency');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (language && !['uk', 'en'].includes(language)) {
|
|
27
|
+
throw new Error('invalid params: language');
|
|
28
|
+
}
|
|
21
29
|
|
|
22
30
|
const result = await controller[service]({
|
|
23
|
-
|
|
31
|
+
orderId,
|
|
32
|
+
amount,
|
|
33
|
+
referer,
|
|
34
|
+
uid,
|
|
35
|
+
debug,
|
|
36
|
+
action,
|
|
37
|
+
expiredDate,
|
|
38
|
+
description,
|
|
39
|
+
language,
|
|
40
|
+
paytypes,
|
|
24
41
|
}, pg);
|
|
25
42
|
return result;
|
|
26
43
|
}
|
|
@@ -9,7 +9,9 @@ const { host = 'https://www.liqpay.ua', publicKey, privateKey } = config.integra
|
|
|
9
9
|
|
|
10
10
|
const liqpay = new LiqPay(publicKey, privateKey);
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const pg = pgClients.client;
|
|
13
|
+
|
|
14
|
+
export default async function liqpayCheckStatus(id, { origin } = {}) {
|
|
13
15
|
if (!id) throw new Error('not enough params: id');
|
|
14
16
|
if (!publicKey || !privateKey) throw new Error('empty integration params: liqpay');
|
|
15
17
|
|
|
@@ -38,6 +40,26 @@ export default async function liqpayCheckStatus(id, origin, pg = pgClients.clien
|
|
|
38
40
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
39
41
|
},
|
|
40
42
|
});
|
|
43
|
+
|
|
41
44
|
const result = response?.startsWith?.('{') ? JSON.parse(response) : response;
|
|
45
|
+
|
|
46
|
+
// ! receipt can only be sent to email, no link provided by liqpay
|
|
47
|
+
/* const data1 = Buffer.from(JSON.stringify({
|
|
48
|
+
action: 'ticket',
|
|
49
|
+
version: '3',
|
|
50
|
+
order_id: id,
|
|
51
|
+
email: 'email@gmail.com',
|
|
52
|
+
language: result?.language || 'uk',
|
|
53
|
+
})).toString('base64');
|
|
54
|
+
const signature1 = liqpay.str_to_sign(privateKey + data1 + privateKey);
|
|
55
|
+
|
|
56
|
+
const response1 = await request(`${host}/api/request`, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
form: { data: data1, signature: signature1 },
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
61
|
+
},
|
|
62
|
+
}); */
|
|
63
|
+
|
|
42
64
|
return result;
|
|
43
65
|
}
|
|
@@ -7,7 +7,7 @@ import pgClients from '../pg/pgClients.js';
|
|
|
7
7
|
import createPayment from './createPayment.js';
|
|
8
8
|
|
|
9
9
|
const { prefix = '/api' } = config;
|
|
10
|
-
const { table = 'pay.transactions' } = config.integrations?.pay || {};
|
|
10
|
+
const { table = 'pay.transactions', backUrl: bUrl } = config.integrations?.pay || {};
|
|
11
11
|
|
|
12
12
|
const {
|
|
13
13
|
host = 'https://www.liqpay.ua',
|
|
@@ -17,18 +17,24 @@ const {
|
|
|
17
17
|
const liqpay = new LiqPay(publicKey, privateKey);
|
|
18
18
|
|
|
19
19
|
export default async function liqpayCreateTransaction({
|
|
20
|
-
id,
|
|
20
|
+
id, orderId, amount, referer, uid, debug, expiredDate, description, paytypes, action = 'pay', currency = 'uah', language = 'uk',
|
|
21
21
|
}, pg = pgClients.client) {
|
|
22
|
+
const backUrl = bUrl || referer.slice(0, referer.indexOf('/', 7));
|
|
23
|
+
|
|
22
24
|
if (!publicKey || !privateKey) {
|
|
23
25
|
throw new Error('invalid liqpay integration settings');
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
if (!
|
|
27
|
-
throw new Error('
|
|
28
|
+
if (!['pay', 'hold', 'subscribe', 'paydonate'].includes(action)) {
|
|
29
|
+
throw new Error('invalid params: action');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (paytypes && paytypes.split(',').find(el => !['apay', 'gpay', 'card', 'privat24', 'moment_part', 'paypart', 'cash', 'invoice', 'qr'].includes(el))) {
|
|
33
|
+
throw new Error('invalid params: paytypes');
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
if (!
|
|
31
|
-
throw new Error('not enough params:
|
|
36
|
+
if (!id && !amount) {
|
|
37
|
+
throw new Error('not enough params: id or amount required');
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
if (!uid) {
|
|
@@ -40,32 +46,42 @@ export default async function liqpayCreateTransaction({
|
|
|
40
46
|
const paymentData = id && pg.pk?.[table]
|
|
41
47
|
? await pg.query(`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]}=$1`, [id]).then(el => el.rows?.[0])
|
|
42
48
|
: await createPayment({
|
|
49
|
+
service: 'liqpay',
|
|
43
50
|
paymentId,
|
|
44
|
-
|
|
51
|
+
orderId,
|
|
52
|
+
amount,
|
|
45
53
|
referer,
|
|
46
54
|
uid,
|
|
47
|
-
|
|
55
|
+
expiredDate,
|
|
56
|
+
description,
|
|
57
|
+
action,
|
|
58
|
+
currency,
|
|
59
|
+
language,
|
|
60
|
+
paytypes,
|
|
48
61
|
});
|
|
49
62
|
|
|
50
63
|
if (!paymentData?.id) {
|
|
51
64
|
throw new Error('payment not found / creation failed');
|
|
52
65
|
}
|
|
53
66
|
|
|
54
|
-
const paymentAmount = id ? paymentData.amount :
|
|
67
|
+
const paymentAmount = id ? paymentData.amount : amount;
|
|
55
68
|
|
|
56
69
|
if (!paymentAmount) {
|
|
57
70
|
throw new Error('invalid payment amount');
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
const paymentOptions = {
|
|
61
|
-
action
|
|
74
|
+
action,
|
|
62
75
|
amount: paymentAmount,
|
|
63
|
-
currency:
|
|
64
|
-
description: 'regular payment',
|
|
76
|
+
currency: currency.toUpperCase(),
|
|
77
|
+
description: description || 'regular payment',
|
|
65
78
|
order_id: paymentData.id,
|
|
66
79
|
version: '3',
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
language,
|
|
81
|
+
paytypes,
|
|
82
|
+
expired_date: expiredDate && new Date(expiredDate) ? new Date(expiredDate).toISOString().split('.')[0].replace('T', ' ') : undefined,
|
|
83
|
+
// server_url: `${backUrl}${prefix}/liqpay/${paymentData.id}/success`, // update payment info - api url, does not work on sdk package out of the box
|
|
84
|
+
result_url: `${backUrl}${prefix}/liqpay/${paymentData.id}/success`, // redirect user after payment - page url
|
|
69
85
|
};
|
|
70
86
|
|
|
71
87
|
// html interface for debug via browser only
|
|
@@ -10,7 +10,9 @@ const {
|
|
|
10
10
|
|
|
11
11
|
const timeoutMs = 5000;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
const pg = pgClients.client;
|
|
14
|
+
|
|
15
|
+
export default async function monopayCheckStatus(id, { origin } = {}) {
|
|
14
16
|
if (!id) { throw new Error('not enough params: id'); }
|
|
15
17
|
|
|
16
18
|
const {
|
|
@@ -44,5 +46,11 @@ export default async function monopayCheckStatus(id, origin, pg = pgClients.clie
|
|
|
44
46
|
? await resp.json()
|
|
45
47
|
: { error: await resp.text() };
|
|
46
48
|
|
|
47
|
-
return {
|
|
49
|
+
return {
|
|
50
|
+
...body,
|
|
51
|
+
paytype: body.paymentInfo?.maskedPan ? 'card' : null,
|
|
52
|
+
payment_id: body.paymentInfo?.tranId,
|
|
53
|
+
amount: body.amount / 100,
|
|
54
|
+
status: body.status,
|
|
55
|
+
};
|
|
48
56
|
}
|
|
@@ -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' } = config.integrations?.pay || {};
|
|
8
|
+
const { table = 'pay.transactions', backUrl } = config.integrations?.pay || {};
|
|
9
9
|
|
|
10
10
|
const {
|
|
11
11
|
host = 'https://api.monobank.ua',
|
|
@@ -15,23 +15,21 @@ const {
|
|
|
15
15
|
|
|
16
16
|
const timeoutMs = 5000;
|
|
17
17
|
|
|
18
|
+
const ccyObj = {
|
|
19
|
+
uah: 980,
|
|
20
|
+
usd: 840,
|
|
21
|
+
eur: 978,
|
|
22
|
+
};
|
|
23
|
+
|
|
18
24
|
export default async function monopayCreateTransaction({
|
|
19
|
-
id,
|
|
25
|
+
id, orderId, amount, referer, uid, debug, expiredDate, description, action, currency, language,
|
|
20
26
|
}, pg = pgClients.client) {
|
|
21
|
-
if (!host) {
|
|
22
|
-
throw new Error('
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!token) {
|
|
26
|
-
throw new Error('empty integration token');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (!id && !price) {
|
|
30
|
-
throw new Error('not enough params: id or price required');
|
|
27
|
+
if (!host || !token || !backUrl) {
|
|
28
|
+
throw new Error('invalid monopay integration settings');
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
if (!
|
|
34
|
-
throw new Error('not enough params:
|
|
31
|
+
if (!id && !amount) {
|
|
32
|
+
throw new Error('not enough params: id or amount required');
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
if (!uid) {
|
|
@@ -43,33 +41,42 @@ export default async function monopayCreateTransaction({
|
|
|
43
41
|
const paymentData = id && pg.pk?.[table]
|
|
44
42
|
? await pg.query(`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]}=$1`, [id]).then(el => el.rows?.[0])
|
|
45
43
|
: await createPayment({
|
|
44
|
+
service: 'monopay',
|
|
46
45
|
paymentId,
|
|
47
|
-
|
|
46
|
+
orderId,
|
|
47
|
+
amount,
|
|
48
48
|
referer,
|
|
49
49
|
uid,
|
|
50
|
-
|
|
50
|
+
expiredDate,
|
|
51
|
+
description,
|
|
52
|
+
action,
|
|
53
|
+
currency,
|
|
54
|
+
language,
|
|
51
55
|
});
|
|
52
56
|
|
|
53
57
|
if (!paymentData?.id) {
|
|
54
58
|
throw new Error('payment not found / creation failed');
|
|
55
59
|
}
|
|
56
|
-
|
|
60
|
+
|
|
61
|
+
const paymentAmount = id ? paymentData.amount : amount;
|
|
57
62
|
|
|
58
63
|
if (!paymentAmount) {
|
|
59
64
|
throw new Error('invalid payment amount');
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
// redirect to api to check payment status and redirect user to page afterwards
|
|
63
|
-
const serverUrl = `${
|
|
68
|
+
const serverUrl = `${backUrl}${prefix}/monopay/${paymentId}/success`;
|
|
69
|
+
|
|
70
|
+
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;
|
|
64
71
|
|
|
65
72
|
const paymentOptions = {
|
|
66
|
-
amount: paymentAmount * 100, //
|
|
67
|
-
ccy: 980, // ISO 4217, 980 = UAH
|
|
73
|
+
amount: paymentAmount * 100, // amount=1 === 0.01 uah
|
|
74
|
+
ccy: ccyObj[currency] || 980, // ISO 4217, 980 = UAH
|
|
68
75
|
merchantPaymInfo,
|
|
69
76
|
redirectUrl: serverUrl,
|
|
70
77
|
// redirectUrl: (paymentRedirect ? `${rootPageUrl}${paymentRedirect.replace(/{{id}}/g, paymentId)}` : headers?.referer) || rootPageUrl,
|
|
71
78
|
webHookUrl: serverUrl,
|
|
72
|
-
validity
|
|
79
|
+
validity, // 1 day by default, then invalid
|
|
73
80
|
paymentType: 'debit',
|
|
74
81
|
// displayType: 'iframe', // default = null > url to mono
|
|
75
82
|
};
|
|
@@ -10,6 +10,7 @@ export default async function paymentInfo(id, pg = pgClients.client) {
|
|
|
10
10
|
amount,
|
|
11
11
|
status,
|
|
12
12
|
service,
|
|
13
|
+
order_id: orderId,
|
|
13
14
|
referer_url: referer,
|
|
14
15
|
user_id: userId,
|
|
15
16
|
} = id && pg.pk?.[table] ? await pg.query(`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]}=$1`, [id])
|
|
@@ -17,7 +18,8 @@ export default async function paymentInfo(id, pg = pgClients.client) {
|
|
|
17
18
|
|
|
18
19
|
return {
|
|
19
20
|
id, // id for existing transtaction
|
|
20
|
-
|
|
21
|
+
orderId, // order ID, optional
|
|
22
|
+
amount, // amount for new transtaction as is, else - preCreated
|
|
21
23
|
status, // payment status
|
|
22
24
|
service, // service name
|
|
23
25
|
referer, // redirect user after finish of transaction
|
|
@@ -12,7 +12,9 @@ const {
|
|
|
12
12
|
payeeId,
|
|
13
13
|
} = config.integrations?.portmone || {};
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const pg = pgClients.client;
|
|
16
|
+
|
|
17
|
+
export default async function portmoneCheckStatus(id, { body, origin } = {}) {
|
|
16
18
|
if (!payeeId) throw new Error('empty integration params: payeeId');
|
|
17
19
|
if (!id) throw new Error('not enough params: id');
|
|
18
20
|
|
|
@@ -31,12 +33,25 @@ export default async function portmoneCheckStatus(id, origin, pg = pgClients.cli
|
|
|
31
33
|
|
|
32
34
|
const isValidOrigin = origin && origin === host;
|
|
33
35
|
|
|
34
|
-
if (!(login && password) && !isValidOrigin) {
|
|
36
|
+
/* if (!(login && password) && !isValidOrigin) {
|
|
35
37
|
throw new Error('empty integration params: portmone 2');
|
|
38
|
+
} */
|
|
39
|
+
|
|
40
|
+
if (!isValidOrigin) {
|
|
41
|
+
throw new Error('access denied');
|
|
36
42
|
}
|
|
37
43
|
|
|
44
|
+
const mockResult = {
|
|
45
|
+
...body || {},
|
|
46
|
+
paytype: !body?.RESULT || body?.CARD_MASK ? 'card' : null,
|
|
47
|
+
billAmount: body?.BILL_AMOUNT || data.amount,
|
|
48
|
+
receipt_url: body?.RECEIPT_URL,
|
|
49
|
+
payment_id: body?.SHOPBILLID,
|
|
50
|
+
status: !body?.RESULT || body?.RESULT === '0' ? 'success' : 'error',
|
|
51
|
+
};
|
|
52
|
+
|
|
38
53
|
// ? likely cause timeout, skip be origin check or check credentials i.e.: login, password
|
|
39
|
-
const response = isValidOrigin ?
|
|
54
|
+
const response = isValidOrigin ? mockResult : await request({
|
|
40
55
|
url: `${host}/gateway/`,
|
|
41
56
|
method: 'POST',
|
|
42
57
|
formData,
|
|
@@ -7,26 +7,22 @@ import createPayment from './createPayment.js';
|
|
|
7
7
|
|
|
8
8
|
const { prefix = '/api' } = config;
|
|
9
9
|
|
|
10
|
-
const { table = 'pay.transactions' } = config.integrations?.pay || {};
|
|
10
|
+
const { table = 'pay.transactions', backUrl } = config.integrations?.pay || {};
|
|
11
11
|
const { host = 'https://www.portmone.com.ua', payeeId } = config.integrations?.portmone || {};
|
|
12
12
|
|
|
13
13
|
export default async function portmoneCreateTransaction({
|
|
14
|
-
id,
|
|
14
|
+
id, orderId, amount, referer, uid, debug, expiredDate, description, action, currency = 'uah', language = 'uk', paytypes,
|
|
15
15
|
}, pg = pgClients.client) {
|
|
16
|
-
if (!host) {
|
|
17
|
-
throw new Error('
|
|
16
|
+
if (!host || !payeeId || !backUrl) {
|
|
17
|
+
throw new Error('invalid portmone integration settings');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
if (!
|
|
21
|
-
throw new Error('
|
|
20
|
+
if (paytypes && paytypes.split(',').find(el => !['card', 'portmone', 'token', 'clicktopay', 'privat', 'gpay', 'createtokenonly', 'gpayonly', 'applepay', 'applepayonly', 'link'].includes(el))) {
|
|
21
|
+
throw new Error('invalid params: paytypes');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
if (!id && !
|
|
25
|
-
throw new Error('not enough params: id or
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!rootUrl) {
|
|
29
|
-
throw new Error('not enough params: rootUrl required');
|
|
24
|
+
if (!id && !amount) {
|
|
25
|
+
throw new Error('not enough params: id or amount required');
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
if (!uid) {
|
|
@@ -38,18 +34,25 @@ export default async function portmoneCreateTransaction({
|
|
|
38
34
|
const paymentData = id && pg.pk?.[table]
|
|
39
35
|
? await pg.query(`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]}=$1`, [id]).then(el => el.rows?.[0])
|
|
40
36
|
: await createPayment({
|
|
37
|
+
service: 'portmone',
|
|
41
38
|
paymentId,
|
|
42
|
-
|
|
39
|
+
orderId,
|
|
40
|
+
amount,
|
|
43
41
|
referer,
|
|
44
42
|
uid,
|
|
45
|
-
|
|
43
|
+
expiredDate,
|
|
44
|
+
description,
|
|
45
|
+
action,
|
|
46
|
+
currency,
|
|
47
|
+
language,
|
|
48
|
+
paytypes,
|
|
46
49
|
});
|
|
47
50
|
|
|
48
51
|
if (!paymentData?.id) {
|
|
49
52
|
throw new Error('payment not found / creation failed');
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
const paymentAmount = id ? paymentData.amount :
|
|
55
|
+
const paymentAmount = id ? paymentData.amount : amount;
|
|
53
56
|
|
|
54
57
|
if (!paymentAmount) {
|
|
55
58
|
throw new Error('invalid payment amount');
|
|
@@ -57,12 +60,13 @@ export default async function portmoneCreateTransaction({
|
|
|
57
60
|
|
|
58
61
|
const formData = {
|
|
59
62
|
payee_id: payeeId,
|
|
60
|
-
description: `Послуга - поповнення рахунку на суму ${paymentAmount} грн`,
|
|
63
|
+
description: description || `Послуга - поповнення рахунку на суму ${paymentAmount} грн`,
|
|
61
64
|
shop_order_number: paymentData.id,
|
|
62
65
|
bill_amount: paymentAmount,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
bill_currency: currency.toUpperCase(),
|
|
67
|
+
success_url: `${backUrl}${prefix}/portmone/${paymentData.id}/success`,
|
|
68
|
+
failure_url: `${backUrl}${prefix}/portmone/${paymentData.id}/error`,
|
|
69
|
+
lang: language === 'uk' ? 'ua' : language,
|
|
66
70
|
};
|
|
67
71
|
|
|
68
72
|
// portmone always returns error without simple: false options
|
|
@@ -15,13 +15,15 @@ const controller = {
|
|
|
15
15
|
const newDate = () => {
|
|
16
16
|
const date = new Date();
|
|
17
17
|
const tzOffset = date.getTimezoneOffset();
|
|
18
|
-
const
|
|
19
|
-
return new Date(
|
|
18
|
+
const currentTimeWithTimezone = date - tzOffset * 60 * 1000;
|
|
19
|
+
return new Date(currentTimeWithTimezone).toISOString();
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
const { table = 'pay.transactions' } = config.integrations?.pay || {};
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const pg = pgClients.client;
|
|
25
|
+
|
|
26
|
+
export default async function processTransaction(id, { body, origin } = {}) {
|
|
25
27
|
if (!id) throw new Error('not enough params: id');
|
|
26
28
|
|
|
27
29
|
const {
|
|
@@ -39,7 +41,7 @@ export default async function processTransaction(id, origin, pg = pgClients.clie
|
|
|
39
41
|
throw new Error('Transaction was already processed');
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
const result = await controller[service](id, origin);
|
|
44
|
+
const result = await controller[service](id, { origin, body });
|
|
43
45
|
|
|
44
46
|
const { status = 'error', err_description: err } = result || {};
|
|
45
47
|
|
|
@@ -57,6 +59,10 @@ export default async function processTransaction(id, origin, pg = pgClients.clie
|
|
|
57
59
|
table,
|
|
58
60
|
data: {
|
|
59
61
|
data: result,
|
|
62
|
+
payment_id: result?.payment_id, // payment service ID
|
|
63
|
+
customer: result?.customer, // payee
|
|
64
|
+
paytype: result?.paytype, // card, gpay etc
|
|
65
|
+
receipt_url: result?.receipt_url,
|
|
60
66
|
status,
|
|
61
67
|
balance: +result.amount + +balance,
|
|
62
68
|
success_date: newDate(),
|
|
@@ -39,8 +39,8 @@ export default async function paymentInfo({
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const { rows = [] } = await pg.query(
|
|
42
|
-
`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]} = $1 and user_id = $2`,
|
|
43
|
-
[params.id, user.uid],
|
|
42
|
+
`select ${pg.pk?.[table]} as id, * from ${table} where ${pg.pk?.[table]} = $1 and ${user?.user_type?.includes?.('admin') ? '1=1' : 'user_id = $2'}`,
|
|
43
|
+
[params.id, user?.user_type?.includes?.('admin') ? null : user.uid].filter(Boolean),
|
|
44
44
|
);
|
|
45
45
|
|
|
46
46
|
if (!rows.length) {
|
|
@@ -6,32 +6,27 @@ import createTransaction from '../../../plugins/payment/liqpay.createTransaction
|
|
|
6
6
|
|
|
7
7
|
export default async function liqpayRedirect(req, reply) {
|
|
8
8
|
const {
|
|
9
|
-
|
|
9
|
+
headers, pg = pgClients.client, query = {}, user = {},
|
|
10
10
|
} = req;
|
|
11
11
|
|
|
12
12
|
if (!user?.uid) {
|
|
13
13
|
return reply.status(401).send('unauthorized');
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const domain = hostname?.split?.(':')?.shift?.();
|
|
17
|
-
const rootUrl = `${protocol || 'https'}://${domain}`;
|
|
18
|
-
|
|
19
16
|
const paymentData = query.id ? await paymentInfo(query.id) : {
|
|
20
|
-
|
|
17
|
+
orderId: query.order_id,
|
|
18
|
+
amount: query.amount, // amount for new transtaction as is, else - preCreated
|
|
21
19
|
service: 'portmone',
|
|
22
20
|
referer: headers?.referer, // redirect user after finish of transaction
|
|
23
21
|
uid: user.uid, // user id
|
|
24
22
|
};
|
|
25
23
|
|
|
26
|
-
// redirect back from payment service to API endpoint, provide protocol + domain
|
|
27
|
-
Object.assign(paymentData, { rootUrl });
|
|
28
|
-
|
|
29
24
|
if (query.id && paymentData.status === 'success') {
|
|
30
25
|
return reply.status(400).send('already processed');
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
if (!paymentData?.
|
|
34
|
-
return reply.status(400).send('
|
|
28
|
+
if (!paymentData?.amount) {
|
|
29
|
+
return reply.status(400).send('amount is required');
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
// create if not exists, then request to payment service
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import pgClients from '../../../plugins/pg/pgClients.js';
|
|
8
8
|
|
|
9
9
|
import processTransaction from '../../../plugins/payment/processTransaction.js';
|
|
10
|
+
import dataUpdate from '../../../plugins/crud/dataUpdate.js';
|
|
10
11
|
|
|
11
12
|
// import sendNotification from '../../../utils/sendNotification.js';
|
|
12
13
|
|
|
@@ -4,36 +4,31 @@ import pgClients from '../../../plugins/pg/pgClients.js';
|
|
|
4
4
|
import paymentInfo from '../../../plugins/payment/paymentInfo.js';
|
|
5
5
|
import createTransaction from '../../../plugins/payment/monopay.createTransaction.js';
|
|
6
6
|
|
|
7
|
-
// http://billing.local.ua/api/monopay-redirect?
|
|
7
|
+
// http://billing.local.ua/api/monopay-redirect?amount=50
|
|
8
8
|
|
|
9
9
|
export default async function monopayRedirect(req, reply) {
|
|
10
10
|
const {
|
|
11
|
-
|
|
11
|
+
headers, pg = pgClients.client, query = {}, user = {},
|
|
12
12
|
} = req;
|
|
13
13
|
|
|
14
14
|
if (!user?.uid) {
|
|
15
15
|
return reply.status(401).send('unauthrized');
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const domain = hostname?.split?.(':')?.shift?.();
|
|
19
|
-
const rootUrl = `${protocol || 'https'}://${domain}`;
|
|
20
|
-
|
|
21
18
|
const paymentData = query.id ? await paymentInfo(query.id) : {
|
|
22
|
-
|
|
19
|
+
orderId: query.order_id, // order ID, optional
|
|
20
|
+
amount: query.amount, // amount for new transtaction as is, else - preCreated
|
|
23
21
|
service: 'portmone',
|
|
24
22
|
referer: headers?.referer, // redirect user after finish of transaction
|
|
25
23
|
uid: user.uid, // user id
|
|
26
24
|
};
|
|
27
25
|
|
|
28
|
-
// redirect back from payment service to API endpoint, provide protocol + domain
|
|
29
|
-
Object.assign(paymentData, { rootUrl });
|
|
30
|
-
|
|
31
26
|
if (query.id && paymentData.status === 'success') {
|
|
32
27
|
return reply.status(400).send('already processed');
|
|
33
28
|
}
|
|
34
29
|
|
|
35
|
-
if (!paymentData?.
|
|
36
|
-
return reply.status(400).send('
|
|
30
|
+
if (!paymentData?.amount) {
|
|
31
|
+
return reply.status(400).send('amount is required');
|
|
37
32
|
}
|
|
38
33
|
|
|
39
34
|
// create if not exists, then request to payment service
|
|
@@ -11,7 +11,7 @@ import createTransaction from '../../../plugins/payment/portmone.createTransacti
|
|
|
11
11
|
* @alias PortmonePaymentRedirect
|
|
12
12
|
* @summary redirect на сервіс оплати карткою
|
|
13
13
|
* @type api
|
|
14
|
-
* @param {String}
|
|
14
|
+
* @param {String} amount - Сума поповнення рахунку
|
|
15
15
|
* @returns {Number} status - номер помилки. Повертається, якщо була допущенна помилка в отриманих параметрах, або з бази. Це може бути 400 або 500
|
|
16
16
|
* @returns {String} error - опис помилки
|
|
17
17
|
* @returns {String} redirect - шлях до переадресації
|
|
@@ -22,31 +22,26 @@ import createTransaction from '../../../plugins/payment/portmone.createTransacti
|
|
|
22
22
|
|
|
23
23
|
export default async function portmoneRedirect(req, reply) {
|
|
24
24
|
const {
|
|
25
|
-
|
|
25
|
+
headers, pg = pgClients.client, user = {}, query = {},
|
|
26
26
|
} = req;
|
|
27
27
|
if (!user?.uid) {
|
|
28
28
|
return reply.status(401).send('unauthrized');
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const domain = hostname?.split?.(':')?.shift?.();
|
|
32
|
-
const rootUrl = `${protocol || 'https'}://${domain}`;
|
|
33
|
-
|
|
34
31
|
const paymentData = query.id ? await paymentInfo(query.id) : {
|
|
35
|
-
|
|
32
|
+
orderId: query.order_id, // order ID, optional
|
|
33
|
+
amount: query.amount, // amount for new transtaction as is, else - preCreated
|
|
36
34
|
service: 'portmone',
|
|
37
35
|
referer: headers?.referer, // redirect user after finish of transaction
|
|
38
36
|
uid: user.uid, // user id
|
|
39
37
|
};
|
|
40
38
|
|
|
41
|
-
// redirect back from payment service to API endpoint, provide protocol + domain
|
|
42
|
-
Object.assign(paymentData, { rootUrl });
|
|
43
|
-
|
|
44
39
|
if (query.id && paymentData.status === 'success') {
|
|
45
40
|
return reply.status(400).send('already processed');
|
|
46
41
|
}
|
|
47
42
|
|
|
48
|
-
if (!paymentData?.
|
|
49
|
-
return reply.status(400).send('
|
|
43
|
+
if (!paymentData?.amount) {
|
|
44
|
+
return reply.status(400).send('amount is required');
|
|
50
45
|
}
|
|
51
46
|
|
|
52
47
|
// create if not exists, then request to payment service
|
|
@@ -48,7 +48,7 @@ export default async function portmoneStatus(req, reply) {
|
|
|
48
48
|
|
|
49
49
|
const paymentId = body?.shopOrderNumber || params?.id;
|
|
50
50
|
|
|
51
|
-
const trx = await processTransaction(paymentId, origin);
|
|
51
|
+
const trx = await processTransaction(paymentId, { body, origin });
|
|
52
52
|
|
|
53
53
|
if (!trx) {
|
|
54
54
|
return reply.status(404).send('Transaction not found');
|