LBank-exchange / lbank-official-api-docs

Official Documentation for the LBank
https://www.lbank.com
29 stars 19 forks source link

Can't get private API to work with JavaScript #30

Closed staniboy closed 11 months ago

staniboy commented 11 months ago

Hello, I spent whole day with no luck pulling private end-point with RSA or Hmac... I be glad if someone can point out what am I doing wrong... I came here for the lack of JS examples in documentation or on the internet...

output is data: { result: 'false', error_code: 10003, ts: 1690383105167 } // Invalid parameters

did double checked key and secret...

Help is highly appreciated :'(

upd: I wasn't sending post method correctly. So I got over 10003 and now on 10007(invalid signature)

As always help is highly appreciated.


import { createHash, createHmac, createSign } from "crypto";
import { configDotenv } from "dotenv";

configDotenv();

const api_key = process.env.LBANK_API_KEY;
const secret = process.env.LBANK_SECRET;

const baseURL = "https://api.lbkex.com";
const timestampEP = "/v2/timestamp.do";
const userInfoEP = "/v2/supplement/user_info.do";

async function getTimestamp(): Promise<number> {
  const { data } = await axios.get(baseURL + timestampEP);
  return data.ts;
}

function generateRandomString(length: number): string {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  const charactersLength = characters.length;

  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }

  return result;
}
function objectToString(obj: Object): string {
  return Object.entries(obj)
    .map(([key, value]) => `${key}=${value}`)
    .sort()
    .join("&");
}

function generateMD5Hex(input: string): string {
  const hash = createHash("md5");
  hash.update(input);
  const md5Hex = hash.digest("hex").toUpperCase();
  return md5Hex;
}
function signWithRSA(secretKey: string, data: string): string {
  const pemKey = `
-----BEGIN RSA PRIVATE KEY-----
${secretKey}
-----END RSA PRIVATE KEY-----
`;
  const sign = createSign("RSA-SHA256");
  sign.write(Buffer.from(data).toString("base64"));
  sign.end();

  const signature = sign.sign(pemKey, "base64");
  return signature;
}

function signWithHmacSHA256(secretKey, data) {
  const hmac = createHmac("sha256", secretKey);
  hmac.update(data);

  const signature = hmac.digest("base64");
  return signature;
}

async function getUserInfo() {
  const echostr = generateRandomString(35);
  const timestamp = await getTimestamp();
  const headers = {
    "Content-Type": "application/x-www-form-urlencoded",
  };

  const params = {
    api_key: api_key,
    echostr,
    signature_method: "RSA",
    timestamp,
  };

  const dataString = objectToString(params);
  const digest = generateMD5Hex(dataString);
  const sign = signWithRSA(secret, digest);

  const requestBody = new URLSearchParams();
  requestBody.append("api_key", api_key);
  requestBody.append("echostr", echostr);
  requestBody.append("signature_method", "RSA");
  requestBody.append("timestamp", timestamp.toString());
  requestBody.append("sign", sign);

  const data = await axios.post(baseURL + userInfoEP, requestBody.toString(), {
    headers,
  });
  return data;
}

const data = await getUserInfo();
console.log(data);
staniboy commented 11 months ago

Finally figured out how to set it up properly. Here is updated code in anyone ever need it. You will need RSA API for this to work.


import axios from "axios";
import { createHash, createSign } from "crypto";
import { configDotenv } from "dotenv";

configDotenv();

const api_key = process.env.LBANK_API_KEY;
const secret = process.env.LBANK_SECRET;

const baseURL = "https://api.lbkex.com";
const timestampEP = "/v2/timestamp.do";
const userInfoEP = "/v2/supplement/user_info.do";
const systemStatusEP = "/v2/supplement/system_status.do";

// Getting time from the server

async function getTimestamp(): Promise<number> {
  const { data } = await axios.get(baseURL + timestampEP);
  return data.ts;
}

// Generating a random string for echostr (must me 30-40 chars long)

function generateRandomString(length: number): string {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  const charactersLength = characters.length;

  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }

  return result;
}
function objectToString(obj: Object): string {
  return Object.entries(obj)
    .map(([key, value]) => `${key}=${value}`)
    .sort()
    .join("&");
}

// Turns string into MD5 digest

function generateMD5Hex(input: string): string {
  const hash = createHash("md5");
  hash.update(input);
  const md5Hex = hash.digest("hex").toUpperCase();
  return md5Hex;
}

// Signs string with RSA secret key

function signWithRSA(secretKey: string, data: string): string {
  const pemKey = `
-----BEGIN RSA PRIVATE KEY-----
${secretKey}
-----END RSA PRIVATE KEY-----
`;
  const sign = createSign("RSA-SHA256");
  sign.write(data);
  sign.end();

  const signature = sign.sign(pemKey, "base64");
  return signature;
}

// Request function

async function getUserInfo() {
  const echostr = generateRandomString(35);
  const timestamp = await getTimestamp();
  const headers = {
    "Content-Type": "application/x-www-form-urlencoded",
  };

  const params = {
    api_key: api_key,
    echostr,
    signature_method: "RSA",
    timestamp,
  };

  const dataString = objectToString(params); // 1. Get parameter String(need to be signed)
  const digest = generateMD5Hex(dataString); // 2. Turn parameters into MD5 digest
  const sign = signWithRSA(secret, digest); // 3. Signature

  // 4. Submit (Note this is POST request)

  const requestBody = new URLSearchParams();
  requestBody.append("api_key", api_key);
  requestBody.append("echostr", echostr);
  requestBody.append("signature_method", "RSA");
  requestBody.append("timestamp", timestamp.toString());
  requestBody.append("sign", sign);

  const data = await axios.post(baseURL + userInfoEP, requestBody.toString(), {
    headers,
  });
  return data;
}

const data = await getUserInfo();
console.log(data.data);
Prokopev-S commented 11 months ago

Hello, I had the same problem. Thanks for updated code. But it didn't work for me there is a new error. Output is data: error:0909006C:PEM routines:get_name:no start line. I don't understand what I'm doing wrong