Java微信小程序支付Demo

基础包 SpringBoot,xStream,hutool工具类,lombok,自定义异常模块

XStream

	<dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.4.11.1</version>
    </dependency>

hutool 工具类

	<dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.3.4</version>
    </dependency>

下面我们开始写一个微信小程序支付的demo
1.新建三个对象用于接收微信返回xml报文的解析内容
①.WxPayNotifyReq

 /**
 * 支付结果通知报文
 * @author wuhx  2020-05-14 11:55:56
 */	 
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WxPayNotifyReq {
    @XStreamAlias("appid")
    private String appId;
    
	@XStreamAlias("bank_type")
    private String bankType;

    @XStreamAlias("cash_fee")
    private float cashFee;

    @XStreamAlias("fee_type")
    private String feeType;

    @XStreamAlias("is_subscribe")
    private String isSubscribe;

    @XStreamAlias("mch_id")
    private String mchId;

    @XStreamAlias("nonce_str")
    private String nonceStr;

    @XStreamAlias("openid")
    private String openId;

    @XStreamAlias("out_trade_no")
    private String outTradeNo;

    @XStreamAlias("return_code")
    private String resultCode;

    @XStreamAlias("result_code")
    private String returnCode;

    @XStreamAlias("sign")
    private String sign;

    @XStreamAlias("time_end")
    private String timeEnd;

    @XStreamAlias("total_fee")
    private String totalFee;

    @XStreamAlias("trade_type")
    private String tradeType;

    @XStreamAlias("transaction_id")
    private String transactionId;
}

②WxPayNotifyResp

/**
 * 支付异步通知报文
 * @author wuhx  2020-05-14 13:54:50
 */
@Data
public class WxPayNotifyResp {

    private String returnCode;
    private String returnMsg;

}

③WxPayResp

	/**
	 * 解析预支付(统一下单接口)
	 * 具体字段参数见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1
	 * XStreamAlias的原因,解析XML的参数只能多不能少
	 * @author wuhx  2020-05-14 15:46:37
	 */
	@Data
	@NoArgsConstructor
	@AllArgsConstructor
	@XStreamAlias("xml")
	public class WxPayResp {

    @XStreamAlias("err_code")
    private String errCode;

    @XStreamAlias("err_code_des")
    private String errCodeDes;

    @XStreamAlias("code_url")
    private String codeUrl;

    @XStreamAlias("device_info")
    private String deviceInfo;

    @XStreamAlias("openid")
    private String openId;

    @XStreamAlias("result_code")
    private String resultCode;


    @XStreamAlias("return_code")
    private String returnCode;

    @XStreamAlias("return_msg")
    private String returnMsg;
    /**
     * 随机字符串
     */
    @XStreamAlias("nonce_str")
    private String noticeStr;

    /**
     * 预支付交易会话标识
     */
    @XStreamAlias("prepay_id")
	private String prepayId;

    @XStreamAlias("sign")
    private String sign;

    @XStreamAlias("appid")
    private String appId;

    @XStreamAlias("mch_id")
    private String mchId;

    /**
     * 交易类型
     */
    @XStreamAlias("trade_type")
    private String tradeType;

}	

2.编写配置文件信息
用于接收配置文件中的小程序信息在这里插入图片描述
3.自定义异常模块(用自己的也行,只要能捕抓到异常)
在这里插入图片描述
在这里插入图片描述

/**
 * @author wuhx  2020-05-12 13:48:44
 */
public class BaseError {
		
	public enum ErrorCodeEnum{

        SYS_ERROR("10001","系统错误,请重试"),
        UNKNOWN_ERROR("10002","未知异常,请重试"),
        /*用户错误*/
        USER_NOT_LOGGED_IN("20001", "用户未登录,请先登录"),
        USER_LOGIN_ERROR("20002", "账号不存在或密码错误"),
        /*HTTP异常*/
        HTTP_SEND_GET_ERROR("30001","发送GET请求失败"),
        HTTP_SEND_POST_ERROR("30002","发送POST请求失败"),
        /* 权限错误:70001-79999 */
        PERMISSION_UNAUTHENTICATED("70001","此操作需要登陆系统!"),
        PERMISSION_UNAUTHORISE("70002","权限不足,无权操作!"),
        PERMISSION_EXPIRE("70003","登录状态过期!"),
        PERMISSION_TOKEN_EXPIRED("70004", "token已过期"),
        PERMISSION_LIMIT("70005", "访问次数受限制"),
        PERMISSION_TOKEN_INVALID("70006", "无效token"),
        PERMISSION_SIGNATURE_ERROR("70007", "签名失败"),
        PERMISSION_TOKEN_NULL("70008","token为空"),
        ILLEGAL_ARGS("70009","参数异常,请检查参数"),
        /*微信返回的错误 80001-89999*/
        WX_MSG_ERROR("80001","获取微信信息异常"),
        WX_SYS_ERROR("80002","系统繁忙"),
        WX_PAY_SENDXML_ERROR("80003","报文发送失败"),
        WX_PAY_READRESP_ERROR("80004","解析响应内容失败"),
        WX_PAY_ERROR("80005","微信支付请求失败"),
        WX_PAY_SIGN_ERROR("80006","签名失败"),
        /*XML错误 90001-99999*/
        XML_PARSE_ERROR("90001","XML解析错误")
        ;
        private String code;
        private String desc;

        public String getCode() {
            return code;
        }

        public String getDesc() {
            return desc;
        }
        public void setDesc(String desc) {
            this.desc = desc;
        }

        ErrorCodeEnum(String code,String desc){
            this.code = code;
            this.desc = desc;
        }

        public static ErrorCodeEnum getEnumByCode(String code){
            for (ErrorCodeEnum result : values()){
                if (StrUtil.equals(result.getCode(),code)){
                    return result;
                }
            }
            return null;
        }
    }
}

最后调用就行了

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import com.thoughtworks.xstream.XStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import zhiyan.applets.base.execption.BaseError;
import zhiyan.applets.base.execption.HttpException;
import zhiyan.applets.base.execption.WxPayException;
import zhiyan.applets.base.execption.XmlException;
import zhiyan.applets.base.utils.LogUtils;
import zhiyan.applets.entity.HttpResult;
import zhiyan.applets.tencent.login.model.WxInfo;
import zhiyan.applets.tencent.pay.model.WxPayResp;

import java.util.*;

/**
 * @author wuhx  2020-05-14 14:01:34
 */
public class WxPay {

    private static final Logger log = LoggerFactory.getLogger(WxPay.class);
    /**
     * 下单地址
     */
    private static final String URL_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    private static final String SUCCESS = "SUCCESS";
    /**
     * 加密方式 默认 md5
     */
    private static final String SIGN_TYPE = "MD5";

    private final XStream xStream;

    public WxPay(XStream xStream) {
        this.xStream = xStream;
    }


    /**
     * 下单(预支付)
     * @param orderNumber 订单编号
     * @param payMoney 金额 单位分
     * @param body 描述
     * @param openId 用户openid
     * @param notifyUrl 异步回调地址(配合内网穿透使用)
     * @param ip ip地址
     * @param wxInfo 微信小程序配置信息
     * @return wxPayResp
     */
    public WxPayResp unifiedOrder(String orderNumber, Double payMoney, String body, String openId, String notifyUrl,String ip, WxInfo wxInfo){
        Map<String, String> map = new HashMap<>(11);
        map.put("appid",wxInfo.getAppId());
        map.put("mch_id", wxInfo.getMchId());
        map.put("nonce_str", RandomUtil.randomString(16));
        map.put("body",wxInfo.getPayTitle()+body);
        map.put("out_trade_no", orderNumber);
        map.put("total_fee", StrUtil.toString(payMoney));
        map.put("spbill_create_ip",ip);
        map.put("notify_url",notifyUrl);
        map.put("trade_type","JSAPI");
        map.put("sign",sign(map,wxInfo.getAppKey()));
        try {
            String result = HttpUtil.post(URL_UNIFIED_ORDER, XmlUtil.mapToXmlStr(map));
            return (WxPayResp) xStream.fromXML(result);
        }catch (WxPayException e){
            log.error(LogUtils.text(e));
            throw new WxPayException(BaseError.ErrorCodeEnum.WX_PAY_ERROR);
        }catch (XmlException e){
            log.error(LogUtils.text(e));
            throw new XmlException(BaseError.ErrorCodeEnum.XML_PARSE_ERROR);
        }catch (HttpException e){
            log.error(LogUtils.text(e));
            throw new HttpException(BaseError.ErrorCodeEnum.HTTP_SEND_POST_ERROR);
        }
    }

    /**
     * 支付
     * @param wxPayResp
     * @return
     */
    public HttpResult wxPay(WxPayResp wxPayResp){
        Map<String,Object> resMap = new HashMap<>();
        if (StrUtil.equalsIgnoreCase(wxPayResp.getReturnCode(),SUCCESS)){
            //注意 这个I 要大写
            resMap.put("appId",wxPayResp.getAppId());
            resMap.put("nonceStr",wxPayResp.getNoticeStr());
            resMap.put("package","prepay_id="+wxPayResp.getPrepayId());
            resMap.put("signType",SIGN_TYPE);
            resMap.put("timeStamp", System.currentTimeMillis());
            resMap.put("paySign",wxPayResp.getSign());
            return HttpResult.mapSuccess(resMap);
        }
        return HttpResult.error("微信请求支付失败");
    }


    /**
     * 按照微信规定格式签名
     *
     * @author lingting 2019-08-06 15:23:54
     */
    private static String sign(Map<String, String> map,String appKey) {
        // 对参数的 key 按照指定规则排序
        String[] keys = asciiSort(map.keySet());
        // 按照排序后的key拼接成字符串
        String mapString = join(map, keys,appKey);
        // 获取签名
        String sign = SecureUtil.md5(mapString).toUpperCase();
        if (StrUtil.isNotEmpty(sign)) {
            return sign;
        } else {
            throw new WxPayException(BaseError.ErrorCodeEnum.WX_PAY_SIGN_ERROR);
        }
    }

    private static String join(Map<String, String> map, String[] keys,String appKey) {
        StringBuilder str = new StringBuilder();
        for (String key : keys) {
            str.append(key).append("=").append(map.get(key)).append("&");
        }
        // 拼接秘钥
        str.append("key=").append(appKey);
        return str.toString();
    }

    /**
     * 按照 首字符 ASCII 码排序
     *
     * @author wuhx 2020-05-14 14:37
     */
    public static String[] asciiSort(Set<String> set) {
        return asciiSort(set.toArray(new String[0]));
    }

    public static String[] asciiSort(String[] keys) {
        List<String> list = new ArrayList<>(Arrays.asList(keys));
        Collections.sort(list);
        return list.toArray(new String[0]);
    }
}

接下来最后一步就是写异步回调地址,如果线上测试的话不需要内网穿透,本地测试的话要有内网穿透。并且这个接口不能有任何的权限(POST),然后根据解析的WxPayNotifyReq 内容,处理你的
业务逻辑就行了。

/**
 * 回调处理
 * @param request
 * @param response
 * @return
 * @throws IOException
 */
public WxPayNotifyReq asyNotice(HttpServletRequest request, HttpServletResponse response) throws IOException {
    try (PrintWriter writer = response.getWriter()) {
        String result = parseRequest(request);
        log.info(LogUtils.text("通知报文:" + result));
        //结果返回,微信要求如果成功需要返回return_code "SUCCESS"
        return (WxPayNotifyReq) xStream.fromXML(result);
    } catch (WxPayException e) {
        throw new WxPayException(BaseError.ErrorCodeEnum.WX_MSG_ERROR);
    }
}

public static String parseRequest(HttpServletRequest request) throws UnsupportedEncodingException {
    request.setCharacterEncoding("utf-8");
    String body = "";
    try {
        ServletInputStream inputStream = request.getInputStream();
        // 设置编码格式“utf-8”否则获取中文为乱码
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
        while (true) {
            String info = br.readLine();
            if (info == null) {
                break;
            }
            if (StrUtil.isNotEmpty(body)) {
                body = info;
            } else {
                body += info;
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return body;
}

最后处理完业务逻辑后给微信返回return_code “SUCCESS”

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值