Native支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
扫码支付分两种模式
【模式一】:商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。模式一适用于线下单品支付
【模式二】:商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。模式二适用于线上支付
以下介绍常用的线上扫码支付,即模式二
先看下业务流程吧:
业务流程说明:
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况(回调地址在notify_url设置),通知微信后台系统不再发送该单的支付通知。用户付款后需要向商户后台查询数据,如果已支付,再把付款信息,产品信息存放到数据库
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(12)商户确认订单已支付后给用户发货。
主要代码:
public class Pay {
// 公众账号id
private String appid;
// 商户号
private String mch_id;
// 商品描述
private String body;
// 随机字符串
private String nonce_str;
// 签名
private String sign;
// 商户订单号
private String out_trade_no;
// 标价金额(单位为分)
private int total_fee;
// 终端IP
private String spbill_create_ip;
// 通知地址(用户付款完跳转的地址,该页面包括用户付款信息,结果等),前提要在商户平台设置合法域名,否则无法通知
private String notify_url;
// 交易类型
private String trade_type;
// 用户标识(trade_type=JSAPI时(即JSAPI支付),此参数必传)
private String openid;
// 商品ID(trade_type=NATIVE时,此参数必传)
private String product_id;
}
主程序:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zz.config.Parameter;
import com.zz.entity.Pay;
import com.zz.util.HttpsUtil;
import com.zz.util.PayUtil;
import com.zz.util.QRCodeUtil;
import com.zz.util.Sign;
import com.zz.util.XStreamUtil;
import com.zz.util.XmlUtil;
@RestController
@RequestMapping("pay")
public class PayController {
/**
* NATIVE支付:生成的二维码链接有效期为两个小时,可以提醒用户订单在两个小时内付款
* @return
*/
@RequestMapping("nativePay")
public static Map<String,Object> getNativePay(){
int total_fee=62;
String product_id=PayUtil.getRandomString(15);
String nonce_str=PayUtil.getRandomString(20);
String out_trade_no=PayUtil.getCurrentTime()+PayUtil.getRandomString(5);
SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
parameters.put("appid", Parameter.appID);
parameters.put("mch_id", Parameter.mch_id);
parameters.put("nonce_str", nonce_str);
parameters.put("body", Parameter.body);
parameters.put("out_trade_no", out_trade_no);
parameters.put("total_fee", total_fee);
parameters.put("spbill_create_ip", Parameter.spbill_create_ip);
parameters.put("notify_url", Parameter.notify_url);
parameters.put("trade_type", "NATIVE");
parameters.put("product_id", product_id);
String sign=Sign.sign(parameters);
Pay pay=new Pay();
pay.setAppid(Parameter.appID);
pay.setMch_id(Parameter.mch_id);
pay.setNonce_str(nonce_str);
pay.setBody(Parameter.body);
pay.setOut_trade_no(out_trade_no);
pay.setTotal_fee(total_fee);
pay.setSpbill_create_ip(Parameter.spbill_create_ip);
pay.setNotify_url(Parameter.notify_url);
pay.setTrade_type("NATIVE");
pay.setProduct_id(product_id);
pay.setSign(sign);
XStreamUtil.xstream.alias("xml", Pay.class);
String reqXml=XStreamUtil.xstream.toXML(pay);
reqXml=reqXml.replaceAll("__", "_");
String code_url=null;
try {
String resXml=HttpsUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", reqXml);
//此时的code_url可以直接支付的,可以在前端按钮链接里设置href=code_url,类似与h5调起支付,然后对结果是否支付成功处理。
code_url=XmlUtil.parseXml(resXml, "code_url");
System.out.println(code_url);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//展示使用 JavaScript 生成二维码链接参考:https://www.runoob.com/w3cnote/javascript-qrcodejs-library.html
//下面通过生成base格式的二维码图片返回到前端,前端通过以下便签便可显示二维码
// <img src="https://img-blog.csdnimg.cn/2022010618365355506.jpg" class="images" border="5px"/>
QRCodeUtil qr_codeutil=new QRCodeUtil();
String qr_code= qr_codeutil.getImage(code_url);
Map<String,Object> map=new HashMap<String,Object>();
map.put("qr_code", qr_code);
return map;
}
/**
* 扫码支付回调
*
* @param request
* @return
* @throws NumberFormatException
* @throws Exception
*/
@RequestMapping("dealwith")
public String dealwith(HttpServletRequest request) throws NumberFormatException, Exception {
//获取回调信息
InputStream inputStream=null;
inputStream=request.getInputStream();
StringBuffer sb=new StringBuffer();
BufferedReader in=new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
String s;
while(null!=(s=in.readLine())){
sb.append(s);
}
in.close();
inputStream.close();
String resXml=sb.toString();
System.out.println("------------:"+resXml);
// 解析xml成map
Map<String,String> map = new HashMap<>();
map = XmlUtil.parseAllXml(resXml);
//设置 TreeMap
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
packageParams.putAll(map);
// 判断签名是否正确
Boolean isSign=Sign.isSign(packageParams);
if(isSign){
// ------------------------------
// 处理业务开始
// ------------------------------
if ("SUCCESS".equals(XmlUtil.parseXml(resXml, "result_code"))) {
System.out.println("支付成功");
//顾客具体信息,地址存入数据库,提供发货,通知用户下单成功
//向工作人员发送支付成功消息等(消息回复,模板消息)
// 执行自己的业务逻辑
System.out.println("验签成功");
String orderId = (String) packageParams.get("out_trade_no");
String transactionId = (String) packageParams.get("transaction_id");
if (orderId != null) {
// ReturnBody body = orderService.pay(orderId, transactionId, "微信支付");
}
// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
System.out.println("支付失败,错误信息:" + packageParams.get("err_code"));
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
// ------------------------------
// 处理业务完毕
// ------------------------------
return resXml;
} else {
System.out.println("通知签名验证失败");
}
return "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>"+"</xml>";
}
public static void main(String[] args) {
getNativePay();
}
}
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript" src="js/nativePay.js"></script>
</head>
<body id="qrcode">
<h1>啥也不是</h1>
<!-- <img src="https://img-blog.csdnimg.cn/2022010618365380128.jpg" class="images" border="5px"/> -->
</body>
</html>
js
$(document).ready(function(){
// Native支付
$.getJSON("pay/nativePay/",function(json){
console.log(json.qr_code);
$("#qrcode").append("<img src='https://img-blog.csdnimg.cn/2022010618365380128.jpg"+json.qr_code+"' class='images' border='5px'/>")
})
})