paj_ramp 1.5.2 → 1.5.4

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.
@@ -8,7 +8,9 @@ export interface CreateOfframpOrder {
8
8
  fiatAmount?: number;
9
9
  mint: string;
10
10
  chain: Chain;
11
+ description?: string;
11
12
  webhookURL: string;
13
+ fee?: number;
12
14
  }
13
15
  export interface OfframpOrder {
14
16
  id: string;
@@ -1,8 +1,10 @@
1
1
  import { post } from "../../utils/api.js";
2
2
  export const createOfframpOrder = async (order, sessionToken) => {
3
3
  try {
4
+ const { fee, ...rest } = order;
4
5
  return await post("/pub/offramp", {
5
- ...order,
6
+ ...rest,
7
+ businessUSDCFee: fee,
6
8
  }, {
7
9
  Authorization: `Bearer ${sessionToken}`,
8
10
  });
@@ -8,6 +8,7 @@ export interface CreateOnrampOrder {
8
8
  mint: string;
9
9
  chain: Chain;
10
10
  webhookURL: string;
11
+ fee?: number;
11
12
  }
12
13
  export interface OnrampOrder {
13
14
  id: string;
@@ -1,8 +1,10 @@
1
1
  import { post } from "../../utils/api.js";
2
2
  export const createOnrampOrder = async (order, sessionToken) => {
3
3
  try {
4
+ const { fee, ...rest } = order;
4
5
  return await post("/pub/onramp", {
5
- ...order,
6
+ ...rest,
7
+ businessUSDCFee: fee,
6
8
  }, {
7
9
  Authorization: `Bearer ${sessionToken}`,
8
10
  });
@@ -0,0 +1,21 @@
1
+ import { Country, IdType } from "../../../utils/enums.js";
2
+ export interface SubmitKyc {
3
+ message: string;
4
+ }
5
+ /**
6
+ * Submits KYC (government ID) data for the authenticated user.
7
+ * Requires the session to have already completed email verification.
8
+ *
9
+ * Args:
10
+ * token: The session token returned by `verify`.
11
+ * idNumber: The user's government ID number (e.g. BVN or NIN value).
12
+ * idType: The type of ID — `BVN` or `NIN`.
13
+ * country: The ID-issuing country (`NG`, `GH`, `TZ`, `KE`, `ZA`).
14
+ *
15
+ * Returns:
16
+ * An object with a `message` confirming successful submission.
17
+ *
18
+ * Raises:
19
+ * Throws an error if the request fails.
20
+ */
21
+ export declare const submitKyc: (token: string, idNumber: string, idType: IdType, country: Country) => Promise<SubmitKyc>;
@@ -0,0 +1,32 @@
1
+ import { post } from "../../../utils/api.js";
2
+ /**
3
+ * Submits KYC (government ID) data for the authenticated user.
4
+ * Requires the session to have already completed email verification.
5
+ *
6
+ * Args:
7
+ * token: The session token returned by `verify`.
8
+ * idNumber: The user's government ID number (e.g. BVN or NIN value).
9
+ * idType: The type of ID — `BVN` or `NIN`.
10
+ * country: The ID-issuing country (`NG`, `GH`, `TZ`, `KE`, `ZA`).
11
+ *
12
+ * Returns:
13
+ * An object with a `message` confirming successful submission.
14
+ *
15
+ * Raises:
16
+ * Throws an error if the request fails.
17
+ */
18
+ export const submitKyc = async (token, idNumber, idType, country) => {
19
+ try {
20
+ return await post("/pub/kyc", {
21
+ idNumber,
22
+ idType,
23
+ country,
24
+ }, {
25
+ Authorization: `Bearer ${token}`,
26
+ });
27
+ }
28
+ catch (err) {
29
+ console.error("Error submitting KYC:", err);
30
+ throw err;
31
+ }
32
+ };
package/dist/sdk.d.ts CHANGED
@@ -12,6 +12,7 @@ export { resolveBankAccount, ResolveBankAccount, } from "./lib/utility/bank/reso
12
12
  export { addBankAccount, AddBankAccount, } from "./lib/utility/bank/addBankAccount.js";
13
13
  export { getBankAccounts, GetBankAccounts, } from "./lib/utility/bank/getBankAccounts.js";
14
14
  export { getTokenInfo, TokenInfo, } from "./lib/utility/token/getTokenInfo.js";
15
+ export { submitKyc, SubmitKyc, } from "./lib/utility/kyc/submitKyc.js";
15
16
  export { getAllTransactions } from "./lib/utility/transaction/getAllTransactions.js";
16
17
  export { getTransaction, PajTransaction, } from "./lib/utility/transaction/getTransaction.js";
17
18
  export { createOfframpOrder, CreateOfframpOrder, OfframpOrder, } from "./lib/off_ramp/createOrder.js";
@@ -19,5 +20,5 @@ export { createOnrampOrder, CreateOnrampOrder, OnrampOrder, } from "./lib/on_ram
19
20
  export { observeOrder, ObserveOrderOptions, ObserveOrderReturn, } from "./lib/on_ramp/observeOrder.js";
20
21
  export { getOnrampValue, OnrampValue, } from "./lib/utility/value/getOnrampValue.js";
21
22
  export { getOfframpValue, OfframpValue, ValueQuery, } from "./lib/utility/value/getOfframpValue.js";
22
- export { RateType, Currency, TransactionType, TransactionStatus, Environment, } from "./utils/enums.js";
23
+ export { RateType, Currency, TransactionType, TransactionStatus, Environment, IdType, Country, } from "./utils/enums.js";
23
24
  export { Chain, OnRampStatus, OnRampOrderUpdate, OnRampSocketEvents, OnRampSocketOptions, OnRampSocketInstance, } from "./utils/onramp-socket.js";
package/dist/sdk.js CHANGED
@@ -30,6 +30,8 @@ export { addBankAccount, } from "./lib/utility/bank/addBankAccount.js";
30
30
  export { getBankAccounts, } from "./lib/utility/bank/getBankAccounts.js";
31
31
  // Token Operations
32
32
  export { getTokenInfo, } from "./lib/utility/token/getTokenInfo.js";
33
+ // KYC
34
+ export { submitKyc, } from "./lib/utility/kyc/submitKyc.js";
33
35
  // Transaction History
34
36
  export { getAllTransactions } from "./lib/utility/transaction/getAllTransactions.js";
35
37
  export { getTransaction, } from "./lib/utility/transaction/getTransaction.js";
@@ -44,5 +46,5 @@ export { observeOrder, } from "./lib/on_ramp/observeOrder.js";
44
46
  export { getOnrampValue, } from "./lib/utility/value/getOnrampValue.js";
45
47
  export { getOfframpValue, } from "./lib/utility/value/getOfframpValue.js";
46
48
  // Enums
47
- export { RateType, Currency, TransactionType, TransactionStatus, Environment, } from "./utils/enums.js";
49
+ export { RateType, Currency, TransactionType, TransactionStatus, Environment, IdType, Country, } from "./utils/enums.js";
48
50
  export { Chain, OnRampStatus, } from "./utils/onramp-socket.js";
@@ -24,3 +24,14 @@ export declare enum Environment {
24
24
  Production = "production",
25
25
  Local = "local"
26
26
  }
27
+ export declare enum IdType {
28
+ NIN = "NIN",
29
+ BVN = "BVN"
30
+ }
31
+ export declare enum Country {
32
+ NG = "NG",
33
+ GH = "GH",
34
+ TZ = "TZ",
35
+ KE = "KE",
36
+ ZA = "ZA"
37
+ }
@@ -34,3 +34,16 @@ export var Environment;
34
34
  Environment["Production"] = "production";
35
35
  Environment["Local"] = "local";
36
36
  })(Environment || (Environment = {}));
37
+ export var IdType;
38
+ (function (IdType) {
39
+ IdType["NIN"] = "NIN";
40
+ IdType["BVN"] = "BVN";
41
+ })(IdType || (IdType = {}));
42
+ export var Country;
43
+ (function (Country) {
44
+ Country["NG"] = "NG";
45
+ Country["GH"] = "GH";
46
+ Country["TZ"] = "TZ";
47
+ Country["KE"] = "KE";
48
+ Country["ZA"] = "ZA";
49
+ })(Country || (Country = {}));
@@ -81,6 +81,8 @@ async function main() {
81
81
  mint,
82
82
  webhookURL,
83
83
  chain: Chain.SOLANA,
84
+ fee: 0.1,
85
+ description: "Groceries bill",
84
86
  },
85
87
  sessionToken,
86
88
  );
@@ -96,6 +98,8 @@ async function main() {
96
98
  console.log("Fiat Amount (to receive):", order.fiatAmount);
97
99
  console.log("Rate:", order.rate);
98
100
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
101
+ console.log("fee:", order.fee);
102
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
99
103
 
100
104
  console.log("\n📝 Next Steps:");
101
105
  console.log("1. Send", order.amount, "tokens to address:", order.address);
@@ -36,13 +36,13 @@ async function main() {
36
36
  // In a real application, you would wait for the user to receive and enter the OTP
37
37
  // For this example, we assume you have the OTP from your email
38
38
  console.log(
39
- "\n⏳ Please check your email for the OTP and add it to your .env file"
39
+ "\n⏳ Please check your email for the OTP and add it to your .env file",
40
40
  );
41
41
 
42
42
  const otp = process.env.OTP;
43
43
  if (!otp) {
44
44
  console.error(
45
- "❌ OTP not found in .env file. Please add OTP=your_otp to .env"
45
+ "❌ OTP not found in .env file. Please add OTP=your_otp to .env",
46
46
  );
47
47
  process.exit(1);
48
48
  }
@@ -58,12 +58,12 @@ async function main() {
58
58
  os: "MacOS",
59
59
  browser: "Chrome",
60
60
  },
61
- apiKey
61
+ apiKey,
62
62
  );
63
63
  console.log("✅ Session verified successfully!");
64
64
  console.log(
65
65
  "Token (first 20 chars):",
66
- verified.token.substring(0, 20) + "..."
66
+ verified.token.substring(0, 20) + "...",
67
67
  );
68
68
 
69
69
  const sessionToken = verified.token;
@@ -78,8 +78,9 @@ async function main() {
78
78
  mint,
79
79
  chain: Chain.SOLANA,
80
80
  webhookURL,
81
+ fee: 0.12,
81
82
  },
82
- sessionToken
83
+ sessionToken,
83
84
  );
84
85
 
85
86
  console.log("\n✅ Order created successfully!");
@@ -92,11 +93,13 @@ async function main() {
92
93
  console.log(
93
94
  "Fiat Amount (to send):",
94
95
  order.fiatAmount,
95
- process.env.CURRENCY
96
+ process.env.CURRENCY,
96
97
  );
97
98
  console.log("Token Amount (to receive):", order.amount);
98
99
  console.log("Bank:", order.bank);
99
100
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
101
+ console.log("fee:", order.fee);
102
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
100
103
 
101
104
  // Step 5: Fetch the transaction details
102
105
  console.log("\n🔍 Fetching transaction details...");
@@ -146,14 +149,14 @@ async function main() {
146
149
  "1. Transfer",
147
150
  order.fiatAmount,
148
151
  process.env.CURRENCY,
149
- "to the account number above"
152
+ "to the account number above",
150
153
  );
151
154
  console.log("2. Your webhook will receive status updates");
152
155
  console.log("3. Once payment is confirmed, you will receive tokens");
153
156
  } catch (error) {
154
157
  console.error(
155
158
  "\n❌ Error:",
156
- error instanceof Error ? error.message : String(error)
159
+ error instanceof Error ? error.message : String(error),
157
160
  );
158
161
  if (error && typeof error === "object" && "response" in error) {
159
162
  console.error("Response data:", (error as any).response.data);
@@ -0,0 +1,709 @@
1
+ # PAJ Ramp — HTTP API Reference
2
+
3
+ This document describes the raw HTTP endpoints that the `paj_ramp` npm SDK wraps,
4
+ so they can be called from any backend (Go, Python, Ruby, PHP, Java, etc.).
5
+
6
+ All requests use JSON. Timestamps are ISO 8601 strings.
7
+
8
+ ---
9
+
10
+ ## 1. Environments
11
+
12
+ | Environment | Base URL |
13
+ |---|---|
14
+ | Production | `https://api.paj.cash` |
15
+ | Staging | `https://api-staging.paj.cash` |
16
+
17
+ Use the base URL as the prefix for every path below.
18
+
19
+ ---
20
+
21
+ ## 2. Authentication
22
+
23
+ There are two credentials involved:
24
+
25
+ 1. **`apiKey`** — issued by PAJ to your business. Sent as header `x-api-key`.
26
+ Used only for the two session endpoints (`/pub/initiate`, `/pub/verify`).
27
+ 2. **`sessionToken`** — returned by `/pub/verify` after OTP confirmation.
28
+ Sent as header `Authorization: Bearer <sessionToken>` for every other
29
+ authenticated endpoint. It has an `expiresAt` — re-verify when expired.
30
+
31
+ A typical integration flow:
32
+
33
+ ```
34
+ apiKey ──► POST /pub/initiate ──► OTP sent to email/phone
35
+ apiKey ──► POST /pub/verify ──► sessionToken returned
36
+ sessionToken ──► all onramp / offramp / bank / transaction / value endpoints
37
+ ```
38
+
39
+ Standard headers for every request:
40
+
41
+ ```
42
+ Content-Type: application/json
43
+ ```
44
+
45
+ Error responses: any non-2xx response returns a JSON error payload from the
46
+ server. The SDK re-throws `error.response.data` as-is; treat any non-2xx
47
+ status as a failure and read the JSON body for the message.
48
+
49
+ ---
50
+
51
+ ## 3. Session
52
+
53
+ ### 3.1 Initiate session (send OTP)
54
+
55
+ ```
56
+ POST /pub/initiate
57
+ ```
58
+
59
+ Headers:
60
+ ```
61
+ x-api-key: <business_api_key>
62
+ Content-Type: application/json
63
+ ```
64
+
65
+ Body — send **either** `email` **or** `phone` (E.164 format, e.g. `+2349053231563`):
66
+
67
+ ```json
68
+ { "email": "user@example.com" }
69
+ ```
70
+ or
71
+ ```json
72
+ { "phone": "+2349053231563" }
73
+ ```
74
+
75
+ Response `200`:
76
+ ```json
77
+ { "email": "user@example.com" }
78
+ ```
79
+ or
80
+ ```json
81
+ { "phone": "+2349053231563" }
82
+ ```
83
+
84
+ An OTP is delivered to the given recipient.
85
+
86
+ ---
87
+
88
+ ### 3.2 Verify session (exchange OTP for token)
89
+
90
+ ```
91
+ POST /pub/verify
92
+ ```
93
+
94
+ Headers:
95
+ ```
96
+ x-api-key: <business_api_key>
97
+ Content-Type: application/json
98
+ ```
99
+
100
+ Body:
101
+ ```json
102
+ {
103
+ "email": "user@example.com",
104
+ "otp": "123456",
105
+ "device": {
106
+ "uuid": "device-uuid",
107
+ "device": "Desktop",
108
+ "os": "MacOS",
109
+ "browser": "Chrome",
110
+ "ip": "127.0.0.1"
111
+ }
112
+ }
113
+ ```
114
+
115
+ - Use `"phone"` instead of `"email"` if you initiated with a phone number.
116
+ - In `device`, only `uuid` and `device` are required; `os`, `browser`, `ip` are optional.
117
+
118
+ Response `200`:
119
+ ```json
120
+ {
121
+ "recipient": "user@example.com",
122
+ "isActive": "true",
123
+ "expiresAt": "2026-04-20T10:30:00.000Z",
124
+ "token": "eyJhbGciOi..."
125
+ }
126
+ ```
127
+
128
+ Store `token` — it is the `sessionToken` used by every endpoint below.
129
+
130
+ ---
131
+
132
+ ## 4. Rate
133
+
134
+ ### 4.1 Get all rates
135
+
136
+ ```
137
+ GET /pub/rate
138
+ ```
139
+
140
+ No auth required.
141
+
142
+ Response `200`:
143
+ ```json
144
+ {
145
+ "onRampRate": {
146
+ "baseCurrency": "USD",
147
+ "targetCurrency": "NGN",
148
+ "isActive": true,
149
+ "rate": 1510,
150
+ "type": "onRamp"
151
+ },
152
+ "offRampRate": {
153
+ "baseCurrency": "USD",
154
+ "targetCurrency": "NGN",
155
+ "isActive": true,
156
+ "rate": 1525,
157
+ "type": "offRamp"
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### 4.2 Get rate by amount
163
+
164
+ ```
165
+ GET /pub/rate/{amount}
166
+ ```
167
+
168
+ Path param: `amount` — fiat amount (integer or decimal).
169
+
170
+ Response `200`:
171
+ ```json
172
+ {
173
+ "rate": {
174
+ "baseCurrency": "USD",
175
+ "targetCurrency": "NGN",
176
+ "rate": 1510
177
+ },
178
+ "amounts": {
179
+ "userTax": 0,
180
+ "merchantTax": 0,
181
+ "amountUSD": 33.11,
182
+ "userAmountFiat": 50000
183
+ }
184
+ }
185
+ ```
186
+
187
+ ### 4.3 Get rate by type
188
+
189
+ ```
190
+ GET /pub/rate/{type}
191
+ ```
192
+
193
+ Path param: `type` — one of `onRamp`, `offRamp`.
194
+
195
+ Response `200`:
196
+ ```json
197
+ {
198
+ "baseCurrency": "USD",
199
+ "targetCurrency": "NGN",
200
+ "isActive": true,
201
+ "rate": 1525,
202
+ "type": "offRamp"
203
+ }
204
+ ```
205
+
206
+ ---
207
+
208
+ ## 5. Value conversion
209
+
210
+ These take a session token and convert between fiat and token amounts for a
211
+ specific token/currency pair.
212
+
213
+ ### 5.1 Fiat → token (onramp value)
214
+
215
+ ```
216
+ GET /pub/rates/onramp-value?amount={fiatAmount}&mint={mint}&currency={currency}
217
+ ```
218
+
219
+ Headers:
220
+ ```
221
+ Authorization: Bearer <sessionToken>
222
+ ```
223
+
224
+ Query params:
225
+ | name | type | notes |
226
+ |---|---|---|
227
+ | `amount` | number | fiat amount (OR set `fiatAmount`) |
228
+ | `fiatAmount` | number | alternative to `amount` |
229
+ | `mint` | string | token mint address |
230
+ | `currency` | string | `NGN`, `GHS`, `TZS`, `KES`, `ZAR`, `USD` |
231
+
232
+ Response `200`:
233
+ ```json
234
+ {
235
+ "amount": 33.11,
236
+ "fiatAmount": 50000,
237
+ "mint": "EPjFW...",
238
+ "currency": "NGN",
239
+ "rate": 1510,
240
+ "tokenRate": 1
241
+ }
242
+ ```
243
+
244
+ ### 5.2 Token → fiat (offramp value)
245
+
246
+ ```
247
+ GET /pub/rates/offramp-value?amount={tokenAmount}&mint={mint}&currency={currency}
248
+ ```
249
+
250
+ Headers:
251
+ ```
252
+ Authorization: Bearer <sessionToken>
253
+ ```
254
+
255
+ Query params: same shape as 5.1 — set `amount` to the token amount.
256
+
257
+ Response `200`:
258
+ ```json
259
+ {
260
+ "amount": 100,
261
+ "fiatAmount": 152500,
262
+ "mint": "EPjFW...",
263
+ "currency": "NGN",
264
+ "tokenRate": 1
265
+ }
266
+ ```
267
+
268
+ ---
269
+
270
+ ## 6. Banks
271
+
272
+ ### 6.1 List banks
273
+
274
+ ```
275
+ GET /pub/bank
276
+ ```
277
+
278
+ Headers:
279
+ ```
280
+ Authorization: Bearer <sessionToken>
281
+ ```
282
+
283
+ Response `200`:
284
+ ```json
285
+ [
286
+ {
287
+ "id": "bank_id",
288
+ "code": "058",
289
+ "name": "GTBank",
290
+ "logo": "https://cdn.paj.cash/banks/gtb.png",
291
+ "country": "NG"
292
+ }
293
+ ]
294
+ ```
295
+
296
+ ### 6.2 Resolve (confirm) a bank account
297
+
298
+ ```
299
+ GET /pub/bank-account/confirm?bankId={bankId}&accountNumber={accountNumber}
300
+ ```
301
+
302
+ Headers:
303
+ ```
304
+ Authorization: Bearer <sessionToken>
305
+ ```
306
+
307
+ Response `200`:
308
+ ```json
309
+ {
310
+ "accountName": "JOHN DOE",
311
+ "accountNumber": "0123456789",
312
+ "bank": {
313
+ "id": "bank_id",
314
+ "name": "GTBank",
315
+ "code": "058",
316
+ "country": "NG"
317
+ }
318
+ }
319
+ ```
320
+
321
+ ### 6.3 Add (save) a bank account
322
+
323
+ ```
324
+ POST /pub/bank-account
325
+ ```
326
+
327
+ Headers:
328
+ ```
329
+ Authorization: Bearer <sessionToken>
330
+ Content-Type: application/json
331
+ ```
332
+
333
+ Body:
334
+ ```json
335
+ {
336
+ "bankId": "bank_id",
337
+ "accountNumber": "0123456789"
338
+ }
339
+ ```
340
+
341
+ Response `200`:
342
+ ```json
343
+ {
344
+ "id": "saved_account_id",
345
+ "accountName": "JOHN DOE",
346
+ "accountNumber": "0123456789",
347
+ "bank": "bank_id"
348
+ }
349
+ ```
350
+
351
+ ### 6.4 List saved bank accounts
352
+
353
+ ```
354
+ GET /pub/bank-account
355
+ ```
356
+
357
+ Headers:
358
+ ```
359
+ Authorization: Bearer <sessionToken>
360
+ ```
361
+
362
+ Response `200`:
363
+ ```json
364
+ [
365
+ {
366
+ "id": "saved_account_id",
367
+ "accountName": "JOHN DOE",
368
+ "accountNumber": "0123456789",
369
+ "bank": "bank_id"
370
+ }
371
+ ]
372
+ ```
373
+
374
+ ---
375
+
376
+ ## 7. KYC
377
+
378
+ ### 7.1 Submit government-ID KYC
379
+
380
+ ```
381
+ POST /pub/kyc
382
+ ```
383
+
384
+ Headers:
385
+ ```
386
+ Authorization: Bearer <sessionToken>
387
+ Content-Type: application/json
388
+ ```
389
+
390
+ Body:
391
+ ```json
392
+ {
393
+ "idNumber": "12345678901",
394
+ "idType": "BVN",
395
+ "country": "NG"
396
+ }
397
+ ```
398
+
399
+ Rules:
400
+ - The session must already have completed email verification
401
+ (i.e. obtained from `/pub/verify`).
402
+ - `idType` — one of `BVN`, `NIN`.
403
+ - `country` — one of `NG`, `GH`, `TZ`, `KE`, `ZA`.
404
+ - A given (`idNumber`, `idType`, `country`) combination may only be linked
405
+ to one user; reusing one tied to a different user returns
406
+ `400 IdNumber already used`.
407
+
408
+ Response `200`:
409
+ ```json
410
+ {
411
+ "message": "KYC submitted successfully"
412
+ }
413
+ ```
414
+
415
+ ---
416
+
417
+ ## 8. Token info
418
+
419
+ ### 8.1 Get token metadata
420
+
421
+ ```
422
+ GET /token/{mint}?chain={chain}
423
+ ```
424
+
425
+ No auth required.
426
+
427
+ Path param: `mint` — token mint address.
428
+ Query param: `chain` — `SOLANA` or `MONAD`.
429
+
430
+ Response `200`:
431
+ ```json
432
+ {
433
+ "name": "USD Coin",
434
+ "symbol": "USDC",
435
+ "logo": "https://cdn.paj.cash/tokens/usdc.png",
436
+ "mint": "EPjFW...",
437
+ "decimals": 6,
438
+ "chain": "SOLANA"
439
+ }
440
+ ```
441
+
442
+ ---
443
+
444
+ ## 9. Offramp (crypto → fiat)
445
+
446
+ ### 9.1 Create offramp order
447
+
448
+ ```
449
+ POST /pub/offramp
450
+ ```
451
+
452
+ Headers:
453
+ ```
454
+ Authorization: Bearer <sessionToken>
455
+ Content-Type: application/json
456
+ ```
457
+
458
+ Body:
459
+ ```json
460
+ {
461
+ "bank": "bank_id",
462
+ "accountNumber": "0123456789",
463
+ "currency": "NGN",
464
+ "amount": 100,
465
+ "fiatAmount": 152500,
466
+ "mint": "EPjFW...",
467
+ "chain": "SOLANA",
468
+ "description": "Payout",
469
+ "webhookURL": "https://your-domain.com/webhook",
470
+ "businessUSDCFee": 0.5
471
+ }
472
+ ```
473
+
474
+ Rules:
475
+ - Provide **either** `amount` (token) **or** `fiatAmount` (fiat), not necessarily both.
476
+ - `currency` — one of `NGN`, `GHS`, `TZS`, `KES`, `ZAR`, `USD`.
477
+ - `chain` — `SOLANA` or `MONAD`.
478
+ - `description` is optional.
479
+ - `businessUSDCFee` is optional, denominated in USDC. **Important:** the SDK
480
+ exposes this as `fee` in its TypeScript input, but it is sent over the wire
481
+ as `businessUSDCFee`. Use `businessUSDCFee` in your HTTP body.
482
+
483
+ Response `200`:
484
+ ```json
485
+ {
486
+ "id": "order_id",
487
+ "address": "DepositWalletAddress...",
488
+ "mint": "EPjFW...",
489
+ "currency": "NGN",
490
+ "amount": 100,
491
+ "fiatAmount": 152500,
492
+ "rate": 1525,
493
+ "fee": 0.5
494
+ }
495
+ ```
496
+
497
+ After receiving the response, the user transfers `amount` of `mint` on
498
+ `chain` to `address`. Order status updates are pushed to `webhookURL`
499
+ (see section 12).
500
+
501
+ ---
502
+
503
+ ## 10. Onramp (fiat → crypto)
504
+
505
+ ### 10.1 Create onramp order
506
+
507
+ ```
508
+ POST /pub/onramp
509
+ ```
510
+
511
+ Headers:
512
+ ```
513
+ Authorization: Bearer <sessionToken>
514
+ Content-Type: application/json
515
+ ```
516
+
517
+ Body:
518
+ ```json
519
+ {
520
+ "amount": 33.11,
521
+ "fiatAmount": 50000,
522
+ "currency": "NGN",
523
+ "recipient": "RecipientWalletAddress...",
524
+ "mint": "EPjFW...",
525
+ "chain": "SOLANA",
526
+ "webhookURL": "https://your-domain.com/webhook",
527
+ "businessUSDCFee": 0.5
528
+ }
529
+ ```
530
+
531
+ Rules:
532
+ - Provide **either** `amount` (token) **or** `fiatAmount` (fiat).
533
+ - `recipient` is the destination wallet address that will receive the crypto.
534
+ - `chain` — `SOLANA` or `MONAD`.
535
+ - `businessUSDCFee` — same note as offramp: the SDK field is named `fee`
536
+ but the wire field is `businessUSDCFee`.
537
+
538
+ Response `200`:
539
+ ```json
540
+ {
541
+ "id": "order_id",
542
+ "accountNumber": "0123456789",
543
+ "accountName": "PAJ CASH",
544
+ "amount": 33.11,
545
+ "fiatAmount": 50000,
546
+ "bank": "GTBank",
547
+ "rate": 1510,
548
+ "recipient": "RecipientWalletAddress...",
549
+ "currency": "NGN",
550
+ "mint": "EPjFW...",
551
+ "fee": 0.5
552
+ }
553
+ ```
554
+
555
+ The user sends `fiatAmount` in `currency` to `accountNumber` at `bank`.
556
+ Once PAJ detects the payment, it disburses the crypto to `recipient`
557
+ and notifies `webhookURL`.
558
+
559
+ ---
560
+
561
+ ## 11. Transactions
562
+
563
+ ### 11.1 List all transactions
564
+
565
+ ```
566
+ GET /pub/transactions
567
+ ```
568
+
569
+ Headers:
570
+ ```
571
+ Authorization: Bearer <sessionToken>
572
+ ```
573
+
574
+ Response `200`: array of `PajTransaction` (see 11.2).
575
+
576
+ ### 11.2 Get a single transaction
577
+
578
+ ```
579
+ GET /pub/transactions/{id}
580
+ ```
581
+
582
+ Headers:
583
+ ```
584
+ Authorization: Bearer <sessionToken>
585
+ ```
586
+
587
+ Response `200`:
588
+ ```json
589
+ {
590
+ "id": "order_id",
591
+ "address": "...",
592
+ "signature": "SolanaTxSignature...",
593
+ "mint": "EPjFW...",
594
+ "currency": "NGN",
595
+ "amount": 100,
596
+ "usdcAmount": 33.11,
597
+ "fiatAmount": 152500,
598
+ "recipient": "RecipientWalletAddress...",
599
+ "rate": 1525,
600
+ "status": "COMPLETED",
601
+ "transactionType": "OFF_RAMP",
602
+ "createdAt": "2026-04-17T10:30:00.000Z",
603
+ "fee": 0.5
604
+ }
605
+ ```
606
+
607
+ `status` values: `INIT`, `PAID`, `COMPLETED`.
608
+ `transactionType` values: `ON_RAMP`, `OFF_RAMP`.
609
+
610
+ ---
611
+
612
+ ## 12. Webhook payload (onramp & offramp)
613
+
614
+ When an order progresses, PAJ `POST`s a JSON body to the `webhookURL` you
615
+ supplied at order creation. The shape:
616
+
617
+ ```json
618
+ {
619
+ "id": "order_id",
620
+ "address": "...",
621
+ "signature": "OnChainTxSignature...",
622
+ "mint": "EPjFW...",
623
+ "currency": "NGN",
624
+ "amount": 100,
625
+ "usdcAmount": 33.11,
626
+ "fiatAmount": 152500,
627
+ "sender": "...",
628
+ "recipient": "...",
629
+ "rate": 1525,
630
+ "status": "COMPLETED",
631
+ "transactionType": "OFF_RAMP"
632
+ }
633
+ ```
634
+
635
+ Respond with `2xx` to acknowledge. Treat the webhook as the authoritative
636
+ source of truth for final order state.
637
+
638
+ ---
639
+
640
+ ## 13. Enum reference
641
+
642
+ | Enum | Values |
643
+ |---|---|
644
+ | `Currency` | `NGN`, `GHS`, `TZS`, `KES`, `ZAR`, `USD` |
645
+ | `Country` | `NG`, `GH`, `TZ`, `KE`, `ZA` |
646
+ | `IdType` | `BVN`, `NIN` |
647
+ | `Chain` | `SOLANA`, `MONAD` |
648
+ | `TransactionStatus` | `INIT`, `PAID`, `COMPLETED` |
649
+ | `TransactionType` | `ON_RAMP`, `OFF_RAMP` |
650
+ | `RateType` | `onRamp`, `offRamp` |
651
+
652
+ ---
653
+
654
+ ## 14. End-to-end example flows
655
+
656
+ ### Offramp (crypto → NGN)
657
+
658
+ 1. `POST /pub/initiate` with `apiKey` → OTP sent.
659
+ 2. `POST /pub/verify` with `apiKey` + OTP → `sessionToken`.
660
+ 3. `GET /pub/bank` → list banks, pick `bankId`.
661
+ 4. `GET /pub/bank-account/confirm?bankId=...&accountNumber=...` → confirm name.
662
+ 5. `POST /pub/bank-account` → save account, receive saved account `id`.
663
+ 6. `POST /pub/offramp` with saved `bank` id + `accountNumber` → deposit `address`.
664
+ 7. User transfers `amount` of `mint` on `chain` to `address`.
665
+ 8. PAJ `POST`s the webhook to `webhookURL` when `status` becomes `COMPLETED`.
666
+
667
+ ### Onramp (NGN → crypto)
668
+
669
+ 1. `POST /pub/initiate` + `POST /pub/verify` → `sessionToken`.
670
+ 2. `POST /pub/onramp` with `recipient` wallet + amount → bank `accountNumber`
671
+ and `accountName`.
672
+ 3. User transfers `fiatAmount` in `currency` to that account.
673
+ 4. PAJ `POST`s the webhook when the crypto is disbursed.
674
+
675
+ ---
676
+
677
+ ## 15. cURL quickstart
678
+
679
+ ```bash
680
+ # 1. Initiate
681
+ curl -X POST https://api-staging.paj.cash/pub/initiate \
682
+ -H "x-api-key: $API_KEY" \
683
+ -H "Content-Type: application/json" \
684
+ -d '{"email":"user@example.com"}'
685
+
686
+ # 2. Verify
687
+ curl -X POST https://api-staging.paj.cash/pub/verify \
688
+ -H "x-api-key: $API_KEY" \
689
+ -H "Content-Type: application/json" \
690
+ -d '{
691
+ "email":"user@example.com",
692
+ "otp":"123456",
693
+ "device":{"uuid":"dev-1","device":"Desktop"}
694
+ }'
695
+ # → { "token": "...", ... }
696
+
697
+ # 3. Create onramp order
698
+ curl -X POST https://api-staging.paj.cash/pub/onramp \
699
+ -H "Authorization: Bearer $SESSION_TOKEN" \
700
+ -H "Content-Type: application/json" \
701
+ -d '{
702
+ "fiatAmount": 10000,
703
+ "currency": "NGN",
704
+ "recipient": "WALLET_ADDRESS",
705
+ "mint": "EPjFW...",
706
+ "chain": "SOLANA",
707
+ "webhookURL": "https://your-domain.com/webhook"
708
+ }'
709
+ ```
@@ -10,7 +10,9 @@ export interface CreateOfframpOrder {
10
10
  fiatAmount?: number;
11
11
  mint: string;
12
12
  chain: Chain;
13
+ description?: string;
13
14
  webhookURL: string;
15
+ fee?: number;
14
16
  }
15
17
 
16
18
  export interface OfframpOrder {
@@ -29,10 +31,12 @@ export const createOfframpOrder = async (
29
31
  sessionToken: string,
30
32
  ) => {
31
33
  try {
34
+ const { fee, ...rest } = order;
32
35
  return await post<OfframpOrder>(
33
36
  "/pub/offramp",
34
37
  {
35
- ...order,
38
+ ...rest,
39
+ businessUSDCFee: fee,
36
40
  },
37
41
  {
38
42
  Authorization: `Bearer ${sessionToken}`,
@@ -10,6 +10,7 @@ export interface CreateOnrampOrder {
10
10
  mint: string;
11
11
  chain: Chain;
12
12
  webhookURL: string;
13
+ fee?: number;
13
14
  }
14
15
 
15
16
  export interface OnrampOrder {
@@ -31,10 +32,12 @@ export const createOnrampOrder = async (
31
32
  sessionToken: string,
32
33
  ): Promise<OnrampOrder> => {
33
34
  try {
35
+ const { fee, ...rest } = order;
34
36
  return await post<OnrampOrder>(
35
37
  "/pub/onramp",
36
38
  {
37
- ...order,
39
+ ...rest,
40
+ businessUSDCFee: fee,
38
41
  },
39
42
  {
40
43
  Authorization: `Bearer ${sessionToken}`,
@@ -0,0 +1,46 @@
1
+ import { post } from "../../../utils/api.js";
2
+ import { Country, IdType } from "../../../utils/enums.js";
3
+
4
+ export interface SubmitKyc {
5
+ message: string;
6
+ }
7
+
8
+ /**
9
+ * Submits KYC (government ID) data for the authenticated user.
10
+ * Requires the session to have already completed email verification.
11
+ *
12
+ * Args:
13
+ * token: The session token returned by `verify`.
14
+ * idNumber: The user's government ID number (e.g. BVN or NIN value).
15
+ * idType: The type of ID — `BVN` or `NIN`.
16
+ * country: The ID-issuing country (`NG`, `GH`, `TZ`, `KE`, `ZA`).
17
+ *
18
+ * Returns:
19
+ * An object with a `message` confirming successful submission.
20
+ *
21
+ * Raises:
22
+ * Throws an error if the request fails.
23
+ */
24
+ export const submitKyc = async (
25
+ token: string,
26
+ idNumber: string,
27
+ idType: IdType,
28
+ country: Country
29
+ ) => {
30
+ try {
31
+ return await post<SubmitKyc>(
32
+ "/pub/kyc",
33
+ {
34
+ idNumber,
35
+ idType,
36
+ country,
37
+ },
38
+ {
39
+ Authorization: `Bearer ${token}`,
40
+ }
41
+ );
42
+ } catch (err) {
43
+ console.error("Error submitting KYC:", err);
44
+ throw err;
45
+ }
46
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paj_ramp",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "paj offramp/onramp service",
5
5
  "main": "dist/sdk.js",
6
6
  "types": "dist/sdk.d.ts",
package/sdk.ts CHANGED
@@ -70,6 +70,12 @@ export {
70
70
  TokenInfo,
71
71
  } from "./lib/utility/token/getTokenInfo.js";
72
72
 
73
+ // KYC
74
+ export {
75
+ submitKyc,
76
+ SubmitKyc,
77
+ } from "./lib/utility/kyc/submitKyc.js";
78
+
73
79
  // Transaction History
74
80
  export { getAllTransactions } from "./lib/utility/transaction/getAllTransactions.js";
75
81
  export {
@@ -116,6 +122,8 @@ export {
116
122
  TransactionType,
117
123
  TransactionStatus,
118
124
  Environment,
125
+ IdType,
126
+ Country,
119
127
  } from "./utils/enums.js";
120
128
  export {
121
129
  Chain,
package/utils/enums.ts CHANGED
@@ -33,3 +33,16 @@ export enum Environment {
33
33
  Production = "production",
34
34
  Local = "local",
35
35
  }
36
+
37
+ export enum IdType {
38
+ NIN = "NIN",
39
+ BVN = "BVN",
40
+ }
41
+
42
+ export enum Country {
43
+ NG = "NG",
44
+ GH = "GH",
45
+ TZ = "TZ",
46
+ KE = "KE",
47
+ ZA = "ZA",
48
+ }