@opengis/pay 1.3.2 → 2.0.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.
Files changed (34) hide show
  1. package/README.md +45 -12
  2. package/module/pay/pt/marketplace-service-payment-client-template.html +5 -5
  3. package/package.json +9 -7
  4. package/plugin.js +2 -0
  5. package/server/migrations/7_transactions.sql +35 -0
  6. package/server/plugins/hook.js +27 -10
  7. package/server/plugins/payment/createPayment.js +29 -0
  8. package/server/plugins/payment/createTransaction.js +26 -0
  9. package/server/plugins/payment/liqpay.checkStatus.js +43 -0
  10. package/server/plugins/payment/liqpay.createTransaction.js +103 -0
  11. package/server/plugins/payment/monopay.checkStatus.js +48 -0
  12. package/server/plugins/payment/monopay.createTransaction.js +116 -0
  13. package/server/plugins/payment/paymentInfo.js +26 -0
  14. package/server/plugins/payment/portmone.checkStatus.js +51 -0
  15. package/server/plugins/payment/portmone.createTransaction.js +94 -0
  16. package/server/plugins/payment/processTransaction.js +73 -0
  17. package/server/routes/{portmone → core}/controllers/payment.info.js +32 -29
  18. package/server/routes/core/index.mjs +12 -0
  19. package/server/routes/liqpay/controllers/liqpay.redirect.js +25 -115
  20. package/server/routes/liqpay/controllers/liqpay.status.js +31 -150
  21. package/server/routes/monopay/controllers/monopay.redirect.js +25 -117
  22. package/server/routes/monopay/controllers/monopay.status.js +8 -56
  23. package/server/routes/portmone/controllers/portmone.redirect.js +27 -116
  24. package/server/routes/portmone/controllers/portmone.status.js +36 -136
  25. package/server/routes/portmone/index.mjs +5 -6
  26. package/utils.js +11 -0
  27. package/server/migrations/1_users.sql +0 -121
  28. package/server/migrations/2_accounts.sql +0 -59
  29. package/server/migrations/3_utils.sql +0 -74
  30. package/server/migrations/4_subscriptions.sql +0 -36
  31. package/server/migrations/5_payments.sql +0 -39
  32. package/server/migrations/6_invoices.sql +0 -55
  33. package/server/routes/liqpay/utils/check.status.js +0 -34
  34. package/server/routes/portmone/utils/portmone.check.status.js +0 -34
@@ -1,38 +1,26 @@
1
- import LiqPay from 'liqpayjs-sdk';
2
- import path from 'node:path';
3
- import { readFileSync } from 'node:fs';
4
- import { fileURLToPath } from 'url';
1
+ /* eslint-disable no-console */
2
+ // import path from 'node:path';
3
+ // import { readFileSync } from 'node:fs';
4
+ // import { fileURLToPath } from 'url';
5
5
 
6
- import config from '../../../../config.js';
6
+ // import config from '../../../../config.js';
7
7
  import pgClients from '../../../plugins/pg/pgClients.js';
8
- import dataUpdate from '../../../plugins/crud/dataUpdate.js';
9
8
 
10
- import sendNotification from '../../../utils/sendNotification.js';
9
+ import processTransaction from '../../../plugins/payment/processTransaction.js';
11
10
 
12
- import checkStatus from '../utils/check.status.js';
11
+ // import sendNotification from '../../../utils/sendNotification.js';
13
12
 
14
- const dirname = path.dirname(fileURLToPath(import.meta.url));
15
- const ptBody = readFileSync(path.join(dirname, '../../../../module/pay/pt/marketplace-service-payment-client-template.html'), { encoding: 'utf8' });
16
-
17
- const newDate = () => {
18
- const date = new Date();
19
- const tzOffset = date.getTimezoneOffset();
20
- const currentTimeWithTimezome = date - tzOffset * 60 * 1000;
21
- return new Date(currentTimeWithTimezome).toISOString();
22
- };
23
-
24
- const { publicKey, privateKey } = config.integrations?.liqpay || {};
25
- const liqpay = new LiqPay(publicKey, privateKey);
13
+ // const dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ // const ptBody = readFileSync(path.join(dirname, '../../../../module/pay/pt/marketplace-service-payment-client-template.html'), { encoding: 'utf8' });
26
15
 
27
16
  /**
28
- * Апі перенаправлює з liqpay назад на сторінку замовлення
17
+ * Апі перенаправлює з liqpay назад на сторінку транзакції
29
18
  *
30
19
  * @method POST
31
20
  * @alias liqpayStatus
32
- * @summary redirect з liqpay на сторінку замовлення
21
+ * @summary redirect з liqpay на сторінку транзакції
33
22
  * @type api
34
- * @param {String} paymentId - Ідентифікатор замовлення
35
- * @param {String} refillSum - Сума поповнення рахунку
23
+ * @param {String} params.id - Ідентифікатор транзакції
36
24
  * @returns {Number} status - номер помилки. Повертається, якщо була допущенна помилка в отриманих параметрах, або з бази. Це може бути 400 або 500
37
25
  * @returns {String} error - опис помилки
38
26
  * @returns {String} redirect - шлях до переадресації
@@ -43,146 +31,38 @@ const liqpay = new LiqPay(publicKey, privateKey);
43
31
 
44
32
  export default async function liqpayStatus(req, reply) {
45
33
  const {
46
- pg = pgClients.client, params = {}, headers = {}, body = {}, user = {}, unittest,
34
+ pg = pgClients.client, headers = {}, params = {},
47
35
  } = req;
48
36
 
49
- if (!publicKey || !privateKey) {
50
- return reply.status(400).send('invalid liqpay integration settings');
51
- }
52
-
53
- const origin = headers?.origin || headers?.referer;
54
- const { status: responseStatus, id: paymentId } = params;
37
+ const { origin = headers?.referer } = headers;
55
38
 
56
- if (!paymentId) {
39
+ if (!params?.id) {
57
40
  return reply.status(400).send('not enough params: id');
58
41
  }
59
42
 
60
- if (req.method === 'GET') {
61
- const result = await checkStatus(params.id) || {};
62
- const { status, err_description: err } = result;
63
- if (status !== 'success' && !unittest) {
64
- return reply.status(400).send(err || 'Помилка перевірки статусу платежу');
65
- }
66
- Object.assign(body, result);
43
+ if (params.status !== 'success') {
44
+ return reply.status(400).send('transaction aborted');
67
45
  }
68
46
 
69
- if (req.method === 'POST') {
70
- if (!body?.data || !body?.signature) {
71
- return reply.status(400).send('not enough body params: data, signature');
72
- }
73
-
74
- const sign = liqpay.str_to_sign(
75
- privateKey
76
- + body.data
77
- + privateKey,
78
- );
79
-
80
- if (body.signature !== sign) {
81
- console.warn('payments/warn', JSON.stringify({
82
- type: 'status',
83
- service: 'liqpay',
84
- paymentId,
85
- origin,
86
- uid: user?.uid || '0',
87
- message: 'Transaction signature does not match',
88
- }));
89
- return reply.status(403).send('access restricted: signature mismatch');
90
- }
91
- }
92
-
93
- const data = await pg.query('select * from billing.payments where payment_id=$1', [paymentId])
94
- .then(el => el.rows?.[0] || {});
95
-
96
- const {
97
- payment_id: trx,
98
- payment_amount: refillSum = 0,
99
- referer_url: redirectUrl,
100
- payment_status: trxStatus,
101
- payment_service: paymentService,
102
- account_id: accountId,
103
- } = data;
104
-
105
- if (trxStatus === 'success') {
106
- console.info('payments/skip', JSON.stringify({
107
- type: 'status',
108
- service: 'liqpay',
109
- paymentId,
110
- origin,
111
- uid: user?.uid || '0',
112
- message: 'Transaction was already processed',
113
- }));
114
- return reply.status(400).send('Transaction was already processed');
115
- }
47
+ const trx = await processTransaction(params.id);
116
48
 
117
49
  if (!trx) {
118
50
  return reply.status(404).send('Transaction not found');
119
51
  }
120
52
 
121
- if (paymentService !== 'liqpay') {
122
- return reply.status(400).send('Payment not registered via liqpay service');
123
- }
124
-
125
- console.log('payments/info', JSON.stringify({
126
- type: 'status',
127
- service: 'liqpay',
128
- paymentId,
129
- origin,
130
- uid: user?.uid || '0',
131
- refill: refillSum,
132
- body,
133
- params,
134
- }));
135
-
136
- // last trx contains total balance
137
- const { total_balance: totalBalance = 0 } = await pg.query(`select total_balance from billing.payments
138
- where account_id=$1
139
- and payment_status in ('success', 'error')
140
- and total_balance is not null
141
- order by cdate desc limit 1`, [accountId]).then(el => el.rows?.[0] || {});
142
-
143
- await dataUpdate({
144
- table: 'billing.payments',
145
- data: {
146
- payment_data: body,
147
- payment_status: body.status || responseStatus,
148
- total_balance: +refillSum + +totalBalance,
149
- success_date: newDate(),
150
- },
151
- id: paymentId,
152
- uid: user?.uid || '0',
153
- });
154
-
155
- if (unittest) {
156
- console.log(`id:${trx}, status: ${responseStatus}, before: ${totalBalance || 0}, after: ${+refillSum + +totalBalance} (${refillSum})`);
157
- }
158
-
159
- const { account_email: to } = await pg.query('select account_email from billing.accounts where account_id=$1', [accountId])
160
- .then(el => el.rows?.[0] || {});
161
-
162
- console.log('payments/info2', JSON.stringify({
163
- type: 'status',
164
- service: 'liqpay',
165
- paymentId,
166
- responseStatus,
167
- to,
168
- origin,
169
- redirect: redirectUrl,
170
- uid: user.uid || '0',
171
- }));
172
-
173
- Object.assign(data, {
174
- domain: req.hostname,
175
- total_balance: +refillSum + +totalBalance,
176
- });
177
-
178
- if (to) {
53
+ /* if (to) {
54
+ Object.assign(trx, {
55
+ domain: req.hostname,
56
+ total_balance: trx.balance,
57
+ });
179
58
  const pt = (ptBody || 'empty template')
180
59
  .replace(/{{domain}}/g, req.hostname)
181
- .replace(/{{total_balance}}/g, +(refillSum || 0) + (+totalBalance || 0))
60
+ .replace(/{{balance}}/g, trx.balance || '0')
182
61
  .replace(/{{username}}/g, to)
183
- .replace(/{{created_at}}/g, (data?.cdate || data?.created_at || new Date()).toLocaleString('uk-UA'))
184
- .replace(/{{payment_status}}/g, responseStatus)
185
- .replace(/{{payment_amount}}/g, refillSum || 0);
62
+ .replace(/{{created_at}}/g, (trx?.created_at || new Date()).toLocaleString('uk-UA'))
63
+ .replace(/{{status}}/g, trx.status)
64
+ .replace(/{{amount}}/g, trx.amount || 0);
65
+
186
66
  const options = {
187
67
  pg,
188
68
  to,
@@ -191,6 +71,7 @@ export default async function liqpayStatus(req, reply) {
191
71
  nocache: config?.local,
192
72
  };
193
73
  await sendNotification(options);
194
- }
195
- return reply.redirect(redirectUrl || '/');
74
+ } */
75
+
76
+ return reply.redirect(trx.referer_url || '/');
196
77
  }
@@ -1,141 +1,49 @@
1
1
  import config from '../../../../config.js';
2
2
  import pgClients from '../../../plugins/pg/pgClients.js';
3
- import dataInsert from '../../../plugins/crud/dataInsert.js';
4
- import dataUpdate from '../../../plugins/crud/dataUpdate.js';
5
3
 
6
- const { prefix = '/api' } = config;
7
- const {
8
- host = 'https://api.monobank.ua', token, paymentRedirect, merchantPaymInfo,
9
- } = config.integrations?.monopay || {};
4
+ import paymentInfo from '../../../plugins/payment/paymentInfo.js';
5
+ import createTransaction from '../../../plugins/payment/monopay.createTransaction.js';
10
6
 
11
7
  // http://billing.local.ua/api/monopay-redirect?price=50
12
8
 
13
9
  export default async function monopayRedirect(req, reply) {
14
10
  const {
15
- pg = pgClients.client, query = {}, user = {}, headers = {},
11
+ hostname, headers, pg = pgClients.client, query = {}, user = {}, protocol,
16
12
  } = req;
17
13
 
18
- if (!token) {
19
- return reply.status(400).send('invalid monopay integration settings');
14
+ if (!user?.uid) {
15
+ return reply.status(401).send('unauthrized');
20
16
  }
21
17
 
22
- const domain = req.hostname.split(':').shift();
18
+ const domain = hostname?.split?.(':')?.shift?.();
19
+ const rootUrl = `${protocol || 'https'}://${domain}`;
23
20
 
24
- const paymentData = query.id ? await pg.query('select payment_id, account_id, payment_amount, payment_status from billing.payments where payment_id=$1', [query.id])
25
- .then(el => el.rows?.[0] || {}) : {};
21
+ const paymentData = query.id ? await paymentInfo(query.id) : {
22
+ price: query.price, // price for new transtaction as is, else - preCreated
23
+ service: 'portmone',
24
+ referer: headers?.referer, // redirect user after finish of transaction
25
+ uid: user.uid, // user id
26
+ };
27
+
28
+ // redirect back from payment service to API endpoint, provide protocol + domain
29
+ Object.assign(paymentData, { rootUrl });
26
30
 
27
- if (paymentData.payment_status && paymentData.payment_status === 'success') {
31
+ if (query.id && paymentData.status === 'success') {
28
32
  return reply.status(400).send('already processed');
29
33
  }
30
34
 
31
- const paymentAmount = paymentData.payment_amount || query.price;
32
-
33
- if (!paymentAmount) {
35
+ if (!paymentData?.price) {
34
36
  return reply.status(400).send('price is required');
35
37
  }
36
38
 
37
- const accountId = paymentData.account_id
38
- || (user?.uid ? await pg.query('select account_id from billing.account_user where user_id=$1 limit 1', [user?.uid]).then(el => el.rows?.[0]?.account_id) : null);
39
-
40
- if (!accountId) {
41
- return reply.status(400).send('account_id is required');
42
- }
43
-
44
- const { payment_id: paymentId } = !paymentData.payment_id
45
- ? await dataInsert({
46
- table: 'billing.payments',
47
- data: {
48
- payment_amount: paymentAmount,
49
- account_id: accountId,
50
- payment_service: 'monopay',
51
- payment_status: 'inprogress',
52
- },
53
- uid: user?.uid || '0',
54
- }).then(el => el.rows?.[0] || {})
55
- : { payment_id: paymentData.payment_id };
39
+ // create if not exists, then request to payment service
40
+ const result = await createTransaction(paymentData, pg);
56
41
 
57
- if (!paymentId) {
58
- return { message: 'payment not found', status: 404 };
42
+ // raw response from payment service
43
+ if (config.debug && query?.debug) {
44
+ return result;
59
45
  }
60
46
 
61
- await dataUpdate({
62
- table: 'billing.payments',
63
- id: paymentId,
64
- data: {
65
- referer_url: paymentRedirect ? paymentRedirect.replace(/{{id}}/g, paymentId) : headers?.referer,
66
- payment_status: 'inprogress',
67
- payment_service: 'monopay',
68
- },
69
- uid: user?.uid || '0',
70
- });
71
-
72
- const protocol = headers?.referer?.split?.('://')?.shift?.() || req.protocol || 'https';
73
- const rootPageUrl = `${protocol || 'https'}://${domain}`;
74
-
75
- // redirect to api to check payment status and redirect user to page afterwards
76
- const serverUrl = `${rootPageUrl}${prefix}/monopay/${paymentId}/success`;
77
-
78
- const paymentOptions = {
79
- amount: paymentAmount * 100, // price=1 === 0.01 uah
80
- ccy: 980, // ISO 4217, 980 = UAH
81
- merchantPaymInfo,
82
- redirectUrl: serverUrl,
83
- // redirectUrl: (paymentRedirect ? `${rootPageUrl}${paymentRedirect.replace(/{{id}}/g, paymentId)}` : headers?.referer) || rootPageUrl,
84
- webHookUrl: serverUrl,
85
- validity: 86400, // 1 day by default, then invalid
86
- paymentType: 'debit',
87
- // displayType: 'iframe', // default = null > url to mono
88
- };
89
-
90
- const logObj = {
91
- type: 'redirect',
92
- paymentId,
93
- accountId,
94
- price: paymentAmount,
95
- service: 'monopay',
96
- domain,
97
- paymentRedirectUrl: serverUrl,
98
- uid: user?.uid || '0',
99
- };
100
-
101
- try {
102
- // create payment, get redirect url
103
- const resp = await fetch(`${host}/api/merchant/invoice/create`, {
104
- method: 'POST',
105
- headers: { 'x-token': token },
106
- body: JSON.stringify(paymentOptions),
107
- signal: AbortSignal.timeout(5000),
108
- });
109
-
110
- const contentType = resp.headers.get('Content-Type');
111
-
112
- const body = contentType.includes('application/json') && resp.status === 200
113
- ? await resp.json()
114
- : await resp.text();
115
-
116
- if (query.debug) {
117
- return body;
118
- }
119
-
120
- await dataUpdate({
121
- table: 'billing.payments',
122
- id: paymentId,
123
- data: {
124
- payment_data: resp.status === 200 ? body : { response: body },
125
- },
126
- uid: user?.uid || '0',
127
- });
128
-
129
- console.log('payments/info', JSON.stringify({ ...logObj, response: body }));
130
-
131
- if (!body.pageUrl) {
132
- return reply.status(501).send('monopay service temporary unavailable');
133
- }
134
-
135
- return reply.redirect(body.pageUrl);
136
- }
137
- catch (err) {
138
- console.error('payments/error', JSON.stringify(logObj), err.toString(), err.stack);
139
- return reply.status(500).send(err.toString());
140
- }
47
+ // link to payment service
48
+ return reply.redirect(result);
141
49
  }
@@ -1,70 +1,22 @@
1
- import config from '../../../../config.js';
2
- import pgClients from '../../../plugins/pg/pgClients.js';
3
- import dataUpdate from '../../../plugins/crud/dataUpdate.js';
4
-
5
- const {
6
- host = 'https://api.monobank.ua',
7
- token,
8
- } = config.integrations?.monopay || {};
9
-
10
- const timeoutMs = 5000;
1
+ import processTransaction from '../../../plugins/payment/processTransaction.js';
11
2
 
12
3
  export default async function monopayStatus({
13
- pg = pgClients.client, params = {}, user = {},
4
+ params = {},
14
5
  }, reply) {
15
6
  const { id, status = 'success' } = params;
16
-
17
7
  if (!id) {
18
8
  return reply.status(400).send('not enough params: id');
19
9
  }
20
10
 
21
- const {
22
- paymentId, refererUrl, paymentStatus, invoiceId, accountId,
23
- } = await pg.query('select payment_id as "paymentId", account_id as "accountId", payment_status as "paymentStatus", referer_url as "refererUrl", payment_data->>\'invoiceId\' as "invoiceId" from billing.payments where payment_id=$1', [id])
24
- .then(el => el.rows?.[0] || {});
25
-
26
- if (!paymentId) {
27
- return reply.status(404).send('payment not found');
28
- }
29
- if (!invoiceId) {
30
- return reply.status(404).send('invoice not found');
31
- }
32
-
33
- if (['success', 'error'].includes(paymentStatus)) {
34
- return reply.status(404).send('payment already processed');
35
- }
36
-
37
11
  if (status !== 'success') {
38
- return reply.redirect(refererUrl);
12
+ return reply.status(400).send('transaction aborted');
39
13
  }
40
14
 
41
- const resp = await fetch(`${host}/api/merchant/invoice/status?invoiceId=${invoiceId}`, {
42
- method: 'GET',
43
- headers: { 'x-token': token },
44
- signal: AbortSignal.timeout(timeoutMs),
45
- }).catch(err => reply.status(501).send('monopay request timeout'));
46
-
47
- const contentType = resp.headers.get('Content-Type');
15
+ const trx = await processTransaction(id);
48
16
 
49
- const body = contentType.includes('application/json') && resp.status === 200
50
- ? await resp.json()
51
- : await resp.text();
52
-
53
- const totalBalanceBefore = await pg.query(`select total_balance from billing.payments
54
- where account_id=$1 and payment_status in ('success', 'error') and total_balance is not null order by cdate desc limit 1`, [accountId])
55
- .then(el => el.rows?.[0]?.total_balance || 0);
56
-
57
- await dataUpdate({
58
- table: 'billing.payments',
59
- id: paymentId,
60
- data: {
61
- total_balance: (body.amount / 100) + +totalBalanceBefore,
62
- success_date: new Date(), // newDate(),
63
- payment_status: body?.status || 'error',
64
- payment_data: resp.status === 200 ? body : { response: body },
65
- },
66
- uid: user?.uid || '0',
67
- });
17
+ if (!trx) {
18
+ return reply.status(404).send('Transaction not found');
19
+ }
68
20
 
69
- return reply.redirect(refererUrl || '/');
21
+ return reply.redirect(trx.referer_url || '/');
70
22
  }
@@ -1,11 +1,8 @@
1
- import request from 'request-promise';
2
-
3
1
  import config from '../../../../config.js';
4
2
  import pgClients from '../../../plugins/pg/pgClients.js';
5
- import dataInsert from '../../../plugins/crud/dataInsert.js';
6
- import dataUpdate from '../../../plugins/crud/dataUpdate.js';
7
3
 
8
- const { host, payeeId, paymentRedirect = config.paymentRedirect } = config.integrations?.pay || {};
4
+ import paymentInfo from '../../../plugins/payment/paymentInfo.js';
5
+ import createTransaction from '../../../plugins/payment/portmone.createTransaction.js';
9
6
 
10
7
  /**
11
8
  * Апі перенаправлює до стороннього сервісу оплати карткою онлайн
@@ -23,129 +20,43 @@ const { host, payeeId, paymentRedirect = config.paymentRedirect } = config.integ
23
20
  * @returns {String} file - шлях файла або його назва
24
21
  */
25
22
 
26
- export default async function PortmoneRedirect(req, reply) {
23
+ export default async function portmoneRedirect(req, reply) {
27
24
  const {
28
- pg = pgClients.client, user = {}, query = {}, headers = {}, unittest,
25
+ hostname, headers, pg = pgClients.client, user = {}, query = {}, protocol,
29
26
  } = req;
30
-
31
- if (!host) {
32
- return reply.status(400).send('empty integration host');
33
- }
34
-
35
- if (!payeeId) {
36
- return reply.status(400).send('empty integration payeeId');
27
+ if (!user?.uid) {
28
+ return reply.status(401).send('unauthrized');
37
29
  }
38
30
 
39
- const domain = req.hostname.split(':').shift();
40
-
41
- const data = query.id ? await pg.query('select payment_id, account_id, payment_amount, payment_status from billing.payments where payment_id=$1', [query.id])
42
- .then(el => el.rows?.[0] || {}) : {};
31
+ const domain = hostname?.split?.(':')?.shift?.();
32
+ const rootUrl = `${protocol || 'https'}://${domain}`;
43
33
 
44
- if (data.payment_status && data.payment_status === 'success') {
45
- return { message: 'already processed', status: 400 };
46
- }
47
-
48
- const paymentAmount = data.payment_amount || query.price;
49
-
50
- if (!paymentAmount) {
51
- return { message: 'price is required', status: 400 };
52
- }
34
+ const paymentData = query.id ? await paymentInfo(query.id) : {
35
+ price: query.price, // price for new transtaction as is, else - preCreated
36
+ service: 'portmone',
37
+ referer: headers?.referer, // redirect user after finish of transaction
38
+ uid: user.uid, // user id
39
+ };
53
40
 
54
- const accountId = data.account_id
55
- || (user?.uid ? await pg.query('select account_id from billing.account_user where user_id=$1 limit 1', [user?.uid]).then(el => el.rows?.[0]?.account_id) : null);
41
+ // redirect back from payment service to API endpoint, provide protocol + domain
42
+ Object.assign(paymentData, { rootUrl });
56
43
 
57
- if (!accountId) {
58
- return { message: 'account_id is required', status: 400 };
44
+ if (query.id && paymentData.status === 'success') {
45
+ return reply.status(400).send('already processed');
59
46
  }
60
47
 
61
- if ((!headers?.referer || !headers.referer.includes(domain))) {
62
- console.warn(
63
- 'payments/warn',
64
- 'invalid referer',
65
- 'portmone',
66
- domain,
67
- headers?.referer,
68
- user?.uid,
69
- );
70
- if (!config.auth?.disableRestricted && !unittest) {
71
- return reply.status(403).send('access restricted: referer');
72
- }
48
+ if (!paymentData?.price) {
49
+ return reply.status(400).send('price is required');
73
50
  }
74
51
 
75
- const { payment_id: paymentId } = !data.payment_id
76
- ? await dataInsert({
77
- table: 'billing.payments',
78
- data: {
79
- payment_amount: paymentAmount,
80
- account_id: accountId,
81
- payment_service: 'portmone',
82
- payment_status: 'inprogress',
83
- },
84
- uid: user?.uid || '0',
85
- }).then(el => el.rows?.[0] || {})
86
- : { payment_id: data.payment_id };
52
+ // create if not exists, then request to payment service
53
+ const result = await createTransaction(paymentData, pg);
87
54
 
88
- if (!paymentId) {
89
- return reply.status(404).send('payment not found');
55
+ // raw response from payment service
56
+ if (config.debug && query.debug) {
57
+ return result;
90
58
  }
91
59
 
92
- await dataUpdate({
93
- table: 'billing.payments',
94
- id: paymentId,
95
- data: {
96
- referer_url: paymentRedirect ? paymentRedirect.replace(/{{id}}/g, paymentId) : headers?.referer,
97
- payment_status: 'inprogress',
98
- },
99
- uid: user?.uid || '0',
100
- });
101
-
102
- const protocol = headers?.referer.split('://').shift();
103
-
104
- const formData = {
105
- payee_id: payeeId,
106
- description: `Послуга - поповнення рахунку на суму ${paymentAmount} грн`,
107
- shop_order_number: paymentId,
108
- bill_amount: paymentAmount,
109
- success_url: `${protocol || 'https'}://${domain}${config?.prefix || '/api'}/portmone/${paymentId}/success`,
110
- failure_url: `${protocol || 'https'}://${domain}${config?.prefix || '/api'}/portmone/${paymentId}/error`,
111
- lang: 'ua',
112
- };
113
-
114
- const logObj = {
115
- type: 'redirect',
116
- service: 'portmone',
117
- payeeId,
118
- paymentId,
119
- accountId,
120
- price: paymentAmount,
121
- domain,
122
- paymentRedirectUrl: paymentRedirect ? paymentRedirect.replace(/{{id}}/g, paymentId) : headers?.referer,
123
- uid: user?.uid || '0',
124
- };
125
-
126
- try {
127
- // portmone always returns error...
128
- const response = await request({
129
- url: `${host}/gateway/`,
130
- method: 'POST',
131
- formData,
132
- followAllRedirects: false,
133
- jar: true,
134
- timeout: 6000,
135
- simple: false, // prevent not 2xx code trigger catch
136
- resolveWithFullResponse: true,
137
- });
138
-
139
- // unusable code, just in case
140
- if (response.statusCode !== 302 || !response.headers?.location) {
141
- throw new Error(response);
142
- }
143
-
144
- console.log('payments/redirect', paymentId, response.headers?.location);
145
- return reply.redirect(response.headers.location);
146
- }
147
- catch (err) {
148
- console.error('payments/error', JSON.stringify(logObj), err.toString(), err.stack);
149
- return { error: err.toString(), status: 500 };
150
- }
60
+ // link to payment service
61
+ return reply.redirect(result);
151
62
  }