• Java微信公众平台开发之扫码支付模式一


    官方文档
    准备工作:已通过微信认证的公众号,必须通过ICP备案域名(否则会报支付失败)
    借鉴了很多大神的文章,在此先谢过了
    大体过程:先扫码(还没有确定实际要支付的金额),这个码是商品的二维码,再生成订单,适用于自动贩卖机之类固定金额的。

    模式一支付的流程如下图,稍微有点复杂

    业务流程说明:
    (1)商户后台系统根据微信支付规定格式生成二维码(规则见下文),展示给用户扫码。
    (2)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
    (3)微信支付系统收到客户端请求,发起对商户后台系统支付回调URL的调用。调用请求将带productid和用户的openid等参数,并要求商户系统返回交数据包
    (4)商户后台系统收到微信支付系统的回调请求,根据productid生成商户系统的订单。
    (5)商户系统调用微信支付【统一下单API】请求下单,获取交易会话标识(prepay_id)。
    (6)微信支付系统根据商户系统的请求生成预支付交易,并返回交易会话标识(prepay_id)。
    (7)商户后台系统得到交易会话标识prepay_id(2小时内有效)。
    (8)商户后台系统将prepay_id返回给微信支付系统。
    (9)微信支付系统根据交易会话标识,发起用户端授权支付流程。
    (10)用户在微信客户端输入密码,确认支付后,微信客户端提交支付授权。
    (11)微信支付系统验证后扣款,完成支付交易。
    (12)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
    (13)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
    (14)未收到支付通知的情况,商户后台系统调用【查询订单API】。
    (15)商户确认订单已支付后给用户发货。

    一、设置回调地址

    商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程
    商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改,如下图所示。
    这个支付回调的URL设置的作用是接收用户扫码后微信支付系统发送的数据,根据接收的数据生成支付订单,调用【统一下单API】提交支付交易。

    二、生成微信支付二维码

    参考文档https://pay.weixin.qq.com/wiki/doc/api/native.PHP?chapter=6_6
    二维码长链接示例:weixin:/pay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX 
    推荐转换成短链接 weixin://wxpay/bizpayurl?pr=XXXX
    /**
    	 * 扫码支付模式一生成二维码
    	 * 
    	 * @param request
    	 * @param response
    	 * @throws IOException
    	 */
    	@RequestMapping("qrcode")
    	public String createPayImage(HttpServletRequest request, HttpServletResponse response) {
    		String nonce_str = PayUtil.createNonceStr();
    		String product_id = "product_001"; // 推荐根据商品ID生成
    		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
    		packageParams.put("appid", WechatConfig.APP_ID);
    		packageParams.put("mch_id", WechatConfig.MCH_ID);
    		packageParams.put("product_id", product_id);
    		packageParams.put("time_stamp", PayUtil.createTimeStamp());
    		packageParams.put("nonce_str", nonce_str);
    		String str = PayUtil.createPayImageUrl(packageParams);
    		String sign = SignatureUtil.createSign(packageParams,WechatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
    		packageParams.put("sign", sign);
    		String payurl = "weixin://wxpay/bizpayurl?sign=" + sign + str;
    		logger.info("payurl is " + payurl);	
    		/**** 转成短链接 ****/
    		PayShortUrlParams payShortUrlParams = new PayShortUrlParams();
    		payShortUrlParams.setAppid(WechatConfig.APP_ID);
    		payShortUrlParams.setMch_id(WechatConfig.MCH_ID);
    		payShortUrlParams.setLong_url(payurl);
    		payShortUrlParams.setNonce_str(nonce_str);
    		String urlSign = SignatureUtil.createSign(payShortUrlParams,WechatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
    		payShortUrlParams.setSign(urlSign);
    		String longXml = XmlUtil.toSplitXml(payShortUrlParams);
    		String shortResult = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD, WechatConfig.PAY_SHORT_URL, null, longXml);
    		PayShortUrlResult payShortUrlResult = XmlUtil.getObjectFromXML(shortResult, PayShortUrlResult.class);
    		if("SUCCESS".equals(payShortUrlResult.getReturn_code())){
    			payurl = payShortUrlResult.getShort_url();
    		}else{
    			logger.debug("错误信息"+payShortUrlResult.getReturn_msg());
    		}
    		/**** 生成 二维码图片自己实现****/
    		return null;
    	}
    /**
    	 * 生成支付二维码URL
    	 * 
    	 * @param params
    	 * @return
    	 */
    	public static String createPayImageUrl(SortedMap<Object, Object> params) {
    		StringBuffer buffer = new StringBuffer();
    		for (Map.Entry<Object, Object> entry : params.entrySet()) {
    			if (entry.getValue() != null) {
    				buffer.append("&" + entry.getKey() + "=" + entry.getValue());
    			}
    		}
    		return buffer.toString();
    	}
    

    三、回调商户支付URL

    接收用户扫码后微信支付系统发送的数据,根据接收的数据生成支付订单,调用统一下单API提交支付交易
    统一接口文档https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

    package com.phil.wechatpay.controller;
    
    import java.io.BufferedOutputStream;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.io.IOUtils;
    import org.apache.log4j.Logger;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import com.phil.common.config.SystemConfig;
    import com.phil.common.config.WeChatConfig;
    import com.phil.common.util.HttpReqUtil;
    import com.phil.common.util.PayUtil;
    import com.phil.common.util.SignatureUtil;
    import com.phil.common.util.XmlUtil;
    import com.phil.wechatpay.model.rep.PayCallBackParams;
    import com.phil.wechatpay.model.rep.UnifiedOrderParams;
    import com.phil.wechatpay.model.resp.PayCallBackResult;
    import com.phil.wechatpay.model.resp.UnifiedOrderResult;
    
    /**
     * 扫码模式一回调
     * 
     * @author phil
     * @date 2017年6月27日
     *
     */
    @Controller
    @RequestMapping("/wxpay/")
    public class WechatPayCallBackController {
    
    	private static final Logger logger = Logger.getLogger(WechatPayCallBackController.class);
    
    	@RequestMapping("/callback")//payone
    	public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		String resXml = "";// 反馈给微信服务器
    		// 微信支付系统发送的数据(<![CDATA[product_001]]>格式)
    		String xml = HttpReqUtil.inputStreamToStrFromByte(request.getInputStream());
    		// logger.info("微信支付系统发送的数据"+xml);
    		/**** 微信支付系统发送的数据其实就是回调地址输入的参数Xml****/
    		// 验证签名
    		if (SignatureUtil.checkIsSignValidFromWeiXin(xml, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING)) {
    			// 转换成输入参数,
    			PayCallBackParams payCallBackParams = XmlUtil.getObjectFromXML(xml, PayCallBackParams.class);
    			// appid openid mch_id is_subscribe nonce_str product_id sign
    			// 统一下单
    			String openid = payCallBackParams.getOpenid();
    			String product_id = payCallBackParams.getProduct_id();
    			/**** product_id 等 生成自己系统的订单****/
    			int total_fee = 1; // 根据product_id算出价格
    			String out_trade_no = PayUtil.createOutTradeNo(); //生成订单号	
    			String body = product_id; // 商品名称设置为product_id
    			String attach = "XXX店"; // 附加数据
    			String nonce_str = PayUtil.createNonceStr();
    			String spbill_create_ip = HttpReqUtil.getRemortIP(request);
    			// 组装统一下单的请求参数
    			UnifiedOrderParams unifiedOrderParams = new UnifiedOrderParams();
    			unifiedOrderParams.setAppid(WeChatConfig.APP_ID);// 必须
    			unifiedOrderParams.setMch_id(WeChatConfig.MCH_ID);// 必须
    			unifiedOrderParams.setOut_trade_no(out_trade_no);
    			unifiedOrderParams.setBody(body);
    			unifiedOrderParams.setAttach(attach);
    			unifiedOrderParams.setTotal_fee(total_fee);// 必须
    			unifiedOrderParams.setNonce_str(nonce_str); // 必须
    			unifiedOrderParams.setSpbill_create_ip(spbill_create_ip); // 必须
    			unifiedOrderParams.setTrade_type("NATIVE");// 必须
    			unifiedOrderParams.setOpenid(openid);
    			unifiedOrderParams.setNotify_url(WeChatConfig.NOTIFY_URL); // 异步通知URL
    			// 签名
    			String sign = SignatureUtil.createSign(unifiedOrderParams, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
    			unifiedOrderParams.setSign(sign);
    			// 统一下单 请求的Xml
    			String unifiedXmL = XmlUtil.toSplitXml(unifiedOrderParams);
    			// 统一下单 返回的xml
    			String unifiedOrderResultXmL = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD,WeChatConfig.UNIFIED_ORDER_URL, null, unifiedXmL);
    			// 统一下单返回 验证签名
    			if (SignatureUtil.checkIsSignValidFromWeiXin(unifiedOrderResultXmL, WeChatConfig.API_KEY,SystemConfig.CHARACTER_ENCODING)) {
    				UnifiedOrderResult unifiedOrderResult = (UnifiedOrderResult) XmlUtil.getObjectFromXML(unifiedOrderResultXmL, UnifiedOrderResult.class);
    				if("SUCCESS".equals(unifiedOrderResult.getReturn_code()) && "SUCCESS".equals(unifiedOrderResult.getResult_code())){
    					PayCallBackResult payCallBackResult = new PayCallBackResult();
    					payCallBackResult.setReturn_code("SUCCESS");
    					payCallBackResult.setAppid(WeChatConfig.APP_ID);
    					payCallBackResult.setMch_id(WeChatConfig.MCH_ID);
    					payCallBackResult.setNonce_str(unifiedOrderResult.getNonce_str());//直接用微信返回的签名即可
    					/**** prepay_id 2小时内都有效,根据product_id再次支付方法自己写 ****/
    					payCallBackResult.setPrepay_id(unifiedOrderResult.getPrepay_id());
    					payCallBackResult.setResult_code("SUCCESS");
    					String callsign = SignatureUtil.createSign(payCallBackResult, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
    					payCallBackResult.setSign(callsign);
    					resXml = XmlUtil.toXml(payCallBackResult).replace("__", "_");
    					//将数据包返回给微信支付系统处理
    				}
    			} else {
    				logger.info("签名验证错误");
    				resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";
    			}
    		} else {
    			logger.info("签名验证错误");
    			resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";
    		}
    		BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
    		out.write(resXml.getBytes());
    		out.flush();
    		IOUtils.closeQuietly(out);
    	}
    }
    

    第四步、完成支付并通知支付结果

    可以看下我这篇写的 详情点击 或者这篇文章的五 详情点击

  • 相关阅读:
    jQuery的核心对象、原型对象、静态方法、动态方法
    请写出css中选择器(元素选择器、类选择器、id选择器)的优先级顺序,和当各种选择器组合时,优先级的计算规则是什么?
    css3中的box-sizing常用的属性有哪些?分别有什么作用?
    不同域的页面如何通信(跨域)
    浮动与定位
    获取DOM元素的方式有哪些
    简要说明盒子模型和flex布局
    牛逼哄哄的对象深复制
    欧拉函数
    P2659 美丽的序列
  • 原文地址:https://www.cnblogs.com/phil_jing/p/7244606.html
Copyright © 2020-2023  润新知