前一阵子做了一个微信支付相关的功能,期间走了不少的弯路。在这里给大家趟趟雷,希望大家能因此受益。在这里,我从头到尾一步步给大家捋顺。 目录 一 相关准备 二 开始写代码 三 运行代码 一 相关准备 实现
前一阵子做了一个微信支付相关的功能,期间走了不少的弯路。在这里给大家趟趟雷,希望大家能因此受益。在这里,我从头到尾一步步给大家捋顺。
目录
实现微信支付首先得有对应的商户号。商户号是甲方的,但是现实中甲方对这些往往是两眼一抹黑,所以关于申请的工作往往是开发者弄。但是没关系,跟着腾讯提供的引导一步步走就行了。如果材料齐全的话,大约一到两天的时间就会下来。超管的角色默认和甲方提供的法人电话微信进行绑定,如果超管所绑定的微信用户电话号不是法人,则需要上传授权函,授权函的格式在帮助说明中有。
等到商户号下来之后,有几个东西需要设置,这就和开发有关了。官方对此有比较明确的说明,地址为JSAPI支付-接入前准备 | 微信支付商户平台文档中心。我做的项目是jsapi支付类型,他的形式在腾讯支付文档中有详细的说明。主要是需要下面这几个:1 商户号:这个在后台里就能看到。2 appid:这个需要找甲方要。商户号和appid之间是多对多的关系。绑定商户号和appid需要在商户平台进行申请,然后在appid对应的公众平台后台进行同意。3 商户证书序列号。这个商户证书序列号在申请完证书之后就可以看到。在微信支付:API商户证书序列号serialNo获取-YES开发框架网这篇文章中可以看到如何获取,而且你需要有超级管理员权限。4 商户APIV3密钥。这个在文档中如何配置也有明确的说明。
当你按照文档下载商户证书时,你会得到一个zip文件,解压,你会得到4个文件,一定要保存好。
接下来开始写代码了,整个支付过程中最重要的是4个参数:1商户号,2 商户证书序列号,3 商户APIV3密钥 4 公众号appid,相关代码如下:
package shitang.huidiao;public class WxConfig { public static final String merchantId = "你的商户号"; public static final String privateKeyPath = "D:/apiclient_key.pem"; public static final String merchantSerialNumber = "xxxxxxx"; public static final String apiV3key = "xxxxxxx"; public static final String appid="xxxxxxx";}
至于这几个参数怎么获取,在百度上一搜就可以搜到。
整个代码根据微信提供的demo写的,比较简单,在这个页面可以看到相关url
我的代码是由Maven构建的,pom.xml中相关的依赖为
com.GitHub.wechatpay-apiv3 wechatpay-apache-Httpclient 0.2.2 com.github.wechatpay-apiv3 wechatpay-java 0.2.5
首先是支付界面,是个简单的jsp界面,就是向后台提供支付参数,但是因为是在微信的架构中,所以我仔细说说,代码如下
请选择要充值的金额:50100150250500
这个页面的中心逻辑是向后台提供支付参数,后台在接受到支付参数之后,首先先向微信平台发送请求,进行预支付,相关代码如下:
package com.bocomsoft.controller.wxpay;import java.text.SimpleDateFORMat;import java.util.Date;import java.util.stream.Collectors;import java.util.stream.Stream;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.WEB.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.client.RestTemplate;import org.springframework.web.servlet.ModelAndView;import com.alibaba.fastjson.JSONObject;import com.wechat.pay.java.core.RSAAutoCertificateConfig;import com.wechat.pay.java.service.payments.jsapi.JsapiService;import com.wechat.pay.java.service.payments.jsapi.model.Amount;import com.wechat.pay.java.service.payments.jsapi.model.Payer;import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;@RequestMapping("/wxpay")@Controllerpublic class WxpayController {private RSAAutoCertificateConfig config;@AutowiredWxService service;//这个是内部业务逻辑,不用管@RequestMapping("/test")@ResponseBodypublic JSONObject test() throws Exception {JSONObject json1 = new JSONObject();json1.put("code", 1);return json1;}@Autowiredpublic void setConfig(){config = new RSAAutoCertificateConfig.Builder().merchantId(WxConfig.merchantId).privateKeyFromPath(WxConfig.privateKeyPath).merchantSerialNumber(WxConfig.merchantSerialNumber).apiV3Key(WxConfig.apiV3key).build();}@RequestMapping("/yuzhifu")@ResponseBodypublic JSONObject yuzhifu(@RequestParam(value = "openid") String openid,@RequestParam(value = "number") String number, @RequestParam(value = "staff_no") String staff_no,@RequestParam(value = "jine") String jine,@RequestParam(value = "NAME") String NAME) throws Exception {Date now=new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd");String shijian=sdf.format(now);String order_no=shijian+number;System.out.println("order_no="+order_no); JsapiService service = new JsapiService.Builder().config(config).build();// request.setXxx(val)设置所需参数,具体参数可见Request定义PrepayRequest request = new PrepayRequest();Amount amount = new Amount();amount.setTotal((int)(Double.parseDouble(jine)*100));//amount.setTotal(1);request.setAmount(amount);request.setAppid(WxConfig.appid);request.setMchid(WxConfig.merchantId);request.setDescription("食堂账户充值");request.setNotifyUrl("回调url");//这个回调url必须是https开头的request.setOutTradeNo(order_no);Payer payer = new Payer();payer.setOpenid(openid);request.setPayer(payer);PrepayResponse response = service.prepay(request);System.out.println(response.getPrepayId());String prepayid=response.getPrepayId(); String nonceStr = WXPayUtil.generateNonceStr(); long timeStamp = WXPayUtil.getCurrentTimestamp(); String prepayidstr= "prepay_id=" + prepayid; String signType = "RSA"; String signatureStr = Stream.of(WxConfig.appid, String.valueOf(timeStamp), nonceStr, prepayidstr) .collect(Collectors.joining("\n", "", "\n")); String paySign = WXPayUtil.getSign(signatureStr); resultjson.put("code", 0); resultjson.put("appId",WxConfig.appid); resultjson.put("timeStamp", String.valueOf(timeStamp)); resultjson.put("nonceStr", nonceStr); resultjson.put("package", prepayidstr); resultjson.put("signType",signType); resultjson.put("paySign", paySign); resultjson.put("openid", openid);return resultjson;}}
这个是签名加密类,代码如下:
package com.bocomsoft.controller.wxpay;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.net.URISyntaxException;import java.NIO.charset.StandardCharsets;import java.security.InvalidKeyException;import java.security.NoSuchAlGorithmException;import java.security.PrivateKey;import java.security.SecureRandom;import java.security.Signature;import java.security.SignatureException;import java.util.Random;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.util.Base64Utils;import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;public class WXPayUtil {private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final Random RANDOM = new SecureRandom(); public static String getSign(String signatureStr) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException { //replace 根据实际情况,不一定都需要 //String replace = privateKey.replace("\\n", "\n"); File file = new File(WxConfig.privateKeyPath); InputStream in = new FileInputStream(file); PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(in); Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(merchantPrivateKey); sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(sign.sign()); } 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); } public static Logger getLogger() { Logger logger = LoggerFactory.getLogger("wxpay java sdk"); return logger; } public static long getCurrentTimestamp() { return System.currentTimeMillis()/1000; } public static long getCurrentTimestampMs() { return System.currentTimeMillis(); } public static void main(String[] args) { String signatureStr = "ssssss"; try { String paySign = WXPayUtil.getSign(signatureStr); System.out.println("paySign="+paySign); } catch (Exception e) { e.printStackTrace(); }}}
大家注意啊,坑人的地方主要两个地方,首先是签名方法,我才疏学浅,在官网上看得脑瓜都大了,没看明白,感谢伟大的互联网,让我找到了加密方法,先向大神表示感谢,再贴出原文的链接,供大家参考:微信支付V3 JSAPI签名_jsapi v3 签名_Coco_淳的博客-CSDN博客。第二个就是这个,大家注意到这了吗
@Autowired
public void setConfig(){
config = new RSAAutoCertificateConfig.Builder().merchantId(WxConfig.merchantId).privateKeyFromPath(WxConfig.privateKeyPath)
.merchantSerialNumber(WxConfig.merchantSerialNumber).apiV3Key(WxConfig.apiV3key).build();
}
官网提供的demo是一个main函数,直接就执行下去了,但是在java后端的情况下,他会报错,The corresponding provider for the merchant already exists 大致的意思是apiV3的RSAConfig重复build,所以要在Controller中要以单例的方式实现,原文的参考链接如下:微信支付apiV3异常:The corresponding provider for the merchant already exists_迪八戈的博客-CSDN博客。当代码写到这一步了,用户在客户端也进行完支付了。然后进入下个阶段,这个阶段是微信平台向先前预支付设置的地址发送加了密的post请求,当我们接受到请求之后,首先是解密,然后是进行相关的业务内部运行逻辑。代码如下:
package shitang.huidiao;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestHeader;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import com.alibaba.fastjson.JSONObject;import com.wechat.pay.java.core.RSAAutoCertificateConfig;import com.wechat.pay.java.core.notification.NotificationConfig;import com.wechat.pay.java.core.notification.NotificationParser;import com.wechat.pay.java.core.notification.RequestParam;@RestController@RequestMapping("/huidiao")public class HuidiaoController {NotificationConfig config;@Autowiredpublic void setConfig(){config = new RSAAutoCertificateConfig.Builder() .merchantId(WxConfig.merchantId) .privateKeyFromPath(WxConfig.privateKeyPath) .merchantSerialNumber(WxConfig.merchantSerialNumber) .apiV3Key(WxConfig.apiV3key) .build();}@AutowiredHuidiaoService service;@PostMapping(value = "/wxAppPayNotify")public String wxAppPayNotify( @RequestHeader("Wechatpay-Serial") String wechatPayCertificateSerialNumber, @RequestHeader("Wechatpay-Signature") String signature, @RequestHeader("Wechatpay-Timestamp") String timstamp, @RequestHeader("Wechatpay-Nonce") String nonce, @RequestBody String requestBody) {RequestParam requestParam = new RequestParam.Builder() .serialNumber(wechatPayCertificateSerialNumber) .nonce(nonce) .signature(signature) .timestamp(timstamp)// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048 .body(requestBody) .build();// 初始化 NotificationParserNotificationParser parser = new NotificationParser(config);// 验签并解密报文JSONObject decryptObject = parser.parse(requestParam,JSONObject.class);System.out.println("decryptObject="+decryptObject.toJSONString());String trade_state=decryptObject.getString("trade_state");JSONObject jsonResponse = new JSONObject();if(trade_state.equals("SUCCESS")) {//各种业务逻辑}else{ //还是各种业务逻辑 }jsonResponse.put("code", "SUCCESS");jsonResponse.put("message", "成功");return jsonResponse.toJSONString();}}
该打包打包,该运行就运行,没啥说的,但是如果你的jdk版本是8,就会出现问题,(我的本机的jdk版本是11,是否会出现问题待验证,但是服务器的jdk版本是8,不能轻易去动)。
错误出现场景:用户在微信公众号里面发送一条消息后,接口收到的加密数据,在解密的时候报错。
错误内容:java.security.InvalidKeyException: Illegal key size
错误原因描述:JRE本身中自带的“local_policy.jar ”和“US_export_policy.jar”只支持128位密钥的加密算法
需要替换文件,原文链接如下:https://www.cnblogs.com/fswhq/p/16046179.html
最后希望大家共同进步。
--结束END--
本文标题: 微信支付总结(JSAPI)(V3)(JAVA)
本文链接: https://lsjlt.com/news/387142.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0