对接微信支付之网页支付详解

  • 声明:转载请注明出处
  • 阅读对象:本文针对的是网页中的扫码支付
  • 温馨提示:微信支付坑比较多,阅读时请仔细一些,不要放过所有需要注意的内容 , 本人一路踩坑过来,希望大家引以为戒!

对接流程大致为以下几个步骤:

  • 申请认证公众号 (若已有公众号则可跳过, 注意: 1. 公众号类型需要为服务号或者订阅号 2. 公众号需要认证, 认证费用300RMB)点此跳转申请公众号
  • 申请网站备案 (若网站已备案则可跳过,注意:网站的备案内容需要和所出售商品直接关系)
  • 有以上两个条件后可以开始申请开通微信支付,具体的申请流程公众号平台都有,读者可自行去查阅。 注意:1.开通微信支付需要公司账户打款验证,要及时和财务沟通好,查看公司流水。2.开通成功后,会收到邮件 ,邮件中的信息特别重要:示例如下
  • 以上三条均申请完成即可开始配置开发环境。拿到邮件中的信息后,按照邮件中的指引配置好相关信息之后就可以开始开发了。注意:很重要的几个信息,1. API证书 2. API密钥 3. APPID
  • 可以开始开发了!!!
支付类型:

  • 网页支付 一般选择 JSAPI和Native 这两种,点此查看区别
  • 本文选择的是Native支付方式
    呼~,终于开始编码了

代码实现

  • 导入依赖
            <!-- 添加微信支付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>
  • 编写配置实现类
    业务层实现类
    注意:这个实现类不要加@Service 注解,否则报错,因为springboot的@Service 是在项目启动时预创建对象,需要的是公共无参构造,以及所定义的成员变量也要注入。 总之别加就不会报错,使用没有影响,如果不相信可以加上试试,嘿嘿。

import com.github.wxpay.sdk.WXPayConfig;
import com.juehua.single.constant.PayConstant;
import com.juehua.single.model.qingshiyuanpay.ExceptionUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;/*** 微信 配置实现类* Created by Jason on 2018/11/20.*/
public class WXPayConfigImpl implements WXPayConfig{private byte[] certData;private static WXPayConfigImpl INSTANCE;private static String cert_path;private static String notify_url;private WXPayConfigImpl(String certPath,String notifyUrl) throws Exception{ExceptionUtil.checkEmpty(certPath,"Sorry,Don't found your cert!"); // 校验证书路径ExceptionUtil.checkEmpty(notifyUrl,"Notify url can not null!");    // 校验回路径// 读取API证书File file = new File(certPath);InputStream certStream = new FileInputStream(file);this.certData = new byte[(int) file.length()];certStream.read(this.certData);certStream.close();}/*** 获取实现类对象* @param certPath* @param notifyUrl* @return* @throws Exception*/public static WXPayConfigImpl getInstance(String certPath,String notifyUrl) throws Exception{notify_url = notifyUrl;cert_path = certPath;if (INSTANCE == null) {synchronized (WXPayConfigImpl.class) {if (INSTANCE == null) {INSTANCE = new WXPayConfigImpl(cert_path,notify_url);}}}return INSTANCE;}/*** 获取证书字节流* @return*/public InputStream getCertStream() {ByteArrayInputStream certBis;certBis = new ByteArrayInputStream(this.certData);return certBis;}public String getAppID() {return PayConstant.APPID;}public String getMchID() {return PayConstant.MCHID;}public String getKey() {return PayConstant.APIKEY;}public String getNotifyUrl() {return notify_url;}public String getTradeType() {return PayConstant.TRADE_TYPE;}// 请求连接超时时长public int getHttpConnectTimeoutMs() {return PayConstant.HTTP_CONNECT_TIMEOUT;}// 请求读取超时时长public int getHttpReadTimeoutMs() {return PayConstant.HTTP_READ_TIMEOUT;}public String getPrimaryDomain() {return PayConstant.PRIMARY_DEMAIN;}public String getAlternateDomain() {return PayConstant.ALTERNATE_DEMAIN;}public int getReportWorkerNum() {return PayConstant.REPORT_WORKER_NUM;}public int getReportBatchSize() {return PayConstant.REPORT_BATCH_SIZE;}public String getSpbillCreateIp() {return PayConstant.BILL_CREATE_IP;}
}
  • 支付常量类,可以将配置信息写在这个常量里

/*** 支付接口相关常量* Created by Jason on 2018/11/15.*/
public final class PayConstant {public static String ORDER_NAME = "XXX技术服务费";public static String BODY = "备注";/***  以下常量是 微信 支付相关*/// 公众号public static String APPID ="你的公众号appid";// 商户号public static String MCHID ="你的商户号";// API 安全密钥public static String APIKEY ="你配置的API安全密钥";// 支付类型 NATIVEpublic static String TRADE_TYPE="NATIVE";// 链接超时时长public static Integer HTTP_CONNECT_TIMEOUT=2000;// 请求读取超时时长public static Integer HTTP_READ_TIMEOUT = 10000;public static String PRIMARY_DEMAIN = "api.mch.weixin.qq.com";public static String ALTERNATE_DEMAIN = "api2.mch.weixin.qq.com";public static Integer REPORT_WORKER_NUM = 1;public static Integer REPORT_BATCH_SIZE= 2;public static String BILL_CREATE_IP="192.168.1.1";
}
  • 微信支付业务 省时间没有分接口和实现类 直接写了
import com.github.wxpay.sdk.WXPay;
import com.juehua.single.model.qingshiyuanpay.MD5Util;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;import java.util.*;
import java.util.Map.Entry;/*** 微信支付业务* 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 INSTANCE;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 (INSTANCE == null) {synchronized (WXPayConfigImpl.class) {if (INSTANCE == null) {INSTANCE = new WXPayService(certPath,notifyUrl);}}}return INSTANCE;}/*** 微信下单业务** @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);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);}
}
  • 控制层 我的方式是先生成支付二维码图片,然后进行图片渲染,你们可以直接返回二维码图片流给前端。我为啥不用? 因为我的是客户端,采用swing自己编写的浏览器,和传统的浏览器还是有区别。
  • 讲解一下,前端发送请求过来,具体携带的参数,因人而异。携带的是前端生成的订单号,以及商品的数量。 Controller 接收到请求后,拿到参数,计算价格 商品数量*商品单价,然后下单对接微信平台,下单成功会返回一个二维码的url,然后自行生成二维码。将二维码返回给页面,用户扫码支付后,微信平台会回调你所填写的异步回调通知接口,验证签名之后,解析平台给你返回的参数。
  • 解析微信平台回调你的接口时所返回的数据,这个单独拿出来说一下。这是比较坑的,返回的是一个数据流!!!! 没错,没有任何参数,只是一个流。我们需要把流解析为xml,然后我们把xml解析成map,将map转为json,将json转为你自己定义的对象(这个看你自己了,需要就转),拿到数据,做你的业务就行了。

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.druid.util.StringUtils;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.zxing.WriterException;
import com.juehua.single.constant.DicConstant;
import com.juehua.single.constant.PayConstant;
import com.juehua.single.controller.BaseController;
import com.juehua.single.model.qingshiyuanpay.ExceptionUtil;
import com.juehua.single.model.qingshiyuanpay.UnifiedOrderRespose;
import com.juehua.single.po.biz.BizOrder;
import com.juehua.single.po.sys.SysInfo;
import com.juehua.single.qrcode.DateCompareUtil;
import com.juehua.single.qrcode.QRCodeUtil;
import com.juehua.single.service.biz.BizOrderService;
import com.juehua.single.service.biz.BizServiceService;
import com.juehua.single.service.bse.BseDicService;
import com.juehua.single.service.impl.alipay.AlipayViewServiceImpl;
import com.juehua.single.service.impl.wxpay.WXPayService;
import com.juehua.single.service.sys.SysInfoService;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 微信支付接口开发控制器* Created by Jason on 2018/11/20.*/@Controller
@Slf4j
@RequestMapping("/portal")
public final class WXPayController extends BaseController{// ----------------这些是个人业务 因人而异 --------------@Autowiredprivate AlipayViewServiceImpl alipayViewServiceImpl;@Autowiredprivate BizOrderService bizOrderService;@Autowiredprivate BseDicService bseDicService;@Autowiredprivate BizServiceService bizServiceService;@Autowiredprivate SysInfoService sysInfoService;
// -------------- 这些是在配置文件中配置的 使用@Value注解注入 ----------------@Value("${juehua.request.address}")private String reqPath;     //资源请求路径@Value("${weixin.qrcode.path}")private String qrcodePath; //生成的二维码地址@Value("${weixin.cert.path}")private String certPath; //注入证书路径@Value("${weixin.notify.url}")private String notifyUrl; //回调地址(异步)@Value("${img.server.port}")private String imgPort; //图片服务器端口/*** 映射微信支付** @param orderId 商户单号* @param amount  购买商品总数量* @param map     freemaker 返回渲染数据* @return*/@RequestMapping("/wxpay")public String wxPay(String orderId, Integer amount, String orgId, Map<String, Object> map) throws Exception {// 计算总费用 这里调用你的计算你的Double total_fee = alipayViewServiceImpl.wxCalculatePrice(amount);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!");
// ---------------------这是我的方式,有点无奈,你们可以用第二种写法 方便// 写法1 生成二维码图片保存到服务器,然后渲染页面// 后台生成二维码图片(本地与服务器系统不同)String imgName = orderId+".jpg";log.info("图片名称为:"+imgName);// 物理路径String imgPath = qrcodePath + File.separator + imgName;log.info("图片存放路径为:"+imgPath);// 相对路径(通过请求获得)String reqImgPath = reqPath + ":" + imgPort + File.separator + "qrcode" + File.separator + imgName;log.info("图片请求路径为:"+reqImgPath);QRCodeUtil.createQrCode(imgPath, codeUrl, 900, "JPEG");boolean isLocal = isLocalPath(reqPath);map.put("reqPath", reqPath);     // 请求路径map.put("orderId", orderId);     // 订单号map.put("goodsType", "技术服务"); // 商品类型map.put("amount", amount);       // 商品数量map.put("total_fee", total_fee); // 金额总数if (isLocal) { // 本地路径map.put("codeUrl", imgPath);// 返回二维码图片路径 路径1}elsemap.put("reqImgPath",reqImgPath);// 路径2delOldQrCode(qrcodePath); // 删除旧二维码}return "wxpay";}/*** 是否是本地路径* @param reqPath 请求路径* @return*/private boolean isLocalPath(String reqPath) {log.info("检测到的请求路径为:"+reqPath);if (reqPath != null) {return reqPath.contains("localhost");}throw new NullPointerException("request is null!");}/*** 二维码生成写法2* 渲染二维码 * @param content  二维码内容 返回的二维码链接 页面img src =“/qrcode”* * @param response 响应页面*/@RequestMapping("/qrcode")public void showQrCode(String content,HttpServletResponse response) {try {QRCodeUtil.createQrCodeStream(content,900,"JPEG",response);} catch (IOException e) {e.printStackTrace();} catch (WriterException e) {e.printStackTrace();}}/*** 微信支付成功回调接口** @param map      返回给页面* @param request  接收请求* @param response 响应* @return* @throws Exception*/@RequestMapping("/callback")public void callback(Map<String, Object> map, HttpServletRequest request,HttpServletResponse response) throws Exception {map.put("reqPath", reqPath); // 返回给freemaker 渲染页面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); // 解析成mapString json = JSONUtils.toJSONString(xmlMap); // map 转成json
//      JSON json = xmlSerializer.read(notityXml);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的原因有可能是:订单已完成支付,或者订单已退款}elseresponse.getWriter().write(setXml("FAIL","ORDER IS FINISH"));}/*** 充值服务次数* 加同步锁 处理并发* @param returnPay* @return* @throws Exception*/private synchronized void addServiceCount(UnifiedOrderRespose returnPay,HttpServletResponse response) throws Exception {return false;}/*** 计算商品数量*   商品数量只能为整数,采取四舍五入制* @param total_fee    总金额* @param price      商品单价* @return*/private int getAmount(String total_fee,String price) {ExceptionUtil.checkEmpty(total_fee,"总金额不能为空");ExceptionUtil.checkEmpty(price,"单价不能为空");double v1 = Double.parseDouble(total_fee);double v = Double.parseDouble(price);double amount = v1/v;log.info("商品数量为:"+amount);long num = Math.round(amount);if (num < 1)throw new IllegalArgumentException("服务充值基数不能小于0");elsereturn (int)num;}/***  支付成功,回调成功,返回xml类型的数据* @param returnCode* @param msg* @return* @throws Exception*/private String setXml(String returnCode, String msg) throws Exception {Map<String,String> xmlMap = new HashMap<>();xmlMap.put("return_code",returnCode);xmlMap.put("return_msg",msg);String data = WXPayUtil.mapToXml(xmlMap);return data;}/***  删除旧二维码*/private void delOldQrCode(String qrcodePath) {ExceptionUtil.checkEmpty(qrcodePath,"二维码存放路径不能为空!");String compareRule = "yyyyMMdd"; // 日期比较格式SimpleDateFormat simpleDateFormat = new SimpleDateFormat(compareRule);String nowDate = simpleDateFormat.format(compareRule);try {File qrcodeDir = new File(qrcodePath);if (qrcodeDir.isDirectory()) { // 如果目录存在File[] files = qrcodeDir.listFiles();for (File file:files) {if (file.exists()&&file.isFile()) {String date = file.getName().substring(0, 8);boolean bool = DateCompareUtil.compareDate(date, nowDate, compareRule);if (!bool) {file.delete(); // 删除二维码}}}}} catch (Exception e) {log.error("获取二维码图片路径为空");}}
}

二维码生成工具类


import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Hashtable;/*** 二维码生成工具类* Created by jason on 2018/11/28 0028.*/
public final class QRCodeUtil {/*** 生成包含字符串信息的二维码图片* @param filePath 文件输出流路径* @param content 二维码携带信息* @param qrCodeSize 二维码图片大小* @param imageFormat 二维码的格式* @throws WriterException* @throws IOException*/public static boolean createQrCode(String  filePath, String content, int qrCodeSize, String imageFormat) throws WriterException, IOException {//设置二维码纠错级别MAPHashtable<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=new FileOutputStream(new File(filePath));return ImageIO.write(image, imageFormat, outputStream);}/*** 创建二维码输入流 可以直接将二维码以流的形式返回给页面* @param content     二维码内容* @param qrCodeSize   二维码尺寸* @param imageFormat  图片格式* @param response      响应给页面*/public static void createQrCodeStream(String content, int qrCodeSize, String imageFormat, HttpServletResponse response) throws IOException, WriterException {//设置二维码纠错级别MAPHashtable<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();}}/*** 读二维码并输出携带的信息*/public static void readQrCode(InputStream inputStream) throws IOException{//从输入流中获取字符串信息BufferedImage image = ImageIO.read(inputStream);//将图像转换为二进制位图源LuminanceSource source = new BufferedImageLuminanceSource(image);BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));QRCodeReader reader = new QRCodeReader();Result result = null ;try {result = reader.decode(bitmap);} catch (ReaderException e) {e.printStackTrace();}System.out.println(result.getText());}/*** 测试代码* @throws WriterException*/public static void main(String[] args) throws IOException, WriterException {//       createQrCode("E:\\qrcode.jpg","china is good",900,"JPEG");
//      readQrCode(new FileInputStream(new File("E:\\qrcode.jpg")));}
}

核心代码就告一段落了,摘取你要的,用不到的方法以及代码可以删去。后续会把原代码发上来。
注意: 生成二维码成功后,扫码支付成功后腾讯会回调你的接口callback。
还有一个坑,腾讯如论回调成功还是失败 都会重复回调,所以我加了同步锁,订单状态改变后,就不再执行任何操作,去除重复回调!

后续会更新对接支付宝支付

累死了,码了一晚上,希望大家多多支持。不懂得下方评论,大神可直接忽略。

对接微信支付之网页支付详解相关推荐

  1. 微信公众号网页支付详解

    首先先看微信公众开放文档:https://blog.csdn.net/qq_41971087/article/details/82466647 其实他的下单和我们微信小程序下单是一样的请观看:http ...

  2. PHP支付接口教程,详解微信支付(二)

    PC扫码支付 扫码支付首先是要分清楚两种模式: [模式一]:商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号).[模式二]:商户后台系统调用微信 ...

  3. 本文是对优雅草蜻蜓C影视便捷追剧小程序微信支付配置教程-大部分小程序支付通用,从微信配置到后台的详解,其他小程序系统均可参考

    本文是对优雅草蜻蜓C影视便捷追剧小程序微信支付配置教程-大部分小程序支付通用,从微信配置到后台的详解,其他小程序系统均可参考 微信支付配置 1,下载工具 ​ 编辑切换为居中 添加图片注释,不超过 14 ...

  4. 【Java实战】微信Native扫码支付(主扫)开发详解

    文章目录 前言 一.功能实现 1.前置条件 2.代码实现 二.具体步骤 1.直接调用微信接口 2.基于binarywang对接 2.1.引入依赖 2.2.编写配置类 2.3.编写后台方法 总结 前言 ...

  5. 支付接口教程,详解支付宝接口(二)

    支付宝的接口向来集成过程都让人觉得比较舒服,只有APP支付相对复杂,但也只是配置上复杂一些,只要清楚原理相信也不是什么难事.下面是以前介绍双钥加密原理的传送门: 支付接口教程特别篇,公钥与私钥,双钥加 ...

  6. 支付系统整体架构详解

    2019独角兽企业重金招聘Python工程师标准>>> 支付系统整体架构详解 http://www.dataguru.cn/article-11263-1.html http://w ...

  7. 农行网上在线支付平台接口安装详解

    农行网上在线支付平台接口安装详解 2017年05月07日 16:02:53 白云下载站 阅读数:3426 中国农业银行的网上支付平台接口的安装还是有点复杂的,摸索了很久才搞定,总结了一下与大家共享. ...

  8. Spring MVC+Spring+Mybatis实现支付宝支付功能(图文详解)(转载)

    Spring MVC+Spring+Mybatis实现支付宝支付功能(图文详解) 前言 本教程详细介绍了如何使用ssm框架实现支付宝支付功能.本文章分为两大部分,分别是「支付宝测试环境代码测试」和「将 ...

  9. 微信小程序详解 php,微信小程序canvas基础详解

    canvas 元素用于在网页上绘制图形.HTML5 的 canvas 元素使用 JavaScript 在网页上绘制2D图像.本文主要和大家分享微信小程序canvas基础详解,希望能帮助到大家. 一.了 ...

最新文章

  1. pytorch2——Pytorch基础数据结构——张量(深度之眼)
  2. Linux ALSA声卡驱动之八:ASoC架构中的Platform
  3. scala recursive value x$5 needs type
  4. 数字图像处理特效中彩色墨水效果的设计与实现
  5. Freemarker中如何遍历List
  6. mysql-5.6.16-win32_mysql-5.6.16-win32免安装配置方法
  7. MySQL回闪_MySQL进行BINLOG回闪
  8. 开源阅读_开源如何维持您的阅读习惯
  9. IP DHCP SNOOPING工作原理测试
  10. java 文件流传输_java – 将远程文件流式传输到文件对象中
  11. win10任务栏无反应解决办法
  12. GAX (Guidance Automation Extensions) 与 GAT (Guidance Automation Toolkit)
  13. DevOps平台的“精益创业”之路
  14. matlab floor函数用法,matlab中fix, floor, ceil, round 函数的使用方法
  15. SpringCloud五大神兽之Eureka服务注册(三)——Eureka的自我保护
  16. 我的ubuntu比windows xp欠缺的地方
  17. 前端寒假css(100-181)
  18. 二维空间最近点对问题 python
  19. r中gglot怎么组合多张图_R语言之可视化①⑧子图组合patchwork包
  20. OpenGL编程轻松入门之使用颜色

热门文章

  1. NOIp 2020 微信步数 题解
  2. 【C++基础知识】常成员函数,常引用
  3. 越豪华越危险 家装豪华程度与环境污染成正比
  4. 不能与牛肉一起吃的食物
  5. GOOGLE卫星地图
  6. 鼠标滑轮只能控制声音?
  7. 网络原理练习题(含答案)
  8. org.springframework.data.mapping.PropertyReferenceException: No property item found for type BItem!
  9. vncviewer退出全屏
  10. 软件开发中 常见英文文档 缩写(转)