XRP瑞波幣JAVA接入
注意點:
1,瑞波幣貨幣金額【XRP的數量表示為字串。(XRP的精度相當於64位整數,但JSON整數限制為32位,因此如果以JSON整數表示,XRP可能會溢位。)XRP在“drop”中正式指定,相當於0.000001(1每個XRP的百萬分之一。因此,要在JSON文件中表示1.0 XRP,您可以編寫:"1000000"】
2,日期格式。博主是通過日期來儲存使用者交易記錄處理進度的。有人用的是marker儲存進度。
3,使用者充值地址。博主通過在主地址上加使用者id(address_userId)。告知使用者充值地址。使用者充值時需主動處理地址。在備註中寫入userId。
package com.tn.web.service.coin; import java.io.IOException; import java.math.BigDecimal; import java.text.MessageFormat; import java.util.*;
import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.tn.base.Log; import com.tn.constant.CoinConstant; import com.tn.util.BigDecimalUtil; import com.tn.util.DateUtil; import com.tn.util.HttpUtil;
/** * XRP操作 * @author xhl * @create 2017/10/27 **/ @Service public class CoinXrpService{
//URL都為正式網路地址可信任 private String getUrl = "https://data.ripple.com"; private String postUrl = "https://s1.ripple.com:51234"; private String address = ""; private String password = "";
private Logger log = Log.get();
private final static String RESULT = "result"; private final static String SUCCESS = "success"; private final static String TES_SUCCESS = "tesSUCCESS"; private final static String METHOD_GET_TRANSACTION = "/v2/accounts/{0}/transactions"; private final static String METHOD_GET_BALANCE = "/v2/accounts/{0}/balances"; private final static String METHOD_POST_SIGN = "sign"; private final static String METHOD_POST_SUBMIT = "submit";
public String getAddress(){ return address; } /** * 傳送交易 * @param address * @param value * @return */ public String send(String toAddress,double value){ String txBlob = this.sign(toAddress, value); if (StringUtils.isEmpty(txBlob)) { log.error("簽名失敗:{}",toAddress); return null; } HashMap<String, Object> params = new HashMap<String, Object>(); params.put("tx_blob", txBlob); //簽名 JSONObject json = doRequest(METHOD_POST_SUBMIT, params); if (!isError(json)) { JSONObject result = json.getJSONObject(RESULT); if (result != null) { if (TES_SUCCESS.equals(result.getString("engine_result"))) { String hash = result.getJSONObject("tx_json").getString("hash"); if (!StringUtils.isEmpty(hash)) { log.info("轉賬成功:toAddress:{},value:{},hash:{}",toAddress,value,hash); return hash; }else { log.error("轉賬失敗:toAddress:{},value:{},hash:{}",toAddress,value,hash); } } } } return null; }
/** * 簽名 * @param address * @param value * @return tx_blob */ public String sign(String toAddress,Double value){ //瑞波幣餘額儲存加六位長度 value = BigDecimalUtil.mul(value, 1000000); Integer vInteger = BigDecimal.valueOf(value).intValue(); JSONObject txJson = new JSONObject(); txJson.put("Account", address); txJson.put("Amount", vInteger.toString()); txJson.put("Destination", toAddress); txJson.put("TransactionType", "Payment"); txJson.put("DestinationTag", "1"); HashMap<String, Object> params = new HashMap<String, Object>(); params.put("secret", password); params.put("tx_json", txJson); params.put("offline", false); //簽名 JSONObject json = doRequest(METHOD_POST_SIGN, params); if (!isError(json)) { JSONObject result = json.getJSONObject(RESULT); if (result != null) { if (SUCCESS.equals(result.getString("status"))) { return result.getString("tx_blob"); } } } return null; } /** * XRP查詢餘額 * @return */ public double getBalance(){ HashMap<String, String> params = new HashMap<String, String>(); params.put("currency", CoinConstant.COIN_XRP); String re = HttpUtil.jsonGet(getUrl + MessageFormat.format(METHOD_GET_BALANCE, address), params); log.info("獲取XRP餘額:{}",re); if(!StringUtils.isEmpty(re)){ JSONObject json = JSON.parseObject(re); if (SUCCESS.equals(json.getString(RESULT))) { JSONArray array = json.getJSONArray("balances"); if (array != null && array.size() > 0) { //總餘額 double balance = array.getJSONObject(0).getDoubleValue("value"); if (balance >= 20) { //可用餘額 xrp會凍結20個幣 return BigDecimalUtil.sub(balance, 20); } } } } return 0.00; } public Long parseTransaction(String startTm) { HashMap<String, String> params = new HashMap<String, String>(); if (!StringUtils.isEmpty(startTm)) { //記錄時間格式目前XRP精確到秒 Date d = new Date(BigDecimalUtil.longAdd(Long.parseLong(startTm), 1000L)); params.put("start", DateUtil.dateToString(d, "yyyy-MM-dd'T'HH:mm:ss")); } params.put("result", "tesSUCCESS"); params.put("type", "Payment"); params.put("limit", "100"); String re = HttpUtil.jsonGet(getUrl + MessageFormat.format(METHOD_GET_TRANSACTION, address), params); if(!StringUtils.isEmpty(re)){ JSONObject json = JSON.parseObject(re); if (SUCCESS.equals(json.getString(RESULT))) { // marker = json.getString("marker"); JSONArray transactions = json.getJSONArray("transactions"); if (transactions != null && transactions.size() > 0) { for (Object object : transactions) { JSONObject transaction = (JSONObject)object; String hash = transaction.getString("hash"); String dateString = transaction.getString("date"); Date date =DateUtil.getStringToDate(dateString,"yyyy-MM-dd'T'HH:mm:ss"); JSONObject tx = transaction.getJSONObject("tx"); String destinationTag = tx.getString("DestinationTag"); if (StringUtils.isEmpty(destinationTag)) { log.info("非使用者充值記錄"); return date.getTime(); } String to = tx.getString("Destination"); if (!address.equals(to)) { log.info("非使用者充值記錄,地址不一致"); return date.getTime(); } //根據tag查詢使用者ID /*UserEntity user = userService.getUserById(Integer.parseInt(destinationTag)); if (user == null) { log.info("使用者不存在:{}",destinationTag); return date.getTime(); }*/ double amount = tx.getDoubleValue("Amount"); if (amount > 0 ) { amount = BigDecimalUtil.div(amount, 1000000, 6); }else { log.error("交易金額異常:{}",amount); return date.getTime(); } //UserCoinRecordEntity record = new UserCoinRecordEntity(); /*record.setCoinType(CoinConstant.COIN_XRP); record.setAddress(to+"_"+destinationTag); record.setTxid(hash); record.setUserId(user.getUserId());*/ try { //rechargeParse(record); return date.getTime(); }catch (DuplicateKeyException e){ log.error("eos hash:{} userid:{} coin:{} 這個區塊已經處理了",hash,"XRP"); return null; } } } } } return null; }
private boolean isError(JSONObject json){ if( json == null || (!StringUtils.isEmpty(json.getString("error")) && json.get("error") != "null")){ return true; } return false; } private JSONObject doRequest(String method,Object... params){ JSONObject param = new JSONObject(); param.put("id",System.currentTimeMillis()+""); param.put("jsonrpc","2.0"); param.put("method",method); if(params != null){ param.put("params",params); } String creb = Base64.encodeBase64String((address+":"+password).getBytes()); Map<String,String> headers = new HashMap<>(2); headers.put("Authorization","Basic "+creb); String resp = ""; try{ resp = HttpUtil.jsonPost(postUrl,headers,param.toJSONString()); }catch (Exception e){ log.info(e.getMessage()); if (e instanceof IOException){ resp = "{}"; } } log.info(resp); return JSON.parseObject(resp); }
}
處理使用者充值:定時任務掃描
/** * XRP處理 */ private void xrpJob(){ //獲取資料庫儲存的記錄處理進度(日期) String startTm = coinParseService.getBlockHeight(CoinConstant.COIN_XRP); log.info("XRP當前處理進度:{}",startTm); Long start = coinXrpService.parseTransaction(startTm); if (!StringUtils.isEmpty(start)) { log.info("xrp執行完畢"); coinParseService.updateBlockRecord(CoinConstant.COIN_XRP, start.toString()); } }