Let me know how this works when included in other projects, or if I need to make any changes to make it less brittle.
You'll obviously want to change the location where the secret is stored.. or (better), change the storage method altogether.
* JAVA RMBTB SECURE API CLASS
*
* COPYRIGHT & LIMITATIONS
* (c) 2013 rmbtb.com, released under the BSD license. You are free to copy, redistribute, and include this
* code in your own application, for personal or commercial purposes.
*
* However, you may NOT brand yourself or any code or services as "RMBTB", "人盟比特币", or explicitly
* or implicitly represent any services or software as being provided by or endorsed by RMBTB.
*
* USAGE
* This class relies on the Apache commons codec and the json-simple modules.
*
* RmbtbSecureApi r = new RmbtbSecureApi("pubkey", "passphrase", "BTCCNY");
* Sytem.out.print(r.addOrder("bid", 1.1, 600.0));
*
* Full documentation is at https://www.rmbtb.com/help-secureapi-en/
*
*/
import java.util.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import javax.net.ssl.HttpsURLConnection;
import java.net.ProtocolException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
// http://commons.apache.org/proper/commons-codec/
import org.apache.commons.codec.binary.Base64;
// json-simple http://code.google.com/p/json-simple/
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
/**
* The RMBTB Secure API class. Set the constants here.
*/
interface RmbtbApi {
// Set this to the location you want to store temporary secrets
public static final String StorLocation = "rmbtb-secret-data.dat";
public static final String urlBaseLoc = "https://www.rmbtb.com/api/secure/";
/**
* Gets useful information about your account
* Authentication required.
* @return JSONObject account details
*/
public JSONObject getInfo() throws Exception;
/**
* Gets your wallet balances
* Authentication required.
* @return JSONObject account balances
*/
public JSONObject getFunds() throws Exception;
/**
* Gets your wallet balances
* Authentication required.
* @return JSONObject account balances
*/
public JSONObject ticker() throws Exception;
/**
* Gets useful market information
* @return JSONObject market info
*/
public JSONObject getOrders() throws Exception;
/**
* Adds an order -- double params
* @param String type bid | ask
* @param double amount the amount to buy/sell
* @param souble price the price to buy/sell at
* @return JSONObject containing the Order ID
*/
public JSONObject addOrder(String type, double amount, double price) throws Exception;
/**
* Adds an order -- long integer params
* @param String type bid | ask
* @param long amount the amount to buy/sell
* @param long price the price to buy/sell at
* @return JSONObject containing the Order ID
*/
public JSONObject addOrder(String type, long amount, long price) throws Exception;
/**
* Cancels an order
* @param long orderId the Order ID to cancel
* @return JSONObject containing the Order ID
*/
public JSONObject cancelOrder(long orderId) throws Exception;
/**
* fetches info about an order
* @param long orderId the Order ID to cancel
* @return JSONObject of info
*/
public JSONObject fetchOrder(long orderId) throws Exception;
/**
* Gets your 50 most recent trades
* @return JSONObject of info
*/
public JSONObject getTrades() throws Exception;
/**
* returns the most recent market transactions
* @return JSONObject of info
*/
public JSONObject lastTrades() throws Exception;
/**
* returns the market depth
* @return JSONObject showing bids and asks
*/
public JSONObject getDepth() throws Exception;
}
public class RmbtbSecureApi implements RmbtbApi {
private String currPair;
private String pubkey;
private String passphrase;
private String secret;
private Calendar expires;
public RmbtbSecureApi(String pubkey, String passphrase) {
this(pubkey, passphrase, "BTCCNY");
}
public RmbtbSecureApi(String pubkey, String passphrase, String currPair) {
this.currPair = currPair;
this.pubkey = pubkey;
this.passphrase = passphrase;
this.secret = "";
this.expires = Calendar.getInstance();
}
public JSONObject getInfo() throws Exception {
return doRequest("getinfo", "GET", true);
}
public JSONObject getFunds() throws Exception {
return doRequest("wallets", "GET", true);
}
public JSONObject ticker() throws Exception {
return doRequest("ticker", "GET", false);
}
public JSONObject getOrders() throws Exception {
return doRequest("orders", "GET", true);
}
public JSONObject addOrder(String type, double amount, double price) throws Exception {
HashMap
params.put("type", type == "ask" ? "ask" : "bid");
params.put("amount", new DecimalFormat("00000000.00000000").format(amount));
params.put("price", new DecimalFormat("00000000.00000000").format(price));
return doRequest("order/add", "POST", true, params);
}
public JSONObject addOrder(String type, long amount, long price) throws Exception {
HashMap
params.put("type", type == "ask" ? "ask" : "bid");
params.put("amount_int", String.valueOf(amount));
params.put("price_int", String.valueOf(price));
return doRequest("order/add", "POST", true, params);
}
public JSONObject cancelOrder(long orderId) throws Exception {
HashMap
params.put("oid", String.valueOf(orderId));
return doRequest("order/cancel", "POST", true, params);
}
public JSONObject fetchOrder(long orderId) throws Exception {
HashMap
params.put("oid", String.valueOf(orderId));
return doRequest("order/fetch", "GET", true, params);
}
public JSONObject getTrades() throws Exception {
return doRequest("trades/mine", "GET", true);
}
public JSONObject lastTrades() throws Exception {
return doRequest("trades/all", "GET", false);
}
public JSONObject getDepth() throws Exception {
return doRequest("depth", "GET", false);
}
private JSONObject doRequest(String api, String httpMethod, boolean auth) throws Exception {
HashMap
return doRequest(api, httpMethod, auth, params);
}
private JSONObject doRequest(String api, String httpMethod, boolean auth, HashMap
if(auth) {
Calendar maxAge = Calendar.getInstance();
maxAge.add(Calendar.MINUTE, -1);
if((this.secret == "") || this.expires.before(maxAge)) {
this.loadSecret();
}
params.put("nonce", String.valueOf(System.nanoTime())+"000");
}
String paramStr = "";
int i = 0;
for(Map.Entry
paramStr += entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), "UTF-8");
i++;
if(i < params.size()) {
paramStr += "&";
}
}
HashMap
if(auth) {
headers.put("Rest-Key", this.pubkey);
headers.put("Rest-Sign", this.getRequestSig(paramStr));
}
JSONObject data = this.doHttpRequest(api, httpMethod, paramStr, headers);
return data;
}
private void loadSecret() throws UnsupportedEncodingException, IOException, ParseException {
File dat = new File(this.StorLocation);
// Load secret from file
if(dat.exists()) {
this.expires.setTimeInMillis(dat.lastModified());
this.expires.add(Calendar.HOUR, 2);
Calendar maxAge = Calendar.getInstance();
maxAge.add(Calendar.MINUTE, -1);
if(this.expires.after(maxAge)) {
StringBuilder datContents = new StringBuilder((int)dat.length());
Scanner scanner = null;
try {
scanner = new Scanner(dat);
while(scanner.hasNextLine()) {
datContents.append(scanner.nextLine());
}
} catch (FileNotFoundException e) {
} finally {
scanner.close();
}
this.secret = datContents.toString();
this.expires = Calendar.getInstance();
this.expires.add(Calendar.HOUR, 2);
return;
}
}
// Need to fetch a new secret
HashMap
headers.put("Rest-Key", this.pubkey);
String params = "api_passphrase=" + URLEncoder.encode(this.passphrase, "UTF-8");
JSONObject data = this.doHttpRequest("getsecret", "POST", params, headers);
this.secret = (String)data.get("secret");
this.expires = Calendar.getInstance();
this.expires.add(Calendar.HOUR, 2);
FileWriter fw = new FileWriter(dat);
try {
fw.write(this.secret);
} catch(IOException e) {
System.out.println(e);
} finally {
fw.close();
}
}
// Perform the request
private JSONObject doHttpRequest(String api, String httpMethod, String params, HashMap
api = (httpMethod == "GET") ? api += "?" + params : api;
URL uObj = new URL(this.urlBaseLoc + this.currPair + "/" + api);
HttpsURLConnection conn = (HttpsURLConnection) uObj.openConnection();
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; RMBTB Java client)");
for(Map.Entry
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
if(httpMethod == "POST") {
conn.setRequestMethod("POST");
conn.setDoOutput(true);
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.writeBytes(params);
out.flush();
out.close();
} else {
conn.setRequestMethod("GET");
}
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inLine;
StringBuffer response = new StringBuffer();
while((inLine = in.readLine()) != null) {
response.append(inLine);
}
in.close();
JSONParser parser = new JSONParser();
JSONObject respJSON = (JSONObject)(parser.parse(response.toString()));
if(respJSON.get("error").getClass().getName() != "java.lang.Boolean") {
String strErr = (String)(respJSON.get("error"));
throw new RuntimeException(strErr);
}
JSONObject data = (JSONObject)respJSON.get("data");
return data;
}
// Signs using HMAC
private String getRequestSig(String data) throws NoSuchAlgorithmException, InvalidKeyException {
Mac mac = Mac.getInstance("HmacSHA512");
SecretKeySpec secret_spec = new SecretKeySpec(this.secret.getBytes(), "HmacSHA512");
mac.init(secret_spec);
return Base64.encodeBase64String(mac.doFinal(data.getBytes())).toString();
}
}