*** 贴完整代码+掰开了揉碎了讲解代码*** #0.整体过程 网上找了很多资料,都很难找到接入比特币支付的教程,只能看英文原版+youtube 上的唯一一个视频教程。 终于调通了,无私分享给大家。 比特币支付的接入,让用户方便的打钱,并在数秒内得到反馈说他已经支付成功,以及支付了多少钱。作为网站主人的我们能够被通知收到了钱。(完全程序化,不是人工去查看);在数小时内,blockchain 会把钱打给我们的账户(根据你指定的 xpub 地址) 所需要实现的部分有两个:1 获得 blockchain 提供的比特币地址(不是我们自己的) 2 回调函数。blockchain 通知我们的时候调用(比如我的是 https://4b9xxxx3.ngrok.io/shop/order/btcCallback.html?invoiceId=500&secret=xxxxxxx3CfA5EcG6j&transaction_hash=xxxxx%20&address=1CmxxxxxxxQgkwxxxxXJgxQEv64&confirmations=5&value=0.0005 )
public class HttpClientUtil {
private final static String USER_AGENT = "Mozilla/5.0";
public static void main(String[] args) throws Exception {
}
public static String getBlockChainAddress(Order order) throws Exception {
JSONObject jo = http(getReceiveUrl( order));
return jo.getString("address");
}
static String getReceiveUrl(Order order) throws UnsupportedEncodingException {
StringBuffer urlStringBuffer = new StringBuffer();
final String secret = Constants.BTC_SECRET;//秘密
String url = Constants.BTC_RECEIVE_URL;
String apiKey = Constants.BTC_API_KEY;
String xpub = Constants.BTC_XPUB;
String callback = Constants.BTC_CALLBACKURL + "?invoiceId="+order.getId()+"&secret=" + secret;
String callbackEncode = URLEncoder.encode(callback, "utf-8");
urlStringBuffer.append(url).append("?key=").append(apiKey)
.append("&xpub=").append(xpub).append("&callback=").append(callbackEncode)
.append("&gap_limit=").append(Constants.BTC_GAP_NUM);
return urlStringBuffer.toString();
}
public static JSONObject http(String url) throws Exception {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url);
//添加请求头
request.addHeader("User-Agent", USER_AGENT);
HttpResponse response = client.execute(request);
// System.out.println("Response Code : " +
// response.getStatusLine().getStatusCode());
if (!"200".equals("" + response.getStatusLine().getStatusCode())) {
//System.out.println("没有取到对方的比特币地址");
return null;
}
BufferedReader rd = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
//变成 json 格式,方便取值
//System.out.println(result.toString());
JSONObject jo = new JSONObject(result.toString());
return jo;
}
}
String blockChainAddress = HttpClientUtil.getBlockChainAddress(order);
//处理支付地址
order.setPayto(blockChainAddress);
orderService.saveOrUpdate(order);
<%--支付相关 js s--%>
<script>
var btcs = new WebSocket('wss://ws.blockchain.info/inv');
// var payTO = '1J9ikqFuwrzPbczsDkquA9uVYeq6dEehsj';
var payTO = '${order.payto}';
btcs.onopen = function()
{
btcs.send( JSON.stringify( {"op":"addr_sub", "addr":payTO} ) );
};
btcs.onmessage = function(onmsg)
{
var response = JSON.parse(onmsg.data);
var getOuts = response.x.out;
var countOuts = getOuts.length;
for(i = 0; i < countOuts; i++)
{
//check every output to see if it matches specified address
var outAdd = response.x.out[i].addr;
var specAdd = payTO;
if (outAdd == specAdd )
{
var amount = response.x.out[i].value;
var calAmount = amount / 100000000;
$('#messages').prepend("Received " + calAmount + " BTC");
var snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=");
snd.play();
}
}
}
</script>
<%--支付相关 js e--%>
<%--支付--%>
<div id="viewCart">
<span id="viewTitle">支付</span><br>
<div id="payAmt">
<font class="total-box-price"><c:out value="${order.total.value}" /></font> BTC
<br>
<img src="http://chart.googleapis.com/chart?chs=125x125&cht=qr&chl=${order.payto}" width="50%">
<br>
<input type="text" id="payBox" style="width:120px;" value="${order.payto}" onclick="this.select();" readonly>
<br><div id="messages"></div>
</div>
</div>
#4.回调函数。 blockchain 收到钱之后,会执行我们的这个函数,里面处理我们自己的业务逻辑:更新订单状态为已付款等
@RequestMapping("/btcCallback.html")
@ResponseBody
public String btcCallback4Blockchain(Model model, HttpServletRequest request) throws Exception {
String result = Constants.BTC_SUCCESS;
String invoiceId =(String) request.getParameter("invoiceId");
String secret =(String) request.getParameter("secret");
String transaction_hash =(String) request.getParameter("transaction_hash");//比特币交易 hash 暂无用
String address =(String) request.getParameter("address");//正常应该跟用户打钱的地址一样,blockchain 提供
String confirmations =(String) request.getParameter("confirmations");//确认的节点数量
String value =(String) request.getParameter("value");
int confirmationInt = 0;
if(!Constants.BTC_SECRET.equals(secret)){
LOGGER.error("secret 不对");
return "error";
}
// if(!(Constants.BTC_XPUB.indexOf(address)>-1)){
// LOGGER.error("address 不是我们的地址");
// return "error";
// }
Long orderId = null;
try{
orderId = Long.valueOf(invoiceId);
confirmationInt = Integer.valueOf(confirmations).intValue();
}catch(Exception e){
LOGGER.error("转换失败:invoiceId="+invoiceId+" | confirmations= "+confirmations);
return "error";
}
if(confirmationInt<Constants.BTC_CONFIRM){
return "notfinish";
}
//get the order
Order order = orderService.getById(orderId);
if(order == null){
LOGGER.error("找不到订单 order");
return "error";
}
if((order.getTotal().doubleValue()*100000000)>(Double.valueOf(value))){
LOGGER.error("转账金额不对:"+value+"----"+order.getTotal().doubleValue());
return "error";
}
order.setStatus(OrderStatus.PROCESSED);
order.setBtcAddress(address);
order.setBtcTransactionHash(transaction_hash);
order.setBtcValue(value);//传递过来的单位是聪。 除以 10000 0000 得 BTC
orderService.saveOrUpdate(order);
return result;
}
我自己的真实数据已被修改,使用的话用你自己的
public Interface Constants {
public final static String BTC_RECEIVE_URL = "https://api.blockchain.info/v2/receive";
public final static String BTC_BALANCE_URL = "https://api.blockchain.info/v2/receive/balance_update";
public final static String BTC_CALLBACKURL = "https://4b9xxxxxx.ngrok.io/shop/order/btcCallback.html";
public final static String BTC_GAP_NUM = "50";
public final static String BTC_SECRET= "ZzsMxxxxxxxxEcG6j";
public final static String BTC_API_KEY= "b0bdf0xxxxxxx-xxxx-xxa06-939c-b8cc2880af99";
public final static String BTC_XPUB= "xpubxxxxxxxxxxxxxxxxxxxxxp5xuuuXcWY7VH27uwW9xxxxxFFnJpUXMbo79QA5tWobDa5Aymoo6X9gMvYRqmrpb2CZvje";
public final static int BTC_CONFIRM= 5;//确认数量,一般超过 5 就确定收到钱了
public final static String BTC_SUCCESS= "*ok*";//成功标志必须是这个,否则 bl 不知道成功了,一直 10 秒一次调用我们的回调方法,服务器会压力增大
}
#6.成功!~
#7.总结及注意及赞助 7.1 比特币不是中本聪创造的,而是上帝,中本聪只是发现者而已。它是人类历史上的一个重要节点。 7.2 测试的最小金额为 0.0005BTC,否则就会白打钱收不到。 7.3 如果你一直调用获取比特币地址却不支付,会在达到 20 次的时候不会再返回给你地址。 解决办法,打钱啊,或者把 gap_limit 参数设为大一点(我给了 50 ) 如果你测试的话,欢迎往我的账户打(***哈***),就当赞助支持这篇干货文章了(我会告诉你研究了 1 个半月?)。 我的比特币地址:1CmJdau9hmvHQgkw5ZhTq1zgTXJgxQEv64 http://chart.googleapis.com/chart?chs=125x125&cht=qr&chl=1CmJdau9hmvHQgkw5ZhTq1zgTXJgxQEv64
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.