微信支付接口

微信支付接口模板

一个简单的比较清晰的微信支付demo:https://www.cnblogs.com/wang-yaz/p/8632624.html

大概流程

调用接口传参获取二维码url(传入所需订单对象,方便后边业务逻辑进行判断)–》扫描通过扫描成功回传参数进行判断–》根据返回成功参数进行下一步操作(对数据库进行数值的增加或修改)

需要的util类: HttpRequestUtil(主要完成发送post请求) , XMLutil(主要完成将对象转成xml字符串,将xml字符串转化成对象),WeChatPayUtil(主要对回调接口进行操作以及对数据的封装),HttpUtil(主要完成获取用户IP地址的操作)

WeChatPayUtil类

public class WeChatPayUtil {
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Random RANDOM = new SecureRandom();
    private static WeChatPayConstant.SignType signType;

    /**
     * 向 对象中 中添加 appid、mch_id、nonce_str、sign_type、sign <br> 该函数适用于商户适用于统一下单等接口,不适用于红包、

代金券接口 @param order @return @throws Exception
     */
    public static OrderPO fillRequestData(OrderPO order) throws Exception {/*appid*/
        order.setAppid(WeChatPayConstant.APPID);/*商户号*/
        order.setMch_id(WeChatPayConstant.MCHID);
        order.setNonce_str(generateNonceStr());
        if (WeChatPayConstant.SignType.MD5.equals(signType)) order.setSign(WeChatPayConstant.MD5);
        else if (WeChatPayConstant.SignType.HMACSHA256.equals(signType))
            order.setSign_type(WeChatPayConstant.HMACSHA256);/*生成签名*/
        order.setSign(WeChatPayUtil.generateSignature(CollectionUtils.classCastMap(order), WeChatPayConstant.getKEY()));
        return order;
    }

    /**
     * 生成签名 @param data 待签名数据 @param key  API密钥 @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key) throws Exception {
        return generateSignature(data, key, WeChatPayConstant.SignType.MD5);
    }

    /**
     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 @param data     待签名数据 @param key      API密钥 

@param signType 签名方式 @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, WeChatPayConstant.SignType signType) 

throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WeChatPayConstant.FIELD_SIGN)) continue;
            if (data.get(k).trim().length() > 0) /* 参数值为空,则不参与签名*/
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        if (WeChatPayConstant.SignType.MD5.equals(signType)) return MD5(sb.toString()).toUpperCase();
        else if (WeChatPayConstant.SignType.HMACSHA256.equals(signType)) return HMACSHA256(sb.toString(), key);
        else throw new Exception(String.format("Invalid sign_type: %s", signType));
    }

    /**
     * 判断签名是否正确 @param xmlStr XML格式数据 @param key API密钥 @return 签名是否正确 @throws Exception
     */
    public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
        Map<String, String> data = XMLUtil.xmlToMap(xmlStr);
        if (!data.containsKey(WeChatPayConstant.FIELD_SIGN)) return false;
        String sign = data.get(WeChatPayConstant.FIELD_SIGN);
        return generateSignature(data, key).equals(sign);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。 @param data Map类型数据 @param key  API密钥 @return 

签名是否正确 @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
        return isSignatureValid(data, key, WeChatPayConstant.SignType.MD5);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。 @param data     Map类型数据 @param key      API密钥 @param 

signType 签名方式 @return 签名是否正确 @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key, WeChatPayConstant.SignType signType) 

throws Exception {
        if (!data.containsKey(WeChatPayConstant.FIELD_SIGN)) return false;
        String sign = data.get(WeChatPayConstant.FIELD_SIGN);
        return generateSignature(data, key, signType).equals(sign);
    }

    /**
     * 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。 @param reqData 向wxpay post的请求数据 @return 签名是否

有效 @throws Exception
     */
    public static boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {/* 返回数据的签名方式和请

求中给定的签名方式是一致的*/
        return WeChatPayUtil.isSignatureValid(reqData, WeChatPayConstant.getKEY(), WeChatPayConstant.SignType.MD5);
    }

    /**
     * 获取随机字符串 Nonce Str @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index)
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        return new String(nonceChars);
    }

    /**
     * 生成 MD5 @param data 待处理数据 @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        return sb.toString().toUpperCase();
    }

    /**
     * 生成 HMACSHA256 @param data 待处理数据 @param key  密钥 @return 加密结果 @throws Exception
     */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        return sb.toString().toUpperCase();
    }

}

常量类:需要对一些必要参数进行封装

{

    public enum SignType {
        MD5, HMACSHA256
    }

    //续费的时长
    public static final int RENEWAL_FEE_TIME = 31536000; //此处可以省略
    //微信平台ID
    public static final String APPID = "XXXXXXXXXXXX";
    //商户ID
    public static final String MCHID = "XXXXXXXXX";
    //商户秘钥
    private static final String KEY = "XXXXXXXXXXXXXXXXXXXX";
    //微信统一下单接口
    public static final String GET_PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    //微信检查订单接口
    public static final String CHECK_ORDER = "https://api.mch.weixin.qq.com/pay/orderquery";
    //交易金额
    private static final Double AMOUNT = 0.01;

    public static final String FIELD_SIGN = "sign";

    public static final String FAIL = "FAIL";

    public static final String MD5 = "MD5";

    public static final String HMACSHA256 = "HMAC-SHA256";


    //交易状态
    /**
     * 支付成功
     */
    public static final String SUCCESS = "SUCCESS";
    /**
     * 转入退款
     */
    public static final String REFUND = "REFUND";
    /**
     * 未支付
     */
    public static final String NOTPAY = "NOTPAY";
    /**
     * 已关闭
     */
    public static final String CLOSED = "CLOSED";
    /**
     * 已撤销(付款码支付)
     */
    public static final String REVOKED = "REVOKED";
    /**
     * 用户支付中(付款码支付)
     */
    public static final String USERPAYING = "USERPAYING";
    /**
     * 支付失败(其他原因,如银行返回失败)
     */
    public static final String PAYERROR = "PAYERROR";

    //商品编号
    /**
     * 微信公众号服务续费一年
     */
    public static final String RENEW_OA_ONE_YEAR = "微信公众号服务续费一年";

    public static String getKEY() {
        return KEY;
    }

    public static Double getAMOUNT() {
        return AMOUNT;
    }


}

具体实现
1 定义创建订单的controller(返回二维码以及将预订单数据插入数据库中,方便之后业务逻辑进行判断)

/* 此处假设业务为将某公众号业务时间增长*/
	 public ResultInfo createOrder(HttpServletRequest HttpServletRequest, @RequestBody CreateOrderDTO createOrderDTO) 
throws Exception {
        /*判断传入的公众号id是否为空*/
        if (createOrderDTO.getOaId() == null) throw new RException(CustomErrcodeEnum.OA_ID_IS_NULL);
        /*返回数据*/
        CreateOrderVO createOrderVO = weChatPayService.retuenWeChatPayCodeUrl(HttpServletRequest, createOrderDTO);
        if (ObjectUtil.isEmpty(createOrderVO)) throw new RException(CustomErrcodeEnum.ADD_ORDER_ERROR);
        else return ResultInfoUtil.success(createOrderVO);
    }


/*其中的实现类retuenWechatPayCodeUrl*/    CreateOrderVO中包含二维码url以及订单编号(订单编号可方便后边进行逻辑判断)
 public CreateOrderVO retuenWeChatPayCodeUrl(HttpServletRequest request, CreateOrderDTO createOrderDTO) throws 

Exception {
	/*通过request获取支付订单所需要的信息*/
	OrderPO order = CreateOrderPO(request); /**其中createOrderPO为将request的信息存储到OrderPO类中
        /*把对象转换成XML*/
        String toXmlString = XMLUtil.toXmlString(order);
        /*调用微信支付统一下单接口,让微信也生成一个预支付订单*/
        String xmlResult = HttpRequestUtil.post(WeChatPayConstant.GET_PAY_URL, toXmlString);
        /*把返回的xml字符串转成对象*/
        WxOrderResultDTO entity = (WxOrderResultDTO) XMLUtil.xmlStrToBean(xmlResult, WxOrderResultDTO.class);
        /*保存订单*/
        if (entity.getCode_url() != null) {/*设置id*/
            order.setId(StringUtil.getUUID());/*设置续费公众号的id*/
            order.setOaId(createOrderDTO.getOaId());/*设置付款结果*/
            order.setDoneFlag(false);/*保存订单信息*/
            orderService.insert(order);
            CreateOrderVO createOrderVO = new CreateOrderVO();
            createOrderVO.setCodeUrl(entity.getCode_url());
            createOrderVO.setOutTradeNo(order.getOut_trade_no());
            return createOrderVO;
        } else return null;
}


/*将request中的信息封装到订单类OrderPO中*/
	private OrderPO CreateOrderPO(HttpServletRequest request) throws Exception {
        double price = WeChatPayConstant.getAMOUNT();
        /*生成订单编号*/
        int number = (int) ((Math.random() * 9) * 1000);
        /*时间*/
        String orderNumber = TimeUtil.yyyyMMddHHmmssNoCut.format(new Date()) + number;
        /*准备调用接口需要的参数*/
        OrderPO order = new OrderPO();
        /*商品描述*/
        order.setBody(WeChatPayConstant.RENEW_OA_ONE_YEAR);
        /*交易类型*/
        order.setTrade_type("NATIVE");
        /*商户订单号*/
        order.setOut_trade_no(orderNumber);
        /*支付金额(单位:分)*/
        order.setTotal_fee((int) (price * 100));
        /*用户ip地址*/
        order.setSpbill_create_ip(HttpUtil.getIpAddress(request)); //获取本机ip地址 
        /*接收支付结果的地址*/
        order.setNotify_url(wechatConfigUtil.getCmkfUrl() + "/XXXXXXXX");  //XXXXX为回调接口的接口名
        return WeChatPayUtil.fillRequestData(order);
    }

定义回调接口

@PostMapping("/name") ------------------------>上方接收支付结果的地址
    public ResultInfo wechatPayBack() throws Exception {
        WxPayResultDTO wxPayResultDTO = null;
        /*从数据流中取出支付结果相关的数据封装到对象当中*/
        try {
            InputStream inStream = request.getInputStream();
            int _buffer_size = 1024;
            if (inStream != null) {
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                byte[] tempBytes = new byte[_buffer_size];
                int count = -1;
                while ((count = inStream.read(tempBytes, 0, _buffer_size)) != -1) outStream.write(tempBytes, 0, count);
                tempBytes = null;
                outStream.flush();
                /*将流转换成字符串*/
                String result = new String(outStream.toByteArray(), "UTF-8");
                wxPayResultDTO = (WxPayResultDTO) XMLUtil.xmlStrToBean(result, WxPayResultDTO.class);
            }
        } catch (Exception e) {
            e.getStackTrace();
        }
        /*如果对象不为空*/
        if (ObjectUtil.isNotEmpty(wxPayResultDTO))
            return ResultInfoUtil.success(weChatPayService.wechatPayBack(wxPayResultDTO));
        else throw new RException(CustomErrcodeEnum.CHECK_SIGN_ERROR);
    }


/**WxPayResultDTO**/            /* 微信回调固定返回的参数*/   
@Getter
@Setter
@ToString
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxPayResultDTO {
    //返回状态码
    private String return_code;
    //业务结果
    private String result_code;
    //返回信息
    private String return_msg;
    //公众账号ID
    private String appid;
    //订单金额
    private String total_fee;
    //现金支付金额
    private String cash_fee;
    //签名
    private String sign;
    //用户标识
    private String openid;
    //微信支付订单号
    private String transaction_id;
    //商户订单号
    private String out_trade_no;
    //商户号
    private String mch_id;
    //随机字符串
    private String nonce_str;
    //付款银行
    private String bank_type;
    //货币种类
    private String fee_type;
    //是否关注公众账号
    private String is_subscribe;
    //支付完成时间
    private String time_end;
    //交易类型
    private String trade_type;

}

支付回调检查

此处为方便前端进行支付成功判断,并跳转页面,故此处可写个通过订单号查询预支付订单中的Trade_Status字段是否为success,并返回给前端进行判断

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值