C++ Error: "Mandatory parameter 'timestamp' was not sent, was empty/null, or malformed."

Hey, I am getting this error: Error: “Mandatory parameter ‘timestamp’ was not sent, was empty/null, or malformed.”
I dont know what to do, I feel like I have tried everything. The request seems to be good, the timestamp is definititely right… Well, Im providing my source code below:

#include <iostream>
#include <sstream>
#include <cstring>
#include <cctype>
#include <curl/curl.h>
#include <openssl/hmac.h>
#include <nlohmann/json.hpp>
#include <chrono>
#include <cmath>

const char* api_key = "MY_API_KEY";
const char* api_secret = "MY_SECRET";

// Function to compute the HMAC-SHA256 hash of the message
std::string hmac_sha256(const std::string& key, const std::string& message)
{
    unsigned char digest[EVP_MAX_MD_SIZE];
    unsigned int digest_len;

    HMAC(EVP_sha256(), key.c_str(), key.length(),
         (unsigned char*) message.c_str(), message.length(),
         digest, &digest_len);

    std::stringstream ss;
    ss << std::hex << std::setfill('0');
    for (unsigned int i = 0; i < digest_len; i++) {
        ss << std::setw(2) << static_cast<unsigned int>(digest[i]);
    }
    std::string mdString = ss.str();

    return mdString;
}

// Callback function for CURL to handle the response
size_t curl_callback(void* contents, size_t size, size_t nmemb, void* userp)
{
    ((std::string*) userp)->append((char*) contents, size * nmemb);
    return size * nmemb;
}

// Function to get the server time from the Binance API
std::string get_server_time() {
    std::string endpoint = "/fapi/v1/time";
    std::string url = "https://fapi.binance.com" + endpoint;

    CURL* curl = curl_easy_init();
    if (curl) {
        std::string response;
        curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        CURLcode res = curl_easy_perform(curl);
        if (res == CURLE_OK) {
            return response;
        } else {
            std::cerr << "Error: " << curl_easy_strerror(res) << std::endl;
        }
        curl_easy_cleanup(curl);
    }
    return "";
}

// Function to send an authenticated GET request to the Binance API
std::string binance_get(const std::string& endpoint, const std::string& data)
{
    std::string url = "https://fapi.binance.com" + endpoint;
    std::string headers = "X-MBX-APIKEY: " + std::string(api_key);
    std::string signature = hmac_sha256(api_secret, data);
    std::string get_data = data + "&signature=" + signature;

    CURL* curl = curl_easy_init();
    if (curl) {
        std::string response;
        curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
        curl_easy_setopt(curl, CURLOPT_URL, (url + "?" + get_data).c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_slist_append(NULL, headers.c_str()));
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        CURLcode res = curl_easy_perform(curl);
        if (res == CURLE_OK) {
            return response;
        } else {
            std::cerr << "Error: " << curl_easy_strerror(res) << std::endl;
        }
        curl_easy_cleanup(curl);
    }
    return "";
}

// Function to send an authenticated POST request to the Binance API
std::string binance_post(const std::string& endpoint, const std::string& data)
{
    std::string url = "https://fapi.binance.com" + endpoint;
    std::string headers = "X-MBX-APIKEY: " + std::string(api_key);
    std::string signature = hmac_sha256(api_secret, data);

    CURL* curl = curl_easy_init();
    if (curl) {
        std::string response;
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_slist_append(NULL, headers.c_str()));
        std::cout << "data: " << data << std::endl;

        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (data + "&signature=" + signature).c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        CURLcode res = curl_easy_perform(curl);
        if (res == CURLE_OK) {
            return response;
        } else {
            std::cerr << "Error: " << curl_easy_strerror(res) << std::endl;
        }
        curl_easy_cleanup(curl);
    }
    return "";
}

bool place_market_order(const std::string& symbol, const std::string& side, double& quantity) {
    std::string endpoint = "/fapi/v1/order";
    std::string server_time_str = get_server_time();
    auto server_time = nlohmann::json::parse(server_time_str)["serverTime"].get<int64_t>();
    std::string timestamp = std::to_string(server_time);
    std::string quantity_str = std::to_string(quantity);
    std::string data = "symbol=" + symbol + "&side=" + side + "&type=MARKET" + "&quantity=" + quantity_str + "&timestamp=" + timestamp;
    std::string response = binance_post(endpoint, data);

    auto json_response = nlohmann::json::parse(response);
    if (json_response.find("code") != json_response.end()) {
        std::cerr << "Error: " << json_response["msg"] << std::endl;
        return false;
    } else {
        std::cout << "Order successfully placed!" << std::endl;
        std::cout << "Symbol: " << json_response["symbol"] << std::endl;
        std::cout << "Side: " << json_response["side"] << std::endl;
        std::cout << "Type: " << json_response["type"] << std::endl;
        std::cout << "Quantity: " << json_response["origQty"] << std::endl;
        std::cout << "Price: " << json_response["price"] << std::endl;
        return true;
    }
}

double get_symbol_price(const std::string& symbol)
{
    std::string endpoint = "/fapi/v1/premiumIndex";
    std::string data = "symbol=" + symbol;
    std::string response = binance_get(endpoint, data);

    auto json_response = nlohmann::json::parse(response);

    // Check for error response
    if (json_response.find("code") != json_response.end()) {
        int code = json_response["code"];
        if (code != 200) {
            throw std::runtime_error("Error getting price for symbol " + symbol + ", error code: " + std::to_string(code));
        }
    }

    std::string mark_price_str = json_response["markPrice"];
    double mark_price = std::stod(mark_price_str);
    return mark_price;
}

int get_precision(double& price) {
    if (price < 10) return 1;
    if (price < 100) return 10;
    if (price < 1000) return 100;
    else return 1000;
}

std::string buy_or_sell(std::string& side) {
    if(side == "b" || side == "buy" || side == "BUY") {
        side = "BUY";
        return "Bought";
    }
    if(side == "s" || side == "sell" || side == "SELL") {
        side = "SELL";
        return "Sold";
    }
    return "None";
}

bool get_input(std::string& symbol, double& qty, std::string& side, double& money) {
    std::cout << "Enter your order (b 2500 eth -> buys ETH for 2500$): " << std::endl;
    std::cin >> side >> money >> symbol;
    if (std::cin.fail()) {
        std::cout << "Invalid input." << std::endl;
        std::cin.clear(); // clear the error state
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore any remaining characters in the buffer
        return false;
    }

    std::string is_valid_side = buy_or_sell(side);
    if (is_valid_side == "None") return false;
    std::transform(symbol.begin(), symbol.end(), symbol.begin(), ::toupper);
    symbol += "USDT"; // gets the actual symbol to work with
    std::cout << symbol << std::endl;
    double price = get_symbol_price(symbol);
    qty = money / price;
    qty = std::round(qty * get_precision(price)) / get_precision(price); // gets the actual quantity to work with
    return true;
}

int main() {
    std::string symbol;
    std::string side;
    double qty;
    double money;

    while(get_input(symbol, qty, side, money)) {
        place_market_order(symbol, side, qty);
        std::cout << buy_or_sell(side) << " " << qty << " of " << symbol << " for " << money << "$" << std::endl;
    }

    return 0;
}

No need to get server time from binance. Get your local server time in UTC+0.

Hi,
Have you verified that the timestamp returned from get_server_time() isn’t null/""? If is returning a timestamp, try printing the data string from line 126 and pasting it here so we can verify all the parameters being sent to the endpoint are correct. In general it would help a lot to add some debugging statements to the code to check that the ‘timestamp’ parameter is being generated correctly and that it’s being included in the request data in the correct format.

Sure, here is the printed timestamp that is passed as well as the data im passing to the binance_post function:

1677824439451
data: symbol=ETHUSDT&side=BUY&type=MARKET&quantity=0.006000&timestamp=1677824439451