资讯详情

JAVA 实现微信Native扫码支付功能

1、在pom.xml中添加依赖

2、在application.yml添加以下配置

3、创建微信支付config配置类

4.微信支付订单

5、支付通知

6.主动查询订单状态

7、apiclient_key.pem(私钥文件)


1、在pom.xml中添加依赖

<!--微信支付SDK--> <dependency>     <groupId>com.github.wechatpay-apiv3</groupId>     <artifactId>wechatpay-apache-httpclient</artifactId>     <version>0.3.0</version> </dependency>  <!-- json处理器:介绍gson依赖 --> <dependency>     <groupId>com.google.code.gson</groupId>     <artifactId>gson</artifactId>     <version>2.8.6</version> </dependency>

2、在application.yml添加以下配置

# 微信支付相关参数 wxpay:   # 商户号   mch-id: xxx1228xx   # 商户API证书序列号   mch-serial-no: xxx6xxx5414D07xxxxxxxxECBBD047AA6xxx   # 商户私钥文件 # 注:本文件放在项目根目目录下   private-key-path: ./apiclient_key.pem   # APIv3密钥   api-v3-key: xxxxxxxycUJLDxxxZplQR683Mxxxxxx   # APPID   appid: xxxxxxc27e0e7cxxx   # 微信服务器地址   domain: https://api.mch.weixin.qq.com   # 接收结果通知地址   # 注:每次重新启动ngrok,需要根据实际情况修改此配置   notify-domain: https://c7c1-240e-3b5-3015-be0-1bc-9bed-fca4-d09b.ngrok.io 

3.创建微信支付config配置类

@Configuration @ConfigurationProperties(prefix = "wxpay") //读取wxpay节点 @Data //使用set方法将wxpay当前类属性填充节点中的值 @Slf4j public class WxPayConfig {      // 商户号     private String mchId;      // 商户API证书序列号     private String mchSerialNo;      // 商户私钥文件     private String privateKeyPath;      // APIv3密钥     private String apiV3Key;      // APPID     private String appid;      // 微信服务器地址     private String domain;      // 接收结果通知地址     private String notifyDomain;      /**      * 获取商户的私钥文件      *      * @param filename      * @return      */     private PrivateKey getPrivateKey(String filename) {          try {             return PemUtil.loadPrivateKey(new FileInputStream(filename));         } catch (FileNotFoundException e) {             throw new RuntimeException("私钥文件不存在", e);         }     }      /**      * 获取签名验证器      *      * @return      */     @Bean     public ScheduledUpdateCertificatesVerifier getVerifier() {          log.info("获取签名验证器");          //获取商户私钥         PrivateKey privateKey = getPrivateKey(privateKeyPath);          //私钥签名对象         PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);          //身份认证对象         WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);          // 使用定期更新的签名验证器,无需传入证书         ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(                 wechatPay2Credentials,                 apiV3Key.getBytes(StandardCharsets.UTF_8));          return verifier;     }       /**      * 获取http请求对象      *      * @param verifier      * @return      */     @Bean(name = "wxPayClient")     public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier) {          log.info("获取httpClient");          //获取商户私钥         PrivateKey privateKey = getPrivateKey(privateKeyPath);          WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()                 .withMerchant(mchId, mchSerialNo, privateKey)                 .withValidator(new WechatPay2Validator(verifier));         // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient          // 通过WechatPayHttpClientBuilder构造的HttpClient,签名和验签将自动处理,并自动更新证书         CloseableHttpClient httpClient = builder.build();          return httpClient;     }      /**      * 获取HttpClient,无需验证应答签名,跳过验签流程      */     @Bean(name = "wxPayNoSignClient")     public CloseableHttpClient getWxPayNoSignClient() {          //获取商户私钥         PrivateKey privateKey = getPrivateKe(privateKeyPath);

        //用于构造HttpClient
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                //设置商户信息
                .withMerchant(mchId, mchSerialNo, privateKey)
                //无需进行签名验证、通过withValidator((response) -> true)实现
                .withValidator((response) -> true);

        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();

        log.info("== getWxPayNoSignClient END ==");

        return httpClient;
    }

}

4、微信支付下单

 Controller:

    /**
     * native下单
     *
     * @param nativeReq
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "微信支付下单", notes = "微信支付下单")
    @PostMapping("/native")
    @Log(BusinessTypeEnum.OTHER)
    public Result<NativeRes> nativePay(@RequestBody NativeReq nativeReq) throws Exception {
        log.info("发起支付请求, params:{}", Json.toJsonString(nativeReq));
        
        UxmOrder uxmOrder = orderService.createOrderByProductId(nativeReq, PayType.WXPAY);
        if (nativeReq.getPrice().compareTo(uxmOrder.getAmount()) != 0) {
            throw new ServiceException(ResultCode.ORDER_AMOUNT_NOT_FIT.getMessage(), ResultCode.ORDER_AMOUNT_NOT_FIT.getCode());
        }
        //生成订单
        orderService.save(uxmOrder);
        //调用统一下单api(返回支付二维码连接和订单号)
        NativeRes nativeRes = wxPayService.nativePay(uxmOrder);
        return Result.success(nativeRes);

    }

service:

    @Resource
    private WxPayConfig wxPayConfig;

    @Resource
    private CloseableHttpClient wxPayClient;

    @Resource
    private IUxmOrderService orderService;

    /**
     * 调用统一下单api
     *
     * @param uxmOrder
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public NativeRes nativePay(UxmOrder uxmOrder) throws Exception {
        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));

        // 请求body参数
        Gson gson = new Gson();
        Map paramsMap = new HashMap();
        paramsMap.put("appid", wxPayConfig.getAppid());
        paramsMap.put("mchid", wxPayConfig.getMchId());
        paramsMap.put("description", uxmOrder.getSubscribePlan());
        paramsMap.put("out_trade_no", uxmOrder.getOrderNo());
        paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxApiType.NATIVE_NOTIFY.getType()));

        Map amountMap = new HashMap();
        //由单位:元 转换为单位:分,并由Bigdecimal转换为整型
        String amount = uxmOrder.getAmount().toString();
        BigDecimal total = BigDecimalUtil.mul(amount, "100");
        amountMap.put("total", total.intValue());
        amountMap.put("currency", "CNY");

        paramsMap.put("amount", amountMap);

        //将参数转换成json字符串
        String jsonParams = gson.toJson(paramsMap);
        log.info("请求参数 ===> {}" + jsonParams);

        StringEntity entity = new StringEntity(jsonParams, "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = wxPayClient.execute(httpPost);

        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
            int statusCode = response.getStatusLine().getStatusCode();//响应状态码
            if (statusCode == 200) { //处理成功
                log.info("成功, 返回结果 = " + bodyAsString);
            } else if (statusCode == 204) { //处理成功,无返回Body
                log.info("成功");
            } else {
                log.info("Native下单失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
                throw new IOException("request failed");
            }

            //响应结果
            Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
            //二维码
            String codeUrl = resultMap.get("code_url");

            //保存二维码
            String orderNo = uxmOrder.getOrderNo();
            orderService.saveCodeUrl(orderNo, codeUrl);

            //返回二维码\
            NativeRes nativeRes = new NativeRes();
            nativeRes.setCodeUrl(codeUrl);
            nativeRes.setOrderNo(uxmOrder.getOrderNo());

            return nativeRes;

        } finally {
            response.close();
        }
    }

封装微信支付api接口:

@AllArgsConstructor
@Getter
public enum WxApiType {

    /**
     * Native下单
     */
    NATIVE_PAY("/v3/pay/transactions/native"),

    /**
     * 查询订单
     */
    ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"),

    /**
     * 关闭订单
     */
    CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),

    /**
     * 支付通知
     */
    NATIVE_NOTIFY("/api/wx-pay/native/notify");

    /**
     * 类型
     */
    private final String type;
}

5、支付通知

Controller:

    /**
     * 支付通知
     * 微信支付通过支付通知接口将用户支付成功消息通知给商户
     */
    @ApiOperation(value = "支付通知", notes = "支付通知")
    @PostMapping("/native/notify")
    @Log(BusinessTypeEnum.OTHER)
    public Result<String> nativeNotify(HttpServletRequest request, HttpServletResponse response) {
        Gson gson = new Gson();
        Map<String, String> map = new HashMap<>();

        try {
            //处理通知参数
            String body = HttpUtils.readData(request);
            Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
            String requestId = (String) bodyMap.get("id");

            //签名的验证
            WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
                    = new WechatPay2ValidatorForRequest(verifier, requestId, body);
            if (!wechatPay2ValidatorForRequest.validate(request)) {

                log.error("通知验签失败");
                //失败应答
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "通知验签失败");
                return Result.success(gson.toJson(map));
            }
            log.info("通知验签成功");

            //处理订单
            wxPayService.processOrder(bodyMap);

            //应答超时
            //模拟接收微信端的重复通知
            TimeUnit.SECONDS.sleep(5);

            //成功应答
            response.setStatus(200);
            map.put("code", "SUCCESS");
            map.put("message", "成功");
            return Result.success(gson.toJson(map));

        } catch (Exception e) {
            e.printStackTrace();
            //失败应答
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "失败");
            return Result.success(gson.toJson(map));
        }
    }

HttpUtils.java

public class HttpUtils {

    /**
     * 将通知参数转化为字符串
     *
     * @param request
     * @return
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 签名验证类:

/**
 * 签名验证
 */
public class WechatPay2ValidatorForRequest {

    protected static final Logger log = LoggerFactory.getLogger(WechatPay2ValidatorForRequest.class);
    /**
     * 应答超时时间,单位为分钟
     */
    protected static final long RESPONSE_EXPIRED_MINUTES = 5;
    protected final Verifier verifier;
    protected final String requestId;
    protected final String body;


    public WechatPay2ValidatorForRequest(Verifier verifier, String requestId, String body) {
        this.verifier = verifier;
        this.requestId = requestId;
        this.body = body;
    }

    protected static IllegalArgumentException parameterError(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("parameter error: " + message);
    }

    protected static IllegalArgumentException verifyFail(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("signature verify fail: " + message);
    }

    public final boolean validate(HttpServletRequest request) throws IOException {
        try {
            //处理请求参数
            validateParameters(request);

            //构造验签名串
            String message = buildMessage(request);

            String serial = request.getHeader(WECHAT_PAY_SERIAL);
            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);

            //验签
            if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
                throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
                        serial, message, signature, requestId);
            }
        } catch (IllegalArgumentException e) {
            log.warn(e.getMessage());
            return false;
        }

        return true;
    }

    protected final void validateParameters(HttpServletRequest request) {

        // NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};

        String header = null;
        for (String headerName : headers) {
            header = request.getHeader(headerName);
            if (header == null) {
                throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
            }
        }

        //判断请求是否过期
        String timestampStr = header;
        try {
            Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
            // 拒绝过期请求
            if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
                throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
            }
        } catch (DateTimeException | NumberFormatException e) {
            throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
        }
    }

    protected final String buildMessage(HttpServletRequest request) throws IOException {
        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(WECHAT_PAY_NONCE);
        return timestamp + "\n"
                + nonce + "\n"
                + body + "\n";
    }

    protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
        HttpEntity entity = response.getEntity();
        return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
    }

}

Service:

    private final ReentrantLock lock = new ReentrantLock();    

    /**
     * 处理订单
     *
     * @param bodyMap
     */
    @Override
    public void processOrder(Map<String, Object> bodyMap) throws GeneralSecurityException {
        log.info("处理订单");

        //解密报文
        String plainText = decryptFromResource(bodyMap);

        //将明文转换成map
        Gson gson = new Gson();
        HashMap plainTextMap = gson.fromJson(plainText, HashMap.class);
        String orderNo = (String) plainTextMap.get("out_trade_no");


        /*在对业务数据进行状态检查和处理之前,
        要采用数据进行并发控制,
        以避免函数重入造成的数据混乱*/
        //尝试获取锁:
        // 成功获取则立即返回true,获取失败则立即返回false。不必一直等待锁的释放
        if (lock.tryLock()) {
            try {
                //处理重复的通知
                //接口调用的幂等性:无论接口被调用多少次,产生的结果是一致的。
                String orderStatus = orderService.getOrderStatus(orderNo);
                if (!OrderStatus.NOTPAY.getType().equals(orderStatus)) {
                    return;
                }

                //模拟通知并发
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //设置订单状态、升级版本、设置过期时间、添加支付记录
                this.setOrderInfo(orderNo, plainText);

            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }
    }

密文解密:

/**
     * 对称解密
     *
     * @param bodyMap
     * @return
     */
    private String decryptFromResource(Map<String, Object> bodyMap) throws GeneralSecurityException {

        log.info("密文解密");

        //通知数据
        Map<String, String> resourceMap = (Map) bodyMap.get("resource");
        //数据密文
        String ciphertext = resourceMap.get("ciphertext");
        //随机串
        String nonce = resourceMap.get("nonce");
        //附加数据
        String associatedData = resourceMap.get("associated_data");

        AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        //数据明文
        String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext);

        return plainText;
    }
    /**
     * 设置订单状态、添加支付记录
     * @param orderNo
     * @param result
     */
    private void setOrderInfo(String orderNo, String result){
        UxmOrder order = orderService.getOne(new LambdaQueryWrapper<UxmOrder>()
                .eq(UxmOrder::getOrderNo, orderNo), false);
        if (OrderStatus.NOTPAY.getType().equals(order.getPayStatus())) {
            //更新订单状态
            orderService.updateStatusByOrderNo(orderNo, OrderStatus.SUCCESS);

            //记录支付日志
            paymentInfoService.createPaymentInfo(result);
        }
    }

6、主动查询订单状态

Controller:

    /**
     * 查询订单状态
     */
    @ApiOperation(value = "查询订单状态", notes = "查询订单状态")
    @PostMapping("/order/confirm/{orderNo}")
    public Result<String> orderConfirm(@PathVariable String orderNo) throws Exception {
        //通过订单号查询订单
        UxmOrder uxmOrder = orderService.getOrderByOrderNo(orderNo);
        if (ObjectUtil.isNull(uxmOrder)) {
            throw new Exception("参数错误,该订单不存在!");
        }
        //判断订单是否已支付,未支付则调用微信查询订单接口
        if (OrderStatus.NOTPAY.getType().equals(uxmOrder.getPayStatus())) {
            //未支付订单状态:调用微信支付查单接口
            return Result.success(wxPayService.checkOrderStatus(orderNo));
        }
        return Result.success(WxTradeState.SUCCESS.getType());//SUCCESS   NOTPAY
    }

Service:

    /**
     * 查询订单状态
     *
     * @param orderNo
     * @return
     */
    @Override
    public String checkOrderStatus(String orderNo) throws Exception {
        log.warn("根据订单号核实订单状态 ===> {}", orderNo);

        //调用微信支付查单接口
        String result = this.queryOrder(orderNo);

        Gson gson = new Gson();
        Map<String, String> resultMap = gson.fromJson(result, HashMap.class);

        //获取微信支付端的订单状态
        String tradeState = resultMap.get("trade_state");

        //判断订单状态
        if (WxTradeState.SUCCESS.getType().equals(tradeState)) {

            log.warn("核实订单已支付 ===> {}", orderNo);

            //设置订单状态、升级版本、设置过期时间、添加支付记录
            this.setOrderInfo(orderNo, result);

            return tradeState;
        }

        return WxTradeState.NOTPAY.getType();
    }
    /**
     * 调用微信支付查单接口
     *
     * @param orderNo
     * @return
     * @throws Exception
     */
    private String queryOrder(String orderNo) throws Exception {

        log.info("查单接口调用 ===> {}", orderNo);

        String url = String.format(WxApiType.ORDER_QUERY_BY_NO.getType(), orderNo);
        url = wxPayConfig.getDomain().concat(url).concat("?mchid=").concat(wxPayConfig.getMchId());

        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = wxPayClient.execute(httpGet);

        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
            int statusCode = response.getStatusLine().getStatusCode();//响应状态码
            if (statusCode == 200) { //处理成功
                log.info("成功, 返回结果 = " + bodyAsString);
            } else if (statusCode == 204) { //处理成功,无返回Body
                log.info("成功");
            } else {
                log.info("查单接口调用,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
                throw new IOException("request failed");
            }

            return bodyAsString;

        } finally {
            response.close();
        }
    }

7、apiclient_key.pem(私钥文件)

 文件内容如下:(注:该文件存放位置根据application.yml中的“商户私钥文件”路径为准)

-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC6KmMvuf/NVU8c
h/0OSkGP3ngmA+quLz/BTg/bcVYgBvEnB6UC3xQg6TdyBcu55t6Toa37+NnPGezh
TNixKPZpNRTkCJ0PV0Y5ns/KmBC7TJ+iiYmT3pJ3jZQR1we4M+cre6z805XohQiq
yp4zJObG4KLE6LJkFB7M2tArXQI8DFHp2j8SnDozzUQEwkffTHZJuO5exHmi214V
t/lNVfXBZXipisn9YQpUk6D+pXrASVdOTepgXQNIdzbQt07jO6N2UHtYxwNDZjO9
OJf6tGOqYYnebMQ7X+HT9xpxqViCVR2LrymPWe2Xl6+1mtmFoNsiE5Cm5skoqraA
bgc8E5LlAgMBAAECggEABgzuoFR5PeEx6wl4bgh3zQc7/HBQJk0e01eIKGesluni
2JPlBwzdCJzL0obhsi8QuNeeYfwaiCKdkkz/FfLw30Z8YVTuVdtOSv0gX8NFd/Dr
l0rFD+tB82TElTfZ5mC5eK5SVv1BeAcq2vIu5hai8X/HioLNmXcV8S6DaYViVzU5
GV7n2YC2rWWJ1vKZpNmXUWtxeVelmiSN+l20WHok3R/aziJ34KR1GXSfrjyYXgVL
WM7UVcAmzywKsHp5o3sU522kCKd3TGgHEsweaWgsBsqsvqvt5QDEMPbhYb99aUS9
TPwBy08Un1Yio7YQR/w7oOVe0pn8l1g7MdVc1gzK2QKBgQDh/Fa+4UF+q0qtQhKb
r/ElfsAb0q9RJ/A5jyxvL6oxVyE7cTzcSNvJVJXmFiakuQ5QMP+qa9dopBye+v63
/iT/yzmLfS82EnPyo1qx13zcCxQ6I9B/K00/597srUIZLVg3OumkTJi/vS4Zu/TJ
lcMjzXkC17RRC1vO8CW+ftgcqwKBgQDS5CNxuJ5BpkpuzLqopTuQ2gYtEwn8wc98
Junr7UvvxUhVf8iExJXe7+tTM/CzlJyyP+YHozdqKtynKAeosB6GhYaJLpUpwEqI
38sBHdvGPpYzcXSKLczHsY3r1CnP8mpxSSmp4Uwf3HwY/5LVR4qXtKJHn4Foz5Fs
e8iQcRPurwKBgA5gKACCgdEWAm0dG+PtgFCbTIs4jtCB0uVGd1QnWxNCcKnAXVfC
BsE68UIuvIyT/RYa19i2fYB5mByA6P05XI8tFV8LOpqc9+VCgP15MMcqqUG9j4DX
d1WOYX760o6ZdNgmlkBOYxUnaqxWaY79SOmZI46LvDu/ljqGyk/g78x7AoGASRgv
vMvLdl+nrs2g6LEUezlGKLtPm96lBpgKPe6qgjlzv8ahfnsQowuvGdCH3gZlZVbG
aOFGZLLkdb9nIC2i9ucy4TtXEfiHHPfMSd/Ke+TXdI8fYIFNV+2PjiykLWINSKSe
HzZqhySJkrSKdQft3nUKRh7f4K8I7Xvd4UqAKRsCgYBXieQ+7hHwUF71l1RXafJL
9N2+hMBRoMXdY8wuwlCiI6mfo92G0bwg0HGBRyWvbD25oBiE8yq+jFRhhaOIZHuY
n6TG1ogxgu/LWlrsJ9m4x2DiWvCe/A+aFdFOXukkCqGq4n5tSzrYycl8JDAg1lQB
d48QoGAp06HNYefd3AT54w==
-----END PRIVATE KEY-----

补充:

微信支付接口文档地址: 产品中心 - 微信支付商户平台

 

到此结束,如若对你有帮助,点个赞再走啦~~~

标签: q24j4pj连接器35100d07no光电传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台