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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PerfectMoneyClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['accountId', 'passPhrase'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.accountId = config.accountId;
|
|
12
|
+
this.passPhrase = config.passPhrase;
|
|
13
|
+
this.alternatePassPhrase = config.alternatePassPhrase || this.passPhrase;
|
|
14
|
+
this.payeeAccount = config.payeeAccount || this.accountId;
|
|
15
|
+
this.baseURL = 'https://perfectmoney.com/api';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
generateHash(data) {
|
|
19
|
+
const hashString = `${data.PAYMENT_ID}:${data.PAYEE_ACCOUNT}:${data.PAYMENT_AMOUNT}:${data.PAYMENT_UNITS}:${data.PAYMENT_BATCH_NUM}:${data.PAYER_ACCOUNT}:${this.alternatePassPhrase}:${data.TIMESTAMPGMT}`;
|
|
20
|
+
|
|
21
|
+
return crypto
|
|
22
|
+
.createHash('md5')
|
|
23
|
+
.update(hashString)
|
|
24
|
+
.digest('hex')
|
|
25
|
+
.toUpperCase();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async createPayment(options) {
|
|
29
|
+
try {
|
|
30
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
31
|
+
|
|
32
|
+
const paymentData = {
|
|
33
|
+
PAYEE_ACCOUNT: this.payeeAccount,
|
|
34
|
+
PAYEE_NAME: options.payeeName || 'Merchant',
|
|
35
|
+
PAYMENT_ID: orderId,
|
|
36
|
+
PAYMENT_AMOUNT: parseFloat(options.amount).toFixed(2),
|
|
37
|
+
PAYMENT_UNITS: options.currency || 'USD',
|
|
38
|
+
STATUS_URL: options.callbackUrl || options.callback_link,
|
|
39
|
+
PAYMENT_URL: options.successUrl || options.callback_link,
|
|
40
|
+
PAYMENT_URL_METHOD: 'GET',
|
|
41
|
+
NOPAYMENT_URL: options.failUrl || options.callback_link,
|
|
42
|
+
NOPAYMENT_URL_METHOD: 'GET',
|
|
43
|
+
SUGGESTED_MEMO: options.description || options.name || 'Payment',
|
|
44
|
+
BAGGAGE_FIELDS: `email=${options.email || ''}&name=${options.name || ''}`
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Create payment URL
|
|
48
|
+
const params = new URLSearchParams(paymentData);
|
|
49
|
+
const paymentUrl = `https://perfectmoney.com/api/step1.asp?${params.toString()}`;
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
status: 'success',
|
|
53
|
+
data: {
|
|
54
|
+
url: paymentUrl,
|
|
55
|
+
orderId: orderId,
|
|
56
|
+
amount: paymentData.PAYMENT_AMOUNT,
|
|
57
|
+
currency: paymentData.PAYMENT_UNITS,
|
|
58
|
+
payeeAccount: this.payeeAccount
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
throw new Error(`Payment creation error: ${error.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async handleCallback(callbackData) {
|
|
67
|
+
try {
|
|
68
|
+
const verification = await this.verifyCallback(callbackData);
|
|
69
|
+
|
|
70
|
+
if (!verification.status) {
|
|
71
|
+
throw new Error(verification.error.message);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
status: 'success',
|
|
76
|
+
orderId: callbackData.PAYMENT_ID,
|
|
77
|
+
transactionId: callbackData.PAYMENT_BATCH_NUM,
|
|
78
|
+
amount: parseFloat(callbackData.PAYMENT_AMOUNT),
|
|
79
|
+
currency: callbackData.PAYMENT_UNITS,
|
|
80
|
+
paymentStatus: 'completed',
|
|
81
|
+
payerAccount: callbackData.PAYER_ACCOUNT,
|
|
82
|
+
timestampGMT: callbackData.TIMESTAMPGMT
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
throw new Error(`Error in Perfect Money callback handling: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async verifyCallback(data) {
|
|
90
|
+
try {
|
|
91
|
+
const receivedHash = data.V2_HASH;
|
|
92
|
+
const expectedHash = this.generateHash(data);
|
|
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 getBalance(accountId, password) {
|
|
120
|
+
try {
|
|
121
|
+
const response = await axios.get('https://perfectmoney.com/acct/balance.asp', {
|
|
122
|
+
params: {
|
|
123
|
+
AccountID: accountId || this.accountId,
|
|
124
|
+
PassPhrase: password || this.passPhrase
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Parse response (XML-like format)
|
|
129
|
+
const balances = {};
|
|
130
|
+
const matches = response.data.matchAll(/<input name='(.+?)' type='hidden' value='(.+?)'>/g);
|
|
131
|
+
|
|
132
|
+
for (const match of matches) {
|
|
133
|
+
balances[match[1]] = match[2];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return balances;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
throw new Error(`Error getting balance: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = PerfectMoneyClient;
|
package/lib/phonepe.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PhonePeClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['merchantId', 'saltKey', 'saltIndex'];
|
|
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.saltKey = config.saltKey;
|
|
13
|
+
this.saltIndex = config.saltIndex;
|
|
14
|
+
this.baseURL = config.sandbox
|
|
15
|
+
? 'https://api-preprod.phonepe.com/apis/pg-sandbox'
|
|
16
|
+
: 'https://api.phonepe.com/apis/hermes';
|
|
17
|
+
|
|
18
|
+
this.client = axios.create({
|
|
19
|
+
baseURL: this.baseURL,
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json'
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
generateChecksum(payload) {
|
|
27
|
+
const base64Payload = Buffer.from(JSON.stringify(payload)).toString('base64');
|
|
28
|
+
const checksumString = base64Payload + '/pg/v1/pay' + this.saltKey;
|
|
29
|
+
const checksum = crypto.createHash('sha256').update(checksumString).digest('hex');
|
|
30
|
+
return checksum + '###' + this.saltIndex;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
verifyChecksum(base64Response, checksumHeader) {
|
|
34
|
+
const [receivedChecksum] = checksumHeader.split('###');
|
|
35
|
+
const checksumString = base64Response + this.saltKey;
|
|
36
|
+
const expectedChecksum = crypto.createHash('sha256').update(checksumString).digest('hex');
|
|
37
|
+
|
|
38
|
+
return receivedChecksum === expectedChecksum;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async createPayment(options) {
|
|
42
|
+
try {
|
|
43
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
44
|
+
const transactionId = `TXN-${Date.now()}`;
|
|
45
|
+
|
|
46
|
+
const payload = {
|
|
47
|
+
merchantId: this.merchantId,
|
|
48
|
+
merchantTransactionId: transactionId,
|
|
49
|
+
merchantUserId: options.userId || `USER-${Date.now()}`,
|
|
50
|
+
amount: Math.round(parseFloat(options.amount) * 100), // Amount in paise
|
|
51
|
+
redirectUrl: options.successUrl || options.callback_link,
|
|
52
|
+
redirectMode: 'POST',
|
|
53
|
+
callbackUrl: options.callbackUrl || options.callback_link,
|
|
54
|
+
mobileNumber: options.phone || '',
|
|
55
|
+
paymentInstrument: {
|
|
56
|
+
type: options.paymentType || 'PAY_PAGE' // PAY_PAGE, UPI_COLLECT, etc.
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const base64Payload = Buffer.from(JSON.stringify(payload)).toString('base64');
|
|
61
|
+
const checksum = this.generateChecksum(payload);
|
|
62
|
+
|
|
63
|
+
const response = await this.client.post('/pg/v1/pay', {
|
|
64
|
+
request: base64Payload
|
|
65
|
+
}, {
|
|
66
|
+
headers: {
|
|
67
|
+
'X-VERIFY': checksum
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (response.data.success) {
|
|
72
|
+
return {
|
|
73
|
+
status: 'success',
|
|
74
|
+
data: {
|
|
75
|
+
url: response.data.data.instrumentResponse.redirectInfo.url,
|
|
76
|
+
transactionId: transactionId,
|
|
77
|
+
orderId: orderId,
|
|
78
|
+
amount: payload.amount / 100,
|
|
79
|
+
currency: 'INR'
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error(response.data.message || 'Payment creation failed');
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
throw new Error(`Payment creation error: ${error.response?.data?.message || error.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async handleCallback(callbackData) {
|
|
91
|
+
try {
|
|
92
|
+
const base64Response = callbackData.response;
|
|
93
|
+
const checksumHeader = callbackData['X-VERIFY'];
|
|
94
|
+
|
|
95
|
+
// Verify checksum
|
|
96
|
+
if (!this.verifyChecksum(base64Response, checksumHeader)) {
|
|
97
|
+
throw new Error('Invalid checksum');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Decode response
|
|
101
|
+
const response = JSON.parse(Buffer.from(base64Response, 'base64').toString());
|
|
102
|
+
|
|
103
|
+
// Status mapping
|
|
104
|
+
const statusMapping = {
|
|
105
|
+
'PAYMENT_SUCCESS': 'success',
|
|
106
|
+
'PAYMENT_PENDING': 'pending',
|
|
107
|
+
'PAYMENT_DECLINED': 'failed',
|
|
108
|
+
'PAYMENT_ERROR': 'failed',
|
|
109
|
+
'INTERNAL_SERVER_ERROR': 'failed'
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
status: statusMapping[response.code] || 'unknown',
|
|
114
|
+
orderId: response.data.merchantOrderId,
|
|
115
|
+
transactionId: response.data.merchantTransactionId,
|
|
116
|
+
amount: response.data.amount / 100,
|
|
117
|
+
currency: 'INR',
|
|
118
|
+
paymentStatus: response.code,
|
|
119
|
+
paymentMethod: response.data.paymentInstrument?.type
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
throw new Error(`Error in PhonePe callback handling: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async checkStatus(transactionId) {
|
|
127
|
+
try {
|
|
128
|
+
const checksumString = `/pg/v1/status/${this.merchantId}/${transactionId}` + this.saltKey;
|
|
129
|
+
const checksum = crypto.createHash('sha256').update(checksumString).digest('hex') + '###' + this.saltIndex;
|
|
130
|
+
|
|
131
|
+
const response = await this.client.get(`/pg/v1/status/${this.merchantId}/${transactionId}`, {
|
|
132
|
+
headers: {
|
|
133
|
+
'X-VERIFY': checksum,
|
|
134
|
+
'X-MERCHANT-ID': this.merchantId
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return response.data;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
throw new Error(`Error checking status: ${error.response?.data?.message || error.message}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async refund(transactionId, options = {}) {
|
|
145
|
+
try {
|
|
146
|
+
const refundId = `REFUND-${Date.now()}`;
|
|
147
|
+
|
|
148
|
+
const payload = {
|
|
149
|
+
merchantId: this.merchantId,
|
|
150
|
+
merchantTransactionId: refundId,
|
|
151
|
+
originalTransactionId: transactionId,
|
|
152
|
+
amount: Math.round(parseFloat(options.amount) * 100),
|
|
153
|
+
callbackUrl: options.callbackUrl
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const base64Payload = Buffer.from(JSON.stringify(payload)).toString('base64');
|
|
157
|
+
const checksum = this.generateChecksum(payload);
|
|
158
|
+
|
|
159
|
+
const response = await this.client.post('/pg/v1/refund', {
|
|
160
|
+
request: base64Payload
|
|
161
|
+
}, {
|
|
162
|
+
headers: {
|
|
163
|
+
'X-VERIFY': checksum
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return response.data;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
throw new Error(`Error processing refund: ${error.response?.data?.message || error.message}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = PhonePeClient;
|
package/lib/picpay.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PicPayClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['token', 'sellerToken'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.token = config.token;
|
|
12
|
+
this.sellerToken = config.sellerToken;
|
|
13
|
+
this.baseURL = 'https://appws.picpay.com/ecommerce/public';
|
|
14
|
+
|
|
15
|
+
this.client = axios.create({
|
|
16
|
+
baseURL: this.baseURL,
|
|
17
|
+
headers: {
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
'x-picpay-token': this.token,
|
|
20
|
+
'x-seller-token': this.sellerToken
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async createPayment(options) {
|
|
26
|
+
try {
|
|
27
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
28
|
+
|
|
29
|
+
const paymentData = {
|
|
30
|
+
referenceId: orderId,
|
|
31
|
+
callbackUrl: options.callbackUrl || options.callback_link,
|
|
32
|
+
returnUrl: options.successUrl || options.callback_link,
|
|
33
|
+
value: parseFloat(options.amount),
|
|
34
|
+
expiresAt: options.expiresAt || new Date(Date.now() + 24*60*60*1000).toISOString(),
|
|
35
|
+
buyer: {
|
|
36
|
+
firstName: options.firstName || options.name?.split(' ')[0] || '',
|
|
37
|
+
lastName: options.lastName || options.name?.split(' ').slice(1).join(' ') || '',
|
|
38
|
+
document: options.document || '',
|
|
39
|
+
email: options.email || '',
|
|
40
|
+
phone: options.phone || ''
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const response = await this.client.post('/payments', paymentData);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
status: 'success',
|
|
48
|
+
data: {
|
|
49
|
+
referenceId: orderId,
|
|
50
|
+
paymentUrl: response.data.paymentUrl,
|
|
51
|
+
qrcode: response.data.qrcode,
|
|
52
|
+
amount: paymentData.value,
|
|
53
|
+
currency: 'BRL',
|
|
54
|
+
expiresAt: response.data.expiresAt
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Error(`Payment creation error: ${error.response?.data?.message || error.message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async handleCallback(callbackData) {
|
|
63
|
+
try {
|
|
64
|
+
const referenceId = callbackData.referenceId;
|
|
65
|
+
|
|
66
|
+
if (!referenceId) {
|
|
67
|
+
throw new Error('Reference ID not found in callback data');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get payment status
|
|
71
|
+
const payment = await this.getPaymentStatus(referenceId);
|
|
72
|
+
|
|
73
|
+
// Status mapping
|
|
74
|
+
const statusMapping = {
|
|
75
|
+
'paid': 'success',
|
|
76
|
+
'completed': 'success',
|
|
77
|
+
'analysis': 'pending',
|
|
78
|
+
'created': 'pending',
|
|
79
|
+
'expired': 'failed',
|
|
80
|
+
'refunded': 'refunded',
|
|
81
|
+
'chargeback': 'disputed'
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
status: statusMapping[payment.status] || 'unknown',
|
|
86
|
+
orderId: payment.referenceId,
|
|
87
|
+
transactionId: payment.authorizationId,
|
|
88
|
+
amount: parseFloat(payment.value),
|
|
89
|
+
currency: 'BRL',
|
|
90
|
+
paymentStatus: payment.status
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new Error(`Error in PicPay callback handling: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async getPaymentStatus(referenceId) {
|
|
98
|
+
try {
|
|
99
|
+
const response = await this.client.get(`/payments/${referenceId}/status`);
|
|
100
|
+
return response.data;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new Error(`Error getting payment status: ${error.response?.data?.message || error.message}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async cancelPayment(referenceId, authorizationId) {
|
|
107
|
+
try {
|
|
108
|
+
const response = await this.client.post(`/payments/${referenceId}/cancellations`, {
|
|
109
|
+
authorizationId: authorizationId
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return response.data;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw new Error(`Error canceling payment: ${error.response?.data?.message || error.message}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = PicPayClient;
|
package/lib/plisio.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PlisioClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['apiKey'];
|
|
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.URL = 'https://api.plisio.net/api/v1';
|
|
13
|
+
|
|
14
|
+
this.client = axios.create({
|
|
15
|
+
baseURL: this.URL,
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/json'
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
this.client.interceptors.response.use(response => {
|
|
22
|
+
return response;
|
|
23
|
+
}, error => {
|
|
24
|
+
if (error.response) {
|
|
25
|
+
throw new Error(`Plisio API error: ${error.response.data.message || error.message}`);
|
|
26
|
+
}
|
|
27
|
+
throw new Error(`Plisio API error: ${error.message}`);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async createPayment(options) {
|
|
32
|
+
try {
|
|
33
|
+
const params = {
|
|
34
|
+
api_key: this.apiKey,
|
|
35
|
+
order_name: options.orderName || options.name || 'Order',
|
|
36
|
+
order_number: options.orderNumber || options.orderId || `ORDER-${Date.now()}`,
|
|
37
|
+
amount: parseFloat(options.amount),
|
|
38
|
+
currency: options.currency || 'USD',
|
|
39
|
+
source_currency: options.sourceCurrency || options.currency || 'USD',
|
|
40
|
+
source_amount: options.sourceAmount || parseFloat(options.amount),
|
|
41
|
+
callback_url: options.callbackUrl || options.callback_link,
|
|
42
|
+
email: options.email,
|
|
43
|
+
language: options.language || 'en_US'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Opsiyonel parametreler
|
|
47
|
+
if (options.plugin) params.plugin = options.plugin;
|
|
48
|
+
if (options.version) params.version = options.version;
|
|
49
|
+
if (options.success_callback) params.success_callback = options.success_callback;
|
|
50
|
+
if (options.fail_callback) params.fail_callback = options.fail_callback;
|
|
51
|
+
|
|
52
|
+
const response = await this.client.get('/invoices/new', { params });
|
|
53
|
+
|
|
54
|
+
if (response.data.status !== 'success') {
|
|
55
|
+
throw new Error(response.data.message || 'Payment creation failed');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
status: 'success',
|
|
60
|
+
data: {
|
|
61
|
+
txnId: response.data.data.txn_id,
|
|
62
|
+
url: response.data.data.invoice_url,
|
|
63
|
+
amount: response.data.data.amount,
|
|
64
|
+
currency: response.data.data.source_currency,
|
|
65
|
+
orderId: params.order_number
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
} catch (error) {
|
|
69
|
+
throw new Error(`Payment creation error: ${error.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async getTransactionDetails(txnId) {
|
|
74
|
+
try {
|
|
75
|
+
const params = {
|
|
76
|
+
api_key: this.apiKey,
|
|
77
|
+
id: txnId
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const response = await this.client.get('/operations/invoice', { params });
|
|
81
|
+
|
|
82
|
+
if (response.data.status !== 'success') {
|
|
83
|
+
throw new Error(response.data.message || 'Failed to get transaction details');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return response.data.data;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new Error(`Transaction details error: ${error.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getBalance(currency) {
|
|
93
|
+
try {
|
|
94
|
+
const params = {
|
|
95
|
+
api_key: this.apiKey,
|
|
96
|
+
currency: currency || 'BTC'
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const response = await this.client.get('/balances', { params });
|
|
100
|
+
|
|
101
|
+
if (response.data.status !== 'success') {
|
|
102
|
+
throw new Error(response.data.message || 'Failed to get balance');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return response.data.data;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new Error(`Balance error: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async handleCallback(callbackData) {
|
|
112
|
+
try {
|
|
113
|
+
const verification = await this.verifyCallback(callbackData);
|
|
114
|
+
|
|
115
|
+
if (!verification.status) {
|
|
116
|
+
throw new Error(verification.error.message);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const data = verification.data;
|
|
120
|
+
|
|
121
|
+
// Status mapping
|
|
122
|
+
const statusMapping = {
|
|
123
|
+
'completed': 'success',
|
|
124
|
+
'pending': 'pending',
|
|
125
|
+
'expired': 'expired',
|
|
126
|
+
'error': 'failed',
|
|
127
|
+
'mismatch': 'failed',
|
|
128
|
+
'cancelled': 'cancelled'
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
status: statusMapping[data.status] || 'unknown',
|
|
133
|
+
orderId: data.order_number,
|
|
134
|
+
txnId: data.txn_id,
|
|
135
|
+
amount: parseFloat(data.amount),
|
|
136
|
+
currency: data.currency,
|
|
137
|
+
sourceCurrency: data.source_currency,
|
|
138
|
+
sourceAmount: parseFloat(data.source_amount),
|
|
139
|
+
paymentStatus: data.status,
|
|
140
|
+
confirmations: data.confirmations || 0
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
throw new Error(`Error in Plisio callback handling: ${error.message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async verifyCallback(data) {
|
|
148
|
+
try {
|
|
149
|
+
// Verify hash signature
|
|
150
|
+
if (data.verify_hash) {
|
|
151
|
+
const expectedHash = this.generateVerifyHash(data);
|
|
152
|
+
|
|
153
|
+
if (data.verify_hash !== expectedHash) {
|
|
154
|
+
return {
|
|
155
|
+
status: false,
|
|
156
|
+
error: {
|
|
157
|
+
code: 401,
|
|
158
|
+
message: 'Invalid verify hash'
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check if payment is completed
|
|
165
|
+
if (data.status === 'error' || data.status === 'cancelled') {
|
|
166
|
+
return {
|
|
167
|
+
status: false,
|
|
168
|
+
error: {
|
|
169
|
+
code: 400,
|
|
170
|
+
message: `Payment ${data.status}`
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
status: true,
|
|
177
|
+
data: data
|
|
178
|
+
};
|
|
179
|
+
} catch (error) {
|
|
180
|
+
return {
|
|
181
|
+
status: false,
|
|
182
|
+
error: {
|
|
183
|
+
code: 500,
|
|
184
|
+
message: error.message
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
generateVerifyHash(data) {
|
|
191
|
+
// Plisio verify hash generation
|
|
192
|
+
const params = {
|
|
193
|
+
'currency': data.currency || '',
|
|
194
|
+
'order_number': data.order_number || '',
|
|
195
|
+
'order_name': data.order_name || '',
|
|
196
|
+
'source_currency': data.source_currency || '',
|
|
197
|
+
'source_rate': data.source_rate || '',
|
|
198
|
+
'amount': data.amount || '',
|
|
199
|
+
'status': data.status || '',
|
|
200
|
+
'tx_urls': data.tx_urls || '',
|
|
201
|
+
'txn_id': data.txn_id || ''
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const orderedParams = Object.keys(params)
|
|
205
|
+
.sort()
|
|
206
|
+
.map(key => params[key])
|
|
207
|
+
.join('|');
|
|
208
|
+
|
|
209
|
+
return crypto
|
|
210
|
+
.createHash('sha1')
|
|
211
|
+
.update(orderedParams + '|' + this.apiKey)
|
|
212
|
+
.digest('hex');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async getSupportedCurrencies() {
|
|
216
|
+
try {
|
|
217
|
+
const params = {
|
|
218
|
+
api_key: this.apiKey
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const response = await this.client.get('/currencies', { params });
|
|
222
|
+
|
|
223
|
+
if (response.data.status !== 'success') {
|
|
224
|
+
throw new Error(response.data.message || 'Failed to get currencies');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return response.data.data;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
throw new Error(`Supported currencies error: ${error.message}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
module.exports = PlisioClient;
|