今天我们来说说微信网页扫码支付,这个支付的步骤和微信公众号网页是差不多的,也和微信小程序的步骤是一致的,不过appid是微信公众号的,我自己收集的微信开发文档希望对大家有用:
里面有扫码支付的api文档
或者直接查看 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 api文档
我们观看了扫码支付的场景后,就直接进入到开发步骤中这里面大家一定要填正确不然是不会成功的
这里我用的是模式二,模式二就和前面我们说的微信网页支付和小程序支付是一样的代码,只不过我们调起支付不是通过微信给我们的方法,而是下单成功时,微信会给我们返回一个支付的二微码链接,我们把这个链接放到页面中就可以去进行支付了 不过我们扫码直接要注意一点的是在下单的接口中我们的交易类型要改变:
交易类型改成NATIVE就可以了,这样我们就可以不用去填openid,如果交易类型是JSAPI那么就必须要填写,不然我们无法下单成功,更加不可能进行支付了,下面就是代码实现了
WeChatIDconfig.java
* 微信公众号的appid(在微信公众号(服务号)后台获取)
*/
public static final String Appid ="xxxxxxx";
/**
* 微信服务号开通的商户号:
*/
public static final String MCH_ID="xxxxxx";
/**
* 商户秘钥:
*/
public static final String KEY="xxxxxxxxxxxx";
// APP和网页支付提交用户端ip, Native支付填调用微信支付API的机器IP, 即:服务器ip地址
public static final String SPBILL_CREATE_IP = "127.0.0.1";
// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。(需要配置)
public static final String NOTIFY_URL = "http://xxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxx/getAccount.do";
// 支付方式,取值如下:JSAPI,NATIVE,APP
public static final String TRADE_TYPE = "NATIVE";
// 微信支付 - 统一下单地址接口
public static final String PLACEANORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
OrderController.java(统一下单的控制层)
* <p>User: qrn
* <p>Date: 2018/8/9 9:25
* <p>Version: 1.0
* 描述: 产品下单:
*/
@Controller
@RequestMapping("/alipay")
public class OrdersController {
/**
* 订单:
*/
@Autowired(required = true)
OrdersService ordersService;
/**
* 微信扫码支付 调用微信api统一下单接口:
*/
@RequestMapping("/createPreOrder")
public String createPreOrder(Model model)throws Exception{
//下单的商品名称
String body ="辣条"
//生成订单号的工具类
Sid sid= new Sid();
// 商户订单号
String out_trade_no = sid.nextShort();
// 订单总金额,单位为分
String total_fee = "2";
// 统一下单
PreOrderResult preOrderResult = ordersService.ordersddd(body, out_trade_no, total_fee);
//下单成功 获取到 code_url放到页面中进行展示二微码进行支付
model.addAttribute("qrCodeUrl",preOrderResult.getCode_url());
return "orders/payQrCode";
}
}
OrderServiceImpl(业务实现类):
* 微信支付:
* @body 商品名称
* @out_trade_no 订单号
* @total_fee 下单的金额
* @return
*/
@Override
public PreOrderResult ordersddd(String body, String out_trade_no, String total_fee) throws Exception{
PreOrder o = new PreOrder();
// 生成随机字符串
String nonce_str = UUID.randomUUID().toString().trim().replaceAll("-", "");
o.setAppid(WeChatIDconfig.Appid);
o.setBody(body);
o.setMch_id(WeChatIDconfig.MCH_ID);
o.setNotify_url(WeChatIDconfig.NOTIFY_URL);
o.setOut_trade_no(out_trade_no);
// 判断有没有输入订单总金额,没有输入默认1分钱
if (total_fee != null && !total_fee.equals("")) {
o.setTotal_fee(Integer.parseInt(total_fee));
} else {
o.setTotal_fee(1);
}
o.setNonce_str(nonce_str);
o.setTrade_type(WeChatIDconfig.TRADE_TYPE);
o.setSpbill_create_ip(WeChatIDconfig.SPBILL_CREATE_IP);
SortedMap<Object, Object> p = new TreeMap<Object, Object>();
p.put("appid", WeChatIDconfig.Appid);
p.put("mch_id", WeChatIDconfig.MCH_ID);
p.put("body", body);
p.put("nonce_str", nonce_str);
p.put("out_trade_no", out_trade_no);
p.put("total_fee", total_fee);
p.put("spbill_create_ip", WeChatIDconfig.SPBILL_CREATE_IP);
p.put("notify_url", WeChatIDconfig.NOTIFY_URL);
p.put("trade_type", WeChatIDconfig.TRADE_TYPE);
// 获得签名 WeChatIDconfig.KEY 商户的支付秘钥
String sign = Sign.createSign("utf-8", p, WeChatIDconfig.KEY);
o.setSign(sign);
// Object转换为XML
String xml = XmlUtil.object2Xml(o, PreOrder.class);
// 统一下单地址
String url = WeChatIDconfig.PLACEANORDER_URL;
// 调用微信统一下单地址
String returnXml = HttpUtil.sendPost(url, xml);
// XML转换为Object
PreOrderResult preOrderResult = (PreOrderResult) XmlUtil.xml2Object(returnXml, PreOrderResult.class);
return preOrderResult;
}
PreOrder.java (实体类):
* 统一下单
* @author KOU
*
*/
public class PreOrder {
private String appid;// 公众账号ID
private String mch_id;// 商户号
private String nonce_str;// 随机字符串
private String sign;// 签名
private String body;// 商品描述
private String out_trade_no;// 商户订单号
private int total_fee;// 订单总金额,单位为分
private String spbill_create_ip;// APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
private String notify_url;// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
private String trade_type;// 取值如下:JSAPI,NATIVE,APP
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public int getTotal_fee() {
return total_fee;
}
public void setTotal_fee(int total_fee) {
this.total_fee = total_fee;
}
public String getSpbill_create_ip() {
return spbill_create_ip;
}
public void setSpbill_create_ip(String spbill_create_ip) {
this.spbill_create_ip = spbill_create_ip;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
}
Sign.java(工具类生产sign签名):
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
public class Sign {
/**
* 微信支付签名算法sign
*
* @param characterEncoding
* @param parameters
* @return
*/
@SuppressWarnings("unchecked")
public static String createSign(String characterEncoding,
SortedMap<Object, Object> parameters, String key) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();// 所有参与传参的参数按照accsii排序(升序)
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k)
&& !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + key);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding)
.toUpperCase();
return sign;
}
}
XmlUtil.java(工具类Object转换为XML):
import java.io.InputStream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
public class XmlUtil {
// private static XStream xstream;
// static {
// xstream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
// }
/**
*
* @Description: xml字符串转换为对象
* @param inputXml
* @param type
* @return
* @throws Exception
*
* @author leechenxiang
* @date 2017年8月31日 下午4:52:13
*/
public static Object xml2Object(String inputXml, Class<?> type) throws Exception {
if (null == inputXml || "".equals(inputXml)) {
return null;
}
XStream xstream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
xstream.alias("xml", type);
return xstream.fromXML(inputXml);
}
/**
*
* @Description: 从inputStream中读取对象
* @param inputStream
* @param type
* @return
* @throws Exception
*
* @author leechenxiang
* @date 2017年8月31日 下午4:52:29
*/
public static Object xml2Object(InputStream inputStream, Class<?> type) throws Exception {
if (null == inputStream) {
return null;
}
XStream xstream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
xstream.alias("xml", type);
return xstream.fromXML(inputStream, type);
}
/**
*
* @Description: 对象转换为xml字符串
* @param ro
* @param types
* @return
* @throws Exception
*
* @author leechenxiang
* @date 2017年8月31日 下午4:52:45
*/
public static String object2Xml(Object ro, Class<?> types) throws Exception {
if (null == ro) {
return null;
}
XStream xstream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
xstream.alias("xml", types);
return xstream.toXML(ro);
}
public static void main(String[] args) throws Exception {
// String xml = "<xml><appid><![CDATA[wx27ba4edf360825f0]]></appid>\r\n" +
// "<bank_type><![CDATA[CFT]]></bank_type>\r\n" +
// "<cash_fee><![CDATA[1]]></cash_fee>\r\n" +
// "<fee_type><![CDATA[CNY]]></fee_type>\r\n" +
// "<is_subscribe><![CDATA[Y]]></is_subscribe>\r\n" +
// "<mch_id><![CDATA[1488079552]]></mch_id>\r\n" +
// "<nonce_str><![CDATA[bedd615ea8bf403c9878a47619a37db2]]></nonce_str>\r\n" +
// "<openid><![CDATA[obzI8wbPdqk46snp2u-Vbr4mtIy4]]></openid>\r\n" +
// "<out_trade_no><![CDATA[170831BN11MFTKGC]]></out_trade_no>\r\n" +
// "<result_code><![CDATA[SUCCESS]]></result_code>\r\n" +
// "<return_code><![CDATA[SUCCESS]]></return_code>\r\n" +
// "<sign><![CDATA[A5391D28AB0A7AAD2A04CA025ECFC9F8]]></sign>\r\n" +
// "<time_end><![CDATA[20170831162215]]></time_end>\r\n" +
// "<total_fee>1</total_fee>\r\n" +
// "<trade_type><![CDATA[NATIVE]]></trade_type>\r\n" +
// "<transaction_id><![CDATA[4009492001201708319326473865]]></transaction_id>\r\n" +
// "</xml>";
//
// xml = "<xml><appid><![CDATA[wx27ba4edf360825f0]]></appid><bank_type><![CDATA[CCB_DEBIT]]></bank_type><cash_fee><![CDATA[2]]></cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[1488079552]]></mch_id><nonce_str><![CDATA[b137e6dc58ca41f9a03ececef3737dd9]]></nonce_str><openid><![CDATA[obzI8wbPdqk46snp2u-Vbr4mtIy4]]></openid><out_trade_no><![CDATA[170831BXB06KDWX4]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[8556332D8BA236995B9F001A29F3527B]]></sign><time_end><![CDATA[20170831164115]]></time_end><total_fee>2</total_fee><trade_type><![CDATA[NATIVE]]></trade_type><transaction_id><![CDATA[4009492001201708319321871970]]></transaction_id></xml>";
String xml = "<xml><appid><![CDATA[wx27ba4edf360825f0]]></appid><bank_type><![CDATA[CFT]]></bank_type><cash_fee><![CDATA[1]]></cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[1488079552]]></mch_id><nonce_str><![CDATA[6c13599fcd4543f1a6e89ca039d413bd]]></nonce_str><openid><![CDATA[obzI8wbPdqk46snp2u-Vbr4mtIy4]]></openid><out_trade_no><![CDATA[170831CC85T0204H]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[75E1E8C59A0D9E518F35F9B68ECAA4EF]]></sign><time_end><![CDATA[20170831172556]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[NATIVE]]></trade_type><transaction_id><![CDATA[4009492001201708319329835501]]></transaction_id></xml>";
PayResult pr = (PayResult)XmlUtil.xml2Object(xml, PayResult.class);
System.out.println(pr.toString());
}
}
PreOrderResult.java(微信支付 - 统一下单返回结果的封装实体类):
/**
*
* @Title: PreOrderResult.java
* @Package com.sihai.wx.entity
* @Description: 微信支付 - 统一下单返回结果的封装entity
* Copyright: Copyright (c) 2016
* Company:FURUIBOKE.SCIENCE.AND.TECHNOLOGY
*
* @author leechenxiang
* @date 2017年8月31日 上午10:39:14
* @version V1.0
*/
public class PreOrderResult {
private String return_code; // 返回状态码
private String return_msg; // 返回信息
private String appid; // 公众账号ID
private String mch_id; // 商户号
private String device_info; // 设备号
private String nonce_str; // 随机字符串
private String sign; // 签名
private String result_code; // 业务结果
private String err_code; // 错误代码
private String err_code_des; // 错误代码描述
private String trade_type; // 交易类型
private String prepay_id; // 预支付交易会话标识
private String code_url; // 二维码链接
public String getReturn_code() {
return return_code;
}
public void setReturn_code(String return_code) {
this.return_code = return_code;
}
public String getReturn_msg() {
return return_msg;
}
public void setReturn_msg(String return_msg) {
this.return_msg = return_msg;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getResult_code() {
return result_code;
}
public void setResult_code(String result_code) {
this.result_code = result_code;
}
public String getErr_code() {
return err_code;
}
public void setErr_code(String err_code) {
this.err_code = err_code;
}
public String getErr_code_des() {
return err_code_des;
}
public void setErr_code_des(String err_code_des) {
this.err_code_des = err_code_des;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getPrepay_id() {
return prepay_id;
}
public void setPrepay_id(String prepay_id) {
this.prepay_id = prepay_id;
}
public String getCode_url() {
return code_url;
}
public void setCode_url(String code_url) {
this.code_url = code_url;
}
@Override
public String toString() {
return "OrderReturn [return_code=" + return_code + ", return_msg="
+ return_msg + ", appid=" + appid + ", mch_id=" + mch_id
+ ", device_info=" + device_info + ", nonce_str=" + nonce_str
+ ", sign=" + sign + ", result_code=" + result_code
+ ", err_code=" + err_code + ", err_code_des=" + err_code_des
+ ", trade_type=" + trade_type + ", prepay_id=" + prepay_id
+ ", code_url=" + code_url + "]";
}
}
HttpUtil.java(工具类,发送请求):
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;
/**
*
* @Title: HttpUtil.java
* @Package com.sihai.wx.util
* @Description: HTTP
* Copyright: Copyright (c) 2016
* Company:FURUIBOKE.SCIENCE.AND.TECHNOLOGY
*
* @author leechenxiang
* @date 2017年8月31日 下午2:58:13
* @version V1.0
*/
public class HttpUtil {
/**
* POST请求
*
* @param url
* @param outStr
* @return
*/
public static String doPostStr(String url, String outStr) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(outStr);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
System.out.println(result);
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输出流、输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
Writer out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new BufferedWriter(new OutputStreamWriter(
conn.getOutputStream(),"UTF-8"));
// 发送请求参数
out.write(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输出流、输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}
现在我们统一下单成功了,并且我们拿到返回的二微信的链接,现在我们要把他在页面中展示进行支付,首先我们要使用jquery中的二微码生成插件 jquery-qrcode 生成二微码进行支付
前端页面代码:
payQrCode.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>微信支付</title>
</head>
<body>
<input type="text" id="qrCodeUrl" value="${qrCodeUrl }"/>
<div id="code"></div>
<script src="<%=request.getContextPath() %>/static/js/jquery.min.js?v=1.0.1" type="text/javascript"></script>
<script src="<%=request.getContextPath() %>/static/js/jquery-qrcode/jquery.qrcode.min.js?v=1.0.1" type="text/javascript"></script>
<script type="text/javascript">
$('#code').qrcode($("#qrCodeUrl").val());
// 如果url中含有中文,使用本方法
function toUtf8(str) {
var out, i, len, c;
out = "";
len = str.length;
for(i = 0; i < len; i++) {
c = str.charCodeAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
out += str.charAt(i);
} else if (c > 0x07FF) {
out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
} else {
out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
}
}
return out;
}
// $('#code').qrcode(toUtf8("我还会回来的!"));
// 查询是否支付成功
function checkPayResult() {
$.get("/wxpay/wxPayIsSuccess.action", function(data) {
// debugger;
console.log(data);
if (data) {
window.location.href = "/wxpay/paySuccess.action";
}
});
}
$(function() {
// 每个3秒调用后台方法,查看订单是否已经支付成功
window.setInterval("checkPayResult()", 3000);
});
</script>
</body>
</html>
好了,不出意外微信的二微码支付就成功了,如果有问题可以在下发评论有时间会及时回复