收藏
回答

API V3 jsapi创建订单(错误的签名),但是我就是按照官方的例子做了,三天了都没搞通


回答关注问题邀请回答
收藏

3 个回答

  • 墨、
    墨、
    2020-09-28

    有没有NODEJS的 解决方案啊 这个大个平台 就三种语言SDK 我是真服了

    2020-09-28
    有用
    回复 1
  • 学无止境
    学无止境
    2020-07-10

    解决了,谢谢

    package org.jeecg.modules.milk.utils;
    
    import lombok.extern.slf4j.Slf4j;
    import okhttp3.HttpUrl;
    
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.security.*;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.util.Base64;
    import java.util.Random;
    
    @Slf4j
    public class WxV3Authorization {
        /**
         * 请求头【authorization】的组装(【Authorization: 认证类型 签名信息】)
         * 1、举例【WECHATPAY2-SHA256-RSA2048 mchid="商户号",
         *      nonce_str="随机字符串32位",
         *      signature="签名",timestamp="时间戳",
         *      serial_no="证书序列号"】
         * 2、注意:前缀固定值(WECHATPAY2-SHA256-RSA2048 )后面有空格
         * 3、组装过程封装为一个工具类【WxV3Authorization】
         * 4、参考页面【https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng】
         */
        /**
         * @param method    请求方法(与接口请求的方法保持一致【必需大写】)
         * @param url       请求的url(即,把请求的url配置在这里,不需要参数)
         * @param body      请求体(需要转换为json字符串)
         * @param mchid     商户号
         * @param serialNo  商户证书序列号
         * @param authorizationType 认证类型,目前格式固定【WECHATPAY2-SHA256-RSA2048】
         * @param serialPath    证书路径(其中存放的是私钥)
         * @return          返回组装好的(Authorization请求头)
         */
        public static String getAuthorization(String method, HttpUrl url, String body, String mchid, String serialNo, String authorizationType, String serialPath){
            //准备32位的随机字符串
            String nonceStr = generateNonceStr();
            //时间戳(去掉后面三个0)
            long timestamp = System.currentTimeMillis() / 1000;
            //组装需要签名的数据
            String message = buildMessage(method, url, timestamp, nonceStr, body);
            //使用私钥对数据进行签名(加密),【数据签名之后需要转换为字节数组(采用utf-8)】
            String signature = null;
            try {
                signature = sign(message.getBytes("utf-8"), serialPath);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return  authorizationType + "mchid=\"" + mchid + "\","
                    + "nonce_str=\"" + nonceStr + "\","
                    + "signature=\"" + signature + "\","
                    + "timestamp=\"" + timestamp + "\","
                    + "serial_no=\"" + serialNo + "\"";
        }
    
        /**对字节数据进行私钥签名(加密)*/
        private static String sign(byte[] message, String serialPath){
            try {
                //签名方式(固定SHA256withRSA)
                Signature sign = Signature.getInstance("SHA256withRSA");
                //使用私钥进行初始化签名(私钥需要从私钥文件【证书】中读取)
                sign.initSign(getPrivateKey(serialPath));
                //签名更新
                sign.update(message);
                //对签名结果进行Base64编码
                return Base64.getEncoder().encodeToString(sign.sign());
            } catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 组装需要签名的字符串
         * @param method    请求接口的方法
         * @param url       请求接口的url(截取其中:1、域名后;2、?问题前面-》中间部分的内容)
         * @param timestamp 时间戳
         * @param nonceStr  随机32位字符串
         * @param body      请求接口提交的数据(json数据字符串)
         * @return  返回组装之后的数据(注意,每一种数据后面都需要换行【包括最后】)
         */
        private static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
            String canonicalUrl = url.encodedPath();
            if (url.encodedQuery() != null) {
                canonicalUrl += "?" + url.encodedQuery();
            }
            return method + "\n"
                    + canonicalUrl + "\n"
                    + timestamp + "\n"
                    + nonceStr + "\n"
                    + body + "\n";
        }
    
        /**
         * 获取私钥。
         *
         * @param filename 私钥文件路径  (required)
         * @return 私钥对象
         */
        private static PrivateKey getPrivateKey(String filename){
            try {
                String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
                String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                        .replace("-----END PRIVATE KEY-----", "")
                        .replaceAll("\\s+", "");
                KeyFactory kf = KeyFactory.getInstance("RSA");
                return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
            } catch (NoSuchAlgorithmException e) {
                log.debug("当前Java环境不支持RSA");
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                log.debug("无效的密钥格式");
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 获取随机字符串 Nonce Str
         * 随机字符从symbols获取
         * SecureRandom真随机数
         * @return String 随机字符串
         */
        private static String generateNonceStr() {
            String symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
            Random random = new SecureRandom();
            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);
        }
    }
    
    
    2020-07-10
    有用
    回复
  • 微信支付技术助手8
    微信支付技术助手8
    2020-07-10

    https://developers.weixin.qq.com/community/develop/article/doc/000cca8440c6a0dca61a3efb053c13你好,参考一下这篇文章。V3签名错误。谢谢。

    2020-07-10
    有用
    回复 3
    • 学无止境
      学无止境
      2020-07-10
      还是不行,原理和操作方法一样,就是签名失败
      2020-07-10
      回复
    • 学无止境
      学无止境
      2020-07-10
      签名方法如图
      2020-07-10
      回复
    • 学无止境
      学无止境
      2020-07-10
      我使用postmain成功返回订单号了(原来为null的的字段不能传),但是程序中现在又有问题了【Authorization不合法????】又是为什么
      2020-07-10
      回复
登录 后发表内容
问题标签