微信支付之PC网站(Native)支付详解

了解支付模式

Native

微信支付之PC网站(Native)支付详解_第1张图片
微信支付之PC网站(Native)支付详解_第2张图片

适用场景:

Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站实体店单品订单媒体广告支付等场景。

解释:
  • 这里描述一下PC端业务场景,例如:你在网站上购买东西,选择好商品后,会生成一个支付二维码,扫码支付后,商品的库存就减少了。 还有,自动贩卖机,选择商品后会生成支付二维码,扫码支付后,商品就会掉下来。

开发前准备

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

环境准备:

添加依赖
		<!-- 添加微信支付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>

示例代码目录

微信支付之PC网站(Native)支付详解_第3张图片

支付:
	/**
	 * 映射微信支付
	 *
	 * @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,然后自行生成二维码。将二维码返回给页面,用户扫码支付后,微信平台会回调你所填写的异步回调通知接口,验证签名之后,解析平台给你返回的参数。

源码后续整理后上传!

你可能感兴趣的:(Java核心技术,java笔记分享)