Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站、实体店单品或订单、媒体广告支付等场景。
<!-- 添加微信支付jar包 -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!-- 二维码生成依赖 -->
<!-- 二维码 -->
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>2.1</version>
</dependency>
/**
* 映射微信支付
*
* @param amount 金额(单位:分)
* @return
*/
@RequestMapping("/wxpay")
public void wxPay(Integer amount,HttpServletResponse response) throws Exception {
// 金额
Double total_fee = amount.doubleValue();
// 生成商家订单号
String orderId = PayUtil.createOrder();
if (total_fee > 0) {
WXPayService wxPayService = WXPayService.getInstance(certPath, notifyUrl);
String codeUrl = wxPayService.doUnifiedOrder(orderId, "收银台名称", total_fee, "产品id");
// ---------- 订单保存到数据库 ----------
// 这里写你自己的订单保存逻辑
// BizOrder bizOrder = new BizOrder();
// bizOrder.setAmount(amount);
// bizOrder.setAppid(PayConstant.APPID);
// bizOrder.setMacId(orderId);
// bizOrder.setTotalFee(String.valueOf(total_fee));
// bizOrder.setOrgId(Long.parseLong(orgId));
// BizOrder bo = bizOrderService.insertOrder(bizOrder);
// if (bo == null)
// throw new NullPointerException("Don't save order!");
// 生成支付二维码(数据流) 返回给前端页面
QRCodeUtil.createQrCodeStream(codeUrl,900,"JPEG",response);
}
}
/**
* 微信支付业务
* Created by Jason on 2018/11/20.
*/
public class WXPayService {
private static Logger logger = LoggerFactory.getLogger(WXPayService.class);
private WXPay wxpay;
private WXPayConfigImpl config;
private static WXPayService wxPayService;
private WXPayService(String certPath, String notifyUrl) throws Exception {
config = WXPayConfigImpl.getInstance(certPath,notifyUrl);
wxpay = new WXPay(config);
}
public static WXPayService getInstance(String certPath,String notifyUrl) throws Exception {
if (wxPayService == null) {
synchronized (WXPayConfigImpl.class) {
if (wxPayService == null) {
wxPayService = new WXPayService(certPath,notifyUrl);
}
}
}
return wxPayService;
}
/**
* 微信下单接口
*
* @param out_trade_no 订单号
* @param body 商家商品名
* @param money 总金额
* @param applyNo 商品编号
* @return
*/
public String doUnifiedOrder(String out_trade_no, String body, Double money, String applyNo) {
String amt = String.valueOf(money * 100);
HashMap<String, String> data = new HashMap<String, String>();
data.put("body", body);
data.put("out_trade_no", out_trade_no);
data.put("device_info", "web");
data.put("fee_type", "CNY");
data.put("total_fee", amt.substring(0, amt.lastIndexOf(".")));
data.put("spbill_create_ip", config.getSpbillCreateIp());
data.put("notify_url", config.getNotifyUrl());
data.put("trade_type", config.getTradeType());
data.put("product_id", applyNo);
System.out.println(String.valueOf(money * 100));
// data.put("time_expire", "20170112104120");
try {
Map<String, String> r = wxpay.unifiedOrder(data);
logger.info("返回的参数是" + r);
return r.get("code_url");
} catch (Exception e) {
e.printStackTrace();
logger.info(e.getMessage());
return null;
}
}
/**
* 退款
*/
public void doRefund(String out_trade_no, String total_fee) {
logger.info("退款时的订单号为:" + out_trade_no + "退款时的金额为:" + total_fee);
String amt = String.valueOf(Double.parseDouble(total_fee) * 100);
logger.info("修正后的金额为:" + amt);
logger.info("最终的金额为:" + amt.substring(0, amt.lastIndexOf(".")));
HashMap<String, String> data = new HashMap<String, String>();
data.put("out_trade_no", out_trade_no);
data.put("out_refund_no", out_trade_no);
data.put("total_fee", amt.substring(0, amt.lastIndexOf(".")));
data.put("refund_fee", amt.substring(0, amt.lastIndexOf(".")));
data.put("refund_fee_type", "CNY");
data.put("op_user_id", config.getMchID());
try {
Map<String, String> r = wxpay.refund(data);
logger.info("退款操作返回的参数为" + r);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 微信验签接口
*
* @return
* @throws DocumentException
*/
public boolean checkSign(String strXML) throws DocumentException {
SortedMap<String, String> smap = new TreeMap<String, String>();
Document doc = DocumentHelper.parseText(strXML);
Element root = doc.getRootElement();
for (Iterator iterator = root.elementIterator(); iterator.hasNext();) {
Element e = (Element) iterator.next();
smap.put(e.getName(), e.getText());
}
return isWechatSign(smap,config.getKey());
}
private boolean isWechatSign(SortedMap<String, String> smap,String apiKey) {
StringBuffer sb = new StringBuffer();
Set<Entry<String, String>> es = smap.entrySet();
Iterator<Entry<String, String>> it = es.iterator();
while (it.hasNext()) {
Entry<String, String> entry = it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (!"sign".equals(k) && null != v && !"".equals(v) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + apiKey);
/** 验证的签名 */
String sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
/** 微信端返回的合法签名 */
String validSign = ((String) smap.get("sign")).toUpperCase();
return validSign.equals(sign);
}
}
/**
* 微信支付成功回调接口
*
* @param map 返回给页面
* @param request 接收请求
* @param response 响应
* @return
* @throws Exception
*/
@RequestMapping("/callback")
public void callback(Map<String, Object> map, HttpServletRequest request,
HttpServletResponse response) throws Exception {
log.info("用户支付成功,回调!");
String inputLine;
String notityXml = "";
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
// 微信给返回的东西
try {
while ((inputLine = request.getReader().readLine()) != null) {
notityXml += inputLine;
}
request.getReader().close();
} catch (Exception e) {
e.printStackTrace();
log.info("xml获取失败");
response.getWriter().write(setXml("FAIL", "xml获取失败"));
}
if (StringUtils.isEmpty(notityXml)) {
log.info("xml为空");
response.getWriter().write(setXml("FAIL", "xml为空"));
}
WXPayService wxService = WXPayService.getInstance(certPath, notifyUrl);
if (!wxService.checkSign(notityXml)) {
response.getWriter().write(setXml("FAIL", "验签失败"));
}
log.info("xml的值为:" + notityXml);
Map<String, String> xmlMap = WXPayUtil.xmlToMap(notityXml); // 解析成map
String json = JSON.toJSONString(xmlMap); // map 转成json
log.info(json);
JSONObject jsonObject = JSONObject.fromObject(json);
UnifiedOrderRespose returnPay = (UnifiedOrderRespose) JSONObject.toBean(jsonObject, UnifiedOrderRespose.class);
log.info(("转换后对象为:" + returnPay.toString()));
log.info(("订单号:" + returnPay.getOut_trade_no() + "总金额:" + returnPay.getTotal_fee()));
if (returnPay.getReturn_code().equals("SUCCESS") && returnPay.getOut_trade_no() != null
&& !returnPay.getOut_trade_no().isEmpty()) {
double fee = Double.parseDouble(returnPay.getTotal_fee());
returnPay.setTotal_fee(String.valueOf(fee / 100));
log.info("微信的支付状态为SUCCESS");
// 支付成功业务
synchronized (WXPayController.class) { // 加入同步锁
Thread.sleep(1000); //睡一秒,防止并发倒致数据不一致
// addServiceCount(returnPay,response); // 写你支付成功后的业务, 比如给用户充值服务次数
}
// 返回false的原因有可能是:订单已完成支付,或者订单已退款
}else
response.getWriter().write(setXml("FAIL","ORDER IS FINISH"));
}
/**
* 创建二维码输入流
* @param content 二维码内容
* @param qrCodeSize 二维码尺寸
* @param imageFormat 图片格式
* @param response 响应给页面
*/
public static void createQrCodeStream(String content, int qrCodeSize, String imageFormat, HttpServletResponse response) throws IOException, WriterException {
//设置二维码纠错级别MAP
Hashtable<EncodeHintType, ErrorCorrectionLevel> hintMap = new Hashtable<EncodeHintType, ErrorCorrectionLevel>();
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 矫错级别
QRCodeWriter qrCodeWriter = new QRCodeWriter();
//创建比特矩阵(位矩阵)的QR码编码的字符串
BitMatrix byteMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, qrCodeSize, qrCodeSize, hintMap);
// 使BufferedImage勾画QRCode (matrixWidth 是行二维码像素点)
int matrixWidth = byteMatrix.getWidth();
BufferedImage image = new BufferedImage(matrixWidth-200, matrixWidth-200, BufferedImage.TYPE_INT_RGB);
image.createGraphics();
Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, matrixWidth, matrixWidth);
// 使用比特矩阵画并保存图像
graphics.setColor(Color.BLACK);
for (int i = 0; i < matrixWidth; i++){
for (int j = 0; j < matrixWidth; j++){
if (byteMatrix.get(i, j)){
graphics.fillRect(i-100, j-100, 1, 1);
}
}
}
// 返回给页面
OutputStream outputStream= response.getOutputStream();
// 关流
try {
ImageIO.write(image, imageFormat, outputStream);
} finally {
outputStream.close();
}
}
讲解一下,前端发送请求过来,具体携带的参数,因人而异。携带的是前端生成的订单号,以及支付价格。微信的支付价格一般是分, Controller 接收到请求后,拿到参数,然后下单对接微信平台,下单成功会返回一个二维码的url,然后自行生成二维码。将二维码返回给页面,用户扫码支付后,微信平台会回调你所填写的异步回调通知接口,验证签名之后,解析平台给你返回的参数。
源码后续整理后上传!