bybit-p2p-sdk 1.0.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aadam Bala Idriss (ENGR ABI)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # bybit-p2p-sdk
2
+
3
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue)](https://www.typescriptlang.org/)
5
+
6
+ ## Bybit P2P API SDK for Node.js (TypeScript)
7
+
8
+ `bybit-p2p-sdk` is the unofficial Node.js SDK for Bybit's P2P API, enabling seamless integration of your software solutions with Bybit's [P2P trading platform](https://www.bybit.com/en/promo/global/p2p-introduce).
9
+
10
+ - **Full Type Support**: Written in TypeScript with complete definitions.
11
+ - **Easy Authentication**: Handles HMAC signature generation automatically.
12
+ - **Actively Maintained**: Supports latest V5 P2P endpoints.
13
+
14
+ ## Features
15
+
16
+ This SDK implements all key methods available for the P2P API:
17
+
18
+ - Create, edit, delete, activate advertisements
19
+ - Get pending orders, mark orders as paid, release assets
20
+ - Get, send text messages, and upload files to chat
21
+ - Get all public advertisements
22
+ - And more!
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install bybit-p2p-sdk
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### TypeScript
33
+
34
+ ```typescript
35
+ import { P2P } from 'bybit-p2p-sdk';
36
+
37
+ const client = new P2P({
38
+ testnet: false, // Set to true for testnet
39
+ apiKey: "YOUR_API_KEY",
40
+ apiSecret: "YOUR_API_SECRET"
41
+ });
42
+
43
+ // 1. Get current balance
44
+ const balance = await client.getCurrentBalance({ accountType: "FUND", coin: "USDT" });
45
+ console.log(balance);
46
+
47
+ // 2. Get pending orders
48
+ const orders = await client.getPendingOrders({ page: 1, size: 10 });
49
+ console.log(orders);
50
+ ```
51
+
52
+ ### Configuration
53
+ The `P2P` constructor accepts the following config:
54
+
55
+ - `testnet`: `boolean` (default: false)
56
+ - `apiKey`: `string`
57
+ - `apiSecret`: `string`
58
+ - `recvWindow`: `number` (default: 20000) - Time window for request validity
59
+
60
+ ## Documentation
61
+
62
+ This library provides a direct wrapper around Bybit's P2P REST API.
63
+ Official Documentation: [P2P API Guide](https://bybit-exchange.github.io/docs/p2p/guide)
64
+
65
+ ### API Method Mapping
66
+
67
+ Here is how the SDK methods map to the API endpoints:
68
+
69
+ #### Advertisements
70
+ | SDK Method | API Endpoint | Description |
71
+ | --- | --- | --- |
72
+ | `getOnlineAds()` | [/v5/p2p/item/online](https://bybit-exchange.github.io/docs/p2p/ad/online-ad-list) | Get public online ads |
73
+ | `postNewAd()` | [/v5/p2p/item/create](https://bybit-exchange.github.io/docs/p2p/ad/post-new-ad) | Post a new ad |
74
+ | `removeAd()` | [/v5/p2p/item/cancel](https://bybit-exchange.github.io/docs/p2p/ad/remove-ad) | Cancel an ad |
75
+ | `updateAd()` | [/v5/p2p/item/update](https://bybit-exchange.github.io/docs/p2p/ad/update-list-ad) | Update or relist an ad |
76
+ | `getAdsList()` | [/v5/p2p/item/personal/list](https://bybit-exchange.github.io/docs/p2p/ad/ad-list) | Get my ads list |
77
+ | `getAdDetails()` | [/v5/p2p/item/info](https://bybit-exchange.github.io/docs/p2p/ad/ad-detail) | Get ad details |
78
+
79
+ #### Orders
80
+ | SDK Method | API Endpoint | Description |
81
+ | --- | --- | --- |
82
+ | `getOrders()` | [/v5/p2p/order/simplifyList](https://bybit-exchange.github.io/docs/p2p/order/order-list) | Get all orders |
83
+ | `getOrderDetails()` | [/v5/p2p/order/info](https://bybit-exchange.github.io/docs/p2p/order/order-detail) | Get order details |
84
+ | `getPendingOrders()` | [/v5/p2p/order/pending/simplifyList](https://bybit-exchange.github.io/docs/p2p/order/pending-order) | Get pending orders |
85
+ | `markAsPaid()` | [/v5/p2p/order/pay](https://bybit-exchange.github.io/docs/p2p/order/mark-order-as-paid) | Mark order as paid |
86
+ | `releaseAssets()` | [/v5/p2p/order/finish](https://bybit-exchange.github.io/docs/p2p/order/release-digital-asset) | Release crypto to buyer |
87
+ | `sendChatMessage()` | [/v5/p2p/order/message/send](https://bybit-exchange.github.io/docs/p2p/order/send-chat-msg) | Send a chat message |
88
+ | `uploadChatFile()` | [/v5/p2p/oss/upload_file](https://bybit-exchange.github.io/docs/p2p/order/upload-chat-file) | Upload a file to chat |
89
+ | `getChatMessages()` | [/v5/p2p/order/message/listpage](https://bybit-exchange.github.io/docs/p2p/order/chat-msg) | Get chat history |
90
+
91
+ #### User Info
92
+ | SDK Method | API Endpoint | Description |
93
+ | --- | --- | --- |
94
+ | `getAccountInformation()` | [/v5/p2p/user/personal/info](https://bybit-exchange.github.io/docs/p2p/user/acct-info) | Get my account info |
95
+ | `getCounterpartyInfo()` | [/v5/p2p/user/order/personal/info](https://bybit-exchange.github.io/docs/p2p/user/counterparty-user-info) | Get counterparty info |
96
+ | `getUserPaymentTypes()` | [/v5/p2p/user/payment/list](https://bybit-exchange.github.io/docs/p2p/user/user-payment) | Get my payment methods |
97
+
98
+ #### Misc
99
+ | SDK Method | API Endpoint | Description |
100
+ | --- | --- | --- |
101
+ | `getCurrentBalance()` | [/v5/asset/transfer/query-account-coins-balance](https://bybit-exchange.github.io/docs/p2p/all-balance) | Get wallet balance |
102
+
103
+ ## Development
104
+
105
+ Contributions are welcome!
106
+
107
+ ### Build
108
+ ```bash
109
+ npm run build
110
+ ```
111
+
112
+ ### Run Examples
113
+ ```bash
114
+ npx ts-node examples/quickstart.ts
115
+ ```
116
+
117
+ ## 🤝 Contributing
118
+
119
+ Contributions are welcome! Please see our contributing guidelines:
120
+
121
+ 1. Fork the repository
122
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
123
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
124
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
125
+ 5. Open a Pull Request
126
+
127
+ ### Code Style
128
+
129
+ - Follow TypeScript best practices
130
+ - Use ESLint and Prettier (configured in project)
131
+ - Write tests for new features
132
+ - Update documentation as needed
133
+
134
+ ## 📄 License
135
+
136
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
137
+
138
+ ## 🔗 Links
139
+
140
+ - **Issues**: [GitHub Issues](https://github.com/ENGR-ABI/bybit-p2p-sdk/issues)
141
+ - **Discussions**: [GitHub Discussions](https://github.com/ENGR-ABI/bybit-p2p-sdk/discussions)
142
+
143
+ ## 📞 Support
144
+
145
+ For questions and support:
146
+ - Check the [Documentation](#documentation) section
147
+ - Open an [Issue](https://github.com/ENGR-ABI/bybit-p2p-sdk/issues)
148
+
149
+ ---
150
+
151
+ **Built with ❤️ for traders by [ENGR-ABI](https://github.com/ENGR-ABI)**
@@ -0,0 +1,19 @@
1
+ import { P2PConfig } from './types';
2
+ import { P2PMethodDef } from './constants';
3
+ export declare class P2PClient {
4
+ private testnet;
5
+ private apiKey;
6
+ private apiSecret;
7
+ private domain;
8
+ private tld;
9
+ private recvWindow;
10
+ private rsa;
11
+ private disableSslChecks;
12
+ private client;
13
+ private url;
14
+ constructor(config: P2PConfig);
15
+ private sign;
16
+ private sortParams;
17
+ request<T = any>(methodDef: P2PMethodDef, params?: any): Promise<T>;
18
+ private handleFileUpload;
19
+ }
package/dist/client.js ADDED
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.P2PClient = void 0;
40
+ const axios_1 = __importDefault(require("axios"));
41
+ const crypto = __importStar(require("crypto"));
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const SUBDOMAIN_TESTNET = "api-testnet";
45
+ const SUBDOMAIN_MAINNET = "api";
46
+ const DOMAIN_MAIN = "bybit";
47
+ const TLD_MAIN = "com";
48
+ class P2PClient {
49
+ constructor(config) {
50
+ this.testnet = config.testnet || false;
51
+ this.apiKey = config.apiKey || "";
52
+ this.apiSecret = config.apiSecret || "";
53
+ this.domain = config.domain || DOMAIN_MAIN;
54
+ this.tld = config.tld || TLD_MAIN;
55
+ this.recvWindow = config.recvWindow || 20000;
56
+ this.rsa = config.rsa || false;
57
+ this.disableSslChecks = config.disableSslChecks || false;
58
+ const subdomain = this.testnet ? SUBDOMAIN_TESTNET : SUBDOMAIN_MAINNET;
59
+ this.url = `https://${subdomain}.${this.domain}.${this.tld}`;
60
+ this.client = axios_1.default.create({
61
+ validateStatus: () => true, // Handle all statuses manually
62
+ httpsAgent: this.disableSslChecks ? new (require('https').Agent)({ rejectUnauthorized: false }) : undefined
63
+ });
64
+ }
65
+ sign(payload, timestamp) {
66
+ const signString = typeof payload === 'string'
67
+ ? `${timestamp}${this.apiKey}${this.recvWindow}${payload}`
68
+ : Buffer.concat([Buffer.from(`${timestamp}${this.apiKey}${this.recvWindow}`), payload]);
69
+ if (this.rsa) {
70
+ const signer = crypto.createSign('SHA256');
71
+ signer.update(signString);
72
+ return signer.sign(this.apiSecret, 'base64');
73
+ }
74
+ else {
75
+ return crypto.createHmac('sha256', this.apiSecret).update(signString).digest('hex');
76
+ }
77
+ }
78
+ sortParams(params) {
79
+ const keys = Object.keys(params).sort();
80
+ return keys.map(key => `${key}=${params[key]}`).join('&');
81
+ }
82
+ // Helper to cast values to strings/ints recursively primarily for POST JSON
83
+ // Mirroring Python's _cast_values logic effectively happens by JSON.stringify
84
+ // But Python explicitly converts some to strings.
85
+ // In JS, JSON.stringify handles types well, but if the API expects strings for numbers, we might need to be careful.
86
+ // For now, we rely on the user passing correct types or simple JSON serialization.
87
+ async request(methodDef, params = {}) {
88
+ // Validate required params
89
+ for (const req of methodDef.requiredParams) {
90
+ if (!(req in params)) {
91
+ throw new Error(`Missing required parameter: ${req}`);
92
+ }
93
+ }
94
+ const timestamp = Date.now();
95
+ let payload = "";
96
+ let signature = "";
97
+ let contentType = "application/json";
98
+ let headers = {};
99
+ let requestUrl = this.url + methodDef.url;
100
+ let axiosConfig = {
101
+ method: methodDef.method === 'FILE' ? 'POST' : methodDef.method,
102
+ url: requestUrl,
103
+ headers: {}
104
+ };
105
+ if (methodDef.method === 'FILE') {
106
+ return this.handleFileUpload(methodDef, params, timestamp);
107
+ }
108
+ else if (methodDef.method === 'GET') {
109
+ const cleanParams = Object.entries(params)
110
+ .filter(([_, v]) => v != null)
111
+ .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
112
+ payload = this.sortParams(cleanParams);
113
+ signature = this.sign(payload, timestamp);
114
+ if (payload) {
115
+ axiosConfig.url += `?${payload}`;
116
+ }
117
+ }
118
+ else {
119
+ // POST
120
+ payload = JSON.stringify(params);
121
+ signature = this.sign(payload, timestamp);
122
+ axiosConfig.data = payload;
123
+ }
124
+ headers = {
125
+ 'X-BAPI-API-KEY': this.apiKey,
126
+ 'X-BAPI-SIGN': signature,
127
+ 'X-BAPI-SIGN-TYPE': '2',
128
+ 'X-BAPI-TIMESTAMP': timestamp.toString(),
129
+ 'X-BAPI-RECV-WINDOW': this.recvWindow.toString(),
130
+ 'Content-Type': contentType
131
+ };
132
+ axiosConfig.headers = { ...axiosConfig.headers, ...headers };
133
+ const response = await this.client.request(axiosConfig);
134
+ // Process response
135
+ if (response.status !== 200) {
136
+ // Basic error handling
137
+ throw new Error(`Request failed with status ${response.status}: ${JSON.stringify(response.data)}`);
138
+ }
139
+ const result = response.data;
140
+ const retCode = result.retCode !== undefined ? result.retCode : result.ret_code;
141
+ const retMsg = result.retMsg || result.ret_msg;
142
+ if (retCode !== 0) {
143
+ throw new Error(`${retMsg} (ErrCode: ${retCode})`);
144
+ }
145
+ return result;
146
+ }
147
+ async handleFileUpload(methodDef, params, timestamp) {
148
+ const uploadFile = params.upload_file;
149
+ let filename = params.filename;
150
+ let fileData;
151
+ if (Buffer.isBuffer(uploadFile)) {
152
+ if (!filename)
153
+ throw new Error("filename is required when passing raw buffer");
154
+ fileData = uploadFile;
155
+ }
156
+ else if (typeof uploadFile === 'string') {
157
+ if (!fs.existsSync(uploadFile))
158
+ throw new Error(`File not found: ${uploadFile}`);
159
+ fileData = fs.readFileSync(uploadFile);
160
+ if (!filename)
161
+ filename = path.basename(uploadFile);
162
+ }
163
+ else {
164
+ throw new Error("Invalid upload_file type. Must be string path or Buffer.");
165
+ }
166
+ const boundary = "boundary-for-file";
167
+ const mimeType = "image/png"; // Python SDK hardcodes this
168
+ // Manual payload construction to match signature
169
+ // Python: f"--{boundary}\r\nContent-Disposition: form-data; name=\"upload_file\"; filename=\"{filename}\"\r\nContent-Type: {mime_type}\r\n\r\n".encode() + data + f"\r\n--{boundary}--\r\n".encode()
170
+ const part1 = Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="upload_file"; filename="${filename}"\r\nContent-Type: ${mimeType}\r\n\r\n`);
171
+ const part2 = fileData;
172
+ const part3 = Buffer.from(`\r\n--${boundary}--\r\n`);
173
+ const payloadBuffer = Buffer.concat([part1, part2, part3]);
174
+ const signature = this.sign(payloadBuffer, timestamp);
175
+ const headers = {
176
+ 'X-BAPI-API-KEY': this.apiKey,
177
+ 'X-BAPI-SIGN': signature,
178
+ 'X-BAPI-SIGN-TYPE': '2',
179
+ 'X-BAPI-TIMESTAMP': timestamp.toString(),
180
+ 'X-BAPI-RECV-WINDOW': this.recvWindow.toString(),
181
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`
182
+ };
183
+ const response = await this.client.post(this.url + methodDef.url, payloadBuffer, { headers });
184
+ if (response.status !== 200) {
185
+ throw new Error(`Request failed with status ${response.status}: ${JSON.stringify(response.data)}`);
186
+ }
187
+ const result = response.data;
188
+ if (result.retCode !== 0) {
189
+ throw new Error(`${result.retMsg} (ErrCode: ${result.retCode})`);
190
+ }
191
+ return result;
192
+ }
193
+ }
194
+ exports.P2PClient = P2PClient;
@@ -0,0 +1,6 @@
1
+ export interface P2PMethodDef {
2
+ url: string;
3
+ method: 'GET' | 'POST' | 'FILE';
4
+ requiredParams: string[];
5
+ }
6
+ export declare const P2PMethods: Record<string, P2PMethodDef>;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.P2PMethods = void 0;
4
+ exports.P2PMethods = {
5
+ GET_CURRENT_BALANCE: { url: "/v5/asset/transfer/query-account-coins-balance", method: "GET", requiredParams: ["accountType"] },
6
+ GET_ACCOUNT_INFORMATION: { url: "/v5/p2p/user/personal/info", method: "POST", requiredParams: [] },
7
+ GET_ADS_LIST: { url: "/v5/p2p/item/personal/list", method: "POST", requiredParams: [] },
8
+ GET_AD_DETAILS: { url: "/v5/p2p/item/info", method: "POST", requiredParams: ["itemId"] },
9
+ UPDATE_AD: {
10
+ url: "/v5/p2p/item/update", method: "POST", requiredParams: [
11
+ "id", "priceType", "premium", "price", "minAmount", "maxAmount", "remark",
12
+ "tradingPreferenceSet", "paymentIds", "actionType", "quantity", "paymentPeriod"
13
+ ]
14
+ },
15
+ REMOVE_AD: { url: "/v5/p2p/item/cancel", method: "POST", requiredParams: ["itemId"] },
16
+ GET_ORDERS: { url: "/v5/p2p/order/simplifyList", method: "POST", requiredParams: ["page", "size"] },
17
+ GET_PENDING_ORDERS: { url: "/v5/p2p/order/pending/simplifyList", method: "POST", requiredParams: ["page", "size"] },
18
+ GET_COUNTERPARTY_INFO: { url: "/v5/p2p/user/order/personal/info", method: "POST", requiredParams: ["originalUid", "orderId"] },
19
+ GET_ORDER_DETAILS: { url: "/v5/p2p/order/info", method: "POST", requiredParams: ["orderId"] },
20
+ RELEASE_ASSETS: { url: "/v5/p2p/order/finish", method: "POST", requiredParams: ["orderId"] },
21
+ MARK_AS_PAID: { url: "/v5/p2p/order/pay", method: "POST", requiredParams: ["orderId", "paymentType", "paymentId"] },
22
+ GET_CHAT_MESSAGES: { url: "/v5/p2p/order/message/listpage", method: "POST", requiredParams: ["orderId", "size"] },
23
+ UPLOAD_CHAT_FILE: { url: "/v5/p2p/oss/upload_file", method: "FILE", requiredParams: ["upload_file"] },
24
+ SEND_CHAT_MESSAGE: { url: "/v5/p2p/order/message/send", method: "POST", requiredParams: ["message", "contentType", "orderId"] },
25
+ POST_NEW_AD: {
26
+ url: "/v5/p2p/item/create", method: "POST", requiredParams: [
27
+ "tokenId", "currencyId", "side", "priceType", "premium", "price", "minAmount",
28
+ "maxAmount", "remark", "tradingPreferenceSet", "paymentIds", "quantity",
29
+ "paymentPeriod", "itemType"
30
+ ]
31
+ },
32
+ GET_ONLINE_ADS: { url: "/v5/p2p/item/online", method: "POST", requiredParams: ["tokenId", "currencyId", "side"] },
33
+ GET_USER_PAYMENT_TYPES: { url: "/v5/p2p/user/payment/list", method: "POST", requiredParams: [] }
34
+ };
@@ -0,0 +1,119 @@
1
+ import { P2PClient } from './client';
2
+ import * as types from './types';
3
+ export * from './types';
4
+ export * from './client';
5
+ export * from './constants';
6
+ export declare class P2P extends P2PClient {
7
+ /**
8
+ * Obtain wallet balance, query asset information of each currency.
9
+ * By default, currency information with assets or liabilities of 0 is not returned.
10
+ */
11
+ getCurrentBalance(params: {
12
+ accountType: string;
13
+ [key: string]: any;
14
+ }): Promise<types.P2PResponse>;
15
+ /**
16
+ * Get Account Information
17
+ */
18
+ getAccountInformation(): Promise<types.P2PResponse>;
19
+ /**
20
+ * Get Ads List
21
+ */
22
+ getAdsList(params?: types.GetAdsListParams): Promise<types.P2PResponse>;
23
+ /**
24
+ * Get Ad Details
25
+ */
26
+ getAdDetails(params: {
27
+ itemId: string;
28
+ [key: string]: any;
29
+ }): Promise<types.P2PResponse>;
30
+ /**
31
+ * Update or activate ads
32
+ */
33
+ updateAd(params: types.UpdateAdParams): Promise<types.P2PResponse>;
34
+ /**
35
+ * Remove ad
36
+ */
37
+ removeAd(params: {
38
+ itemId: string;
39
+ [key: string]: any;
40
+ }): Promise<types.P2PResponse>;
41
+ /**
42
+ * Get orders
43
+ */
44
+ getOrders(params?: types.GetOrdersParams): Promise<types.P2PResponse>;
45
+ /**
46
+ * Get pending orders
47
+ */
48
+ getPendingOrders(params?: types.GetOrdersParams): Promise<types.P2PResponse>;
49
+ /**
50
+ * Get counterparty info
51
+ */
52
+ getCounterpartyInfo(params: {
53
+ originalUid: string;
54
+ orderId: string;
55
+ [key: string]: any;
56
+ }): Promise<types.P2PResponse>;
57
+ /**
58
+ * Get order details
59
+ */
60
+ getOrderDetails(params: {
61
+ orderId: string;
62
+ [key: string]: any;
63
+ }): Promise<types.P2PResponse>;
64
+ /**
65
+ * Release digital asset
66
+ */
67
+ releaseAssets(params: {
68
+ orderId: string;
69
+ [key: string]: any;
70
+ }): Promise<types.P2PResponse>;
71
+ /**
72
+ * Mark order as paid
73
+ */
74
+ markAsPaid(params: {
75
+ orderId: string;
76
+ paymentType: string;
77
+ paymentId: string;
78
+ [key: string]: any;
79
+ }): Promise<types.P2PResponse>;
80
+ /**
81
+ * Get chat messages
82
+ */
83
+ getChatMessages(params: {
84
+ orderId: string;
85
+ size?: number;
86
+ startMessageId?: number;
87
+ [key: string]: any;
88
+ }): Promise<types.P2PResponse>;
89
+ /**
90
+ * Upload file for chats
91
+ */
92
+ uploadChatFile(params: types.UploadChatFileParams): Promise<types.P2PResponse>;
93
+ /**
94
+ * Send chat message
95
+ */
96
+ sendChatMessage(params: {
97
+ message: string;
98
+ contentType: string;
99
+ orderId: string;
100
+ [key: string]: any;
101
+ }): Promise<types.P2PResponse>;
102
+ /**
103
+ * Post new advertisement
104
+ */
105
+ postNewAd(params: types.PostNewAdParams): Promise<types.P2PResponse>;
106
+ /**
107
+ * Get online advertisements list
108
+ */
109
+ getOnlineAds(params: {
110
+ tokenId: string;
111
+ currencyId: string;
112
+ side: string | number;
113
+ [key: string]: any;
114
+ }): Promise<types.P2PResponse>;
115
+ /**
116
+ * Get user payment types
117
+ */
118
+ getUserPaymentTypes(): Promise<types.P2PResponse>;
119
+ }
package/dist/index.js ADDED
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.P2P = void 0;
18
+ const client_1 = require("./client");
19
+ const constants_1 = require("./constants");
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./client"), exports);
22
+ __exportStar(require("./constants"), exports);
23
+ class P2P extends client_1.P2PClient {
24
+ /**
25
+ * Obtain wallet balance, query asset information of each currency.
26
+ * By default, currency information with assets or liabilities of 0 is not returned.
27
+ */
28
+ async getCurrentBalance(params) {
29
+ return this.request(constants_1.P2PMethods.GET_CURRENT_BALANCE, params);
30
+ }
31
+ /**
32
+ * Get Account Information
33
+ */
34
+ async getAccountInformation() {
35
+ return this.request(constants_1.P2PMethods.GET_ACCOUNT_INFORMATION, {});
36
+ }
37
+ /**
38
+ * Get Ads List
39
+ */
40
+ async getAdsList(params = {}) {
41
+ return this.request(constants_1.P2PMethods.GET_ADS_LIST, params);
42
+ }
43
+ /**
44
+ * Get Ad Details
45
+ */
46
+ async getAdDetails(params) {
47
+ return this.request(constants_1.P2PMethods.GET_AD_DETAILS, params);
48
+ }
49
+ /**
50
+ * Update or activate ads
51
+ */
52
+ async updateAd(params) {
53
+ return this.request(constants_1.P2PMethods.UPDATE_AD, params);
54
+ }
55
+ /**
56
+ * Remove ad
57
+ */
58
+ async removeAd(params) {
59
+ return this.request(constants_1.P2PMethods.REMOVE_AD, params);
60
+ }
61
+ /**
62
+ * Get orders
63
+ */
64
+ async getOrders(params = {}) {
65
+ return this.request(constants_1.P2PMethods.GET_ORDERS, params);
66
+ }
67
+ /**
68
+ * Get pending orders
69
+ */
70
+ async getPendingOrders(params = {}) {
71
+ return this.request(constants_1.P2PMethods.GET_PENDING_ORDERS, params);
72
+ }
73
+ /**
74
+ * Get counterparty info
75
+ */
76
+ async getCounterpartyInfo(params) {
77
+ return this.request(constants_1.P2PMethods.GET_COUNTERPARTY_INFO, params);
78
+ }
79
+ /**
80
+ * Get order details
81
+ */
82
+ async getOrderDetails(params) {
83
+ return this.request(constants_1.P2PMethods.GET_ORDER_DETAILS, params);
84
+ }
85
+ /**
86
+ * Release digital asset
87
+ */
88
+ async releaseAssets(params) {
89
+ return this.request(constants_1.P2PMethods.RELEASE_ASSETS, params);
90
+ }
91
+ /**
92
+ * Mark order as paid
93
+ */
94
+ async markAsPaid(params) {
95
+ return this.request(constants_1.P2PMethods.MARK_AS_PAID, params);
96
+ }
97
+ /**
98
+ * Get chat messages
99
+ */
100
+ async getChatMessages(params) {
101
+ return this.request(constants_1.P2PMethods.GET_CHAT_MESSAGES, params);
102
+ }
103
+ /**
104
+ * Upload file for chats
105
+ */
106
+ async uploadChatFile(params) {
107
+ return this.request(constants_1.P2PMethods.UPLOAD_CHAT_FILE, params);
108
+ }
109
+ /**
110
+ * Send chat message
111
+ */
112
+ async sendChatMessage(params) {
113
+ return this.request(constants_1.P2PMethods.SEND_CHAT_MESSAGE, params);
114
+ }
115
+ /**
116
+ * Post new advertisement
117
+ */
118
+ async postNewAd(params) {
119
+ return this.request(constants_1.P2PMethods.POST_NEW_AD, params);
120
+ }
121
+ /**
122
+ * Get online advertisements list
123
+ */
124
+ async getOnlineAds(params) {
125
+ return this.request(constants_1.P2PMethods.GET_ONLINE_ADS, params);
126
+ }
127
+ /**
128
+ * Get user payment types
129
+ */
130
+ async getUserPaymentTypes() {
131
+ return this.request(constants_1.P2PMethods.GET_USER_PAYMENT_TYPES, {});
132
+ }
133
+ }
134
+ exports.P2P = P2P;
@@ -0,0 +1,122 @@
1
+ export interface P2PConfig {
2
+ /**
3
+ * API Key
4
+ */
5
+ apiKey?: string;
6
+ /**
7
+ * API Secret
8
+ */
9
+ apiSecret?: string;
10
+ /**
11
+ * Whether to use Testnet (default: false)
12
+ */
13
+ testnet?: boolean;
14
+ /**
15
+ * Custom domain (default: bybit)
16
+ */
17
+ domain?: string;
18
+ /**
19
+ * Custom TLD (default: com)
20
+ */
21
+ tld?: string;
22
+ /**
23
+ * Receive window in ms (default: 5000)
24
+ */
25
+ recvWindow?: number;
26
+ /**
27
+ * Use RSA authentication (default: false)
28
+ */
29
+ rsa?: boolean;
30
+ /**
31
+ * Disable SSL verification (default: false) - Not recommended
32
+ */
33
+ disableSslChecks?: boolean;
34
+ /**
35
+ * Logging level (not fully implemented in this node version, serves as placeholder)
36
+ */
37
+ loggingLevel?: any;
38
+ }
39
+ export interface P2PResponse<T = any> {
40
+ retCode?: number;
41
+ ret_code?: number;
42
+ retMsg?: string;
43
+ ret_msg?: string;
44
+ result: T;
45
+ retExtInfo?: any;
46
+ ext_code?: string;
47
+ ext_info?: any;
48
+ time?: number;
49
+ time_now?: string;
50
+ }
51
+ export interface GetAdsListParams {
52
+ itemId?: string;
53
+ status?: string | number;
54
+ side?: string | number;
55
+ tokenId?: string;
56
+ page?: number;
57
+ size?: number;
58
+ currencyId?: string;
59
+ [key: string]: any;
60
+ }
61
+ export interface UpdateAdParams {
62
+ id: string;
63
+ priceType?: number;
64
+ premium?: number | string;
65
+ price?: number | string;
66
+ minAmount?: number | string;
67
+ maxAmount?: number | string;
68
+ remark?: string;
69
+ tradingPreferenceSet?: TradingPreferenceSet;
70
+ paymentIds?: string[];
71
+ actionType?: 'MODIFY' | 'ACTIVE';
72
+ quantity?: string;
73
+ paymentPeriod?: string | number;
74
+ [key: string]: any;
75
+ }
76
+ export interface TradingPreferenceSet {
77
+ hasUnPostAd?: number;
78
+ isKyc?: number;
79
+ isEmail?: number;
80
+ isMobile?: number;
81
+ hasRegisterTime?: number;
82
+ registerTimeThreshold?: number;
83
+ orderFinishNumberDay30?: number;
84
+ completeRateDay30?: string;
85
+ nationalLimit?: string;
86
+ hasOrderFinishNumberDay30?: number;
87
+ hasCompleteRateDay30?: number;
88
+ hasNationalLimit?: number;
89
+ [key: string]: any;
90
+ }
91
+ export interface PostNewAdParams {
92
+ tokenId: string;
93
+ currencyId: string;
94
+ side: string | number;
95
+ priceType: number;
96
+ premium?: number | string;
97
+ price?: number | string;
98
+ minAmount?: number | string;
99
+ maxAmount?: number | string;
100
+ remark?: string;
101
+ tradingPreferenceSet?: TradingPreferenceSet;
102
+ paymentIds?: string[];
103
+ quantity?: string;
104
+ paymentPeriod?: string | number;
105
+ itemType?: 'ORIGIN' | 'BULK';
106
+ [key: string]: any;
107
+ }
108
+ export interface GetOrdersParams {
109
+ status?: string;
110
+ beginTime?: string | number;
111
+ endTime?: string | number;
112
+ tokenId?: string;
113
+ side?: number[];
114
+ page?: number;
115
+ size?: number;
116
+ [key: string]: any;
117
+ }
118
+ export interface UploadChatFileParams {
119
+ upload_file: string | Buffer;
120
+ filename?: string;
121
+ [key: string]: any;
122
+ }
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // ... Add more generalized interfaces as needed or use `any` for less common methods
4
+ // to keep the initial conversion manageable but extensible.
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "bybit-p2p-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Bybit P2P Node.js SDK (TypeScript)",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepublishOnly": "npm run build",
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "bybit",
14
+ "p2p",
15
+ "sdk",
16
+ "crypto",
17
+ "exchange",
18
+ "typescript",
19
+ "nodejs",
20
+ "api"
21
+ ],
22
+ "homepage": "https://github.com/ENGR-ABI/bybit-p2p-sdk",
23
+ "bugs": {
24
+ "url": "https://github.com/ENGR-ABI/bybit-p2p-sdk/issues"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/ENGR-ABI/bybit-p2p-sdk.git"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "author": "Aadam Bala Idriss (ENGR ABI)",
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "axios": "^1.6.0",
40
+ "form-data": "^4.0.0",
41
+ "uuid": "^9.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.0.0",
45
+ "@types/uuid": "^9.0.0",
46
+ "ts-node": "^10.9.1",
47
+ "typescript": "^5.0.0"
48
+ }
49
+ }
package/src/client.ts ADDED
@@ -0,0 +1,190 @@
1
+ import axios, { AxiosInstance, AxiosRequestConfig, Method } from 'axios';
2
+ import * as crypto from 'crypto';
3
+ import * as FormData from 'form-data';
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import { P2PConfig, P2PResponse } from './types';
7
+ import { P2PMethodDef } from './constants';
8
+
9
+ const SUBDOMAIN_TESTNET = "api-testnet";
10
+ const SUBDOMAIN_MAINNET = "api";
11
+ const DOMAIN_MAIN = "bybit";
12
+ const TLD_MAIN = "com";
13
+
14
+ export class P2PClient {
15
+ private testnet: boolean;
16
+ private apiKey: string;
17
+ private apiSecret: string;
18
+ private domain: string;
19
+ private tld: string;
20
+ private recvWindow: number;
21
+ private rsa: boolean;
22
+ private disableSslChecks: boolean;
23
+ private client: AxiosInstance;
24
+ private url: string;
25
+
26
+ constructor(config: P2PConfig) {
27
+ this.testnet = config.testnet || false;
28
+ this.apiKey = config.apiKey || "";
29
+ this.apiSecret = config.apiSecret || "";
30
+ this.domain = config.domain || DOMAIN_MAIN;
31
+ this.tld = config.tld || TLD_MAIN;
32
+ this.recvWindow = config.recvWindow || 20000;
33
+ this.rsa = config.rsa || false;
34
+ this.disableSslChecks = config.disableSslChecks || false;
35
+
36
+ const subdomain = this.testnet ? SUBDOMAIN_TESTNET : SUBDOMAIN_MAINNET;
37
+ this.url = `https://${subdomain}.${this.domain}.${this.tld}`;
38
+
39
+ this.client = axios.create({
40
+ validateStatus: () => true, // Handle all statuses manually
41
+ httpsAgent: this.disableSslChecks ? new (require('https').Agent)({ rejectUnauthorized: false }) : undefined
42
+ });
43
+ }
44
+
45
+ private sign(payload: string | Buffer, timestamp: number): string {
46
+ const signString = typeof payload === 'string'
47
+ ? `${timestamp}${this.apiKey}${this.recvWindow}${payload}`
48
+ : Buffer.concat([Buffer.from(`${timestamp}${this.apiKey}${this.recvWindow}`), payload]);
49
+
50
+ if (this.rsa) {
51
+ const signer = crypto.createSign('SHA256');
52
+ signer.update(signString);
53
+ return signer.sign(this.apiSecret, 'base64');
54
+ } else {
55
+ return crypto.createHmac('sha256', this.apiSecret).update(signString).digest('hex');
56
+ }
57
+ }
58
+
59
+ private sortParams(params: any): string {
60
+ const keys = Object.keys(params).sort();
61
+ return keys.map(key => `${key}=${params[key]}`).join('&');
62
+ }
63
+
64
+ // Helper to cast values to strings/ints recursively primarily for POST JSON
65
+ // Mirroring Python's _cast_values logic effectively happens by JSON.stringify
66
+ // But Python explicitly converts some to strings.
67
+ // In JS, JSON.stringify handles types well, but if the API expects strings for numbers, we might need to be careful.
68
+ // For now, we rely on the user passing correct types or simple JSON serialization.
69
+
70
+ async request<T = any>(methodDef: P2PMethodDef, params: any = {}): Promise<T> {
71
+ // Validate required params
72
+ for (const req of methodDef.requiredParams) {
73
+ if (!(req in params)) {
74
+ throw new Error(`Missing required parameter: ${req}`);
75
+ }
76
+ }
77
+
78
+ const timestamp = Date.now();
79
+ let payload = "";
80
+ let signature = "";
81
+ let contentType = "application/json";
82
+ let headers: any = {};
83
+ let requestUrl = this.url + methodDef.url;
84
+ let axiosConfig: AxiosRequestConfig = {
85
+ method: methodDef.method === 'FILE' ? 'POST' : methodDef.method as Method,
86
+ url: requestUrl,
87
+ headers: {}
88
+ };
89
+
90
+ if (methodDef.method === 'FILE') {
91
+ return this.handleFileUpload(methodDef, params, timestamp);
92
+ } else if (methodDef.method === 'GET') {
93
+ const cleanParams = Object.entries(params)
94
+ .filter(([_, v]) => v != null)
95
+ .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
96
+ payload = this.sortParams(cleanParams);
97
+ signature = this.sign(payload, timestamp);
98
+ if (payload) {
99
+ axiosConfig.url += `?${payload}`;
100
+ }
101
+ } else {
102
+ // POST
103
+ payload = JSON.stringify(params);
104
+ signature = this.sign(payload, timestamp);
105
+ axiosConfig.data = payload;
106
+ }
107
+
108
+ headers = {
109
+ 'X-BAPI-API-KEY': this.apiKey,
110
+ 'X-BAPI-SIGN': signature,
111
+ 'X-BAPI-SIGN-TYPE': '2',
112
+ 'X-BAPI-TIMESTAMP': timestamp.toString(),
113
+ 'X-BAPI-RECV-WINDOW': this.recvWindow.toString(),
114
+ 'Content-Type': contentType
115
+ };
116
+
117
+ axiosConfig.headers = { ...axiosConfig.headers, ...headers };
118
+
119
+ const response = await this.client.request(axiosConfig);
120
+
121
+ // Process response
122
+ if (response.status !== 200) {
123
+ // Basic error handling
124
+ throw new Error(`Request failed with status ${response.status}: ${JSON.stringify(response.data)}`);
125
+ }
126
+
127
+ const result = response.data;
128
+
129
+ const retCode = result.retCode !== undefined ? result.retCode : result.ret_code;
130
+ const retMsg = result.retMsg || result.ret_msg;
131
+
132
+ if (retCode !== 0) {
133
+ throw new Error(`${retMsg} (ErrCode: ${retCode})`);
134
+ }
135
+
136
+ return result;
137
+ }
138
+
139
+ private async handleFileUpload<T>(methodDef: P2PMethodDef, params: any, timestamp: number): Promise<T> {
140
+ const uploadFile = params.upload_file;
141
+ let filename = params.filename;
142
+ let fileData: Buffer;
143
+
144
+ if (Buffer.isBuffer(uploadFile)) {
145
+ if (!filename) throw new Error("filename is required when passing raw buffer");
146
+ fileData = uploadFile;
147
+ } else if (typeof uploadFile === 'string') {
148
+ if (!fs.existsSync(uploadFile)) throw new Error(`File not found: ${uploadFile}`);
149
+ fileData = fs.readFileSync(uploadFile);
150
+ if (!filename) filename = path.basename(uploadFile);
151
+ } else {
152
+ throw new Error("Invalid upload_file type. Must be string path or Buffer.");
153
+ }
154
+
155
+ const boundary = "boundary-for-file";
156
+ const mimeType = "image/png"; // Python SDK hardcodes this
157
+
158
+ // Manual payload construction to match signature
159
+ // Python: f"--{boundary}\r\nContent-Disposition: form-data; name=\"upload_file\"; filename=\"{filename}\"\r\nContent-Type: {mime_type}\r\n\r\n".encode() + data + f"\r\n--{boundary}--\r\n".encode()
160
+
161
+ const part1 = Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="upload_file"; filename="${filename}"\r\nContent-Type: ${mimeType}\r\n\r\n`);
162
+ const part2 = fileData;
163
+ const part3 = Buffer.from(`\r\n--${boundary}--\r\n`);
164
+
165
+ const payloadBuffer = Buffer.concat([part1, part2, part3]);
166
+
167
+ const signature = this.sign(payloadBuffer, timestamp);
168
+
169
+ const headers = {
170
+ 'X-BAPI-API-KEY': this.apiKey,
171
+ 'X-BAPI-SIGN': signature,
172
+ 'X-BAPI-SIGN-TYPE': '2',
173
+ 'X-BAPI-TIMESTAMP': timestamp.toString(),
174
+ 'X-BAPI-RECV-WINDOW': this.recvWindow.toString(),
175
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`
176
+ };
177
+
178
+ const response = await this.client.post(this.url + methodDef.url, payloadBuffer, { headers });
179
+
180
+ if (response.status !== 200) {
181
+ throw new Error(`Request failed with status ${response.status}: ${JSON.stringify(response.data)}`);
182
+ }
183
+
184
+ const result = response.data;
185
+ if (result.retCode !== 0) {
186
+ throw new Error(`${result.retMsg} (ErrCode: ${result.retCode})`);
187
+ }
188
+ return result;
189
+ }
190
+ }
@@ -0,0 +1,37 @@
1
+ export interface P2PMethodDef {
2
+ url: string;
3
+ method: 'GET' | 'POST' | 'FILE';
4
+ requiredParams: string[];
5
+ }
6
+
7
+ export const P2PMethods: Record<string, P2PMethodDef> = {
8
+ GET_CURRENT_BALANCE: { url: "/v5/asset/transfer/query-account-coins-balance", method: "GET", requiredParams: ["accountType"] },
9
+ GET_ACCOUNT_INFORMATION: { url: "/v5/p2p/user/personal/info", method: "POST", requiredParams: [] },
10
+ GET_ADS_LIST: { url: "/v5/p2p/item/personal/list", method: "POST", requiredParams: [] },
11
+ GET_AD_DETAILS: { url: "/v5/p2p/item/info", method: "POST", requiredParams: ["itemId"] },
12
+ UPDATE_AD: {
13
+ url: "/v5/p2p/item/update", method: "POST", requiredParams: [
14
+ "id", "priceType", "premium", "price", "minAmount", "maxAmount", "remark",
15
+ "tradingPreferenceSet", "paymentIds", "actionType", "quantity", "paymentPeriod"
16
+ ]
17
+ },
18
+ REMOVE_AD: { url: "/v5/p2p/item/cancel", method: "POST", requiredParams: ["itemId"] },
19
+ GET_ORDERS: { url: "/v5/p2p/order/simplifyList", method: "POST", requiredParams: ["page", "size"] },
20
+ GET_PENDING_ORDERS: { url: "/v5/p2p/order/pending/simplifyList", method: "POST", requiredParams: ["page", "size"] },
21
+ GET_COUNTERPARTY_INFO: { url: "/v5/p2p/user/order/personal/info", method: "POST", requiredParams: ["originalUid", "orderId"] },
22
+ GET_ORDER_DETAILS: { url: "/v5/p2p/order/info", method: "POST", requiredParams: ["orderId"] },
23
+ RELEASE_ASSETS: { url: "/v5/p2p/order/finish", method: "POST", requiredParams: ["orderId"] },
24
+ MARK_AS_PAID: { url: "/v5/p2p/order/pay", method: "POST", requiredParams: ["orderId", "paymentType", "paymentId"] },
25
+ GET_CHAT_MESSAGES: { url: "/v5/p2p/order/message/listpage", method: "POST", requiredParams: ["orderId", "size"] },
26
+ UPLOAD_CHAT_FILE: { url: "/v5/p2p/oss/upload_file", method: "FILE", requiredParams: ["upload_file"] },
27
+ SEND_CHAT_MESSAGE: { url: "/v5/p2p/order/message/send", method: "POST", requiredParams: ["message", "contentType", "orderId"] },
28
+ POST_NEW_AD: {
29
+ url: "/v5/p2p/item/create", method: "POST", requiredParams: [
30
+ "tokenId", "currencyId", "side", "priceType", "premium", "price", "minAmount",
31
+ "maxAmount", "remark", "tradingPreferenceSet", "paymentIds", "quantity",
32
+ "paymentPeriod", "itemType"
33
+ ]
34
+ },
35
+ GET_ONLINE_ADS: { url: "/v5/p2p/item/online", method: "POST", requiredParams: ["tokenId", "currencyId", "side"] },
36
+ GET_USER_PAYMENT_TYPES: { url: "/v5/p2p/user/payment/list", method: "POST", requiredParams: [] }
37
+ };
package/src/index.ts ADDED
@@ -0,0 +1,136 @@
1
+ import { P2PClient } from './client';
2
+ import { P2PMethods } from './constants';
3
+ import * as types from './types';
4
+
5
+ export * from './types';
6
+ export * from './client';
7
+ export * from './constants';
8
+
9
+ export class P2P extends P2PClient {
10
+ /**
11
+ * Obtain wallet balance, query asset information of each currency.
12
+ * By default, currency information with assets or liabilities of 0 is not returned.
13
+ */
14
+ async getCurrentBalance(params: { accountType: string;[key: string]: any }): Promise<types.P2PResponse> {
15
+ return this.request(P2PMethods.GET_CURRENT_BALANCE, params);
16
+ }
17
+
18
+ /**
19
+ * Get Account Information
20
+ */
21
+ async getAccountInformation(): Promise<types.P2PResponse> {
22
+ return this.request(P2PMethods.GET_ACCOUNT_INFORMATION, {});
23
+ }
24
+
25
+ /**
26
+ * Get Ads List
27
+ */
28
+ async getAdsList(params: types.GetAdsListParams = {}): Promise<types.P2PResponse> {
29
+ return this.request(P2PMethods.GET_ADS_LIST, params);
30
+ }
31
+
32
+ /**
33
+ * Get Ad Details
34
+ */
35
+ async getAdDetails(params: { itemId: string;[key: string]: any }): Promise<types.P2PResponse> {
36
+ return this.request(P2PMethods.GET_AD_DETAILS, params);
37
+ }
38
+
39
+ /**
40
+ * Update or activate ads
41
+ */
42
+ async updateAd(params: types.UpdateAdParams): Promise<types.P2PResponse> {
43
+ return this.request(P2PMethods.UPDATE_AD, params);
44
+ }
45
+
46
+ /**
47
+ * Remove ad
48
+ */
49
+ async removeAd(params: { itemId: string;[key: string]: any }): Promise<types.P2PResponse> {
50
+ return this.request(P2PMethods.REMOVE_AD, params);
51
+ }
52
+
53
+ /**
54
+ * Get orders
55
+ */
56
+ async getOrders(params: types.GetOrdersParams = {}): Promise<types.P2PResponse> {
57
+ return this.request(P2PMethods.GET_ORDERS, params);
58
+ }
59
+
60
+ /**
61
+ * Get pending orders
62
+ */
63
+ async getPendingOrders(params: types.GetOrdersParams = {}): Promise<types.P2PResponse> {
64
+ return this.request(P2PMethods.GET_PENDING_ORDERS, params);
65
+ }
66
+
67
+ /**
68
+ * Get counterparty info
69
+ */
70
+ async getCounterpartyInfo(params: { originalUid: string; orderId: string;[key: string]: any }): Promise<types.P2PResponse> {
71
+ return this.request(P2PMethods.GET_COUNTERPARTY_INFO, params);
72
+ }
73
+
74
+ /**
75
+ * Get order details
76
+ */
77
+ async getOrderDetails(params: { orderId: string;[key: string]: any }): Promise<types.P2PResponse> {
78
+ return this.request(P2PMethods.GET_ORDER_DETAILS, params);
79
+ }
80
+
81
+ /**
82
+ * Release digital asset
83
+ */
84
+ async releaseAssets(params: { orderId: string;[key: string]: any }): Promise<types.P2PResponse> {
85
+ return this.request(P2PMethods.RELEASE_ASSETS, params);
86
+ }
87
+
88
+ /**
89
+ * Mark order as paid
90
+ */
91
+ async markAsPaid(params: { orderId: string; paymentType: string; paymentId: string;[key: string]: any }): Promise<types.P2PResponse> {
92
+ return this.request(P2PMethods.MARK_AS_PAID, params);
93
+ }
94
+
95
+ /**
96
+ * Get chat messages
97
+ */
98
+ async getChatMessages(params: { orderId: string; size?: number; startMessageId?: number;[key: string]: any }): Promise<types.P2PResponse> {
99
+ return this.request(P2PMethods.GET_CHAT_MESSAGES, params);
100
+ }
101
+
102
+ /**
103
+ * Upload file for chats
104
+ */
105
+ async uploadChatFile(params: types.UploadChatFileParams): Promise<types.P2PResponse> {
106
+ return this.request(P2PMethods.UPLOAD_CHAT_FILE, params);
107
+ }
108
+
109
+ /**
110
+ * Send chat message
111
+ */
112
+ async sendChatMessage(params: { message: string; contentType: string; orderId: string;[key: string]: any }): Promise<types.P2PResponse> {
113
+ return this.request(P2PMethods.SEND_CHAT_MESSAGE, params);
114
+ }
115
+
116
+ /**
117
+ * Post new advertisement
118
+ */
119
+ async postNewAd(params: types.PostNewAdParams): Promise<types.P2PResponse> {
120
+ return this.request(P2PMethods.POST_NEW_AD, params);
121
+ }
122
+
123
+ /**
124
+ * Get online advertisements list
125
+ */
126
+ async getOnlineAds(params: { tokenId: string; currencyId: string; side: string | number;[key: string]: any }): Promise<types.P2PResponse> {
127
+ return this.request(P2PMethods.GET_ONLINE_ADS, params);
128
+ }
129
+
130
+ /**
131
+ * Get user payment types
132
+ */
133
+ async getUserPaymentTypes(): Promise<types.P2PResponse> {
134
+ return this.request(P2PMethods.GET_USER_PAYMENT_TYPES, {});
135
+ }
136
+ }
package/src/types.ts ADDED
@@ -0,0 +1,134 @@
1
+ export interface P2PConfig {
2
+ /**
3
+ * API Key
4
+ */
5
+ apiKey?: string;
6
+ /**
7
+ * API Secret
8
+ */
9
+ apiSecret?: string;
10
+ /**
11
+ * Whether to use Testnet (default: false)
12
+ */
13
+ testnet?: boolean;
14
+ /**
15
+ * Custom domain (default: bybit)
16
+ */
17
+ domain?: string;
18
+ /**
19
+ * Custom TLD (default: com)
20
+ */
21
+ tld?: string;
22
+ /**
23
+ * Receive window in ms (default: 5000)
24
+ */
25
+ recvWindow?: number;
26
+ /**
27
+ * Use RSA authentication (default: false)
28
+ */
29
+ rsa?: boolean;
30
+ /**
31
+ * Disable SSL verification (default: false) - Not recommended
32
+ */
33
+ disableSslChecks?: boolean;
34
+ /**
35
+ * Logging level (not fully implemented in this node version, serves as placeholder)
36
+ */
37
+ loggingLevel?: any;
38
+ }
39
+
40
+ export interface P2PResponse<T = any> {
41
+ retCode?: number;
42
+ ret_code?: number;
43
+ retMsg?: string;
44
+ ret_msg?: string;
45
+ result: T;
46
+ retExtInfo?: any;
47
+ ext_code?: string;
48
+ ext_info?: any;
49
+ time?: number;
50
+ time_now?: string;
51
+ }
52
+
53
+ // Method Parameter Interfaces
54
+
55
+ export interface GetAdsListParams {
56
+ itemId?: string;
57
+ status?: string | number; // 1 - Sold out, 2 - Available
58
+ side?: string | number; // 0 - Buy, 1 - Sell
59
+ tokenId?: string;
60
+ page?: number;
61
+ size?: number;
62
+ currencyId?: string;
63
+ [key: string]: any;
64
+ }
65
+
66
+ export interface UpdateAdParams {
67
+ id: string; // Advertisement ID
68
+ priceType?: number; // 0 - fixed, 1 - floating
69
+ premium?: number | string;
70
+ price?: number | string;
71
+ minAmount?: number | string;
72
+ maxAmount?: number | string;
73
+ remark?: string;
74
+ tradingPreferenceSet?: TradingPreferenceSet;
75
+ paymentIds?: string[];
76
+ actionType?: 'MODIFY' | 'ACTIVE';
77
+ quantity?: string;
78
+ paymentPeriod?: string | number;
79
+ [key: string]: any;
80
+ }
81
+
82
+ export interface TradingPreferenceSet {
83
+ hasUnPostAd?: number;
84
+ isKyc?: number;
85
+ isEmail?: number;
86
+ isMobile?: number;
87
+ hasRegisterTime?: number;
88
+ registerTimeThreshold?: number;
89
+ orderFinishNumberDay30?: number;
90
+ completeRateDay30?: string;
91
+ nationalLimit?: string;
92
+ hasOrderFinishNumberDay30?: number;
93
+ hasCompleteRateDay30?: number;
94
+ hasNationalLimit?: number;
95
+ [key: string]: any;
96
+ }
97
+
98
+ export interface PostNewAdParams {
99
+ tokenId: string;
100
+ currencyId: string;
101
+ side: string | number; // 0 - Buy, 1 - Sell
102
+ priceType: number;
103
+ premium?: number | string;
104
+ price?: number | string;
105
+ minAmount?: number | string;
106
+ maxAmount?: number | string;
107
+ remark?: string;
108
+ tradingPreferenceSet?: TradingPreferenceSet;
109
+ paymentIds?: string[];
110
+ quantity?: string;
111
+ paymentPeriod?: string | number;
112
+ itemType?: 'ORIGIN' | 'BULK';
113
+ [key: string]: any;
114
+ }
115
+
116
+ export interface GetOrdersParams {
117
+ status?: string;
118
+ beginTime?: string | number;
119
+ endTime?: string | number;
120
+ tokenId?: string;
121
+ side?: number[];
122
+ page?: number;
123
+ size?: number;
124
+ [key: string]: any;
125
+ }
126
+
127
+ export interface UploadChatFileParams {
128
+ upload_file: string | Buffer; // Path string or Buffer
129
+ filename?: string;
130
+ [key: string]: any;
131
+ }
132
+
133
+ // ... Add more generalized interfaces as needed or use `any` for less common methods
134
+ // to keep the initial conversion manageable but extensible.