欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java微信小程序支付Demo

程序员文章站 2022-03-14 13:51:56
...

基础包 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.编写配置文件信息
Java微信小程序支付DemoJava微信小程序支付Demo
3.自定义异常模块(用自己的也行,只要能捕抓到异常)
Java微信小程序支付Demo
Java微信小程序支付Demo

/**
 * @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”

相关标签: java