@opengis/pay 1.2.0 → 1.2.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/pay",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "type": "module",
5
5
  "description": "pay plugins for billing",
6
6
  "main": "plugin.js",
@@ -23,27 +23,27 @@ export default async function liqpayRedirect(req, reply) {
23
23
 
24
24
  const domain = req.hostname.split(':').shift();
25
25
 
26
- 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])
26
+ 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])
27
27
  .then(el => el.rows?.[0] || {}) : {};
28
28
 
29
- if (data.payment_status && data.payment_status === 'success') {
29
+ if (paymentData.payment_status && paymentData.payment_status === 'success') {
30
30
  return { message: 'already processed', status: 400 };
31
31
  }
32
32
 
33
- const paymentAmount = data.payment_amount || query.price;
33
+ const paymentAmount = paymentData.payment_amount || query.price;
34
34
 
35
35
  if (!paymentAmount) {
36
36
  return { message: 'price is required', status: 400 };
37
37
  }
38
38
 
39
- const accountId = data.account_id
39
+ const accountId = paymentData.account_id
40
40
  || (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
41
 
42
42
  if (!accountId) {
43
43
  return { message: 'account_id is required', status: 400 };
44
44
  }
45
45
 
46
- const { payment_id: paymentId } = !data.payment_id
46
+ const { payment_id: paymentId } = !paymentData.payment_id
47
47
  ? await dataInsert({
48
48
  table: 'billing.payments',
49
49
  data: {
@@ -54,7 +54,7 @@ export default async function liqpayRedirect(req, reply) {
54
54
  },
55
55
  uid: user?.uid || '0',
56
56
  }).then(el => el.rows?.[0] || {})
57
- : { payment_id: data.payment_id };
57
+ : { payment_id: paymentData.payment_id };
58
58
 
59
59
  if (!paymentId) {
60
60
  return { message: 'payment not found', status: 404 };
@@ -71,23 +71,26 @@ export default async function liqpayRedirect(req, reply) {
71
71
  });
72
72
 
73
73
  const protocol = headers?.referer?.split?.('://')?.shift?.() || req.protocol || 'https';
74
- const rootPageUrl = `${protocol || 'https'}://${domain}${config.port ? `:${config.port}` : ''}`;
74
+ const rootPageUrl = `${protocol || 'https'}://${domain}`;
75
75
 
76
- const resultUrl = paymentRedirect
76
+ /* const resultUrl = paymentRedirect
77
77
  ? `${rootPageUrl}${handlebarsSync.compile(paymentRedirect)({ id: paymentId })}`
78
- : headers?.referer || `${rootPageUrl}`;
78
+ : headers?.referer || `${rootPageUrl}`; */
79
+
80
+ // redirect to api to check payment status and redirect user to page afterwards
79
81
  const serverUrl = `${rootPageUrl}${prefix}/liqpay/${paymentId}/success`;
80
82
 
81
- const html = liqpay.cnb_form({
83
+ const paymentOptions = {
82
84
  action: 'pay',
83
85
  amount: paymentAmount,
84
86
  currency: 'UAH',
85
87
  description: 'regular payment',
86
88
  order_id: paymentId,
87
89
  version: '3',
88
- server_url: serverUrl, // update payment info - api url
89
- result_url: resultUrl, // redirect user after payment - page url
90
- });
90
+ // server_url: serverUrl, // update payment info - api url, does not work on sdk package out of the box
91
+ result_url: serverUrl, // redirect user after payment - page url
92
+ };
93
+ const { data, signature } = liqpay.cnb_object(paymentOptions) || {};
91
94
 
92
95
  const logObj = {
93
96
  type: 'redirect',
@@ -96,25 +99,21 @@ export default async function liqpayRedirect(req, reply) {
96
99
  price: paymentAmount,
97
100
  service: 'liqpay',
98
101
  domain,
99
- paymentRedirectUrl: resultUrl,
102
+ paymentRedirectUrl: serverUrl,
100
103
  serverUrl,
101
104
  uid: user?.uid || '0',
102
105
  };
103
106
 
104
107
  if (config.debug && query?.debug) {
108
+ const html = liqpay.cnb_form(paymentOptions);
105
109
  return reply.status(302).headers({ 'Content-Type': 'text/html' }).send(html);
106
110
  }
107
111
 
108
- const body = {
109
- data: html.match(/<input type="hidden" name="data" value="([^"]+)"/)?.[1],
110
- signature: html.match(/<input type="hidden" name="signature" value="([^"]+)"/)?.[1],
111
- };
112
-
113
112
  try {
114
113
  const response = await request({
115
114
  url: `${host}/api/3/checkout`,
116
115
  method: 'POST',
117
- form: body,
116
+ form: { data, signature },
118
117
  followAllRedirects: false,
119
118
  jar: true,
120
119
  timeout: 6000,
@@ -6,6 +6,8 @@ import {
6
6
 
7
7
  import { sendNotification } from '@opengis/admin/utils.js';
8
8
 
9
+ import checkStatus from '../utils/check.status.js';
10
+
9
11
  const newDate = () => {
10
12
  const date = new Date();
11
13
  const tzOffset = date.getTimezoneOffset();
@@ -42,29 +44,44 @@ export default async function liqpayStatus(req, reply) {
42
44
  return reply.status(400).send('invalid liqpay integration settings');
43
45
  }
44
46
 
45
- if (!body?.data || !body?.signature) {
46
- return reply.status(400).send('not enough body params: data, signature');
47
- }
48
-
49
- const sign = liqpay.str_to_sign(
50
- privateKey
51
- + body.data
52
- + privateKey,
53
- );
54
-
55
47
  const origin = headers?.origin || headers?.referer;
56
48
  const { status: responseStatus, id: paymentId } = params;
57
49
 
58
- if (body.signature !== sign) {
59
- logger.file('payments/warn', {
60
- type: 'status',
61
- service: 'liqpay',
62
- paymentId,
63
- origin,
64
- uid: user?.uid || '0',
65
- message: 'Transaction signature does not match',
66
- });
67
- return reply.status(403).send('access restricted: signature mismatch');
50
+ if (!paymentId) {
51
+ return reply.status(400).send('not enough params: id');
52
+ }
53
+
54
+ if (req.method === 'GET') {
55
+ const result = await checkStatus(params.id) || {};
56
+ const { status, err_description: err } = result;
57
+ if (status !== 'success' && !unittest) {
58
+ return reply.status(400).send(err || 'Помилка перевірки статусу платежу');
59
+ }
60
+ Object.assign(body, result);
61
+ }
62
+
63
+ if (req.method === 'POST') {
64
+ if (!body?.data || !body?.signature) {
65
+ return reply.status(400).send('not enough body params: data, signature');
66
+ }
67
+
68
+ const sign = liqpay.str_to_sign(
69
+ privateKey
70
+ + body.data
71
+ + privateKey,
72
+ );
73
+
74
+ if (body.signature !== sign) {
75
+ logger.file('payments/warn', {
76
+ type: 'status',
77
+ service: 'liqpay',
78
+ paymentId,
79
+ origin,
80
+ uid: user?.uid || '0',
81
+ message: 'Transaction signature does not match',
82
+ });
83
+ return reply.status(403).send('access restricted: signature mismatch');
84
+ }
68
85
  }
69
86
 
70
87
  const data = await pg.query('select * from billing.payments where payment_id=$1', [paymentId])
@@ -121,7 +138,7 @@ export default async function liqpayStatus(req, reply) {
121
138
  table: 'billing.payments',
122
139
  data: {
123
140
  payment_data: body,
124
- payment_status: responseStatus,
141
+ payment_status: body.status || responseStatus,
125
142
  total_balance: +refillSum + +totalBalance,
126
143
  success_date: newDate(),
127
144
  },
@@ -19,4 +19,12 @@ export default async function plugin(app, opts) {
19
19
  },
20
20
  handler: liqpayStatus,
21
21
  });
22
+ app.route({
23
+ method: 'GET',
24
+ url: '/liqpay/:id/:status',
25
+ config: {
26
+ policy: ['public'],
27
+ },
28
+ handler: liqpayStatus,
29
+ });
22
30
  }
@@ -0,0 +1,34 @@
1
+ import LiqPay from 'liqpayjs-sdk';
2
+ import request from 'request-promise';
3
+
4
+ import { config } from '@opengis/fastify-table/utils.js';
5
+
6
+ const { publicKey, privateKey } = config.integrations?.liqpay || {};
7
+
8
+ const liqpay = new LiqPay(publicKey, privateKey);
9
+ const host = 'https://www.liqpay.ua';
10
+
11
+ export default async function liqpayCheckStatus(id) {
12
+ if (!publicKey || !privateKey) return {};
13
+
14
+ const options = {
15
+ public_key: publicKey, // in case of liqpay.api is auto-assigned
16
+ action: 'status',
17
+ version: '3',
18
+ order_id: id,
19
+ };
20
+ /* const result = liqpay.api('request', options); */
21
+
22
+ const data = Buffer.from(JSON.stringify(options)).toString('base64');
23
+ const signature = liqpay.str_to_sign(privateKey + data + privateKey);
24
+
25
+ const response = await request(`${host}/api/request`, {
26
+ method: 'POST',
27
+ form: { data, signature },
28
+ headers: {
29
+ 'Content-Type': 'application/x-www-form-urlencoded',
30
+ },
31
+ });
32
+ const result = response?.startsWith?.('{') ? JSON.parse(response) : response;
33
+ return result;
34
+ }
@@ -1,4 +1,13 @@
1
- import { metaFormat, pgClients } from '@opengis/fastify-table/utils.js';
1
+ import { dataUpdate, metaFormat, pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ import liqpayCheckStatus from '../../liqpay/utils/check.status.js';
4
+
5
+ const newDate = (dateNum) => {
6
+ const date = dateNum ? new Date(dateNum) : new Date();
7
+ const tzOffset = date.getTimezoneOffset();
8
+ const currentTimeWithTimezome = date - tzOffset * 60 * 1000;
9
+ return new Date(currentTimeWithTimezome).toISOString();
10
+ };
2
11
 
3
12
  /**
4
13
  * Апі для отримання інформації за ID
@@ -30,6 +39,8 @@ export default async function paymentInfo({
30
39
  payment_status,
31
40
  total_balance,
32
41
  payment_num,
42
+ payment_service,
43
+ payment_data,
33
44
  subscription_id
34
45
  from billing.payments
35
46
  where payment_id = $1
@@ -39,6 +50,40 @@ export default async function paymentInfo({
39
50
  return { message: 'payment not found', status: 404 };
40
51
  }
41
52
 
53
+ const {
54
+ account_id: accountId,
55
+ payment_service: service,
56
+ payment_status: status,
57
+
58
+ } = rows[0];
59
+
60
+ if (status !== 'success' && service === 'liqpay') {
61
+ const result = await liqpayCheckStatus(params.id);
62
+ const { amount = 0 } = result;
63
+
64
+ const { total_balance: totalBalance = 0 } = await pg.query(`select total_balance from billing.payments
65
+ where account_id=$1
66
+ and payment_status in ('success', 'error')
67
+ and total_balance is not null
68
+ order by cdate desc limit 1`, [accountId]).then(el => el.rows?.[0] || {});
69
+
70
+ const res = await dataUpdate({
71
+ pg,
72
+ table: 'billing.payments',
73
+ id: params.id,
74
+ data: {
75
+ payment_data: result,
76
+ payment_status: 'success',
77
+ total_balance: +amount + +totalBalance,
78
+ success_date: newDate(result.end_date),
79
+ },
80
+ uid: user?.uid,
81
+ });
82
+
83
+ const res1 = await metaFormat({ rows: [res].filter(el => el), cls: { account_id: 'billing.account_id', payment_status: 'billing.payment_status', subscription_id: 'billing.subscription_id' } });
84
+ return res1;
85
+ }
86
+
42
87
  await metaFormat({ rows, cls: { account_id: 'billing.account_id', payment_status: 'billing.payment_status', subscription_id: 'billing.subscription_id' } });
43
88
 
44
89
  return rows[0];