public class JsonDecorder {

	public <T extends Response<?>> T decode(String json, Class<T> clazz) throws BitbankException {
        ObjectMapper mapper = new ObjectMapper();
        try {
            T result = mapper.readValue(json, clazz);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
} public class Accounts extends Data {

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class Account {
        public String uuid;
        public String label;
        public String address;

        public String toString() {
            return "[Account] uuid " + uuid + ", label " + label + ", address " + address;
        }
    }

    public Account[] accounts;

    public Accounts() {}
    public Accounts(Account[] a) {
        this.accounts = a;
    }
} public class Assets extends Data {

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class Asset {
        public String asset;
        @JsonProperty("amount_precision")
        public int amountPrecision;
        @JsonProperty("onhand_amount")
        public BigDecimal onhandAmount;
        @JsonProperty("locked_amount")
        public BigDecimal lockedAmount;
        @JsonProperty("free_amount")
        public BigDecimal freeAmount;
        @JsonProperty("withdrawal_fee")
        public Object withdrawalFee;
        @JsonProperty("explorer")
        public String explorer;

        public String toString() {
            return "[Assets] asset " + asset + ", onhand_amount " + onhandAmount;
        }
    }

    public Asset[] assets;

    public Assets(){}
    public Assets(Asset[] a) { this.assets = a; }
} public class Candlestick extends Data {

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class Ohlcvs {
        public BigDecimal[][] ohlcv;
        public String type;

        public class Ohlcv {
            @JsonIgnore
            public int open;
            @JsonIgnore
            public int high;
            @JsonIgnore
            public int low;
            @JsonIgnore
            public int close;
            @JsonIgnore
            public BigDecimal volume;
            @JsonIgnore
            public Date date;

            public Ohlcv(int o, int h, int l, int c, String v, long d) {
                this.open = o;
                this.high = h;
                this.low = l;
                this.close = c;
                this.volume = new BigDecimal(v);
                this.date = new Date(d);
            }

            public String toString() {
                return "[Ohlcv] open " + open + ", high " + high + ", low " + low + ", close " + close +
                        ", volume " + volume + ", date " + date;
             }
        }

        public List<Ohlcv> getOhlcvList() {
			List<Ohlcv> list = new ArrayList<Ohlcv>();
            for(BigDecimal[] i : this.ohlcv) {
                list.add(
                    new Ohlcv(i[0].intValue(), i[1].intValue(), i[2].intValue(), i[3].intValue(),
                        i[4].toString(), i[5].longValue())
                );
            }
            return list;
        }

    }

    public Ohlcvs[] candlestick;
    public Date timestamp;

    Candlestick() {}
    Candlestick(Ohlcvs[] o) {
        this.candlestick = o;
    }
} @JsonIgnoreProperties(ignoreUnknown = true)
public class Depth extends Data {

    private BigDecimal[][] asks;
    private BigDecimal[][] bids;
    private Date timestamp;

    public Depth() {}

    public Depth(BigDecimal[][] asks, BigDecimal[][] bids, Date timestamp) {
        this.asks = asks;
        this.bids = bids;
        this.timestamp = timestamp;
    }

    public BigDecimal[][] getAsks() {
        return asks;
    }

    public BigDecimal[][] getBids() {
        return bids;
    }

    public Date getTimestamp() {
        return timestamp;
    }

    @Override
    public String toString() {
        return "[Depth] " + "ask " + extract(asks) + ", ... bids " + extract(bids) + ", timestamp " + (timestamp == null ? null : timestamp.getTime());
    }

    private <T> T extract(T[][] input) {
        return input == null ? null : input.length == 0 ? null : input[0] == null ? null : input[0].length == 0 ? null : input[0][0];
    }

} @JsonIgnoreProperties(ignoreUnknown = true)
public class Order extends Data {
    @JsonProperty("order_id")
    public long orderId;
    public String pair;
    public OrderSide side;
    public OrderType type;
    @JsonProperty("start_amount")
    public BigDecimal startAmount;
    @JsonProperty("remaining_amount")
    public BigDecimal remainingAmount;
    @JsonProperty("executed_amount")
    public BigDecimal executedAmount;
    public BigDecimal price;
    @JsonProperty("post_only")
    public boolean postOnly;
    @JsonProperty("average_price")
    public BigDecimal averagePrice;
    @JsonProperty("trigger_price")
    public BigDecimal triggerPrice;
    @JsonProperty("ordered_at")
    public Date orderedAt;
    @JsonProperty("expire_at")
    public Date expireAt;
    @JsonProperty("canceled_at")
    public Date canceledAt;
    @JsonProperty("executed_at")
    public Date executedAt;
    @JsonProperty("triggered_at")
    public Date triggeredAt;
    public String status;

    public String toString() {
        return "[Order] orderId " + orderId + ", pair " + pair + ", side " + side + ", type " + type +
                ", remainingAmount " + remainingAmount + ", price " + price + ", status " + status;
    }
} @JsonIgnoreProperties(ignoreUnknown = true)
public class Orders extends Data {
    public Order[] orders;

    public Orders() {}
    public Orders(Order[] orders) {
        this.orders = orders;
    }
} @JsonIgnoreProperties(ignoreUnknown = true)
public class Ticker extends Data {
    public BigDecimal sell;
    public BigDecimal buy;
    public BigDecimal high;
    public BigDecimal low;
    public BigDecimal open;
    public BigDecimal last;
    public BigDecimal vol;
    public Date timestamp;

    @Override
    public String toString() {
        return "[Ticker] buy " + buy + ", sell " + sell + ", high " + high + ", low " + low +
            ", open " + open + ", last " + last + ", vol " + vol + ", timestamp " + timestamp;
    }
} public class Transactions extends Data {

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class Transaction {
        @JsonProperty("transaction_id")
        private long transactionId;

        private OrderSide side;

        private String price;

        private String amount;

        @JsonProperty("executed_at")
        private Date executedAt;

        public String toString() {
            return "[Transaction] transaction_id " + transactionId + ", side " + side + ", price " + price + ", amount " +
                    amount + ", executed_at " + executedAt;
        }

        public Long getTransactionId() {
            return transactionId;
        }

        public OrderSide getSide() {
            return side;
        }

        public BigDecimal getPrice() {
            return new BigDecimal(price);
        }

        public BigDecimal getAmount() {
            return new BigDecimal(amount);
        }

        public Date getExecutedAt() {
            return executedAt;
        }
    }

    public Transaction[] transactions;

    public Transactions () {}
    public Transactions (Transaction[] transactions) {
        this.transactions = transactions;
    }

    public Transaction[] getTransactions() {
        return transactions;
    }
} @JsonIgnoreProperties(ignoreUnknown = true)
public class Withdraw extends Data {

    public String uuid;
    public String asset;
    @JsonProperty("account_uuid")
    public String accountUuid;
    public BigDecimal amount;
    public BigDecimal fee;
    public String label;
    public String address;
    public String txid;
    public String status;
    @JsonProperty("requested_at")
    public Date requestedAt;

    public String toString() {
        return "[Withdraw] uuid " + uuid + ", asset " + asset + ", account_uuid " + accountUuid +
                ", amount " + amount + ", fee " + fee + ", label " + label + ", address " + address + ", status" + status;
    }
} public enum CandleType {
    _1MIN("1min"),
    _5MIN("5min"),
    _15MIN("15min"),
    _30MIN("30min"),
    _1HOUR("1hour"),
    _4HOUR("4hour"),
    _8HOUR("8hour"),
    _12HOUR("12hour"),
    _1DAY("1day"),
    _1WEEK("1week");

    private final String type;

    CandleType(final String type) {
        this.type = type;
    }

    public String getCode() {
        return this.type;
    }
} public enum CurrencyPair {
    BTC_JPY("btc_jpy"),
    LTC_JPY("ltc_jpy"),
    LTC_BTC("ltc_btc"),
    XRP_JPY("xrp_jpy"),
    XRP_BTC("xrp_btc"),
    ETH_JPY("eth_jpy"),
    ETH_BTC("eth_btc"),
    MONA_JPY("mona_jpy"),
    MONA_BTC("mona_btc"),
    BCC_JPY("bcc_jpy"),
    BCC_BTC("bcc_btc"),
    XLM_JPY("xlm_jpy"),
    XLM_BTC("xlm_btc"),
    QTUM_JPY("qtum_jpy"),
    QTUM_BTC("qtum_btc"),
    BAT_JPY("bat_jpy"),
    BAT_BTC("bat_btc"),
    OMG_JPY("omg_jpy"),
    OMG_BTC("omg_btc"),
    XYM_JPY("xym_jpy"),
    XYM_BTC("xym_btc"),
    LINK_JPY("link_jpy"),
    LINK_BTC("link_btc"),
    MKR_JPY("mkr_jpy"),
    MKR_BTC("mkr_btc");


    private final String pair;

    CurrencyPair(final String pair) {
        this.pair = pair;
    }

    public String getCode() {
        return this.pair;
    }
} @JsonDeserialize(using = OrderSideDeserializer.class)
public enum OrderSide {
    BUY("buy"),
    SELL("sell");

    private final String side;

    OrderSide(final String side) {
        this.side = side;
    }

    public String getCode() {
        return this.side;
    }
} @JsonDeserialize(using = OrderTypeDeserializer.class)
public enum OrderType {
    LIMIT("limit"),
    MARKET("market"),
    STOP("stop"),
    STOP_LIMIT("stop_limit");

    private final String type;

    OrderType(final String type) {
        this.type = type;
    }

    public String getCode() {
        return this.type;
    }
} public class CancelBody {
    public String pair;
    @JsonProperty("order_id")
    public long orderId;

    public CancelBody(CurrencyPair pair, long orderId) {
        this.pair = pair.getCode();
        this.orderId = orderId;
    }

    public String toJson() {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            return "{}";
        }
    }
} public class CancelsBody {
    public String pair;
    @JsonProperty("order_ids")
    public long[] orderIds;

    public CancelsBody(CurrencyPair pair, long[] orderIds) {
        this.pair = pair.getCode();
        this.orderIds = orderIds;
    }

    public String toJson() {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            return "{}";
        }
    }
} public class OrderBody {
    public String pair;
    public String amount;
    public BigDecimal price;
    public String side;
    public String type;
    @JsonProperty("post_only")
    public boolean postOnly;
    @JsonProperty("trigger_price")
    public BigDecimal triggerPrice;

    public OrderBody(CurrencyPair pair, BigDecimal amount, BigDecimal price, OrderSide side, OrderType type,
            boolean postOnly, BigDecimal triggerPrice) {
        this.pair = pair.getCode();
        this.amount = amount.toString();
        this.price = price;
        this.side = side.getCode();
        this.type = type.getCode();
        this.postOnly = postOnly;
        this.triggerPrice = triggerPrice;
    }

    public String toJson() {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            return "{}";
        }
    }
} public class WithdrawBody {
    public String asset;
    public String uuid;
    public BigDecimal amount;
    @JsonProperty("otp_token")
    public String otpToken;
    @JsonProperty("sms_token")
    public String smsToken;

    public WithdrawBody(String asset, String uuid, BigDecimal amount, String otpToken, String smsToken) {
        this.asset = asset;
        this.uuid = uuid;
        this.amount = amount;
        this.otpToken = otpToken;
        this.smsToken = smsToken;
    }

    public String toJson() {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            return "{}";
        }
    }
} public class AccountsResponse extends Response<Accounts> {
} public class AssetsResponse extends Response<Assets> {
} public class CandlestickResponse extends Response<Candlestick> {
} public class DepthResponse extends Response<Depth> {
} public class ErrorCodeResponse extends Response<ErrorCode> {
} public class OrderResponse extends Response<Order> {
} public class OrdersResponse extends Response<Orders>
{
} public class Response<T extends Data> {
    public int success;
    public T data;

    @Override
    public String toString() {
        return "[Response] success " + success + ", data " + data;
    }
} public class TickerResponse extends Response<Ticker> {
} public class TransactionsResponse extends Response<Transactions> {
} public class WithdrawResponse extends Response<Withdraw> {
} public class BitbankException extends Exception {

    /**
     *
     */
    private static final long serialVersionUID = -668176882481871028L;

    public static final int DEFAULT_CODE = 0;

    public final int code;

    public BitbankException() {
        this(DEFAULT_CODE);
    }

    public BitbankException(int code) {
        this(code, String.valueOf(code));
    }

    public BitbankException(String message) {
        this(DEFAULT_CODE, message);
    }

    public BitbankException(int code, String message) {

        super(message);

        this.code = code;

    }

}
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import cc.bitbank.deserializer.JsonDecorder;
import cc.bitbank.entity.Accounts;
import cc.bitbank.entity.Assets;
import cc.bitbank.entity.Candlestick;
import cc.bitbank.entity.Depth;
import cc.bitbank.entity.Order;
import cc.bitbank.entity.Orders;
import cc.bitbank.entity.Ticker;
import cc.bitbank.entity.Transactions;
import cc.bitbank.entity.Withdraw;
import cc.bitbank.entity.enums.CandleType;
import cc.bitbank.entity.enums.CurrencyPair;
import cc.bitbank.entity.enums.OrderSide;
import cc.bitbank.entity.enums.OrderType;
import cc.bitbank.entity.request.CancelBody;
import cc.bitbank.entity.request.CancelsBody;
import cc.bitbank.entity.request.OrderBody;
import cc.bitbank.entity.request.WithdrawBody;
import cc.bitbank.entity.response.AccountsResponse;
import cc.bitbank.entity.response.AssetsResponse;
import cc.bitbank.entity.response.CandlestickResponse;
import cc.bitbank.entity.response.DepthResponse;
import cc.bitbank.entity.response.ErrorCodeResponse;
import cc.bitbank.entity.response.OrderResponse;
import cc.bitbank.entity.response.OrdersResponse;
import cc.bitbank.entity.response.Response;
import cc.bitbank.entity.response.TickerResponse;
import cc.bitbank.entity.response.TransactionsResponse;
import cc.bitbank.entity.response.WithdrawResponse;
import cc.bitbank.exception.BitbankException;
        return makePrivateRequestHeader(nonce, createSign(message));
    }

    private String createSign(String message) throws BitbankException {
        try {
            String algo = "HmacSHA256";
            String secret = this.apiSecret;

            SecretKeySpec sk = new SecretKeySpec(secret.getBytes(), algo);
            Mac mac = Mac.getInstance(algo);
            mac.init(sk);
            byte[] macBytes = mac.doFinal(message.getBytes());

            StringBuilder sb = new StringBuilder(2 * macBytes.length);
            for(byte b: macBytes) {
                sb.append(String.format("%02x", b&0xff) );
            }
            return sb.toString();
        } catch (Exception e) {
            throw new BitbankException(e.getMessage());
        }
    }

	private <T extends Response<?>> T httpExecute(HttpClient client, HttpRequestBase http, Class<T> clazz)
			throws BitbankException, IOException {
        try {
            HttpResponse response = client.execute(http);
            HttpEntity entity = response.getEntity();
            String json = EntityUtils.toString(entity);
            //System.out.println(json);

            JsonDecorder decorder = new JsonDecorder();
            T result = decorder.decode(json, clazz);
            if (result == null || result.success != SUCCESS) {
                ErrorCodeResponse error = decorder.decode(json, ErrorCodeResponse.class);
                throw new BitbankException(error.data.code);
            } else {
                return result;
            }
        } catch (IOException e) {
            //System.out.println(e.getLocalizedMessage());
            throw e;
        }
    }

	private <T extends Response<?>> T doHttpGet(URIBuilder builder, Class<T> clazz, List<Header> header)
			throws BitbankException, IOException {
        try {
            URI uri = builder.build();
            HttpGet httpGet = new HttpGet(uri);
            HttpClient client = HttpClientBuilder.create().setDefaultHeaders(header).build();
            return httpExecute(client, httpGet, clazz);
        } catch (URISyntaxException e) {
            throw new BitbankException(e.getMessage());
        }
    }

	private <T extends Response<?>> T doHttpPost(URIBuilder builder, Class<T> clazz, List<Header> header,
			StringEntity entityBody) throws BitbankException, IOException {
        try {
            URI uri = builder.build();
            HttpPost httpPost = new HttpPost(uri);
            httpPost.setEntity(entityBody);
            HttpClient client = HttpClientBuilder.create().setDefaultHeaders(header).build();
            return httpExecute(client, httpPost, clazz);
        } catch (URISyntaxException e) {
            throw new BitbankException(e.getMessage());
        }
    }

    public Ticker getTicker(CurrencyPair pair) throws BitbankException, IOException {
        String path = "/" + pair.getCode() + "/ticker";
        URIBuilder builder = getPublicUriBuilder(path);
        TickerResponse result = doHttpGet(builder, TickerResponse.class, getPublicRequestHeader());
        return result.data;
    }

    public Depth getDepth(CurrencyPair pair) throws BitbankException, IOException {
        String path = "/" + pair.getCode() + "/depth";
        URIBuilder builder = getPublicUriBuilder(path);
        DepthResponse result = doHttpGet(builder, DepthResponse.class, getPublicRequestHeader());
        return result.data;
    }

    public Transactions getTransaction(CurrencyPair pair) throws BitbankException, IOException {
        String path = "/" + pair.getCode() + "/transactions";
        URIBuilder builder = getPublicUriBuilder(path);
        TransactionsResponse result = doHttpGet(builder, TransactionsResponse.class, getPublicRequestHeader());
        return result.data;
    }

    public Transactions getTransaction(CurrencyPair pair, String YYYYMMDD) throws BitbankException, IOException {
        String path = "/" + pair.getCode() + "/transactions/" + YYYYMMDD;
        URIBuilder builder = getPublicUriBuilder(path);
        TransactionsResponse result = doHttpGet(builder, TransactionsResponse.class, getPublicRequestHeader());
        return result.data;
    }

    public Candlestick getCandlestick(CurrencyPair pair, CandleType candleType, String YYYYMMDD) throws BitbankException, IOException {
        String path = "/" + pair.getCode() + "/candlestick/" + candleType.getCode() + "/" + YYYYMMDD;
        URIBuilder builder = getPublicUriBuilder(path);
        CandlestickResponse result = doHttpGet(builder, CandlestickResponse.class, getPublicRequestHeader());
        return result.data;
    }

    public Assets getAsset() throws BitbankException, IOException {
        String path = "/v1/user/assets";
        URIBuilder builder = getPrivateUriBuilder(path);
        AssetsResponse result = doHttpGet(builder, AssetsResponse.class,
                getPrivateRequestHeader(path, Collections.<NameValuePair>emptyList()));
        return result.data;
    }

    public Order getOrder(CurrencyPair pair, long id) throws BitbankException, IOException {
        String path = "/v1/user/spot/order";
        List<NameValuePair> nameValuePair = new ArrayList<NameValuePair>();
        nameValuePair.add(new BasicNameValuePair("pair", pair.getCode()));
        nameValuePair.add(new BasicNameValuePair("order_id", String.valueOf(id)));

        URIBuilder builder = getPrivateUriBuilder(path).setParameters(nameValuePair);
        OrderResponse result = doHttpGet(builder, OrderResponse.class, getPrivateRequestHeader(path, nameValuePair));