aba-payway 0.1.0 → 0.2.1
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/LICENSE +1 -1
- package/README.md +115 -10
- package/dist/index.cjs +258 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +184 -24
- package/dist/index.d.ts +184 -24
- package/dist/index.js +257 -26
- package/dist/index.js.map +1 -1
- package/package.json +18 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
# aba-payway
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/aba-payway)
|
|
4
|
+
[](https://github.com/Joselay/aba-payway/actions)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.npmjs.com/package/aba-payway)
|
|
7
|
+
[](https://www.npmjs.com/package/aba-payway)
|
|
8
|
+
|
|
3
9
|
Type-safe TypeScript SDK for [ABA PayWay](https://www.payway.com.kh/) — Cambodia's #1 payment gateway.
|
|
4
10
|
|
|
5
11
|
> **Unofficial community SDK.** Not affiliated with ABA Bank.
|
|
6
12
|
|
|
13
|
+
## Why aba-payway?
|
|
14
|
+
|
|
15
|
+
ABA PayWay's official SDK is a raw REST API with no official Node.js/TypeScript client. That means you're left dealing with manual HMAC hashing, snake_case field ordering, base64 encoding, and zero type safety. This SDK handles all of that for you.
|
|
16
|
+
|
|
17
|
+
| | `aba-payway` | Rolling your own |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| Type safety | Full TypeScript with strict types | None — guess the field names |
|
|
20
|
+
| Hash computation | Automatic, correct field ordering | Manual HMAC-SHA512, easy to get wrong |
|
|
21
|
+
| Base64 encoding | Automatic for items, URLs, payout | Do it yourself, hope you didn't miss one |
|
|
22
|
+
| Dependencies | **Zero** — only Node.js built-ins | Probably `axios` + `crypto-js` + glue code |
|
|
23
|
+
| API coverage | 15+ methods | Build each one from scratch |
|
|
24
|
+
| Runtime support | Node.js, Bun, Deno, Cloudflare Workers | Whatever you tested |
|
|
25
|
+
|
|
7
26
|
## Features
|
|
8
27
|
|
|
9
|
-
- Zero runtime dependencies — uses Node.js built-in `crypto` and native `fetch`
|
|
10
|
-
- Full TypeScript support with strict types
|
|
11
|
-
- Dual ESM + CJS output
|
|
12
|
-
-
|
|
13
|
-
-
|
|
28
|
+
- **Zero runtime dependencies** — uses Node.js built-in `crypto` and native `fetch`
|
|
29
|
+
- **Full TypeScript support** with strict types and autocomplete
|
|
30
|
+
- **Dual ESM + CJS output** — works everywhere
|
|
31
|
+
- **15+ API methods** — checkout, QR payments, refunds, pre-auth, payouts, and more
|
|
32
|
+
- **Runs anywhere** — Node.js, Bun, Deno, Next.js, Express, Hono, Cloudflare Workers
|
|
33
|
+
- **Tested against ABA's sandbox** — not just unit tests, real API integration tests
|
|
14
34
|
|
|
15
35
|
## Installation
|
|
16
36
|
|
|
@@ -56,7 +76,8 @@ const params = payway.createTransaction({
|
|
|
56
76
|
|---|---|---|---|---|
|
|
57
77
|
| `merchantId` | `string` | Yes | — | Your ABA PayWay merchant ID |
|
|
58
78
|
| `apiKey` | `string` | Yes | — | Your ABA PayWay API key |
|
|
59
|
-
| `
|
|
79
|
+
| `environment` | `'sandbox' \| 'production'` | No | `'sandbox'` | Target environment |
|
|
80
|
+
| `baseUrl` | `string` | No | — | Override the base URL directly (takes priority over `environment`) |
|
|
60
81
|
|
|
61
82
|
### `payway.createTransaction(options)`
|
|
62
83
|
|
|
@@ -96,7 +117,7 @@ Check the status of a transaction.
|
|
|
96
117
|
```typescript
|
|
97
118
|
const result = await payway.checkTransaction('order-001')
|
|
98
119
|
console.log(result.status.code) // '00' = success
|
|
99
|
-
console.log(result.data
|
|
120
|
+
console.log(result.data?.payment_status) // 'APPROVED' | 'DECLINED' | 'PENDING' | ...
|
|
100
121
|
```
|
|
101
122
|
|
|
102
123
|
### `payway.listTransactions(options?)`
|
|
@@ -105,8 +126,8 @@ List transactions with optional filters. Max 3-day date range.
|
|
|
105
126
|
|
|
106
127
|
```typescript
|
|
107
128
|
const list = await payway.listTransactions({
|
|
108
|
-
fromDate: '
|
|
109
|
-
toDate: '
|
|
129
|
+
fromDate: '2026-03-01 00:00:00',
|
|
130
|
+
toDate: '2026-03-03 23:59:59',
|
|
110
131
|
status: 'APPROVED',
|
|
111
132
|
page: 1,
|
|
112
133
|
pagination: 20,
|
|
@@ -123,6 +144,78 @@ const list = await payway.listTransactions({
|
|
|
123
144
|
| `page` | `number` | Page number (default: `1`) |
|
|
124
145
|
| `pagination` | `number` | Records per page (default: `40`, max: `1000`) |
|
|
125
146
|
|
|
147
|
+
### `payway.getTransactionDetails(transactionId)`
|
|
148
|
+
|
|
149
|
+
Get detailed information about a transaction, including its operation history.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const details = await payway.getTransactionDetails('order-001')
|
|
153
|
+
console.log(details.data?.payment_status) // 'APPROVED'
|
|
154
|
+
console.log(details.data?.transaction_operations) // [{ status, amount, ... }]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### `payway.closeTransaction(transactionId)`
|
|
158
|
+
|
|
159
|
+
Close (cancel) a pending transaction. The payment status becomes `CANCELLED`.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const result = await payway.closeTransaction('order-001')
|
|
163
|
+
console.log(result.status.code) // '00' = success
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### `payway.getExchangeRate()`
|
|
167
|
+
|
|
168
|
+
Fetch the latest exchange rates from ABA Bank for 12 currencies.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const rates = await payway.getExchangeRate()
|
|
172
|
+
console.log(rates.exchange_rates.eur) // { sell: '...', buy: '...' }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `payway.generateQR(options)`
|
|
176
|
+
|
|
177
|
+
Generate a dynamic QR code for payment via ABA KHQR, WeChat Pay, or Alipay.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const qr = await payway.generateQR({
|
|
181
|
+
transactionId: 'qr-001',
|
|
182
|
+
amount: 10.00,
|
|
183
|
+
paymentOption: 'abapay_khqr',
|
|
184
|
+
qrImageTemplate: 'template1',
|
|
185
|
+
lifetime: 30,
|
|
186
|
+
})
|
|
187
|
+
console.log(qr.qrString) // QR content string
|
|
188
|
+
console.log(qr.abapay_deeplink) // ABA Mobile deep link
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
| Parameter | Type | Required | Description |
|
|
192
|
+
|---|---|---|---|
|
|
193
|
+
| `transactionId` | `string` | Yes | Unique transaction ID (max 20 chars) |
|
|
194
|
+
| `amount` | `number` | Yes | Payment amount (min: 100 KHR or 0.01 USD) |
|
|
195
|
+
| `paymentOption` | `QRPaymentOption` | Yes | `abapay_khqr`, `wechat` (USD only), `alipay` (USD only) |
|
|
196
|
+
| `qrImageTemplate` | `string` | Yes | QR image template name |
|
|
197
|
+
| `currency` | `'USD' \| 'KHR'` | No | Default: `'USD'` |
|
|
198
|
+
| `lifetime` | `number` | No | Lifetime in minutes (3–43200) |
|
|
199
|
+
| `firstName` | `string` | No | Payer's first name |
|
|
200
|
+
| `lastName` | `string` | No | Payer's last name |
|
|
201
|
+
| `email` | `string` | No | Payer's email |
|
|
202
|
+
| `phone` | `string` | No | Payer's phone |
|
|
203
|
+
| `purchaseType` | `'purchase' \| 'pre-auth'` | No | Default: `'purchase'` |
|
|
204
|
+
| `items` | `string` | No | Item description (auto base64-encoded) |
|
|
205
|
+
| `callbackUrl` | `string` | No | Payment callback URL (auto base64-encoded) |
|
|
206
|
+
| `returnDeeplink` | `string` | No | Mobile deeplink (auto base64-encoded) |
|
|
207
|
+
| `payout` | `string` | No | Payout JSON string (auto base64-encoded) |
|
|
208
|
+
|
|
209
|
+
### `payway.getTransactionsByRef(merchantRef)`
|
|
210
|
+
|
|
211
|
+
Get transactions by merchant reference. Returns up to the last 50 transactions.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const txns = await payway.getTransactionsByRef('REF-001')
|
|
215
|
+
for (const txn of txns.data) {
|
|
216
|
+
console.log(txn.transaction_id, txn.payment_status)
|
|
217
|
+
}
|
|
218
|
+
|
|
126
219
|
## Error Handling
|
|
127
220
|
|
|
128
221
|
```typescript
|
|
@@ -147,7 +240,7 @@ import { PayWay } from 'aba-payway'
|
|
|
147
240
|
const payway = new PayWay({
|
|
148
241
|
merchantId: process.env.PAYWAY_MERCHANT_ID!,
|
|
149
242
|
apiKey: process.env.PAYWAY_API_KEY!,
|
|
150
|
-
|
|
243
|
+
environment: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox',
|
|
151
244
|
})
|
|
152
245
|
|
|
153
246
|
export async function POST(request: Request) {
|
|
@@ -202,6 +295,18 @@ app.post('/pay', (req, res) => {
|
|
|
202
295
|
|
|
203
296
|
For the full ABA PayWay API documentation, see [aba-payway-docs](https://github.com/Joselay/aba-payway-docs).
|
|
204
297
|
|
|
298
|
+
## Contributing
|
|
299
|
+
|
|
300
|
+
Found a bug or have a feature request? [Open an issue](https://github.com/Joselay/aba-payway/issues) — all feedback is welcome.
|
|
301
|
+
|
|
302
|
+
Pull requests are also welcome. Please make sure tests pass before submitting:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
bun run test
|
|
306
|
+
bun run typecheck
|
|
307
|
+
bun run lint
|
|
308
|
+
```
|
|
309
|
+
|
|
205
310
|
## License
|
|
206
311
|
|
|
207
312
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
BASE_URLS: () => BASE_URLS,
|
|
23
24
|
ENDPOINTS: () => ENDPOINTS,
|
|
24
25
|
PRODUCTION_BASE_URL: () => PRODUCTION_BASE_URL,
|
|
25
26
|
PayWay: () => PayWay,
|
|
@@ -37,18 +38,27 @@ __export(index_exports, {
|
|
|
37
38
|
module.exports = __toCommonJS(index_exports);
|
|
38
39
|
|
|
39
40
|
// src/constants.ts
|
|
40
|
-
var
|
|
41
|
-
|
|
41
|
+
var BASE_URLS = {
|
|
42
|
+
sandbox: "https://checkout-sandbox.payway.com.kh",
|
|
43
|
+
production: "https://checkout.payway.com.kh"
|
|
44
|
+
};
|
|
45
|
+
var SANDBOX_BASE_URL = BASE_URLS.sandbox;
|
|
46
|
+
var PRODUCTION_BASE_URL = BASE_URLS.production;
|
|
42
47
|
var ENDPOINTS = {
|
|
43
48
|
purchase: "/api/payment-gateway/v1/payments/purchase",
|
|
44
49
|
checkTransaction: "/api/payment-gateway/v1/payments/check-transaction-2",
|
|
45
|
-
transactionList: "/api/payment-gateway/v1/payments/transaction-list-2"
|
|
50
|
+
transactionList: "/api/payment-gateway/v1/payments/transaction-list-2",
|
|
51
|
+
transactionDetail: "/api/payment-gateway/v1/payments/transaction-detail",
|
|
52
|
+
closeTransaction: "/api/payment-gateway/v1/payments/close-transaction",
|
|
53
|
+
exchangeRate: "/api/payment-gateway/v1/exchange-rate",
|
|
54
|
+
generateQR: "/api/payment-gateway/v1/payments/generate-qr",
|
|
55
|
+
getTransactionsByRef: "/api/payment-gateway/v1/payments/get-transactions-by-mc-ref"
|
|
46
56
|
};
|
|
47
57
|
|
|
48
58
|
// src/errors.ts
|
|
49
59
|
var PayWayError = class extends Error {
|
|
50
|
-
constructor(message) {
|
|
51
|
-
super(message);
|
|
60
|
+
constructor(message, options) {
|
|
61
|
+
super(message, options);
|
|
52
62
|
this.name = "PayWayError";
|
|
53
63
|
}
|
|
54
64
|
};
|
|
@@ -94,17 +104,24 @@ function formatRequestTime(date = /* @__PURE__ */ new Date()) {
|
|
|
94
104
|
const seconds = date.getUTCSeconds().toString().padStart(2, "0");
|
|
95
105
|
return `${year}${month}${day}${hours}${minutes}${seconds}`;
|
|
96
106
|
}
|
|
97
|
-
function
|
|
98
|
-
const
|
|
107
|
+
function filterParams(params) {
|
|
108
|
+
const out = {};
|
|
99
109
|
for (const [key, value] of Object.entries(params)) {
|
|
100
|
-
if (value !== void 0 && value !==
|
|
101
|
-
|
|
110
|
+
if (value !== void 0 && value !== "") {
|
|
111
|
+
out[key] = value;
|
|
102
112
|
}
|
|
103
113
|
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
function buildFormData(params) {
|
|
117
|
+
const formData = new FormData();
|
|
118
|
+
for (const [key, value] of Object.entries(filterParams(params))) {
|
|
119
|
+
formData.append(key, String(value));
|
|
120
|
+
}
|
|
104
121
|
return formData;
|
|
105
122
|
}
|
|
106
123
|
function formatAmount(amount, currency = "USD") {
|
|
107
|
-
if (currency
|
|
124
|
+
if (currency === "KHR") {
|
|
108
125
|
return Math.round(amount).toString();
|
|
109
126
|
}
|
|
110
127
|
return amount.toFixed(2);
|
|
@@ -118,6 +135,11 @@ var PayWay = class {
|
|
|
118
135
|
merchantId;
|
|
119
136
|
apiKey;
|
|
120
137
|
baseUrl;
|
|
138
|
+
timeout;
|
|
139
|
+
/**
|
|
140
|
+
* @param config - Merchant credentials and environment selection.
|
|
141
|
+
* @throws {PayWayConfigError} If `merchantId` or `apiKey` is empty.
|
|
142
|
+
*/
|
|
121
143
|
constructor(config) {
|
|
122
144
|
if (!config.merchantId) {
|
|
123
145
|
throw new PayWayConfigError("merchantId is required");
|
|
@@ -127,13 +149,28 @@ var PayWay = class {
|
|
|
127
149
|
}
|
|
128
150
|
this.merchantId = config.merchantId;
|
|
129
151
|
this.apiKey = config.apiKey;
|
|
130
|
-
this.baseUrl = config.
|
|
152
|
+
this.baseUrl = config.baseUrl ?? BASE_URLS[config.environment ?? "sandbox"];
|
|
153
|
+
this.timeout = config.timeout ?? 3e4;
|
|
131
154
|
}
|
|
132
155
|
/**
|
|
133
156
|
* Generate checkout parameters for a transaction.
|
|
134
157
|
* Returns form params to be submitted from the browser via ABA's checkout JS SDK.
|
|
135
158
|
*/
|
|
136
159
|
createTransaction(options) {
|
|
160
|
+
if (!options.transactionId) {
|
|
161
|
+
throw new PayWayConfigError("transactionId is required");
|
|
162
|
+
}
|
|
163
|
+
if (options.transactionId.length > 20) {
|
|
164
|
+
throw new PayWayConfigError(
|
|
165
|
+
"transactionId must be at most 20 characters"
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
if (options.amount <= 0) {
|
|
169
|
+
throw new PayWayConfigError("amount must be greater than 0");
|
|
170
|
+
}
|
|
171
|
+
if (options.lifetime !== void 0 && (options.lifetime < 3 || options.lifetime > 43200)) {
|
|
172
|
+
throw new PayWayConfigError("lifetime must be between 3 and 43200");
|
|
173
|
+
}
|
|
137
174
|
const reqTime = formatRequestTime();
|
|
138
175
|
const currency = options.currency ?? "USD";
|
|
139
176
|
const amount = formatAmount(options.amount, currency);
|
|
@@ -170,8 +207,6 @@ var PayWay = class {
|
|
|
170
207
|
currency,
|
|
171
208
|
options.customFields ?? "",
|
|
172
209
|
options.returnParams ?? "",
|
|
173
|
-
options.viewType ?? "",
|
|
174
|
-
options.paymentGate?.toString() ?? "",
|
|
175
210
|
payout,
|
|
176
211
|
options.lifetime?.toString() ?? "",
|
|
177
212
|
options.additionalParams ?? "",
|
|
@@ -210,7 +245,15 @@ var PayWay = class {
|
|
|
210
245
|
payment_gate: options.paymentGate?.toString() ?? ""
|
|
211
246
|
};
|
|
212
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Check the status of a transaction.
|
|
250
|
+
* @param transactionId - The unique transaction ID to look up.
|
|
251
|
+
* @throws {PayWayAPIError} If the API returns a non-2xx response.
|
|
252
|
+
*/
|
|
213
253
|
async checkTransaction(transactionId) {
|
|
254
|
+
if (!transactionId) {
|
|
255
|
+
throw new PayWayConfigError("transactionId is required");
|
|
256
|
+
}
|
|
214
257
|
const reqTime = formatRequestTime();
|
|
215
258
|
const hashValues = [reqTime, this.merchantId, transactionId];
|
|
216
259
|
const hash = createHash(hashValues, this.apiKey);
|
|
@@ -225,6 +268,11 @@ var PayWay = class {
|
|
|
225
268
|
params
|
|
226
269
|
);
|
|
227
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* List transactions with optional filters. Max 3-day date range.
|
|
273
|
+
* @param options - Filter and pagination options.
|
|
274
|
+
* @throws {PayWayAPIError} If the API returns a non-2xx response.
|
|
275
|
+
*/
|
|
228
276
|
async listTransactions(options = {}) {
|
|
229
277
|
const reqTime = formatRequestTime();
|
|
230
278
|
const hashValues = [
|
|
@@ -256,29 +304,212 @@ var PayWay = class {
|
|
|
256
304
|
params
|
|
257
305
|
);
|
|
258
306
|
}
|
|
259
|
-
|
|
307
|
+
/**
|
|
308
|
+
* Get detailed information about a transaction, including its operation history.
|
|
309
|
+
* @param transactionId - The unique transaction ID to look up.
|
|
310
|
+
* @throws {PayWayAPIError} If the API returns a non-2xx response.
|
|
311
|
+
*/
|
|
312
|
+
async getTransactionDetails(transactionId) {
|
|
313
|
+
if (!transactionId) {
|
|
314
|
+
throw new PayWayConfigError("transactionId is required");
|
|
315
|
+
}
|
|
316
|
+
const reqTime = formatRequestTime();
|
|
317
|
+
const hashValues = [reqTime, this.merchantId, transactionId];
|
|
318
|
+
const hash = createHash(hashValues, this.apiKey);
|
|
319
|
+
const params = {
|
|
320
|
+
hash,
|
|
321
|
+
tran_id: transactionId,
|
|
322
|
+
req_time: reqTime,
|
|
323
|
+
merchant_id: this.merchantId
|
|
324
|
+
};
|
|
325
|
+
return this.request(
|
|
326
|
+
ENDPOINTS.transactionDetail,
|
|
327
|
+
params,
|
|
328
|
+
"json"
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Close (cancel) a pending transaction.
|
|
333
|
+
* @param transactionId - The transaction ID to close.
|
|
334
|
+
* @throws {PayWayAPIError} If the API returns a non-2xx response.
|
|
335
|
+
*/
|
|
336
|
+
async closeTransaction(transactionId) {
|
|
337
|
+
if (!transactionId) {
|
|
338
|
+
throw new PayWayConfigError("transactionId is required");
|
|
339
|
+
}
|
|
340
|
+
const reqTime = formatRequestTime();
|
|
341
|
+
const hashValues = [reqTime, this.merchantId, transactionId];
|
|
342
|
+
const hash = createHash(hashValues, this.apiKey);
|
|
343
|
+
const params = {
|
|
344
|
+
hash,
|
|
345
|
+
tran_id: transactionId,
|
|
346
|
+
req_time: reqTime,
|
|
347
|
+
merchant_id: this.merchantId
|
|
348
|
+
};
|
|
349
|
+
return this.request(
|
|
350
|
+
ENDPOINTS.closeTransaction,
|
|
351
|
+
params,
|
|
352
|
+
"json"
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Fetch the latest exchange rates from ABA Bank.
|
|
357
|
+
* @throws {PayWayAPIError} If the API returns a non-2xx response.
|
|
358
|
+
*/
|
|
359
|
+
async getExchangeRate() {
|
|
360
|
+
const reqTime = formatRequestTime();
|
|
361
|
+
const hashValues = [reqTime, this.merchantId];
|
|
362
|
+
const hash = createHash(hashValues, this.apiKey);
|
|
363
|
+
const params = {
|
|
364
|
+
hash,
|
|
365
|
+
req_time: reqTime,
|
|
366
|
+
merchant_id: this.merchantId
|
|
367
|
+
};
|
|
368
|
+
return this.request(
|
|
369
|
+
ENDPOINTS.exchangeRate,
|
|
370
|
+
params,
|
|
371
|
+
"json"
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Generate a QR code for payment via ABA KHQR, WeChat Pay, or Alipay.
|
|
376
|
+
* @param options - QR generation options.
|
|
377
|
+
* @throws {PayWayConfigError} If required options are missing or invalid.
|
|
378
|
+
* @throws {PayWayAPIError} If the API returns a non-2xx response.
|
|
379
|
+
*/
|
|
380
|
+
async generateQR(options) {
|
|
381
|
+
if (!options.transactionId) {
|
|
382
|
+
throw new PayWayConfigError("transactionId is required");
|
|
383
|
+
}
|
|
384
|
+
if (options.amount <= 0) {
|
|
385
|
+
throw new PayWayConfigError("amount must be greater than 0");
|
|
386
|
+
}
|
|
387
|
+
if (!options.paymentOption) {
|
|
388
|
+
throw new PayWayConfigError("paymentOption is required");
|
|
389
|
+
}
|
|
390
|
+
if (!options.qrImageTemplate) {
|
|
391
|
+
throw new PayWayConfigError("qrImageTemplate is required");
|
|
392
|
+
}
|
|
393
|
+
if (options.lifetime !== void 0 && (options.lifetime < 3 || options.lifetime > 43200)) {
|
|
394
|
+
throw new PayWayConfigError("lifetime must be between 3 and 43200");
|
|
395
|
+
}
|
|
396
|
+
const reqTime = formatRequestTime();
|
|
397
|
+
const currency = options.currency ?? "USD";
|
|
398
|
+
const amount = formatAmount(options.amount, currency);
|
|
399
|
+
const items = options.items ? toBase64(options.items) : "";
|
|
400
|
+
const callbackUrl = options.callbackUrl ? toBase64(options.callbackUrl) : "";
|
|
401
|
+
const returnDeeplink = options.returnDeeplink ? toBase64(options.returnDeeplink) : "";
|
|
402
|
+
const customFields = options.customFields ? toBase64(options.customFields) : "";
|
|
403
|
+
const payout = options.payout ? toBase64(options.payout) : "";
|
|
404
|
+
const hashValues = [
|
|
405
|
+
reqTime,
|
|
406
|
+
this.merchantId,
|
|
407
|
+
options.transactionId,
|
|
408
|
+
amount,
|
|
409
|
+
items,
|
|
410
|
+
options.firstName ?? "",
|
|
411
|
+
options.lastName ?? "",
|
|
412
|
+
options.email ?? "",
|
|
413
|
+
options.phone ?? "",
|
|
414
|
+
options.purchaseType ?? "",
|
|
415
|
+
options.paymentOption,
|
|
416
|
+
callbackUrl,
|
|
417
|
+
returnDeeplink,
|
|
418
|
+
currency,
|
|
419
|
+
customFields,
|
|
420
|
+
options.returnParams ?? "",
|
|
421
|
+
payout,
|
|
422
|
+
options.lifetime?.toString() ?? "",
|
|
423
|
+
options.qrImageTemplate
|
|
424
|
+
];
|
|
425
|
+
const hash = createHash(hashValues, this.apiKey);
|
|
426
|
+
const params = {
|
|
427
|
+
hash,
|
|
428
|
+
req_time: reqTime,
|
|
429
|
+
merchant_id: this.merchantId,
|
|
430
|
+
tran_id: options.transactionId,
|
|
431
|
+
amount: options.amount,
|
|
432
|
+
currency,
|
|
433
|
+
payment_option: options.paymentOption,
|
|
434
|
+
lifetime: options.lifetime,
|
|
435
|
+
qr_image_template: options.qrImageTemplate,
|
|
436
|
+
first_name: options.firstName,
|
|
437
|
+
last_name: options.lastName,
|
|
438
|
+
email: options.email,
|
|
439
|
+
phone: options.phone,
|
|
440
|
+
purchase_type: options.purchaseType,
|
|
441
|
+
items: items || void 0,
|
|
442
|
+
callback_url: callbackUrl || void 0,
|
|
443
|
+
return_deeplink: returnDeeplink || void 0,
|
|
444
|
+
custom_fields: customFields || void 0,
|
|
445
|
+
return_params: options.returnParams,
|
|
446
|
+
payout: payout || void 0
|
|
447
|
+
};
|
|
448
|
+
return this.request(
|
|
449
|
+
ENDPOINTS.generateQR,
|
|
450
|
+
params,
|
|
451
|
+
"json"
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Get transactions by merchant reference. Returns up to the last 50 transactions.
|
|
456
|
+
* @param merchantRef - Your merchant reference number.
|
|
457
|
+
* @throws {PayWayAPIError} If the API returns a non-2xx response.
|
|
458
|
+
*/
|
|
459
|
+
async getTransactionsByRef(merchantRef) {
|
|
460
|
+
if (!merchantRef) {
|
|
461
|
+
throw new PayWayConfigError("merchantRef is required");
|
|
462
|
+
}
|
|
463
|
+
const reqTime = formatRequestTime();
|
|
464
|
+
const hashValues = [reqTime, this.merchantId, merchantRef];
|
|
465
|
+
const hash = createHash(hashValues, this.apiKey);
|
|
466
|
+
const params = {
|
|
467
|
+
hash,
|
|
468
|
+
merchant_ref: merchantRef,
|
|
469
|
+
req_time: reqTime,
|
|
470
|
+
merchant_id: this.merchantId
|
|
471
|
+
};
|
|
472
|
+
return this.request(
|
|
473
|
+
ENDPOINTS.getTransactionsByRef,
|
|
474
|
+
params,
|
|
475
|
+
"json"
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
async request(endpoint, params, format = "form") {
|
|
260
479
|
const url = `${this.baseUrl}${endpoint}`;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
480
|
+
let body;
|
|
481
|
+
const headers = {};
|
|
482
|
+
if (format === "json") {
|
|
483
|
+
body = JSON.stringify(filterParams(params));
|
|
484
|
+
headers["Content-Type"] = "application/json";
|
|
485
|
+
} else {
|
|
486
|
+
body = buildFormData(params);
|
|
487
|
+
}
|
|
488
|
+
let response;
|
|
489
|
+
try {
|
|
490
|
+
response = await fetch(url, {
|
|
491
|
+
method: "POST",
|
|
492
|
+
body,
|
|
493
|
+
headers,
|
|
494
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
495
|
+
});
|
|
496
|
+
} catch (error) {
|
|
497
|
+
throw new PayWayError(
|
|
498
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
499
|
+
{ cause: error }
|
|
500
|
+
);
|
|
266
501
|
}
|
|
267
|
-
const response = await fetch(url, {
|
|
268
|
-
method: "POST",
|
|
269
|
-
body: formData
|
|
270
|
-
});
|
|
271
502
|
if (!response.ok) {
|
|
272
503
|
const text = await response.text();
|
|
273
|
-
let
|
|
504
|
+
let responseBody = text;
|
|
274
505
|
try {
|
|
275
|
-
|
|
506
|
+
responseBody = JSON.parse(text);
|
|
276
507
|
} catch {
|
|
277
508
|
}
|
|
278
509
|
throw new PayWayAPIError(
|
|
279
510
|
`PayWay API error: ${response.status} ${response.statusText}`,
|
|
280
511
|
response.status,
|
|
281
|
-
|
|
512
|
+
responseBody
|
|
282
513
|
);
|
|
283
514
|
}
|
|
284
515
|
return await response.json();
|
|
@@ -286,6 +517,7 @@ var PayWay = class {
|
|
|
286
517
|
};
|
|
287
518
|
// Annotate the CommonJS export names for ESM import in node:
|
|
288
519
|
0 && (module.exports = {
|
|
520
|
+
BASE_URLS,
|
|
289
521
|
ENDPOINTS,
|
|
290
522
|
PRODUCTION_BASE_URL,
|
|
291
523
|
PayWay,
|