app微信支付后端接口编写

刚做完app微信支付,我这块主要负责的是后端,现在我将我怎么开发的以及踩过的坑给大家写出来

微信支付主要有3步,

第一步:是生成一个预付订单

第二步:第二次签名的信息传到app端

第三步:最后接受到微信的回调信息,并处理业务逻辑

微信api开发文档https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1

1.第一步生成预付订单

package com.odfly.module.payManager.weixin.app.utils;

import com.odfly.module.component.entity.PayConfig;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

public class WxUtil {


    /**
     * 得到微信预付单的返回ID
     *
     * @param orderId  商户自己的订单号
     * @param totalFee 总金额  (分)
     * @param notifyUrl 回调地址
     * @return
     */
    public static Map<String, String> getPreyId(String orderId, String totalFee, String body, PayConfig payConfig,String notifyUrl) {
        Map<String, String> reqMap = new HashMap<String, String>();
        reqMap.put("appid", payConfig.getAppId());//appid
        reqMap.put("mch_id", payConfig.getMchId());//商户id
        reqMap.put("nonce_str", getRandomString());//随机字符串
        reqMap.put("notify_url", payConfig.getDomain() + "/v2/weixin/weixinPay/"+notifyUrl); //通知地址notifyUrl

        reqMap.put("body", body);//商品描述
        reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
        reqMap.put("total_fee", YuanTofen(totalFee)); //订单总金额,单位为分
        reqMap.put("spbill_create_ip", getHostIp()); //用户端实际ip
        reqMap.put("trade_type", "APP"); //交易类型
        String sign = getSign(reqMap, payConfig.getAppKey());
        reqMap.put("sign", sign);

        String reqStr = creatXml(reqMap);
    //https://api.mch.weixin.qq.com/pay/unifiedorder 统一下单api
        String retStr = HttpClientUtil.postHtpps(ConstantUtil.PAY_ORDER_URL, reqStr);//ConstantUtil.PAY_ORDER_URL统一下单地址
        return getInfoByXml(retStr,payConfig.getAppKey());
    }




    /**
     * 得到本地机器的IP
     *
     * @return
     */
    private static String getHostIp() {
        String ip = "";
        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return ip;
    }


    /**
     * 得到加密值
     *
     * @param map
     * @param appKey
     * @return
     */
    public static String getSign(Map<String, String> map, String appKey) {
        String[] keys = map.keySet().toArray(new String[0]);
        Arrays.sort(keys);
        StringBuffer reqStr = new StringBuffer();
        for (String key : keys) {
            String v = map.get(key);
            if (v != null && !v.equals("")) {
                reqStr.append(key).append("=").append(v).append("&");
            }
        }
    

    //请注意此appKey是            
        reqStr.append("key").append("=").append(appKey);

        return WeiMd5.encode(reqStr.toString()).toUpperCase();
    }

    /**
     * 得到随机字符串
     *
     * @return
     */
    public static String getRandomString() {
        int length = 32;
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();

        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < length; ++i) {
            int number = random.nextInt(62);//[0,62)
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

    /**
     *将map文件转换成xml
     *
     * @param reqMap
     * @return
     */
    public static String creatXml(Map<String, String> reqMap) {
        Set<String> set = reqMap.keySet();
        StringBuffer b = new StringBuffer();
//        b.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        b.append("<xml>");
        for (String key : set) {
            b.append("<" + key + ">").append(reqMap.get(key)).append("</" + key + ">");
        }
        b.append("</xml>");
        return b.toString();
    }

/**
 * 解析从微信返回的数据,并处理部分数据仅二次签名,并将签名数据返回给app 端
 * @param xmlStr 
 * @param appKey 商户秘钥
 * @return
 */
 public static Map<String, String> getInfoByXml(String xmlStr, String appKey) {
        try {
            Map<String, String> m = new HashMap<String, String>();
            Document d = DocumentHelper.parseText(xmlStr);
            Element root = d.getRootElement();
            for (Iterator<?> i = root.elementIterator(); i.hasNext(); ) {
                Element element = (Element) i.next();
                String name = element.getName();
                if (!element.isTextOnly()) {
                    //不是字符串 跳过。确定了微信放回的xml只有根目录
                    continue;
                } else {
                    m.put(name, element.getTextTrim());
                }
            }
            //对返回结果做校验.去除sign 字段再去加密
            String retSign = m.get("sign");
            m.remove("sign");
            m.remove("trade_type");
            m.remove("return_msg");
            m.remove("result_code");
            m.remove("return_code");
            m.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
            m.put("package", "Sign=WXPay");

            //微信生成预支付订单后二次签名,注:只有以下6个字段参与签名
            Map<String, String> reqMap = new HashMap<String, String>();
            reqMap.put("appid",m.get("appid"));
            reqMap.put("noncestr",m.get("nonce_str"));
            reqMap.put("package",m.get("package"));
            reqMap.put("partnerid",m.get("mch_id"));
            reqMap.put("prepayid",m.get("prepay_id"));
            reqMap.put("timestamp",m.get("timestamp"));
            String rightSing = getSign(reqMap, appKey);


            m.put("sign", rightSing);
            return m;
        } catch (DocumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

 

2,接收微信的回调信息

 

private static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(WxPayService.class);
/**
 *
 * 微信回调
 * 签约回调
 * @param request
 * @param response
 */
@RequestMapping(value = "/WXApp_Contract_pay_callback", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public void wxAppContractPayCallback(HttpServletRequest request, HttpServletResponse response) {
    try {
        DataInputStream in;
        String wxNotifyXml = (String) request.getAttribute("requestParams");
        Map xmlMap = XMLUtil.doXMLParse(wxNotifyXml);
        // 解析各种数据
        String appid = (String) xmlMap.get("appid");//应用ID
        String attach = (String) xmlMap.get("attach");//商家数据包
        String bank_type = (String) xmlMap.get("bank_type");//付款银行
        String cash_fee = (String) xmlMap.get("cash_fee");//现金支付金额
        String fee_type = (String) xmlMap.get("fee_type");//货币种类
        String is_subscribe = (String) xmlMap.get("is_subscribe");//是否关注公众账号
        String mch_id = (String) xmlMap.get("mch_id");//商户号
        String nonce_str = (String) xmlMap.get("nonce_str");//随机字符串
        String openid = (String) xmlMap.get("openid");//用户标识
        String out_trade_no = (String) xmlMap.get("out_trade_no");// 获取商户订单号
        String result_code = (String) xmlMap.get("result_code");// 业务结果
        String return_code = (String) xmlMap.get("return_code");// SUCCESS/FAIL
        String sign = (String) xmlMap.get("sign");// 获取签名
        String time_end = (String) xmlMap.get("time_end");//支付完成时间
        String total_fee = (String) xmlMap.get("total_fee");// 获取订单金额
        String trade_type = (String) xmlMap.get("trade_type");//交易类型
        String transaction_id = (String) xmlMap.get("transaction_id");//微信支付订单号
        if ("SUCCESS".equals(result_code) && "SUCCESS".equals(return_code)){
            //获取支付订单
            PayOrder payOrder = payOrderService.get(new PayOrder().setTradeNo(out_trade_no));
            if (null != payOrder) {
                aliPayService.payOrderContractStatusUpate(out_trade_no,transaction_id);
            }
        }

    } catch (Exception e) {
        LOGGER.error(StringUtil.getStackMsg(e));
        LOGGER.error("微信支付回调失败!" );
    }
    if (LOGGER.isInfoEnabled()) {
        LOGGER.info("微信支付回调结束...");
    }
}

 

3,自己遇到的坑

 

 

  • 将支付信息拼凑成到map后,进行签名,这个过程中一定要用商户平台的秘钥,路径 key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
  • 当你秘钥还是这样设置,且第一步获取预支付订单id签名还是错误的,并且检查签名信息正确(使用微信签名信息)的时候,果断更换秘钥,重置一个微信秘钥,这个时候签名会正确,这个微信的坑,已经遇到的第二次了
  • 生成签名信息请在这里生成,然后再发送,看看返回的信息是否正确https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1
  • 收到异步消息。返回结果是一个xml字符串,对此xml字符串进行处理,将没有必要的字段移除掉 
    m.remove("sign");
    m.remove("trade_type");
    m.remove("return_msg");
    m.remove("result_code");
    m.remove("return_code");
    注:将sign去掉,然后重新组装map,进行二次签名
    //微信生成预支付订单后二次签名,注:只有以下6个字段参与签名
    Map<String, String> reqMap = new HashMap<String, String>();
    reqMap.put("appid",m.get("appid"));
    reqMap.put("noncestr",m.get("nonce_str"));
    reqMap.put("package",m.get("package"));
    reqMap.put("partnerid",m.get("mch_id"));
    reqMap.put("prepayid",m.get("prepay_id"));
    reqMap.put("timestamp",m.get("timestamp"));
    String rightSing = getSign(reqMap, appKey);

以上就是个人开发的总结,有问题请留言,多谢交流

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值