quickpos 1.0.910 → 1.0.912

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 (131) hide show
  1. package/PROVIDERS-DETAILS.md +1544 -0
  2. package/examples/example-2checkout.js +78 -0
  3. package/examples/example-bitpay.js +83 -0
  4. package/examples/example-cardcom.js +80 -0
  5. package/examples/example-cashfree.js +109 -0
  6. package/examples/example-checkout.js +85 -0
  7. package/examples/example-coingate.js +101 -0
  8. package/examples/example-coinpayments.js +89 -0
  9. package/examples/example-doku.js +27 -0
  10. package/examples/example-epay.js +64 -0
  11. package/examples/example-epoint.js +91 -0
  12. package/examples/example-freekassa.js +26 -0
  13. package/examples/example-heleket.js +139 -0
  14. package/examples/example-konnect.js +227 -0
  15. package/examples/example-midtrans.js +80 -0
  16. package/examples/example-noonpayments.js +297 -0
  17. package/examples/example-nowpayments.js +289 -0
  18. package/examples/example-omise.js +27 -0
  19. package/examples/example-paycom.js +82 -0
  20. package/{example-paydisini.js → examples/example-paydisini.js} +1 -1
  21. package/examples/example-payid19.js +87 -0
  22. package/examples/example-paykun.js +29 -0
  23. package/examples/example-payme.js +202 -0
  24. package/examples/example-paymentwall.js +201 -0
  25. package/examples/example-paynet.js +104 -0
  26. package/examples/example-paynettr.js +18 -0
  27. package/examples/example-payoneer.js +74 -0
  28. package/examples/example-payop.js +351 -0
  29. package/examples/example-paypal.js +200 -0
  30. package/examples/example-payriff.js +89 -0
  31. package/examples/example-paysend.js +81 -0
  32. package/examples/example-payspace.js +103 -0
  33. package/examples/example-payssion.js +27 -0
  34. package/examples/example-paytabs.js +28 -0
  35. package/examples/example-paytm.js +78 -0
  36. package/examples/example-payuindia.js +108 -0
  37. package/examples/example-payulatam.js +75 -0
  38. package/examples/example-phonepe.js +27 -0
  39. package/examples/example-picpay.js +27 -0
  40. package/examples/example-plisio.js +84 -0
  41. package/examples/example-portwallet.js +90 -0
  42. package/examples/example-primepayments.js +250 -0
  43. package/examples/example-razorpay.js +30 -0
  44. package/examples/example-senangpay.js +28 -0
  45. package/examples/example-shurjopay.js +94 -0
  46. package/examples/example-toyyibpay.js +80 -0
  47. package/examples/example-tripay.js +89 -0
  48. package/examples/example-unitpay.js +26 -0
  49. package/examples/example-urway.js +28 -0
  50. package/examples/example-volet.js +80 -0
  51. package/examples/example-xendit.js +28 -0
  52. package/examples/example-yallapay.js +253 -0
  53. package/examples/example-yookassa.js +27 -0
  54. package/examples/example-youcanpay.js +28 -0
  55. package/examples/example-zarinpal.js +98 -0
  56. package/{example.js → examples/example.js} +1 -1
  57. package/lib/2checkout.js +165 -0
  58. package/lib/amazonpay.js +161 -0
  59. package/lib/bitpay.js +122 -0
  60. package/lib/cardcom.js +193 -0
  61. package/lib/cashfree.js +184 -0
  62. package/lib/checkout.js +248 -0
  63. package/lib/coinbase.js +150 -0
  64. package/lib/coingate.js +137 -0
  65. package/lib/coinpayments.js +245 -0
  66. package/lib/doku.js +173 -0
  67. package/lib/epay.js +175 -0
  68. package/lib/epoint.js +162 -0
  69. package/lib/freekassa.js +128 -0
  70. package/lib/heleket.js +67 -1
  71. package/lib/instamojo.js +158 -0
  72. package/lib/konnect.js +211 -0
  73. package/lib/midtrans.js +227 -0
  74. package/lib/noonpayments.js +650 -0
  75. package/lib/nowpayments.js +311 -0
  76. package/lib/omise.js +150 -0
  77. package/lib/paddle.js +180 -0
  78. package/lib/paycom.js +216 -0
  79. package/lib/payid19.js +211 -0
  80. package/lib/paykun.js +144 -0
  81. package/lib/payme.js +302 -0
  82. package/lib/paymentwall.js +205 -0
  83. package/lib/paynet.js +186 -0
  84. package/lib/paynettr.js +165 -0
  85. package/lib/payoneer.js +128 -0
  86. package/lib/payop.js +256 -0
  87. package/lib/paypal.js +542 -0
  88. package/lib/payriff.js +148 -0
  89. package/lib/paysend.js +189 -0
  90. package/lib/payspace.js +168 -0
  91. package/lib/payssion.js +177 -0
  92. package/lib/paytabs.js +145 -0
  93. package/lib/paytm.js +253 -0
  94. package/lib/payuindia.js +162 -0
  95. package/lib/payulatam.js +179 -0
  96. package/lib/perfectmoney.js +143 -0
  97. package/lib/phonepe.js +174 -0
  98. package/lib/picpay.js +119 -0
  99. package/lib/plisio.js +234 -0
  100. package/lib/portwallet.js +152 -0
  101. package/lib/primepayments.js +256 -0
  102. package/lib/razorpay.js +205 -0
  103. package/lib/senangpay.js +130 -0
  104. package/lib/shurjopay.js +159 -0
  105. package/lib/toyyibpay.js +151 -0
  106. package/lib/tripay.js +220 -0
  107. package/lib/unitpay.js +223 -0
  108. package/lib/urway.js +182 -0
  109. package/lib/volet.js +147 -0
  110. package/lib/xendit.js +206 -0
  111. package/lib/yallapay.js +279 -0
  112. package/lib/yookassa.js +193 -0
  113. package/lib/youcanpay.js +124 -0
  114. package/lib/zarinpal.js +157 -0
  115. package/package.json +138 -64
  116. package/readme.md +348 -105
  117. package/test.js +492 -0
  118. package/example-heleket.js +0 -83
  119. package/lib/vallet.js +0 -22
  120. /package/{example-anypay.js → examples/example-anypay.js} +0 -0
  121. /package/{example-bufpay.js → examples/example-bufpay.js} +0 -0
  122. /package/{example-cryptomus.js → examples/example-cryptomus.js} +0 -0
  123. /package/{example-esnekpos.js → examples/example-esnekpos.js} +0 -0
  124. /package/{example-fedapay.js → examples/example-fedapay.js} +0 -0
  125. /package/{example-iyzico.js → examples/example-iyzico.js} +0 -0
  126. /package/{example-papara.js → examples/example-papara.js} +0 -0
  127. /package/{example-payeer.js → examples/example-payeer.js} +0 -0
  128. /package/{example-paymaya.js → examples/example-paymaya.js} +0 -0
  129. /package/{example-shopier.js → examples/example-shopier.js} +0 -0
  130. /package/{ipaymu.js → examples/ipaymu.js} +0 -0
  131. /package/{oderopay.js → examples/oderopay.js} +0 -0
@@ -0,0 +1,130 @@
1
+ const axios = require('axios');
2
+ const crypto = require('crypto');
3
+
4
+ class SenangPayClient {
5
+ constructor(config) {
6
+ const requiredFields = ['merchantId', 'secretKey'];
7
+ for (let field of requiredFields) {
8
+ if (!config[field]) throw new Error(`Missing required field: ${field}`);
9
+ }
10
+
11
+ this.merchantId = config.merchantId;
12
+ this.secretKey = config.secretKey;
13
+ this.baseURL = config.sandbox
14
+ ? 'https://sandbox.senangpay.my/payment'
15
+ : 'https://app.senangpay.my/payment';
16
+ }
17
+
18
+ generateHash(detail, amount, orderId) {
19
+ const hashString = this.secretKey + detail + amount + orderId;
20
+ return crypto.createHash('md5').update(hashString).digest('hex');
21
+ }
22
+
23
+ async createPayment(options) {
24
+ try {
25
+ const orderId = options.orderId || `ORDER-${Date.now()}`;
26
+ const amount = parseFloat(options.amount).toFixed(2);
27
+ const detail = options.description || options.name || 'Payment';
28
+
29
+ const hash = this.generateHash(detail, amount, orderId);
30
+
31
+ const paymentData = {
32
+ detail: detail,
33
+ amount: amount,
34
+ order_id: orderId,
35
+ name: options.name || options.customerName || '',
36
+ email: options.email || '',
37
+ phone: options.phone || '',
38
+ hash: hash
39
+ };
40
+
41
+ // Create payment URL
42
+ const params = new URLSearchParams(paymentData);
43
+ const paymentUrl = `${this.baseURL}/${this.merchantId}?${params.toString()}`;
44
+
45
+ return {
46
+ status: 'success',
47
+ data: {
48
+ url: paymentUrl,
49
+ orderId: orderId,
50
+ amount: amount,
51
+ currency: 'MYR',
52
+ hash: hash
53
+ }
54
+ };
55
+ } catch (error) {
56
+ throw new Error(`Payment creation error: ${error.message}`);
57
+ }
58
+ }
59
+
60
+ async handleCallback(callbackData) {
61
+ try {
62
+ const verification = await this.verifyCallback(callbackData);
63
+
64
+ if (!verification.status) {
65
+ throw new Error(verification.error.message);
66
+ }
67
+
68
+ // Status mapping
69
+ const statusMapping = {
70
+ '1': 'success', // Success
71
+ '0': 'failed' // Failed
72
+ };
73
+
74
+ return {
75
+ status: statusMapping[callbackData.status_id] || 'unknown',
76
+ orderId: callbackData.order_id,
77
+ transactionId: callbackData.transaction_id,
78
+ amount: parseFloat(callbackData.amount),
79
+ currency: 'MYR',
80
+ paymentStatus: callbackData.status_id === '1' ? 'completed' : 'failed',
81
+ message: callbackData.msg
82
+ };
83
+ } catch (error) {
84
+ throw new Error(`Error in SenangPay callback handling: ${error.message}`);
85
+ }
86
+ }
87
+
88
+ async verifyCallback(data) {
89
+ try {
90
+ const receivedHash = data.hash;
91
+ const hashString = this.secretKey + data.status_id + data.order_id + data.transaction_id + data.msg;
92
+ const expectedHash = crypto.createHash('md5').update(hashString).digest('hex');
93
+
94
+ if (receivedHash !== expectedHash) {
95
+ return {
96
+ status: false,
97
+ error: {
98
+ code: 401,
99
+ message: 'Invalid hash'
100
+ }
101
+ };
102
+ }
103
+
104
+ return {
105
+ status: true,
106
+ data: data
107
+ };
108
+ } catch (error) {
109
+ return {
110
+ status: false,
111
+ error: {
112
+ code: 500,
113
+ message: error.message
114
+ }
115
+ };
116
+ }
117
+ }
118
+
119
+ async getTransactionStatus(orderId) {
120
+ try {
121
+ // SenangPay doesn't have a standard API for checking transaction status
122
+ // This would need to be implemented based on their specific API if available
123
+ throw new Error('Transaction status check not available in standard SenangPay integration');
124
+ } catch (error) {
125
+ throw new Error(`Error getting transaction status: ${error.message}`);
126
+ }
127
+ }
128
+ }
129
+
130
+ module.exports = SenangPayClient;
@@ -0,0 +1,159 @@
1
+ const axios = require('axios');
2
+ const crypto = require('crypto');
3
+
4
+ class ShurjoPayClient {
5
+ constructor(config) {
6
+ const requiredFields = ['username', 'password', 'prefix'];
7
+ for (let field of requiredFields) {
8
+ if (!config[field]) throw new Error(`Missing required field: ${field}`);
9
+ }
10
+
11
+ this.username = config.username;
12
+ this.password = config.password;
13
+ this.prefix = config.prefix;
14
+ this.baseURL = config.sandbox
15
+ ? 'https://sandbox.shurjopayment.com'
16
+ : 'https://engine.shurjopayment.com';
17
+
18
+ this.token = null;
19
+
20
+ this.client = axios.create({
21
+ baseURL: this.baseURL,
22
+ headers: {
23
+ 'Content-Type': 'application/json'
24
+ }
25
+ });
26
+ }
27
+
28
+ async authenticate() {
29
+ try {
30
+ const response = await this.client.post('/api/get_token', {
31
+ username: this.username,
32
+ password: this.password
33
+ });
34
+
35
+ if (response.data.token) {
36
+ this.token = response.data.token;
37
+ this.client.defaults.headers['Authorization'] = `Bearer ${this.token}`;
38
+ return this.token;
39
+ } else {
40
+ throw new Error('Authentication failed');
41
+ }
42
+ } catch (error) {
43
+ throw new Error(`Authentication error: ${error.response?.data?.message || error.message}`);
44
+ }
45
+ }
46
+
47
+ async createPayment(options) {
48
+ try {
49
+ if (!this.token) {
50
+ await this.authenticate();
51
+ }
52
+
53
+ const orderId = options.orderId || `${this.prefix}${Date.now()}`;
54
+
55
+ const paymentData = {
56
+ prefix: this.prefix,
57
+ token: this.token,
58
+ return_url: options.successUrl || options.callback_link,
59
+ cancel_url: options.failUrl || options.callback_link,
60
+ store_id: this.prefix,
61
+ amount: parseFloat(options.amount),
62
+ order_id: orderId,
63
+ currency: options.currency || 'BDT',
64
+ customer_name: options.name || options.customerName || '',
65
+ customer_address: options.address || '',
66
+ customer_city: options.city || 'Dhaka',
67
+ customer_phone: options.phone || '',
68
+ customer_email: options.email || '',
69
+ client_ip: options.clientIp || '127.0.0.1',
70
+ intent: 'sale',
71
+ discsount_amount: options.discount || 0,
72
+ disc_percent: options.discountPercent || 0
73
+ };
74
+
75
+ const response = await this.client.post('/api/secret-pay', paymentData);
76
+
77
+ if (response.data.checkout_url) {
78
+ return {
79
+ status: 'success',
80
+ data: {
81
+ url: response.data.checkout_url,
82
+ orderId: orderId,
83
+ amount: paymentData.amount,
84
+ currency: paymentData.currency,
85
+ sp_order_id: response.data.sp_order_id
86
+ }
87
+ };
88
+ } else {
89
+ throw new Error(response.data.message || 'Payment creation failed');
90
+ }
91
+ } catch (error) {
92
+ throw new Error(`Payment creation error: ${error.response?.data?.message || error.message}`);
93
+ }
94
+ }
95
+
96
+ async handleCallback(callbackData) {
97
+ try {
98
+ const orderId = callbackData.order_id;
99
+
100
+ if (!orderId) {
101
+ throw new Error('Order ID not found in callback data');
102
+ }
103
+
104
+ // Verify payment
105
+ const verification = await this.verifyPayment(orderId);
106
+
107
+ // Status mapping
108
+ const statusMapping = {
109
+ 'success': 'success',
110
+ 'Success': 'success',
111
+ 'failed': 'failed',
112
+ 'Failed': 'failed',
113
+ 'pending': 'pending',
114
+ 'Pending': 'pending',
115
+ 'cancel': 'failed',
116
+ 'Cancel': 'failed'
117
+ };
118
+
119
+ return {
120
+ status: statusMapping[verification[0].sp_status] || 'unknown',
121
+ orderId: verification[0].order_id,
122
+ transactionId: verification[0].bank_trx_id,
123
+ amount: parseFloat(verification[0].amount),
124
+ currency: verification[0].currency,
125
+ paymentStatus: verification[0].sp_status,
126
+ method: verification[0].method,
127
+ bankStatus: verification[0].bank_status
128
+ };
129
+ } catch (error) {
130
+ throw new Error(`Error in ShurjoPay callback handling: ${error.message}`);
131
+ }
132
+ }
133
+
134
+ async verifyPayment(orderId) {
135
+ try {
136
+ if (!this.token) {
137
+ await this.authenticate();
138
+ }
139
+
140
+ const response = await this.client.post('/api/verification', {
141
+ order_id: orderId
142
+ });
143
+
144
+ return response.data;
145
+ } catch (error) {
146
+ throw new Error(`Error verifying payment: ${error.response?.data?.message || error.message}`);
147
+ }
148
+ }
149
+
150
+ async getPaymentStatus(orderId) {
151
+ try {
152
+ return await this.verifyPayment(orderId);
153
+ } catch (error) {
154
+ throw new Error(`Error getting payment status: ${error.message}`);
155
+ }
156
+ }
157
+ }
158
+
159
+ module.exports = ShurjoPayClient;
@@ -0,0 +1,151 @@
1
+ const axios = require('axios');
2
+ const crypto = require('crypto');
3
+
4
+ class ToyyibPayClient {
5
+ constructor(config) {
6
+ const requiredFields = ['secretKey', 'categoryCode'];
7
+ for (let field of requiredFields) {
8
+ if (!config[field]) throw new Error(`Missing required field: ${field}`);
9
+ }
10
+
11
+ this.secretKey = config.secretKey;
12
+ this.categoryCode = config.categoryCode;
13
+ this.URL = 'https://dev.toyyibpay.com';
14
+
15
+ this.client = axios.create({
16
+ baseURL: this.URL,
17
+ headers: {
18
+ 'Content-Type': 'application/x-www-form-urlencoded'
19
+ }
20
+ });
21
+
22
+ this.client.interceptors.response.use(response => {
23
+ return response;
24
+ }, error => {
25
+ if (error.response) {
26
+ throw new Error(`ToyyibPay API error: ${error.response.data.message || error.message}`);
27
+ }
28
+ throw new Error(`ToyyibPay API error: ${error.message}`);
29
+ });
30
+ }
31
+
32
+ async createPayment(options) {
33
+ try {
34
+ const params = new URLSearchParams();
35
+ params.append('userSecretKey', this.secretKey);
36
+ params.append('categoryCode', this.categoryCode);
37
+ params.append('billName', options.billName || options.name || 'Bill');
38
+ params.append('billDescription', options.billDescription || options.description || 'Payment');
39
+ params.append('billPriceSetting', '1');
40
+ params.append('billPayorInfo', '1');
41
+ params.append('billAmount', Math.round(parseFloat(options.amount) * 100));
42
+ params.append('billReturnUrl', options.returnUrl || options.callback_link);
43
+ params.append('billCallbackUrl', options.callbackUrl || options.callback_link);
44
+ params.append('billExternalReferenceNo', options.referenceNo || options.orderId || `ORDER-${Date.now()}`);
45
+ params.append('billTo', options.billTo || options.customerName || options.name || 'Customer');
46
+ params.append('billEmail', options.billEmail || options.email);
47
+ params.append('billPhone', options.billPhone || options.phone || '60123456789');
48
+ params.append('billSplitPayment', '0');
49
+ params.append('billSplitPaymentArgs', '');
50
+ params.append('billPaymentChannel', options.paymentChannel || '0');
51
+ params.append('billContentEmail', options.contentEmail || 'Thank you for your payment');
52
+
53
+ if (options.billChargeToCustomer) {
54
+ params.append('billChargeToCustomer', options.billChargeToCustomer);
55
+ }
56
+
57
+ const response = await this.client.post('/index.php/api/createBill', params);
58
+
59
+ if (response.data[0].BillCode) {
60
+ return {
61
+ status: 'success',
62
+ data: {
63
+ billCode: response.data[0].BillCode,
64
+ url: `${this.URL}/${response.data[0].BillCode}`,
65
+ referenceNo: options.referenceNo || options.orderId
66
+ }
67
+ };
68
+ } else {
69
+ throw new Error(response.data[0].error || 'Payment creation failed');
70
+ }
71
+ } catch (error) {
72
+ throw new Error(`Payment creation error: ${error.message}`);
73
+ }
74
+ }
75
+
76
+ async getBillTransactions(billCode) {
77
+ try {
78
+ const params = new URLSearchParams();
79
+ params.append('billCode', billCode);
80
+
81
+ const response = await this.client.post('/index.php/api/getBillTransactions', params);
82
+ return response.data;
83
+ } catch (error) {
84
+ throw new Error(`Bill transactions error: ${error.message}`);
85
+ }
86
+ }
87
+
88
+ async handleCallback(callbackData) {
89
+ try {
90
+ const verification = await this.verifyCallback(callbackData);
91
+
92
+ if (!verification.status) {
93
+ throw new Error(verification.error.message);
94
+ }
95
+
96
+ const data = verification.data;
97
+
98
+ // Status mapping: 1 = success, 2 = pending, 3 = failed
99
+ const statusMapping = {
100
+ '1': 'success',
101
+ '2': 'pending',
102
+ '3': 'failed'
103
+ };
104
+
105
+ return {
106
+ status: statusMapping[data.status_id] || 'unknown',
107
+ billCode: data.billcode,
108
+ orderId: data.order_id || data.billExternalReferenceNo,
109
+ amount: parseFloat(data.amount) / 100,
110
+ transactionId: data.transaction_id,
111
+ paymentStatus: data.status_id,
112
+ transactionTime: data.transaction_time,
113
+ payerName: data.billpayorname,
114
+ payerEmail: data.billpayoremail,
115
+ payerPhone: data.billpayorphone
116
+ };
117
+ } catch (error) {
118
+ throw new Error(`Error in ToyyibPay callback handling: ${error.message}`);
119
+ }
120
+ }
121
+
122
+ async verifyCallback(data) {
123
+ try {
124
+ // ToyyibPay uses hash verification
125
+ if (data.status_id === '3') {
126
+ return {
127
+ status: false,
128
+ error: {
129
+ code: 400,
130
+ message: 'Payment failed'
131
+ }
132
+ };
133
+ }
134
+
135
+ return {
136
+ status: true,
137
+ data: data
138
+ };
139
+ } catch (error) {
140
+ return {
141
+ status: false,
142
+ error: {
143
+ code: 500,
144
+ message: error.message
145
+ }
146
+ };
147
+ }
148
+ }
149
+ }
150
+
151
+ module.exports = ToyyibPayClient;
package/lib/tripay.js ADDED
@@ -0,0 +1,220 @@
1
+ const axios = require('axios');
2
+ const crypto = require('crypto');
3
+
4
+ class TripayClient {
5
+ constructor(config) {
6
+ const requiredFields = ['apiKey', 'privateKey', 'merchantCode'];
7
+ for (let field of requiredFields) {
8
+ if (!config[field]) throw new Error(`Missing required field: ${field}`);
9
+ }
10
+
11
+ this.apiKey = config.apiKey;
12
+ this.privateKey = config.privateKey;
13
+ this.merchantCode = config.merchantCode;
14
+ this.isProduction = config.isProduction || false;
15
+
16
+ this.URL = this.isProduction
17
+ ? 'https://tripay.co.id/api'
18
+ : 'https://tripay.co.id/api-sandbox';
19
+
20
+ this.client = axios.create({
21
+ baseURL: this.URL,
22
+ headers: {
23
+ 'Authorization': `Bearer ${this.apiKey}`,
24
+ 'Content-Type': 'application/json'
25
+ }
26
+ });
27
+
28
+ this.client.interceptors.response.use(response => {
29
+ return response;
30
+ }, error => {
31
+ if (error.response) {
32
+ throw new Error(`Tripay API error: ${error.response.data.message || error.message}`);
33
+ }
34
+ throw new Error(`Tripay API error: ${error.message}`);
35
+ });
36
+ }
37
+
38
+ async getPaymentChannels() {
39
+ try {
40
+ const response = await this.client.get('/merchant/payment-channel');
41
+ return response.data;
42
+ } catch (error) {
43
+ throw new Error(`Payment channels error: ${error.message}`);
44
+ }
45
+ }
46
+
47
+ async createPayment(options) {
48
+ try {
49
+ const merchantRef = options.merchantRef || options.orderId || `ORDER-${Date.now()}`;
50
+ const amount = parseInt(options.amount);
51
+
52
+ // Generate signature
53
+ const signature = crypto
54
+ .createHmac('sha256', this.privateKey)
55
+ .update(this.merchantCode + merchantRef + amount)
56
+ .digest('hex');
57
+
58
+ const requestData = {
59
+ method: options.paymentMethod || 'BRIVA',
60
+ merchant_ref: merchantRef,
61
+ amount: amount,
62
+ customer_name: options.customerName || options.name || 'Customer',
63
+ customer_email: options.customerEmail || options.email,
64
+ customer_phone: options.customerPhone || options.phone || '08123456789',
65
+ order_items: options.orderItems || [{
66
+ sku: 'ITEM-001',
67
+ name: options.itemName || options.name || 'Product',
68
+ price: amount,
69
+ quantity: 1
70
+ }],
71
+ callback_url: options.callbackUrl || options.callback_link,
72
+ return_url: options.returnUrl || options.callbackUrl || options.callback_link,
73
+ expired_time: options.expiredTime || (Math.floor(Date.now() / 1000) + (24 * 60 * 60)),
74
+ signature: signature
75
+ };
76
+
77
+ const response = await this.client.post('/transaction/create', requestData);
78
+
79
+ if (!response.data.success) {
80
+ throw new Error(response.data.message || 'Payment creation failed');
81
+ }
82
+
83
+ return {
84
+ status: 'success',
85
+ data: {
86
+ reference: response.data.data.reference,
87
+ merchantRef: response.data.data.merchant_ref,
88
+ paymentUrl: response.data.data.checkout_url,
89
+ qrUrl: response.data.data.qr_url,
90
+ amount: response.data.data.amount,
91
+ fee: response.data.data.total_fee,
92
+ totalAmount: response.data.data.amount_received,
93
+ expiredTime: response.data.data.expired_time,
94
+ paymentMethod: response.data.data.payment_method,
95
+ paymentName: response.data.data.payment_name,
96
+ instructions: response.data.data.instructions
97
+ }
98
+ };
99
+ } catch (error) {
100
+ throw new Error(`Payment creation error: ${error.message}`);
101
+ }
102
+ }
103
+
104
+ async getTransactionDetail(reference) {
105
+ try {
106
+ const response = await this.client.get(`/transaction/detail?reference=${reference}`);
107
+
108
+ if (!response.data.success) {
109
+ throw new Error(response.data.message || 'Failed to get transaction detail');
110
+ }
111
+
112
+ return response.data.data;
113
+ } catch (error) {
114
+ throw new Error(`Transaction detail error: ${error.message}`);
115
+ }
116
+ }
117
+
118
+ async handleCallback(callbackData) {
119
+ try {
120
+ const verification = await this.verifyCallback(callbackData);
121
+
122
+ if (!verification.status) {
123
+ throw new Error(verification.error.message);
124
+ }
125
+
126
+ const data = verification.data;
127
+
128
+ // Status mapping
129
+ const statusMapping = {
130
+ 'PAID': 'success',
131
+ 'UNPAID': 'pending',
132
+ 'EXPIRED': 'expired',
133
+ 'FAILED': 'failed',
134
+ 'REFUND': 'refunded'
135
+ };
136
+
137
+ return {
138
+ status: statusMapping[data.status] || 'unknown',
139
+ reference: data.reference,
140
+ merchantRef: data.merchant_ref,
141
+ orderId: data.merchant_ref,
142
+ amount: parseInt(data.amount),
143
+ totalAmount: parseInt(data.amount_received),
144
+ fee: parseInt(data.total_fee),
145
+ paymentMethod: data.payment_method,
146
+ paymentName: data.payment_name,
147
+ customerName: data.customer_name,
148
+ customerEmail: data.customer_email,
149
+ customerPhone: data.customer_phone,
150
+ paymentStatus: data.status,
151
+ paidAt: data.paid_at
152
+ };
153
+ } catch (error) {
154
+ throw new Error(`Error in Tripay callback handling: ${error.message}`);
155
+ }
156
+ }
157
+
158
+ async verifyCallback(data) {
159
+ try {
160
+ const callbackSignature = data.signature || '';
161
+ const privateKey = this.privateKey;
162
+
163
+ // Calculate signature
164
+ const json = JSON.stringify(data);
165
+ const expectedSignature = crypto
166
+ .createHmac('sha256', privateKey)
167
+ .update(json)
168
+ .digest('hex');
169
+
170
+ if (callbackSignature !== expectedSignature) {
171
+ return {
172
+ status: false,
173
+ error: {
174
+ code: 401,
175
+ message: 'Invalid callback signature'
176
+ }
177
+ };
178
+ }
179
+
180
+ if (data.status === 'FAILED') {
181
+ return {
182
+ status: false,
183
+ error: {
184
+ code: 400,
185
+ message: 'Payment failed'
186
+ }
187
+ };
188
+ }
189
+
190
+ return {
191
+ status: true,
192
+ data: data
193
+ };
194
+ } catch (error) {
195
+ return {
196
+ status: false,
197
+ error: {
198
+ code: 500,
199
+ message: error.message
200
+ }
201
+ };
202
+ }
203
+ }
204
+
205
+ async getFeeCalculator(amount, code) {
206
+ try {
207
+ const response = await this.client.get(`/merchant/fee-calculator?amount=${amount}&code=${code}`);
208
+
209
+ if (!response.data.success) {
210
+ throw new Error(response.data.message || 'Failed to calculate fee');
211
+ }
212
+
213
+ return response.data.data;
214
+ } catch (error) {
215
+ throw new Error(`Fee calculator error: ${error.message}`);
216
+ }
217
+ }
218
+ }
219
+
220
+ module.exports = TripayClient;