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.
- package/PROVIDERS-DETAILS.md +1544 -0
- package/examples/example-2checkout.js +78 -0
- package/examples/example-bitpay.js +83 -0
- package/examples/example-cardcom.js +80 -0
- package/examples/example-cashfree.js +109 -0
- package/examples/example-checkout.js +85 -0
- package/examples/example-coingate.js +101 -0
- package/examples/example-coinpayments.js +89 -0
- package/examples/example-doku.js +27 -0
- package/examples/example-epay.js +64 -0
- package/examples/example-epoint.js +91 -0
- package/examples/example-freekassa.js +26 -0
- package/examples/example-heleket.js +139 -0
- package/examples/example-konnect.js +227 -0
- package/examples/example-midtrans.js +80 -0
- package/examples/example-noonpayments.js +297 -0
- package/examples/example-nowpayments.js +289 -0
- package/examples/example-omise.js +27 -0
- package/examples/example-paycom.js +82 -0
- package/{example-paydisini.js → examples/example-paydisini.js} +1 -1
- package/examples/example-payid19.js +87 -0
- package/examples/example-paykun.js +29 -0
- package/examples/example-payme.js +202 -0
- package/examples/example-paymentwall.js +201 -0
- package/examples/example-paynet.js +104 -0
- package/examples/example-paynettr.js +18 -0
- package/examples/example-payoneer.js +74 -0
- package/examples/example-payop.js +351 -0
- package/examples/example-paypal.js +200 -0
- package/examples/example-payriff.js +89 -0
- package/examples/example-paysend.js +81 -0
- package/examples/example-payspace.js +103 -0
- package/examples/example-payssion.js +27 -0
- package/examples/example-paytabs.js +28 -0
- package/examples/example-paytm.js +78 -0
- package/examples/example-payuindia.js +108 -0
- package/examples/example-payulatam.js +75 -0
- package/examples/example-phonepe.js +27 -0
- package/examples/example-picpay.js +27 -0
- package/examples/example-plisio.js +84 -0
- package/examples/example-portwallet.js +90 -0
- package/examples/example-primepayments.js +250 -0
- package/examples/example-razorpay.js +30 -0
- package/examples/example-senangpay.js +28 -0
- package/examples/example-shurjopay.js +94 -0
- package/examples/example-toyyibpay.js +80 -0
- package/examples/example-tripay.js +89 -0
- package/examples/example-unitpay.js +26 -0
- package/examples/example-urway.js +28 -0
- package/examples/example-volet.js +80 -0
- package/examples/example-xendit.js +28 -0
- package/examples/example-yallapay.js +253 -0
- package/examples/example-yookassa.js +27 -0
- package/examples/example-youcanpay.js +28 -0
- package/examples/example-zarinpal.js +98 -0
- package/{example.js → examples/example.js} +1 -1
- package/lib/2checkout.js +165 -0
- package/lib/amazonpay.js +161 -0
- package/lib/bitpay.js +122 -0
- package/lib/cardcom.js +193 -0
- package/lib/cashfree.js +184 -0
- package/lib/checkout.js +248 -0
- package/lib/coinbase.js +150 -0
- package/lib/coingate.js +137 -0
- package/lib/coinpayments.js +245 -0
- package/lib/doku.js +173 -0
- package/lib/epay.js +175 -0
- package/lib/epoint.js +162 -0
- package/lib/freekassa.js +128 -0
- package/lib/heleket.js +67 -1
- package/lib/instamojo.js +158 -0
- package/lib/konnect.js +211 -0
- package/lib/midtrans.js +227 -0
- package/lib/noonpayments.js +650 -0
- package/lib/nowpayments.js +311 -0
- package/lib/omise.js +150 -0
- package/lib/paddle.js +180 -0
- package/lib/paycom.js +216 -0
- package/lib/payid19.js +211 -0
- package/lib/paykun.js +144 -0
- package/lib/payme.js +302 -0
- package/lib/paymentwall.js +205 -0
- package/lib/paynet.js +186 -0
- package/lib/paynettr.js +165 -0
- package/lib/payoneer.js +128 -0
- package/lib/payop.js +256 -0
- package/lib/paypal.js +542 -0
- package/lib/payriff.js +148 -0
- package/lib/paysend.js +189 -0
- package/lib/payspace.js +168 -0
- package/lib/payssion.js +177 -0
- package/lib/paytabs.js +145 -0
- package/lib/paytm.js +253 -0
- package/lib/payuindia.js +162 -0
- package/lib/payulatam.js +179 -0
- package/lib/perfectmoney.js +143 -0
- package/lib/phonepe.js +174 -0
- package/lib/picpay.js +119 -0
- package/lib/plisio.js +234 -0
- package/lib/portwallet.js +152 -0
- package/lib/primepayments.js +256 -0
- package/lib/razorpay.js +205 -0
- package/lib/senangpay.js +130 -0
- package/lib/shurjopay.js +159 -0
- package/lib/toyyibpay.js +151 -0
- package/lib/tripay.js +220 -0
- package/lib/unitpay.js +223 -0
- package/lib/urway.js +182 -0
- package/lib/volet.js +147 -0
- package/lib/xendit.js +206 -0
- package/lib/yallapay.js +279 -0
- package/lib/yookassa.js +193 -0
- package/lib/youcanpay.js +124 -0
- package/lib/zarinpal.js +157 -0
- package/package.json +138 -64
- package/readme.md +348 -105
- package/test.js +492 -0
- package/example-heleket.js +0 -83
- package/lib/vallet.js +0 -22
- /package/{example-anypay.js → examples/example-anypay.js} +0 -0
- /package/{example-bufpay.js → examples/example-bufpay.js} +0 -0
- /package/{example-cryptomus.js → examples/example-cryptomus.js} +0 -0
- /package/{example-esnekpos.js → examples/example-esnekpos.js} +0 -0
- /package/{example-fedapay.js → examples/example-fedapay.js} +0 -0
- /package/{example-iyzico.js → examples/example-iyzico.js} +0 -0
- /package/{example-papara.js → examples/example-papara.js} +0 -0
- /package/{example-payeer.js → examples/example-payeer.js} +0 -0
- /package/{example-paymaya.js → examples/example-paymaya.js} +0 -0
- /package/{example-shopier.js → examples/example-shopier.js} +0 -0
- /package/{ipaymu.js → examples/ipaymu.js} +0 -0
- /package/{oderopay.js → examples/oderopay.js} +0 -0
package/lib/senangpay.js
ADDED
|
@@ -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;
|
package/lib/shurjopay.js
ADDED
|
@@ -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;
|
package/lib/toyyibpay.js
ADDED
|
@@ -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;
|