APP微信支付的那些坑

APP微信支付不同于扫码支付,我做java后端和APP交互,客户端直接调取微信支付接口,不需要我们生成二维码进行扫码支付.
但是需要java后端统一下单.
APP支付流程大致分为3大步.
1,统一下单
a,应用场景
商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再在APP里面调起支付。

b, 接口链接
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

c, 是否需要证书
不需要
这个是必须要传入参数的有
应用ID appid (这个是微信账户id)
商户号 mch_id (商户号)
随机字符串 nonce_str (用于生成签名中用)
签名 sign

商品描述 body
商户订单号 out_trade_no
总金额 total_fee
终端IP spbill_create_ip
通知地址 notify_url
交易类型 trade_type

@Service
@Transactional
public class ChatPayServiceImpl implements ChatPayService {

	private static final Logger log = LoggerFactory.getLogger("adminLogger");
	
    public static final String SUCCESS_CODE = "200";
    public static final String SUCCESS_MSG = "支付成功";	
	public static final String EMPTY_CODE = "401";
	public static final String EMPTY_MSG = "订单为空";
    public static final String ERROR_CODE = "400";
    public static final String ERROR_MSG = "系统错误";		
	
    @Autowired
    private ProOrderDao orderMapper;
    
    @Autowired
    private PayInfoService payInfoService;

    @Autowired
	private ProOrderDao proOrderDao;
	@Override
	public Map<String, Object> goWeChatPay(String orderId, HttpServletRequest request) {
		Map<String, Object> map = new HashMap<>();
		if(EmptyUtil.isEmpty(orderId)){
			map.put("code", EMPTY_CODE);
			map.put("msg", EMPTY_MSG);
			return map;
		}
		//获取订单信息
		ProOrder payParameter = orderMapper.getByOrderId(orderId);
		long prices = payParameter.getPaidMoney();

//		double price = prices.doubleValue();
		System.out.println("price:" + prices);
        // 微信开放平台审核通过的应用APPID		
		System.out.println("appid是:" + WxpayConfig.appid);
		System.out.println("mch_id是:" + WxpayConfig.mch_id);
		String nonce_str = RandomStrUtil.getRandomString(30);
        System.out.println("随机字符串是:" + nonce_str);
        String total_price = null;// 订单总金额,单位为分,详见支付金额
        String spbill_create_ip = WXSignUtils.getRemortIP(request);// "127.0.0.1";
        String notify_url = WxpayConfig.notify_url;
        String trade_type = "APP";

        // 参数:开始生成签名
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        parameters.put("appid", WxpayConfig.appid);//微信平台id应用ID
        parameters.put("body", payParameter.getBf1());//商品描述
        parameters.put("mch_id", WxpayConfig.mch_id);//商户号
        parameters.put("nonce_str", nonce_str);//随机字符串
        parameters.put("notify_url", notify_url);//通知地址及回调地址
        parameters.put("out_trade_no", String.valueOf(payParameter.getOrderNum()));//商户订单号
        parameters.put("spbill_create_ip",spbill_create_ip);//终端IP
        parameters.put("total_fee", 1);//总金额       测试暂时用一分
        parameters.put("trade_type", trade_type);//交易类型
        String sign = WXSignUtils.createSign("UTF-8", parameters);
        System.out.println("第一次签名是:" + sign);
        Unifiedorder unifiedorder = new Unifiedorder();
        unifiedorder.setAppid(WxpayConfig.appid);//微信平台id应用ID
        unifiedorder.setBody(payParameter.getBf1());//商品描述
        unifiedorder.setMch_id(WxpayConfig.mch_id);//商户号
        unifiedorder.setNonce_str(nonce_str);//随机字符串
        unifiedorder.setNotify_url(notify_url);//通知地址及回调地址
        unifiedorder.setOut_trade_no(String.valueOf(payParameter.getOrderNum()));//商户订单号
        unifiedorder.setSpbill_create_ip(spbill_create_ip);//终端IP
        unifiedorder.setTotal_fee("1");//单位 分
        unifiedorder.setTrade_type(trade_type);//交易类型
        unifiedorder.setSign(sign);//第一次签名
        // 构造xml参数 , 向微信发送内容都是xml形式的,所以要生成xml格式
        String xmlInfo = HttpXmlUtils.xmlInfo(unifiedorder);
        System.out.println("xmlInfo:" + xmlInfo);
        String wxUrl = WxpayConfig.url;
        String method = "POST";
		String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString();// 请求微信
        System.out.println("weixinPost:" + weixinPost);

		UnifiedorderResult unifiedorderResult = ParseXMLUtils.jdomParseXml(weixinPost);// 解析微信的反馈
        
        if (unifiedorderResult != null){
        	if ("SUCCESS".equals(unifiedorderResult.getReturn_code())){
        		if("INVALID_REQUEST".equals(unifiedorderResult.getErr_code())){
                    map.put("code", ERROR_CODE);
                    map.put("msg", "参数错误");
                    return map;       			
        		}
        		//二次签名
				SortedMap<Object, Object> parameters1 = new TreeMap<Object, Object>();
				parameters1.put("appid", WxpayConfig.appid);//微信平台id应用ID
				parameters1.put("partnerid", WxpayConfig.mch_id);//商户号
				parameters1.put("prepayid",ParseXMLUtils.jdomParseXml(weixinPost).getPrepay_id());//第一次签名
				parameters1.put("noncestr", ParseXMLUtils.jdomParseXml(weixinPost).getNonce_str());//随机字符串
				parameters1.put("timestamp", String.valueOf(new Date().getTime()).substring(0, 10));
				parameters1.put("package",WxpayConfig.wx_package);
				String sign1 = WXSignUtils.createSign("UTF-8", parameters1);
				System.out.println("第二次签名是:" + sign1);
        		// 开始拼接App调起微信的参数
                SortedMap<Object, Object> wxAppparameters = new TreeMap<Object, Object>();
                wxAppparameters.put("appId", unifiedorderResult.getAppid());
                wxAppparameters.put("partnerId", unifiedorderResult.getMch_id());
                wxAppparameters.put("prepayId", unifiedorderResult.getPrepay_id());
                wxAppparameters.put("appPackage", WxpayConfig.wx_package);//扩展字段
                wxAppparameters.put("nonceStr", ParseXMLUtils.jdomParseXml(weixinPost).getNonce_str());
                wxAppparameters.put("timeStamp", String.valueOf(new Date().getTime()).substring(0, 10));
                wxAppparameters.put("sign", sign1);
                map.put("code", SUCCESS_CODE);
                map.put("msg", SUCCESS_MSG);
                map.put("data", wxAppparameters);
                return map;     		
        	} else {

                System.out.println("错误原因为:" + unifiedorderResult.getReturn_msg());
                map.put("code", ERROR_CODE);
                map.put("msg", unifiedorderResult.getReturn_msg());
                return map;
        	}
        } else {
            System.out.println("服务端请求微信的返回值异常。");
            map.put("code", ERROR_CODE);
            map.put("msg", "服务端请求微信的返回值异常。");
            return map;      
        }
	}



/**
     * @Function: 微信回调接口
     * @author:   
     * @Date:     
     */

	@Override
	@Transactional(rollbackFor=Exception.class)
	public String weChatNotify(HttpServletRequest request) throws Exception {
		Map<String, String> map = new HashMap<>();
		System.out.println("----------------微信回调开始啦----------------------");
		// 读取参数
		InputStream inputStream;
		StringBuffer sb = new StringBuffer();
		inputStream = request.getInputStream();
		String s;
		BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
		while ((s = in.readLine()) != null) {
			sb.append(s);
		}
		in.close();
		inputStream.close();
		// 解析xml成map
		Map<String, String> m = new HashMap<String, String>();
		m = WXSignUtils.doXMLParse(sb.toString());
		System.out.println(m);
		// 过滤空 设置 TreeMap
		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
		Iterator<String> it = m.keySet().iterator();
		while (it.hasNext()){
			String parameter = it.next();
			String parameterValue = m.get(parameter);
			String v = "";
			if (null != parameterValue){
				v = parameterValue.trim();
			}
			System.out.println("p:" + parameter + ",v:" + v);
			packageParams.put(parameter, v);
		}
		// 微信支付的API密钥
		String key = WxpayConfig.key;
		if(!isTenpaySign("UTF-8", packageParams, key)){
            map.put("return_code", "FAIL");
            map.put("return_msg", "return_code不正确");
            return StrUtil.GetMapToXML(map);
		}
		//返回状态存入redis中
		if(m.get("return_code").equals("SUCCESS")){
			RedisUtil.set("wx"+m.get("out_trade_no"),m.get("result_code"),300);
		}
		if (isTenpaySign("UTF-8", packageParams, key)){
			// 验证通过
			if ("SUCCESS".equals((String) packageParams.get("return_code"))){
				 String out_trade_no = (String) packageParams.get("out_trade_no");

				 /* 订单不为空 */
				 if (!EmptyUtil.isEmpty(out_trade_no)){
					//支付成功后的业务处理  更新订单状态  插入支付记录
					 ProOrder order = proOrderDao.getByOrderNum(out_trade_no);

					 String orderId = order.getOrderId();
					 int i = proOrderDao.update1("20", orderId);//修改订单状态
					 //插入支付信息数据(支付信息表)
					 PayInfo payInfo=new PayInfo();
					 	payInfo.setPayId(OrderNumGenerateUtil.getUUID());
						payInfo.setUserId(order.getUserId());//用户id
						payInfo.setOrderNum(order.getOrderNum());//订单号
						payInfo.setPayPlatform("2");//微信支付
						payInfo.setStatus("1");//支付成功
						payInfo.setFlowNo((String) packageParams.get("flow_no"));//支付流水账号
						payInfo.setCreateTime(new Date());
						payInfo.setUpdateTime(new Date());
					 JsonResult save = payInfoService.save(payInfo);//保存支付信息
					 	map.put("return_code", "SUCCESS");
	                    map.put("return_msg", save.toString());
	                    return StrUtil.GetMapToXML(map);
				 }
			}
		} else {
            System.out.println("支付失败");
            map.put("return_code", "error");
            map.put("return_msg", "支付失败");
            return StrUtil.GetMapToXML(map);
		}
        System.out.println("支付失败");
        System.out.println("支付失败");
        map.put("return_code", "error");
        map.put("return_msg", "支付失败");
        return StrUtil.GetMapToXML(map);
	}
	
	
        WXSignUtils是生成签名的工具类
  以下是工具类的写法

/**
 * @Description: 微信支付签名
 * @author: Administrator
 * @date: 
 */
public class WXSignUtils {
    /* API秘钥 */
	private static String Key ="d9f5df2e7e187672ba112623dec18478";
    /**
     * 微信支付签名算法sign
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + Key);
        System.out.println("字符串拼接后是:"+sb.toString());
        String sign = MD5Util.encode(sb.toString()).toUpperCase();
        return sign;
    }
    
    public static String getSign(String characterEncoding,Map<String, String> data){
        StringBuffer sb = new StringBuffer();
        Set es = data.entrySet();//所有参与传参的参数按照accsii排序(升序)
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + Key);
        System.out.println("字符串拼接后是:"+sb.toString());
        String sign = MD5Util.encode(sb.toString()).toUpperCase();
        return sign;
    }    
    
    /**
     * @Function: 获取IP
     * @author:   
     * @Date:     
     */

    public static String getRemortIP(HttpServletRequest request) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        return request.getHeader("x-forwarded-for");
    }

    /**
     * @Function: 解析XML
     * @author:   
     * @Date:     
     */
    public static Map doXMLParse(String strxml) throws JDOMException,
            IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
        if (null == strxml || "".equals(strxml)) {
            return null;
        }
        Map m = new HashMap();
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        org.jdom.Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while (it.hasNext()) {
            org.jdom.Element e = (org.jdom.Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }
 
            m.put(k, v);
        }
 
        // 关闭流
        in.close();
 
        return m;
    }


    
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if (!children.isEmpty()) {
            Iterator it = children.iterator();
            while (it.hasNext()) {
                org.jdom.Element e = (org.jdom.Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }
 
        return sb.toString();
    }

 
    
    
    
}

这个代码中其中要用到两次签名,去支付的 模块中,生成第一个签名,和必须要输入的参数请求微信 HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo)
这是请求微信是的xml格式,当然,微信返回给我们的,也是xml形式的,所以获取的时候也要对结果进行解析.

<xml>
   <appid>wx2421b1c4370ec43b</appid>`在这里插入代码片`
   <body>APP支付测试</body>
   <mch_id>10000100</mch_id>
   <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
   <notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
   <out_trade_no>1415659990</out_trade_no>
   <spbill_create_ip>14.23.150.211</spbill_create_ip>
   <total_fee>1</total_fee>
   <trade_type>APP</trade_type>
   <sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>

2,调起支付接口

解析完以后,我们获取一个
预支付交易会话标识 prepay_id
SortedMap<Object, Object> wxAppparameters = new TreeMap<Object, Object>();
wxAppparameters.put(“appId”, unifiedorderResult.getAppid());
wxAppparameters.put(“partnerId”, unifiedorderResult.getMch_id());
wxAppparameters.put(“prepayId”, unifiedorderResult.getPrepay_id());//这个就是生成的会话标识
wxAppparameters.put(“appPackage”, WxpayConfig.wx_package);//扩展字段
wxAppparameters.put(“nonceStr”, ParseXMLUtils.jdomParseXml(weixinPost).getNonce_str());
wxAppparameters.put(“timeStamp”, String.valueOf(new Date().getTime()).substring(0, 10));
wxAppparameters.put(“sign”, sign1);

其中这里面的顺序和参数不能更改,
其中wxAppparameters.put(“sign”, sign1)这里的sign1是第二次生成的签名,第二次生成的签名要用的字符串要和第一次的字符串一样,要不然调不了支付接口.(这是一个小坑)
调取支付接口要用的参数分别有

应用IDappid
商户号partnerid
预支付交易会话IDprepayid
扩展字段package
随机字符串noncestr
时间戳timestamp
签名sign
商户号

(详见APP支付接口https://pay.weixin.qq.com/wiki/doc/api)
传入app接口就可以,APP端接入微信支付接口跳转支付页面,支付成功以后,
微信调取回调接口.你写的notify_url回调地址,
因为我的这个项目java后端还要用成功之后微信给的回调地址中的信息.
支付信息表,修改支付状态等等.
但是在回调地址中不是写你IP地址.微信就能识别你的IP的.
(在局域网是无法进行回调的,必须将你的服务端放在公网上进行测试,)

3,支付结果通知
通过结果通知截取你想要的信息去增删改数据信息.

写的有点乱.写的不对的地方敬请指正.
欢迎大家一起来探讨.

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值