微信v3版本native支付java踩过的坑和代码,这个东西真的是麻烦死了,文档上的东西太难找了花了两天终于解决

微信v3版本native支付java踩过的坑和代码,这个东西真的是麻烦死了,文档上的东西太难找了花了两天终于解决

首先是要在微信的商户平台上弄一些调接口的相关参数例如appid,mchid等等,下图为相关配置。

v3.appid=
#native支付统一下单接口地址
v3.payUrl=https://api.mch.weixin.qq.com/v3/pay/transactions/native
#native支付查询接口地址
v3.findUrl=https://api.mch.weixin.qq.com/v3/pay/transactions
#秘钥
v3.keyPath=apiclient_key.pem的地址(从商户平台下载弄进项目中)
#CA证书 格式.pem
v3.certPath=apiclient_cert.pem的地址(从商户平台下载弄进项目中)
#CA证书 格式.p12
v3.certP12Path==apiclient_cert.p12的地址(从商户平台下载弄进项目中)
#证书序列号
v3.certSerialNo=(在商户平台中即可看见)
#服务商id/商户id
v3.mchid=
# apiv3 秘钥
v3.apiKey3=(商户平台中设置的)

接下来是获取签名的类

package net.bx.util;

import net.bx.controller.AttachmentController;
import okhttp3.HttpUrl;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;

public class V3Util {
    private static final Logger logger = LoggerFactory.getLogger(AttachmentController.class);

    //method(请求类型GET、POST url(请求url) body(请求body,GET请求时body传"",POST请求时body为请求参数的json串)  merchantId(商户号) certSerialNo(API证书序列号) keyPath(API证书路径)
    public static String getToken(String method,String url, String body,String merchantId,String certSerialNo,String keyPath) throws Exception {
        String signStr = "";
        HttpUrl httpurl = HttpUrl.parse(url);
        String nonceStr = MD5Utils.md5(new Date().getTime()+"");
        long timestamp = System.currentTimeMillis() / 1000;
        if(StringUtils.isEmpty(body)){
            body = "";
        }

        String message = buildMessage(method, httpurl, timestamp, nonceStr, body);
        byte[] by;
        by = message.getBytes("ISO-8859-1");
        String signature = sign(by,keyPath);
        int length = 0;
        for (int i = 0; i < signature.length(); i++) {
            int ascii = Character.codePointAt(signature, i);
            if (ascii >= 0 && ascii <= 255) {
                length++;
            } else {
                length += 2;
            }
        }
        System.out.println(length);
        signStr = "mchid=\"" + merchantId
                + "\",nonce_str=\"" + nonceStr
                + "\",signature=\"" + signature
                + "\",timestamp=\"" + timestamp
                + "\",serial_no=\"" + certSerialNo+ "\"";
        logger.info("Authorization Token:" +signStr);
        return signStr;
    }

    public 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";
    }



    public static String sign(byte[] message,String keyPath) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(keyPath));
        sign.update(message);
        return Base64.encodeBase64String(sign.sign());
    }

    /**
     * 获取私钥。
     *
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) throws IOException {

        String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
        logger.info("File content:"+content);
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            logger.info("privateKey:"+privateKey);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            logger.info("异常:"+e);
            throw new RuntimeException("无效的密钥格式");
        }
    }
}

接下来是controller层,为了方便你们看业务我将所有逻辑都写在controller中了,上代码:

package net.bx.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import net.bx.entity.CourseOrder;
import net.bx.po.CourseOrderParam;
import net.bx.repository.CourseOrderRepository;
import net.bx.util.BaseResult;
import net.bx.util.V3Util;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping(value = "/weXin")
public class WeXinPayController {

    private static final Logger logger = LoggerFactory.getLogger(WeXinPayController.class);


    @Value("${v3.appid}")
    private String appid;
    @Value("${v3.mchid}")
    private String mchid;
    @Value("${v3.payUrl}")
    private String payUrl;
    @Value("${v3.keyPath}")
    private String keyPath;
    @Value("${v3.certPath}")
    private String certPath;
    @Value("${v3.certSerialNo}")
    private String certSerialNo;
    @Value("${v3.findUrl}")
    private String findUrl;

    @Autowired
    CourseOrderRepository courseOrderRepository;

    /**
     * 生成微信支付二维码
     *
     * @param outTradeNo 订单号
     * @param totalFee   金额(分)
     */
    @RequestMapping(value = "/createNative")
    public BaseResult createNative(@RequestParam("outTradeNo") String outTradeNo,
                                   @RequestParam("totalFee") int totalFee,
                                   @RequestParam("description") String description,
                                   HttpServletRequest request, HttpServletResponse response) throws Exception {
        CourseOrder byCourseOrderCode = courseOrderRepository.findByCourseOrderCode(outTradeNo);
        //判断该订单是否存在
        if (byCourseOrderCode != null) {
            return new BaseResult<Object>(true, "000002", "该订单号已存在,不支持再次下单");
        }
        HttpPost httpPost = new HttpPost(payUrl);
        Map req = new HashMap();
        Map map = new HashMap();
        req.put("appid", appid);    //公众号
        req.put("mchid", mchid);  // 商户号
        req.put("description", description); // 商品描述
        req.put("out_trade_no", outTradeNo);   // 商户订单号
        map.put("total", totalFee);    //金额
        map.put("currency", "CNY");     //金额类型
        req.put("amount", map);
        req.put("notify_url", "https://weixin.qq.com/");  // 回调地址
        String body = JSONObject.toJSONString(req);
        logger.info("调用v3微信native支付统一下单接口入参{}:" + body);
        String sign = V3Util.getToken("POST", payUrl, body, mchid, certSerialNo, keyPath);
        //请求体
        httpPost.setEntity(new StringEntity(body));
        //请求头
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setHeader("Authorization", "WECHATPAY2-SHA256-RSA2048" + " " + sign);
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //完成签名并执行请求
        CloseableHttpResponse resp = httpClient.execute(httpPost);

        try {
            int statusCode = resp.getStatusLine().getStatusCode();
            Map maps = (Map) JSON.parse(EntityUtils.toString(resp.getEntity()));
            logger.info("调用v3微信native支付统一下单接口出参{}:" + JSON.toJSONString(maps));
            if (statusCode == 200) { //处理成功
                return new BaseResult<Object>(true, maps, "000000", "操作成功");
            } else if (statusCode == 204) { //处理成功,无返回Body
                return new BaseResult<Object>(true, maps, "000000", "操作成功");
            } else {
                return new BaseResult<Object>(true, maps, "000001", "操作失败");
            }
        } finally {
            resp.close();
        }
    }
}

到这一个简单的native统一下单接口就实现了,微信那边会返回一个url,返给前端即可生成二维码。

**注意**:返回错误的签名,验签失败这种情况不要慌,我也是这么过来的。
问题主要出现在一下几个方面:
1.参数是否有误,请求方式是否有问题
2.传入汉字了试试改成英文,看看是不是编码的问题
3.就是时间戳和随机串,加请求头Authorization的时候是否和签名时一致
4。商户证书序列号就是从微信商户后台就能看到,不用调接口去获取,调接口的那个是平台证书。

这几点检查没问题那就应该没问题了,相信大家的问题都能迎刃而解!!!!



  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值