Spring Boot 微信小程序接入微信支付

1、导入相关依赖 pom.yml

<!-- 微信支付 SDK -->
<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>0.0.3</version>
</dependency>

2、文件配置微信公众号的基础信息 application.yml

# 微信支付配置 notifyUrl:微信支付异步回调地址
pay:
  appId: #小程序应用id(小程序平台mp.weixin.qq.com)
  apiKey: #商户私钥key(微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置)
  mchId: #商户号(微信商户平台(pay.weixin.qq.com)-->产品中心-->开发配置-->商户号)
  appSecret: #小程序密钥(小程序平台mp.weixin.qq.com)
  notifyUrl: #支付回调地址(服务器地址+回调访问路径 https://xxx.com/ballFishing/wechat/weixin/callback)

3、设置配置文件 WxPayConfig.java

package com.ckm.ball.config.wxpay;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
 
/**
 * 微信支付配置
 * @author lf
 * @date 2023/8/30
 */
@Data
@Component
@Configuration
@ConfigurationProperties(prefix = "pay")
public class WxPayConfig {
 
    /**
     * 微信公众号appid
     */
    private String appId;
 
    /**
     * 公众号设置的API v2密钥
     */
    private String apiKey;
 
    /**
     * 微信商户平台 商户id
     */
    private String mchId;
 
    /**
     *小程序密钥
     */
    private String appSecret;
 
    /**
     * 小程序支付异步回调地址
     */
    private String notifyUrl;
 
}

4、微信支付预下单实体类 WeChatPay.java

package com.ckm.ball.config.wxpay;
import lombok.Data;
import lombok.experimental.Accessors;
 
/**
 * 微信支付预下单实体类
 */
@Data
@Accessors(chain = true)
public class WeChatPay {
 
    /**
     * 返回状态码  此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
     */
    public String return_code;
 
    /**
     * 返回信息 当return_code为FAIL时返回信息为错误原因 ,例如 签名失败 参数格式校验错误
     */
    private String return_msg;
 
    /**
     * 公众账号ID 调用接口提交的公众账号ID
     */
    private String appid;
 
    /**
     * 商户号 调用接口提交的商户号
     */
    private String mch_id;
 
    /**
     * api密钥 详见:https://pay.weixin.qq.com/index.php/extend/employee
     */
    private String api_key;
 
    /**
     * 设备号  自定义参数,可以为请求支付的终端设备号等
     */
    private String device_info;
 
    /**
     * 随机字符串    5K8264ILTKCH16CQ2502SI8ZNMTM67VS   微信返回的随机字符串
     */
    private String nonce_str;
 
    /**
     * 签名 微信返回的签名值,详见签名算法:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
     */
    private String sign;
 
    /**
     * 签名类型
     */
    private String sign_type;
 
 
    /**
     * 业务结果 SUCCESS SUCCESS/FAIL
     */
    private String result_code;
 
    /**
     * 错误代码 当result_code为FAIL时返回错误代码,详细参见下文错误列表
     */
    private String err_code;
 
    /**
     * 错误代码描述 当result_code为FAIL时返回错误描述,详细参见下文错误列表
     */
    private String err_code_des;
 
    /**
     * 交易类型 JSAPI JSAPI -JSAPI支付 NATIVE -Native支付 APP -APP支付 说明详见;https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_2
     */
    private String trade_type;
 
    /**
     * 预支付交易会话标识 微信生成的预支付会话标识,用于后续接口调用中使用,该值有效期为2小时
     */
    private String prepay_id;
 
    /**
     * 二维码链接     weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00 trade_type=NATIVE时有返回,此url用于生成支付二维码,然后提供给用户进行扫码支付。注意:code_url的值并非固定,使用时按照URL格式转成二维码即可
     */
    private String code_url;
 
    /**
     * 商品描述  商品简单描述,该字段请按照规范传递,具体请见 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_2
     */
    private String body;
 
    /**
     * 商家订单号 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一。详见商户订单号 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_2
     */
    private String out_trade_no;
 
    /**
     * 标价金额 订单总金额,单位为分,详见支付金额 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_2
     */
    private String total_fee;
 
    /**
     * 终端IP 支持IPV4和IPV6两种格式的IP地址。用户的客户端IP
     */
    private String spbill_create_ip;
 
    /**
     * 通知地址 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
     */
    private String notify_url;
 
    /**
     * 子商户号 sub_mch_id 非必填(商户不需要传入,服务商模式才需要传入) 微信支付分配的子商户号
     */
    private String sub_mch_id;
 
    /**
     * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
     */
    private String attach;
 
    /**
     * 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
     */
    private String out_refund_no;
 
    /**
     * 退款总金额,单位为分,只能为整数,可部分退款。详见支付金额 https://pay.weixin.qq.com/wiki/doc/api/native_sl.php?chapter=4_2
     */
    private String refund_fee;
 
    /**
     * 退款原因 若商户传入,会在下发给用户的退款消息中体现退款原因 注意:若订单退款金额≤1元,且属于部分退款,则不会在退款消息中体现退款原因
     */
    private String refund_desc;
 
    /**
     * 交易结束时间 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则 注意:最短失效时间间隔必须大于5分钟
     */
    private String time_expire;
 
    /**
     * 用户标识 trade_type=JSAPI,此参数必传,用户在主商户appid下的唯一标识。openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。下单前需要调用【网页授权获取用户信息: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 】接口获取到用户的Openid。
     */
    private String openid;
 
    /**
     * 时间戳
     */
    private String time_stamp;
 
    /**
     * 会员类型
     */
    private String memberShipType;
 
}

5、微信支付API地址 WeChatPayUrlConstants.java

package com.ckm.ball.config.wxpay;
/**
 * 微信支付API地址
 * @author lf
 * @date 2023/8/30
 */
public class WeChatPayUrlConstants {
 
    /**
     * 统一下单预下单接口url
     */
    public static final String Uifiedorder = "https://api.mch.weixin.qq.com/pay/unifiedorder";
 
    /**
     * 订单状态查询接口URL
     */
    public static final String Orderquery = "https://api.mch.weixin.qq.com/pay/orderquery";
 
    /**
     * 订单申请退款
     */
    public static final String Refund = "https://api.mch.weixin.qq.com/secapi/pay/refund";
 
    /**
     * 付款码 支付
     */
    public static final String MicroPay = "https://api.mch.weixin.qq.com/pay/micropay";
 
    /**
     * 微信网页授权 获取“code”请求地址
     */
    public static final String GainCodeUrl = "https://open.weixin.qq.com/connect/oauth2/authorize";
 
    /**
     * 微信网页授权 获取“code” 回调地址
     */
    public static final String GainCodeRedirect_uri = "http://i5jmxe.natappfree.cc/boss/WeChatPayMobile/SkipPage.html";
 
}

6、预下单成功之后返回结果 OrderReturnInfo.java

package com.ckm.ball.config.wxpay;
import lombok.Data;
 
@Data
public class OrderReturnInfo {
 
    private String return_code;
 
    private String return_msg;
 
    private String result_code;
 
    private String appid;
 
    private String mch_id;
 
    private String nonce_str;
 
    private String sign;
 
    private String prepay_id;
 
    private String trade_type;
 
}

7、查询订单返回的实体类 QueryReturnInfo.java

package com.ckm.ball.config.wxpay;
import lombok.Data;
 
 
/**
 * 查询订单返回实体类
 * @author lf
 * @date 2023/9/1
 */
@Data
public class QueryReturnInfo {
 
    private String return_code;
 
    private String return_msg;
 
    private String result_code;
 
    private String err_code;
 
    private String err_code_des;
 
    private String appid;
 
    private String mch_id;
 
    private String nonce_str;
 
    private String sign;
 
    private String prepay_id;
 
    private String trade_type;
 
    private String device_info;
 
    private String openid;
 
    private String is_subscribe;
 
    private String trade_state;
 
    private String bank_type;
 
    private int total_fee;
 
    private int settlement_total_fee;
 
    private String fee_type;
 
    private int cash_fee;
 
    private String cash_fee_type;
 
    private int coupon_fee;
 
    private int coupon_count;
 
    private String coupon_type_$n;
 
    private String coupon_id_$n;
 
    private String transaction_id;
 
    private String out_trade_no;
 
    private String time_end;
 
    private String trade_state_desc;
 
}

8、签名实体类 SignInfo.java

package com.ckm.ball.config.wxpay;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
/**
 * 签名实体类
 * @author lf
 * @date 2023/9/1
 */
@Data
public class SignInfo {
 
    private String appId;//小程序ID
 
    private String timeStamp;//时间戳
 
    private String nonceStr;//随机串
 
    @XStreamAlias("package")
    private String repay_id;
 
    private String signType;//签名方式
 
    public String getAppId() {
        return appId;
    }
 
    public void setAppId(String appId) {
        this.appId = appId;
    }
 
    public String getTimeStamp() {
        return timeStamp;
    }
 
    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }
 
    public String getNonceStr() {
        return nonceStr;
    }
 
    public void setNonceStr(String nonceStr) {
        this.nonceStr = nonceStr;
    }
 
    public String getRepay_id() {
        return repay_id;
    }
 
    public void setRepay_id(String repay_id) {
        this.repay_id = repay_id;
    }
 
    public String getSignType() {
        return signType;
    }
 
    public void setSignType(String signType) {
        this.signType = signType;
    }
 
}

9、Http工具类 HttpRequest.java

package com.ckm.ball.utils;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
/**
 * Http工具类
 * @author lf
 * @date 2023/9/1
 */
public class HttpRequest {
 
    //连接超时时间,默认10秒
    private static final int socketTimeout = 10000;
 
    //传输超时时间,默认30秒
    private static final int connectTimeout = 30000;
 
    /**
     * post请求
     *
     * @throws IOException
     * @throws ClientProtocolException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws KeyManagementException
     * @throws UnrecoverableKeyException
     */
    public static String sendPost(String url, Object xmlObj) throws ClientProtocolException, IOException, UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException {
 
 
        HttpPost httpPost = new HttpPost(url);
        //解决XStream对出现双下划线的bug
        XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
        xStreamForRequestPostData.alias("xml", xmlObj.getClass());
        //将要提交给API的数据对象转换成XML格式数据Post给API
        String postDataXML = xStreamForRequestPostData.toXML(xmlObj);
 
        //得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
 
        //设置请求器的配置
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);
 
        HttpClient httpClient = HttpClients.createDefault();
        HttpResponse response = httpClient.execute(httpPost);
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, "UTF-8");
        return result;
    }
 
    /**
     * 自定义证书管理器,信任所有证书
     *
     * @author pc
     */
    public static class MyX509TrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] arg0, String arg1)
                throws CertificateException {
 
        }
 
        @Override
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] arg0, String arg1)
                throws CertificateException {
 
        }
 
        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }
 
}

10、微信签名 SignUtils.java

package com.ckm.ball.config.wxpay;

import com.ckm.ball.utils.MD5;
import com.thoughtworks.xstream.annotations.XStreamAlias;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
 * 微信签名
 * @author lf
 * @date 2023/9/1
 */
public class SignUtils {

    /**
     * 签名算法
     *
     * @param o 要参与签名的数据对象
     * @return 签名
     * @throws IllegalAccessException
     */
    public static String getSign(Object o,String key) throws IllegalAccessException {
        ArrayList<String> list = new ArrayList<String>();
        Class cls = o.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            if (f.get(o) != null && f.get(o) != "") {
                String name = f.getName();
                XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
                if (anno != null) {
                    name = anno.value();
                }
                list.add(name + "=" + f.get(o) + "&");
            }
        }
        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + key;
        System.out.println("签名数据:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        return result;
    }

    public static String getSign(Map<String, Object> map,String key) {
        ArrayList<String> list = new ArrayList<String>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != "") {
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + key;
        //Util.log("Sign Before MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        //Util.log("Sign Result:" + result);
        return result;
    }
}

11、MD5加密工具类 MD5.java

package com.ckm.ball.utils;

import java.security.MessageDigest;
 
/**
 * MD5加密工具类
 * @author lf
 * @date 2023/9/1
 */
public class MD5 {
    private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
            "8", "9", "a", "b", "c", "d", "e", "f"};
 
    /**
     * 转换字节数组为16进制字串
     *
     * @param b 字节数组
     * @return 16进制字串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for (byte aB : b) {
            resultSb.append(byteToHexString(aB));
        }
        return resultSb.toString();
    }
 
    /**
     * 转换byte到16进制
     *
     * @param b 要转换的byte
     * @return 16进制格式
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }
 
    /**
     * MD5编码
     *
     * @param origin 原始字符串
     * @return 经过MD5加密之后的结果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }
}

12、微信支付service接口 WxPayInfoService.java

package com.ckm.ball.config.wxpay;

import java.util.Map;

/**
 * 微信小程序支付-业务接口层
 * @author lf
 * @date 2023/8/31
 */
public interface WxPayInfoService {

    /**
     * 插入订单记录
     */
    Map<String, Object> insertPayRecord();

    /**
     * 查询订单
     * @param out_trade_no 订单号
     * @return 返回结果
     */
    Map<String, Object> orderQuery(String out_trade_no);

}

13、微信支付service接口实现类 WxPayInfoServiceImpl.java

package com.ckm.ball.config.wxpay;
import cn.hutool.core.util.ObjectUtil;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ckm.ball.utils.HttpRequest;
import com.thoughtworks.xstream.XStream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;

/**
 * 微信小程序支付-业务接口实现层
 * @author lf
 * @date 2023/8/31
 */
@Service
@Slf4j
public class WxPayInfoServiceImpl implements WxPayInfoService {

    @Resource
    private WxPayConfig payProperties;

    private static final DecimalFormat df = new DecimalFormat("#");

    /**
     * 插入订单记录
     * @param payParameterVO 用户ID 会员套餐ID
     * @return 返回结果
     */
    @Override
    @Transactional
    public Map<String, Object> insertPayRecord() {
        //接收返回的参数
        Map<String, Object> map = new HashMap<>();
        String title = "ckm";
        //金额 * 100 以分为单位
        BigDecimal fee = BigDecimal.valueOf(1);
        BigDecimal RMB = new BigDecimal(100);
        BigDecimal totalFee = fee.multiply(RMB);

        try {
            WeChatPay weChatPay = new WeChatPay();
            weChatPay.setAppid(payProperties.getAppId());
            weChatPay.setMch_id(payProperties.getMchId());
            weChatPay.setNonce_str(getRandomStringByLength(32));
            weChatPay.setBody(title);
            weChatPay.setOut_trade_no(getRandomStringByLength(32));
            weChatPay.setTotal_fee( df.format(Double.parseDouble(String.valueOf(totalFee))));
            weChatPay.setSpbill_create_ip("58.213.48.98");
            weChatPay.setNotify_url(payProperties.getNotifyUrl());
            weChatPay.setTrade_type("JSAPI");
            //这里直接使用当前用户的openid
            weChatPay.setOpenid("o_qV97S-4HaPN51ZbQe4qML625j4");
//            weChatPay.setSign_type("MD5");
            //生成签名

            String sign = SignUtils.getSign(weChatPay,payProperties.getApiKey());
            System.out.println(sign);
            weChatPay.setSign(sign);

            String result = HttpRequest.sendPost(WeChatPayUrlConstants.Uifiedorder, weChatPay);
            System.out.println(result);

            //将返回结果从xml格式转换为map格式
            Map<String, String> wxResultMap = WXPayUtil.xmlToMap(result);
            if (ObjectUtil.isNotEmpty(wxResultMap.get("return_code")) && wxResultMap.get("return_code").equals("SUCCESS")){
                if (wxResultMap.get("result_code").equals("FAIL")){
                    map.put("msg", "统一下单失败");
                    map.put("status",500);
                    map.put("data", wxResultMap.get("err_code_des"));
                    return map;
                }
            }
            XStream xStream = new XStream();

            // 允许特定类的反序列化
            xStream.allowTypes(new Class[] { OrderReturnInfo.class });
            // 设置别名
            xStream.alias("xml", OrderReturnInfo.class);
            // 从XML反序列化
            OrderReturnInfo returnInfo = (OrderReturnInfo) xStream.fromXML(result);

            // 二次签名
            if ("SUCCESS".equals(returnInfo.getReturn_code()) && returnInfo.getReturn_code().equals(returnInfo.getResult_code())) {
                SignInfo signInfo = new SignInfo();
                signInfo.setAppId(payProperties.getAppId());
                long time = System.currentTimeMillis() / 1000;
                signInfo.setTimeStamp(String.valueOf(time));
                signInfo.setNonceStr(WXPayUtil.generateNonceStr());
                signInfo.setRepay_id("prepay_id=" + returnInfo.getPrepay_id());
                signInfo.setSignType("MD5");
                //生成签名
                String sign1 = SignUtils.getSign(signInfo,payProperties.getApiKey());
                Map<String, String> payInfo = new HashMap<>();
                payInfo.put("timeStamp", signInfo.getTimeStamp());
                payInfo.put("nonceStr", signInfo.getNonceStr());
                payInfo.put("package", signInfo.getRepay_id());
                payInfo.put("signType", signInfo.getSignType());
                payInfo.put("paySign", sign1);
                map.put("status", 200);
                map.put("msg", "统一下单成功!");
                map.put("data", payInfo);

                //预下单成功,处理业务逻辑
                //****************************//

                // 业务逻辑结束 回传给小程序端唤起支付
                return map;
            }
            map.put("status", 500);
            map.put("msg", "统一下单失败!");
            map.put("data", null);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询订单
     * @param out_trade_no 订单号
     * @return 返回结果
     */
    @Override
    public Map<String, Object> orderQuery(String out_trade_no){

        Map<String, Object> map = new HashMap<>();

        try {
            WeChatPay weChatPay = new WeChatPay();
            weChatPay.setAppid(payProperties.getAppId());
            weChatPay.setMch_id(payProperties.getMchId());
            weChatPay.setNonce_str(WXPayUtil.generateNonceStr());
            weChatPay.setOut_trade_no(out_trade_no);
            //order.setSign_type("MD5");
            //生成签名
            String sign = SignUtils.getSign(weChatPay,payProperties.getApiKey());
            weChatPay.setSign(sign);

            String result = HttpRequest.sendPost(WXPayConstants.ORDERQUERY_URL, weChatPay);
            System.out.println(result);
            XStream xStream = new XStream();
            xStream.alias("xml", QueryReturnInfo.class);

            QueryReturnInfo returnInfo = (QueryReturnInfo) xStream.fromXML(result);
            map.put("status", 500);
            map.put("msg", "统一下单失败!");
            map.put("data", returnInfo);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取一定长度的随机字符串
     *
     * @param length 指定字符串长度
     * @return 一定长度的字符串
     */
    public static String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

}

14、微信支付Controller层 WeChatPayController.java

package com.ckm.ball.config.wxpay;

import com.ckm.ball.utils.StreamUtils;
import com.github.wxpay.sdk.WXPayUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/ballFishing/wechat")
@Api(value = "微信支付接口")
@CrossOrigin    // @CrossOrigin注解 解决uniapp跨域访问后端问题。
public class WeChatPayController {

    @Resource
    private WxPayInfoService wxPayInfoService;
    /**
     * 小程序支付下单接口
     * @return 返回结果
     */
    @ApiOperation("小程序支付功能")
    @PostMapping("/pay")
    public Map<String,Object> wxPay(){
        return wxPayInfoService.insertPayRecord();
    }


    /**
     * 查询订单
     */
    @ApiOperation("订单查询")
    @PostMapping("/wx/query")
    public Map<String, Object> orderQuery(@RequestParam("out_trade_no") String out_trade_no) {
        return wxPayInfoService.orderQuery(out_trade_no);
    }

    /**
     * 微信小程序支付成功回调
     * @param request 请求
     * @param response 响应
     * @return 返回结果
     * @throws Exception 异常处理
     */
    @RequestMapping("/weixin/callback")
    public String callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("接口已被调用");
        ServletInputStream inputStream = request.getInputStream();
        String notifyXml = StreamUtils.inputStream2String(inputStream, "utf-8");
        System.out.println(notifyXml);

        // 解析返回结果
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyXml);
        // 判断支付是否成功
        if ("SUCCESS".equals(notifyMap.get("result_code"))) {

            //支付成功时候,处理业务逻辑

            System.out.println("支付成功");
            System.out.println("<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                    + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ");

            /**
             * 注意
             * 因为微信回调会有八次之多,所以当第一次回调成功了,那么我们就不再执行逻辑了
             * return返回的结果一定是这种格式,当result_code返回的结果是SUCCESS时,则不进行调用了
             * 如果不返回下面的格式,业务逻辑会出现回调多次的情况,我就遇到过这种情况。
             */
            return "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                    + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
        }

        // 创建响应对象:微信接收到校验失败的结果后,会反复的调用当前回调函数
        Map<String, String> returnMap = new HashMap<>();
        returnMap.put("return_code", "FAIL");
        returnMap.put("return_msg", "");
        String returnXml = WXPayUtil.mapToXml(returnMap);
        response.setContentType("text/xml");
        System.out.println("校验失败");
        return returnXml;
    }
}

签名失败

如果出现签名错误,请检查后再试。去重置秘钥:微信商户平台(pay.weixin.qq.com)–>账户设置–>API安全–>密钥设置
在这里插入图片描述

签名成功

在这里插入图片描述

微信小程序方面

onSubmit(){
  if (!this.data.checked) {
    Toast('请先阅读并勾选会员服务协议')
    return
  }
  pay().then((res)=>{
    console.log(res)
    wx.requestPayment({
      timeStamp: res.data.timeStamp,
      nonceStr: res.data.nonceStr,
      package: res.data.package,
      signType: res.data.signType,
      paySign: res.data.paySign,
      success (res) {
        console.log(res)
      },
      fail (res) {
        console.log(res)
       }
    })
  })
}

在这里插入图片描述

在这里插入图片描述

如果您下载了本程序,但是该程序存在问题无法运行,那么您可以选择退款或者寻求我们的帮助(如果找我们帮助的话,是需要追加额外费用的)。另外,您不会使用资源的话(这种情况不支持退款),也可以找我们帮助(需要追加额外费用) 爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python中的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

令人作呕的溏心蛋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值