您的位置:首页 > 运维架构 > 网站架构

java实现网站微信扫码支付

2018-07-18 08:36 811 查看

一、网站微信扫码支付开发并没有现成的java示例,总结一下自己微信扫码支付心得

二、首先去微信公众平台申请账户

https://mp.weixin.qq.com
**

三、账户开通、开发者认证之后就可以进行微信支付开发了

1、微信统一下单接口调用获取预支付id,以及生成二维码所需的codeUrl

/**
* 保存订单,并生成二维码所需的codeUrl
*
* @param request
* @param response
* @param notifyURLBuf
* @param order
* @return
* @throws Exception
*/
@Override
public Map<String, String> getWechatOrderInfo(String ip, Cuser user, String notifyUrl, Order order) throws Exception {
Map<String, String> resultMap = new HashMap<String, String>();
// 生成并保存订单
order.setUserId(user.getId());
// 支付方式 0:银联 1:支付宝 2:网上银行 3:微信 4:其他
order.setPayType("3");
// 生成订单号
order.setOrderNo(OrderNoGenerator.getOrderNo());
// 订单类型 1:消费 2:退款
order.setOrderType("1");
// 订单创建时间
order.setCreateTime(new Date());
// 订单更新时间
order.setUpdateTime(new Date());
// 订单状态 0: 交易中 1:完成 2:已取消
order.setOrderStatus("0");
// 付款状态 0:失败 1:成功 2、待付款
order.setPayStatus("2");
// 设置订单失效时间
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR, 2);
order.setExpireTime(calendar.getTime());
Integer orderId = this.balanceDao.saveOrder(order);
Map<String, String> payPreIdMap = new HashMap<>();
payPreIdMap = WechatUtil.getPayPreId(String.valueOf(orderId), "体检报告", notifyUrl, ip,
String.valueOf((order.getMoney().multiply(new BigDecimal(100)).intValue())), orderId.toString());
String prePayId = payPreIdMap.get("prepay_id");
// 更新
order.setId(orderId);
order.setPrepayId(prePayId);
order.setCodeUrl(payPreIdMap.get("code_url"));
this.balanceDao.updateOrder(order);
// return WechatUtil.QRfromGoogle(order.getCodeUrl(), 300, 0);
resultMap.put("codeUrl", order.getCodeUrl());
resultMap.put("orderId", String.valueOf(order.getId()));
return resultMap;
}

此方法返回的数据如下

<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wxaf0b*****8afbf]]></appid>
<mch_id><![CDATA[1408****02]]></mch_id>
<nonce_str><![CDATA[zf0vGvdtVycBliwB]]></nonce_str>
<sign><![CDATA[A2910F16086211153D747058063B3368]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx201701191109388037e9a12310276591827]]></prepay_id>
<trade_type><![CDATA[NATIVE]]></trade_type>
<code_url><![CDATA[weixin://wxpay/bizpayurl?pr=1UjorNX]]></code_url>
</xml>

2、服务器端接受微信支付结果通知

/**
* 保存微信通知结果
*
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
public String saveWechatNotify(String notifyInfoXml) throws Exception {
Map<String, String> noticeMap = XMLUtil.doXMLParse(notifyInfoXml);
// 这个其实是订单 的id
String outTradeNo = noticeMap.get("out_trade_no");
Order order = this.balanceDao.getOrderById(Integer.valueOf(outTradeNo));
// 如果支付通知信息不为,说明请求已经处理过,直接返回
if (StringUtil.isNotEmpty(order.getNotifyInfo())) {
return "SUCCESS";
}
String sign = noticeMap.get("sign");
noticeMap.remove("sign");
// 验签通过
if (WechatUtil.getSignVeryfy(noticeMap, sign)) {
// 通信成功此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
if ("SUCCESS".equals(noticeMap.get("return_code"))) {
// 交易成功
if ("SUCCESS".equals(noticeMap.get("result_code"))) {
// 商户订单号
// 订单更新时间
order.setUpdateTime(new Date());
// ------------------------------
// 处理业务开始
// ------------------------------
// 是否交易成功,1:成功0:失败
// 微信支付成功
order.setPayStatus("1");
// 订单状态 0: 交易中 1:完成 2:已取消
order.setOrderStatus("1");
// 保存通知信息
order.setNotifyInfo(notifyInfoXml);
this.balanceDao.updateOrder(order);
// 处理业务完毕
} else {
// 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
logger.info("查询验证签名失败或业务错误");
logger.info("retcode:" + noticeMap.get("retcode") + " retmsg:" + noticeMap.get("retmsg"));
}
return "SUCCESS";
} else {
logger.info("后台调用通信失败");
}
return "SUCCESS";
} else {
logger.info("通知签名验证失败");
}
return null;
}

3、上面代码用到的工具方法都在WechatUtil.java工具类中

package com.caifu.tencent.common;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.jdom2.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.caifu.login.utils.XMLUtil;
public class WechatUtil {
private static Logger logger = LoggerFactory.getLogger(WechatUtil.class);
public static final String TAG = "Wechat.Util";
private static final int timeout = 5000;
public static byte[] httpPost(String url, String entity) throws URISyntaxException, IOException {
if (url == null || url.length() == 0) {
logger.info(TAG, "httpPost, url is null");
return null;
}
CloseableHttpClient httpClient = HttpClients.createDefault();
URIBuilder uriBuilder = new URIBuilder(url);
HttpPost httpPost = new HttpPost(uriBuilder.build());
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build();
httpPost.setConfig(requestConfig);
// 避免汉字乱码导致请求失败,
httpPost.setEntity(new StringEntity(entity, "UTF-8"));
CloseableHttpResponse resp = null;
try {
resp = httpClient.execute(httpPost);
if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
logger.info(TAG, "httpGet fail, status code = " + resp.getStatusLine().getStatusCode());
return null;
}
return EntityUtils.toByteArray(resp.getEntity());
} catch (Exception e) {
logger.info(TAG, "httpPost exception, e = " + e.getMessage());
e.printStackTrace();
return null;
} finally {
if (httpClient != null) {
httpClient.close();
}
if (resp != null) {
resp.close();
}
}
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param params
*   需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
/**
* 根据反馈回来的信息,生成签名结果
*
* @param Params
*   通知返回来的参数数组
* @param sign
*   比对的签名结果
* @return 生成的签名结果
*/
public static boolean getSignVeryfy(Map<String, String> Params, String sign) {
// 过滤空值、sign与sign_type参数
// Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
// 获取待签名字符串
String preSignStr = createLinkString(Params);
preSignStr += "&key=" + Configure.getKey();
// 获得签名验证结果
String resultSign = MD5.MD5Encode(preSignStr).toUpperCase();
// String resultSign = MD5Util.MD5Encode(preSignStr.toString(),
// "UTF-8").toLowerCase();
if (sign.equals(resultSign)) {
return true;
} else {
return false;
}
}
/**
* 装配xml,生成请求prePayId所需参数
*
* @param params
* @return
*/
public static String toXml(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
for (int i = 0; i < params.size(); i++) {
sb.append("<" + params.get(i).getName() + ">");
sb.append(params.get(i).getValue());
sb.append("</" + params.get(i).getName() + ">");
}
sb.append("</xml>");
return sb.toString();
}
/**
* 生成签名
*/
public static String genPackageSign(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < params.size(); i++) {
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append("key=");
sb.append(Configure.getKey());
String packageSign = MD5.MD5Encode(sb.toString());
return packageSign;
}
/**
*
* @param goodOrderNo
* @param body
* @param noticeUrl
* @param ip
* @param totalFee
* @return
*/
public static String genProductArgs(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee, String productId) {
StringBuffer xml = new StringBuffer();
try {
String nonceStr = getNonceStr();
xml.append("</xml>");
List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
packageParams.add(new BasicNameValuePair("appid", Configure.getAppid()));
packageParams.add(new BasicNameValuePair("body", body));
packageParams.add(new BasicNameValuePair("mch_id", Configure.getMchid()));
packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
packageParams.add(new BasicNameValuePair("notify_url", noticeUrl));
packageParams.add(new BasicNameValuePair("out_trade_no", goodOrderNo));
packageParams.add(new BasicNameValuePair("product_id", productId));
packageParams.add(new BasicNameValuePair("spbill_create_ip", ip));
packageParams.add(new BasicNameValuePair("total_fee", totalFee));
packageParams.add(new BasicNameValuePair("trade_type", "NATIVE"));
String sign = genPackageSign(packageParams);
packageParams.add(new BasicNameValuePair("sign", sign));
String xmlstring = toXml(packageParams);
return xmlstring;
} catch (Exception e) {
logger.info("genProductArgs fail, ex = " + e.getMessage());
return null;
}
}
/**
* 生成支付签名
*
* @param params
* @return
*/
public static String genAppSign(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < params.size(); i++) {
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append("key=");
sb.append(Configure.getKey());
String appSign = MD5.MD5Encode(sb.toString()).toUpperCase();
logger.info("orion", appSign);
return appSign;
}
/**
* 生成调用微信支付所需参数
*
* @param prepayId
* @return
*/
public static Map<String, String> genPayReq(String prepayId) {
Map<String, String> resultMap = new HashMap<String, String>();
String timeStamp = getTimeStamp();
String nonceStr = getNonceStr();
List<NameValuePair> signParams = new LinkedList<NameValuePair>();
signParams.add(new BasicNameValuePair("appid", Configure.getAppid()));
signParams.add(new BasicNameValuePair("noncestr", nonceStr));
signParams.add(new BasicNameValuePair("package", "Sign=WXPay"));
signParams.add(new BasicNameValuePair("partnerid", Configure.getMchid()));
signParams.add(new BasicNameValuePair("prepayid", prepayId));
signParams.add(new BasicNameValuePair("timestamp", timeStamp));
String sign = genAppSign(signParams);
resultMap.put("appid", Configure.getAppid());
resultMap.put("noncestr", nonceStr);
resultMap.put("packageValue", "Sign=WXPay");
resultMap.put("partnerid", Configure.getMchid());
resultMap.put("prepayid", prepayId);
resultMap.put("timestamp", timeStamp);
resultMap.put("sign", sign);
return resultMap;
}
/**
* 微信支付生成预支付订单
*
* @throws IOException
* @throws JDOMException
*/
public static Map<String, String> getPayPreId(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee, String productId) throws Exception {
String paramsXml = genProductArgs(goodOrderNo, body, noticeUrl, ip, totalFee, productId);
logger.info("orion", paramsXml);
byte[] buf = WechatUtil.httpPost(Configure.UNIFIEDORDER_API, paramsXml);
String contentXml = new String(buf);
Map<String, String> resultMap = XMLUtil.doXMLParse(contentXml);
return resultMap;
}
public static String getNonceStr() {
Random random = new Random();
return MD5.MD5Encode(String.valueOf(random.nextInt(10000)));
}
public static String getTimeStamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
/**
* 生成支付二维码
* @param request
* @param response
* @param width
* @param height
* @param text 微信生成预定id时,返回的codeUrl
*/
public static void getQRcode(HttpServletRequest request, HttpServletResponse response, Integer width, Integer height, String text) {
if (width == null) {
width = 300;
}
if (height == null) {
height = 300;
}
String format = "jpg";
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
BitMatrix bitMatrix;
try {
bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);
MatrixToImageWriter.writeToStream(bitMatrix, format, response.getOutputStream());
} catch (WriterException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

生成二维码需要两jar

<!-- google zxing 二维码jar begin -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.0</version>
</dependency>
<!-- google zxing 二维码jar begin -->

4、下面是用到的配置类

package com.caifu.tencent.common;
/**
* User: rizenguo
* Date: 2014/10/29
* Time: 14:40
* 这里放置各种配置数据
*/
public class Configure {
//这个就是自己要保管好的私有Key了(切记只能放在自己的后台代码里,不能放在任何可能被看到源代码的客户端程序中)
// 每次自己Post数据给API的时候都要用这个key来对所有字段进行签名,生成的签名会放在Sign这个字段,API收到Post数据的时候也会用同样的签名算法对Post过来的数据进行签名和验证
// 收到API的返回的时候也要用这个key来对返回的数据算下签名,跟API的Sign数据进行比较,如果值不一致,有可能数据被第三方给篡改
private static String key = "A6gB0Dy4dsfdssuPCPsdfdshkSCDQcr3eXS";
private static String appSecret="7584sdfdsfe4f26fadsfsdfs56f10728a";
//微信分配的公众号ID(开通公众号之后可以获取到)
private static String appID = "wxaf0b86sdfsdf8afbf";
//微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
private static String mchID = "14012313702";
//受理模式下给子商户分配的子商户号
private static String subMchID = "";
//HTTPS证书的本地路径
private static String certLocalPath = "";
//HTTPS证书密码,默认密码等于商户号MCHID
private static String certPassword = "";
//是否使用异步线程的方式来上报API测速,默认为异步模式
private static boolean useThreadToDoReport = true;
//机器IP
private static String ip = "";
//以下是几个API的路径:
//1)被扫支付API
public static String UNIFIEDORDER_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";
public static String PAY_API = "https://api.mch.weixin.qq.com/pay/micropay";
//2)被扫支付查询API
public static String PAY_QUERY_API = "https://api.mch.weixin.qq.d/pay/orderquery";
//3)退款API
public static String REFUND_API = "https://api.mch.weixin.qq.com/secapi/pay/refund";
//4)退款查询API
public static String REFUND_QUERY_API = "https://api.mch.weixin.qq.com/pay/refundquery";
//5)撤销API
public static String REVERSE_API = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
//6)下载对账单API
public static String DOWNLOAD_BILL_API = "https://api.mch.weixin.qq.com/pay/downloadbill";
//7) 统计上报API
public static String REPORT_API = "https://api.mch.weixin.qq.com/payitil/report";
public static boolean isUseThreadToDoReport() {
return useThreadToDoReport;
}
public static void setUseThreadToDoReport(boolean useThreadToDoReport) {
Configure.useThreadToDoReport = useThreadToDoReport;
}
public static String HttpsRequestClassName = "com.tencent.common.HttpsRequest";
public static void setKey(String key) {
Configure.key = key;
}
public static void setAppID(String appID) {
Configure.appID = appID;
}
public static void setMchID(String mchID) {
Configure.mchID = mchID;
}
public static void setSubMchID(String subMchID) {
Configure.subMchID = subMchID;
}
public static void setCertLocalPath(String certLocalPath) {
Configure.certLocalPath = certLocalPath;
}
public static void setCertPassword(String certPassword) {
Configure.certPassword = certPassword;
}
public static void setIp(String ip) {
Configure.ip = ip;
}
public static String getKey(){
return key;
}
public static String getAppid(){
return appID;
}
public static String getMchid(){
return mchID;
}
public static String getSubMchid(){
return subMchID;
}
public static String getCertLocalPath(){
return certLocalPath;
}
public static String getCertPassword(){
return certPassword;
}
public static String getIP(){
return ip;
}
public static void setHttpsRequestClassName(String name){
HttpsRequestClassName = name;
}
}

在这里需要注意的配置
private static String key = “A6gB0Dy4dsfdssuPCPsdfdshkSCDQcr3eXS”;
这里的key 是登陆https://pay.weixin.qq.com/index.php/core/info (微信商户平台)设置的api_key

5、xml 解析工具类

package com.caifu.login.utils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.dom4j.io.SAXReader;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
/**
* xml工具类
*
* @author miklchen
*
*/
public class XMLUtil {
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
// 关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
*
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
/**
* 将requestxml通知结果转出啊成map
* @param request
* @return
* @throws Exception
*/
public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
// 解析结果存储在HashMap
Map<String, String> map = new HashMap<String, String>();
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
org.dom4j.Document document = reader.read(inputStream);
// 得到xml根元素
org.dom4j.Element root = document.getRootElement();
// 得到根元素的所有子节点
List<org.dom4j.Element> elementList = root.elements();
// 遍历所有子节点
for (org.dom4j.Element e : elementList)
map.put(e.getName(), e.getText());
// 释放资源
inputStream.close();
inputStream = null;
return map;
}
}

6、整个后台服务已经完成,最后关闭页面微信支付二维码,告知用户支付已经完成了

var f;
/* 定时任务方法,异步请求去查询订单是否支付*/
function GetOrder() {
var orderId = $('#orderId').val();
if (orderId != '') {
$.ajax({
url : "${base}/balance/auth/isPay?orderId=" + orderId,
type : "GET",
async : false,
success : function(d) {
if (d == "1") {
//当获取到微信支付结果时,关闭二维码div
$(".weixinpay").css("display", "none");
$("#zhichutankuang").css("display", "block");
////当获取到微信支付结果时,关闭定时任务
clearInterval(f);
// layer.alert('付款成功', {
// skin : 'layui-layer-molv', // 样式类名
// closeBtn : 0
// }, function() {
// location.href = "${base}/balance/auth/presentation?tjNo=" + $("#tjNo").val();
// });
}
}
});
}
}
//异步请求获取生成二维码的url
$(".paylast").click(function() {
var $payType = $('input:radio:checked').val();
var $money = $("#money").val();
var $tjReportType = $("#tjReportType").val();
var $tjNo = $("#tjNo").val();
$.ajax({
url : "${base}/balance/auth/wechatInfo",
type : "POST",
async : false,
data : {
payType : $payType,
money : $money,
tjNo : $tjNo,
tjReportType : $tjReportType
},
success : function(d) {
if (d.resultCode == "1000") {
//当请求成功时,设置二维码图片地址
$("#codeImg").attr('src', d.obj);
$("#orderId").val(d.attributes.orderId);
////当请求成功时,启动定时任务,每隔3秒去后台查询一次订单是否成功
f = setInterval(GetOrder, 3000);
// GetOrder(true);
}
}
});
$(".selpaycon").css("display", "none");
$(".weixinpay").css("display", "block");
});

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 微信 扫码支付