Java实现微信支付(微信公众号JSAPI支付)

article/2025/9/12 22:32:57

Java实现微信支付(微信公众号JSAPI支付)

第一步 开发环境准备

在接入微信支付之前,需要现在微信支付商户平台入驻,成为商家,才能继续后续的开发。

微信支付商户平台网址:https://pay.weixin.qq.com

不过,个人用户是无法成为商家的,只有以下几种用户类型才可以成为商家。
在这里插入图片描述
成为商家之后,需要完成证书申请、秘钥配置、产品申请等操作,具体如下。

1.1 申请证书与秘钥的配置

进入微信支付商户平台,在账号中心→API安全页面,完成API证书的申请,和API秘钥的配置,如下图所示。
在这里插入图片描述
API证书申请的过程稍微复杂,但是官方有详细的申请教程,点击右侧的查看指引按钮,根据教程一步一步来操作即可。
两种API秘钥则是自定义的32个字符的字符串,任意填写并记住即可。

1.2 申请产品

进入微信支付商户平台,在产品中→我的产品页面,可以查看当前商户已开通未开通的产品,根据项目需求,字形申请开通即可。
在这里插入图片描述

1.3 开发配置信息填写

进入微信支付商户平台,在产品中心→开发配置页面,记下本商户的商户号,并填写已申请支付产品的各项授权目录回调链接等信息。
在这里插入图片描述

1.4 APPID账号管理

进入微信支付商户平台,在产品中心→APPID账号管理页面,关联诸如服务号、订阅号、小程序、企业微信、移动应用、网站应用等的APPID,如下图所示。
在这里插入图片描述
至此,微信支付商户的基本信息配置完毕,总结下来,以下五项信息时必须的:

  • 商户号
  • AppID
  • API证书(3个文件)
  • APIv2秘钥
  • APIv3秘钥
  • 回调链接

第二步 项目创建与依赖导入

2.1 创建SpringBoot项目

创建SpringBoot项目的教程太多太多了…比如:https://cxhit.blog.csdn.net/article/details/113782979,所以这里不再赘述。
项目结构如下图所示。
在这里插入图片描述

2.2 导入依赖

在pom.xml文件中,导入微信支付的第三方SDK依赖:

<!-- 微信支付的核心依赖 -->
<!-- https://search.maven.org/artifact/com.github.binarywang/weixin-java-pay -->
<!-- https://github.com/Wechat-Group/WxJava -->
<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-pay</artifactId><version>4.3.0</version>
</dependency>

其中最新版本可前往Maven官方仓库查看。

代码部分

1、 获取openid

第一步:用户同意授权,获取code
前台请求自己写的接口,然后在后台此接口中调用微信授权接口,调用此接口需要有三个注意的地方
1. APPID:公众号的appid
2. REDIRECT_URI:这个地址是调用微信授权后的回调接口,在回调接口中我们可以拿到用户的信息,特别注意:redirect_uri地址需要使用 urlEncode 对链接进行处理
3. SCOPE:此处填snsapi_userinfo,需要用户点授权

/*** 微信授权接口* @param response* @throws IOException*/
@RequestMapping("wxLogin")
public void wxLogin(HttpServletResponse response) throws IOException {//域名String sym = "www.xxx.com";//这里是回调的urlString redirectUri = URLEncoder.encode(sym+"/weChat/callBack","UTF-8");String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" +"appid=wx63d9c9b609ed6579" +"&redirect_uri=" + redirectUri +"&response_type=code" +"&scope=snsapi_userinfo" +"&state=STATE#wechat_redirect";response.sendRedirect(url.replace("APPID","wx63d9c9b609ed6579").replace("REDIRECT_URI",redirectUri).replace("SCOPE","snsapi_base"));
}

接下来就是微信授权的回调接口
1、获取code
2、根据code获取openid

     /*** 微信授权回调接口* @param request* @return*/@RequestMapping("callBack")public String callBack(HttpServletRequest request){//获取回调地址中的codeString code = request.getParameter("code");String appId = "xxxxxxxxxxx";String secret = "xxxxxxxxxxxxxxxxxxxxxxxx";String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +"appid=" + appId +"&secret=" + secret +"&code=" + code +"&grant_type=authorization_code";//获取openIdString data = HttpUtil.doGet(url);AccessToken accessToken = JSONObject.parseObject(data, AccessToken.class);return accessToken.getOpenid();}

补上刚刚用的实体类AccessToken

@Data
public class AccessToken {private String accessToken;private String expiresIn;private String refreshToken;private String openid;private String scope;
}

2、创建配置实体类

import lombok.Data;import java.io.Serializable;/*** 微信支付配置信息* @author Administrator*/
@Data
public class WeChatPayEntity implements Serializable {private static final long serialVersionUID = 1L;/*** 必填:微信支付商户号*/private String mchId;/*** 必填:商户绑定的微信公众号、小程序、开放平台等的appid*/private String appId;/*** 必填:APIv2密钥(调用v2版本的API时,需用APIv2密钥生成签名)*/private String mchKey;/*** 必填:APIv3密钥(调用APIv3的下载平台证书接口、处理回调通知中报文时,要通过该密钥来解密信息)*/private String apiV3Key;/*** 必填:apiclient_cert.p12证书文件的绝对路径,或者以classpath:开头的类路径。*/private String keyPath;/*** 必填:apiclient_key.pem证书文件的绝对路径,或者以classpath:开头的类路径。*/private String privateKeyPath;/*** 必填:apiclient_cert.pem证书文件的绝对路径,或者以classpath:开头的类路径。*/private String privateCertPath;/*** 必填:微信支付异步回调通知地址。通知url必须以https开头(SSL协议),外网可访问,不能携带参数。*/private String notifyUrl;
}

注意:为了减少代码篇幅,此代码引入了lombok。如果没有使用lombok,请自行生成Get和Set方法。

3、实现支付服务类

以下代码实现了基本的配置和支付、退款。查询功能,有详细的注解。
另外注意:以下代码中使用了Hutool组件的Id生成工具(用于生成订单号),请在pom文件中自行添加hutool的依赖

import com.cxhit.pay.wechat.entity.WeChatPayEntity;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;/*** 微信支付服务类(只实现V3接口)** @author* @since*/
@Service
public class WeChatPayService {// 是否启用沙箱环境。微信支付的沙箱环境贼垃圾...好多年不维护...千万不要用。。。private static final Boolean SAND_BOX_ENV = false;// 支付结果回调地址private static final String NOTIFY_URL = "https://open.zxdmy.com/api/wx/pay/notify";/*** 获取微信支付相关接口服务(后续的几个服务方法,实现了基本的实例)* (此接口也可以直接在controller中使用)** @return 微信支付服务接口*/public WxPayService getWxPayService() {// TODO 此处可以从数据库读取微信支付的相关秘钥、证书等配置信息。但我们这里就直接写入静态数据进行演示WeChatPayEntity weChatPayEntity = new WeChatPayEntity();// 1. 填充基本信息(商户号与APPID)weChatPayEntity.setMchId("15333333333");weChatPayEntity.setAppId("wx123456789101112");// 2. 填充秘钥信息weChatPayEntity.setMchKey("abcdefghabcdefghabcdefghabcdefgh");weChatPayEntity.setApiV3Key("abcdefghabcdefghabcdefghabcdefgh");// 3. 填充证书路径信息weChatPayEntity.setKeyPath("E:\\微信支付\\Cert\\apiclient_cert.p12");weChatPayEntity.setPrivateKeyPath("E:\\微信支付\\Cert\\apiclient_key.pem");weChatPayEntity.setPrivateCertPath("E:\\微信支付\\Cert\\apiclient_cert.pem");// 4. 填充回调URLweChatPayEntity.setNotifyUrl(NOTIFY_URL);// 以下代码无需修改// 生成配置WxPayConfig payConfig = new WxPayConfig();// 填充基本配置信息payConfig.setAppId(StringUtils.trimToNull(weChatPayEntity.getAppId()));payConfig.setMchId(StringUtils.trimToNull(weChatPayEntity.getMchId()));payConfig.setMchKey(StringUtils.trimToNull(weChatPayEntity.getMchKey()));payConfig.setApiV3Key(StringUtils.trimToNull(weChatPayEntity.getApiV3Key()));payConfig.setKeyPath(StringUtils.trimToNull(weChatPayEntity.getKeyPath()));payConfig.setPrivateCertPath(StringUtils.trimToNull(weChatPayEntity.getPrivateCertPath()));payConfig.setPrivateKeyPath(StringUtils.trimToNull(weChatPayEntity.getPrivateKeyPath()));payConfig.setNotifyUrl(StringUtils.trimToNull(weChatPayEntity.getNotifyUrl()));// 创建配置服务WxPayService wxPayService = new WxPayServiceImpl();wxPayService.setConfig(payConfig);// 可以指定是否使用沙箱环境payConfig.setUseSandboxEnv(SAND_BOX_ENV);if (SAND_BOX_ENV) {try {payConfig.setMchKey(wxPayService.getSandboxSignKey());wxPayService.setConfig(payConfig);} catch (WxPayException e) {throw new RuntimeException(e.getMessage());}}// 返回结果return wxPayService;}/*** 下单接口(只设置了必填信息)(V3版本)** @param tradeType   必填:交易类型:jsapi(含小程序)、app、h5、native* @param description 必填:商品描述(商品标题)* @param outTradeNo  必填:商家订单号* @param total       必填:商品金额(单位:分)* @param openId      特殊必填:支付用户的OpenId,JSAPI支付时必填。* @return 支付返回结果:{0:Y|N,1:支付结果} <br>* 关于支付结果: <br>* APP支付、JSAPI支付为[预支付交易会话标识] <br>* Native支付为[二维码链接] <br>* H5支付为[支付跳转链接]*/public String[] pay(String tradeType, String description, String outTradeNo, Integer total, String openId) {// 构建统一下单请求参数对象WxPayUnifiedOrderV3Request wxPayUnifiedOrderV3Request = new WxPayUnifiedOrderV3Request();// 对象中写入数据wxPayUnifiedOrderV3Request// 【1】必填信息// 商品描述:必填.setDescription(description)// 商户订单号:必填,同一个商户号下唯一.setOutTradeNo(outTradeNo)// 通知地址:必填,公网域名必须为https,外网可访问。可不填,通过配置信息读取(但这个组件没写...).setNotifyUrl(NOTIFY_URL)// 订单金额:单位(分).setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(total))// 【2】选填信息// 附加信息.setAttach("附加信息")// 订单优惠标记// ....setGoodsTag("ABCD");try {// 根据请求类型,返回指定类型,其中包含:【3】条件必填信息switch (tradeType.toLowerCase()) {// Native支付case "native":return new String[]{"Y", this.getWxPayService().unifiedOrderV3(TradeTypeEnum.NATIVE, wxPayUnifiedOrderV3Request).getCodeUrl()};// JSAPI支付case "jsapi":// 用户在直连商户appid下的唯一标识。 下单前需获取到用户的OpenidwxPayUnifiedOrderV3Request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(openId));return new String[]{"Y", this.getWxPayService().unifiedOrderV3(TradeTypeEnum.JSAPI, wxPayUnifiedOrderV3Request).getPrepayId()};// H5支付case "h5":wxPayUnifiedOrderV3Request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo()// 用户终端IP.setPayerClientIp("12.34.56.78").setH5Info(new WxPayUnifiedOrderV3Request.H5Info()// 场景类型.setType("wechat")));return new String[]{"Y", this.getWxPayService().unifiedOrderV3(TradeTypeEnum.H5, wxPayUnifiedOrderV3Request).getH5Url()};// APP支付case "app":return new String[]{"Y", this.getWxPayService().unifiedOrderV3(TradeTypeEnum.APP, wxPayUnifiedOrderV3Request).getPrepayId()};default:// throw new RuntimeException("输入的[" + tradeType + "]不合法,只能为native、jsapi、h5、app其一,请核实!");return new String[]{"N", "输入的[" + tradeType + "]不合法,只能为native、jsapi、h5、app其一,请核实!"};}} catch (WxPayException e) {// throw new RuntimeException(e.getMessage());return new String[]{"N", e.getMessage()};}}/*** 订单查询接口(新版V3)** @param transactionId 微信订单号* @param outTradeNo    商户系统内部的订单号,当没提供微信订单号(transactionId)时需要传* @return 订单成功(SUCCESS):{0:Y,1:商户单号,2:微信单号,3:订单金额(分),4:交易时间,5:交易状态,6:交易描述}* 订单异常:{0:N,1:订单状态,2:订单描述}* 查询错误:{0:E,1:错误代码,2:错误描述}*/public String[] query(String transactionId, String outTradeNo) {// 商家单号和微信单号不能同时为空if (null == transactionId && null == outTradeNo) {return new String[]{"E","ERROR","微信单号和商户单号不能同时为空,请检查!"};}try {// 执行查询并返回查询结果WxPayOrderQueryV3Result wxPayOrderQueryV3Result = this.getWxPayService().queryOrderV3(transactionId, outTradeNo);// 如果交易成功,或者在退款中if ("SUCCESS".equals(wxPayOrderQueryV3Result.getTradeState()) || "REFUND".equals(wxPayOrderQueryV3Result.getTradeState())) {return new String[]{"Y",wxPayOrderQueryV3Result.getOutTradeNo(),wxPayOrderQueryV3Result.getTransactionId(),String.valueOf(wxPayOrderQueryV3Result.getAmount().getTotal()),wxPayOrderQueryV3Result.getSuccessTime(),wxPayOrderQueryV3Result.getTradeState(),wxPayOrderQueryV3Result.getTradeStateDesc()};} else {return new String[]{"N",wxPayOrderQueryV3Result.getTradeState(),wxPayOrderQueryV3Result.getTradeStateDesc()};}} catch (WxPayException e) {// throw new RuntimeException(e.getMessage());return new String[]{"E",e.getErrCode(),e.getErrCodeDes()};}}/*** 退款接口(新版V3)** @param outTradeNo  商户订单号* @param outRefundNo 商户退款单号* @param total       订单总金额(单位:分)* @param refund      退款金额(单位:分)* @return 退款成功或退款处理中:{0:Y,1:商户单号,2:微信单号,3:退款单号,4:订单金额(分),5:退款金额(分),6:退款时间}<br>* 订单异常:{0:N,1:订单状态,2:订单描述}* 退款错误:{0:E,1:错误代码,2:错误描述}*/public String[] refund(String outTradeNo, String outRefundNo, Integer total, Integer refund) {// 几个参数不能为空if (null == outTradeNo || null == outRefundNo || null == total || null == refund) {return new String[]{"E","ERROR","商户单号、退款单号、订单金额、退款金额均不能为空,请检查!"};}// 构造请求参数WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();wxPayRefundV3Request.setOutTradeNo(outTradeNo).setOutRefundNo(outRefundNo).setAmount(new WxPayRefundV3Request.Amount().setTotal(total).setRefund(refund).setCurrency("CNY"));try {// 执行请求并返回信息WxPayRefundV3Result wxPayRefundV3Result = this.getWxPayService().refundV3(wxPayRefundV3Request);// 退款处理中 || 退款成功if ("PROCESSING".equals(wxPayRefundV3Result.getStatus()) || "SUCCESS".equals(wxPayRefundV3Result.getStatus())) {return new String[]{"Y",wxPayRefundV3Result.getOutTradeNo(),wxPayRefundV3Result.getTransactionId(),wxPayRefundV3Result.getOutRefundNo(),String.valueOf(wxPayRefundV3Result.getAmount().getTotal()),String.valueOf(wxPayRefundV3Result.getAmount().getRefund()),wxPayRefundV3Result.getCreateTime()};} else {return new String[]{"N",wxPayRefundV3Result.getStatus(),"退款失败"};}} catch (WxPayException e) {// throw new RuntimeException(e.getMessage());return new String[]{"E",e.getErrCode(),e.getErrCodeDes()};}}
}

3.1 服务类的补充说明

一般来说,我们将配置信息放在yaml文件中。这种操作是没有问题的。
但本文中的支付服务类的实现方案,可以实现将微信支付的配置信息,经过加密后,存储在数据库中。
当需要发起支付的时候,从数据库中读取信息后,经过解密,再写入到微信的支付配置类中。
对于本演示项目,其配置信息就直接写在代码里了,如下图所示。
在这里插入图片描述

4、实现控制类

代码中有详细注释,不过多解释

import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import com.cxhit.pay.wechat.service.WeChatPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;/*** 微信支付控制类** @author* @since*/
@RestController
@RequestMapping("weChat")
public class WeChatPayController {@Autowiredprivate WeChatPayService weChatPayService;/*** 微信支付接口** @param title 商品名称* @param price 商品价格* @return 返回结果*/@PostMapping(value = "/pay")@ResponseBodypublic Dict pay(String title, String price,String openId) {// 生成商家单号String outTradeNo = IdUtil.simpleUUID();// 支付宝价格转换成浮点数后,乘100,再取整,得以分为单位的价格Integer total = (int) (Float.parseFloat(price) * 100);// 发起支付请求String[] result = weChatPayService.pay("jsapi", title, outTradeNo, total, openId);// 返回结果:下单成功if ("Y".equals(result[0])) {return Dict.create().set("code", 200).set("qrcode", result[1]).set("outTradeNo", outTradeNo);}// 下单失败else {return Dict.create().set("code", 500).set("msg", result[1]);}}/*** 退款接口** @param outTradeNo 商家单号* @param amount     退款金额(不能大于总金额)* @return 退款结果*/@PostMapping(value = "/refund")@ResponseBodypublic Dict refund(String outTradeNo, String amount) {// 生成商家退款单号String outRefundNo = IdUtil.simpleUUID();// 查询订单金额String[] query = weChatPayService.query(null, outTradeNo);// 查询成功if (query[0].equals("Y")) {int total = Integer.parseInt(query[3]);// 支付宝价格转换成浮点数后,乘100,再取整,得以分为单位的价格int refund = (int) (Float.parseFloat(amount) * 100);if (refund > total) {return Dict.create().set("code", 500).set("msg", "退款错误:退款金额不能大于支付金额!");}// 发起退款String[] result = weChatPayService.refund(outTradeNo, outRefundNo, total, refund);// 退款成功if (result[0].equals("Y")) {return Dict.create().set("code", 200).set("msg", "退款进行中,稍后到账!" +" <br>商户单号:" + result[1] +" <br>退款单号:" + result[3] +" <br>订单金额:" + result[4] + "分" +" <br>退款金额:" + result[5] + "分" +" <br>退款时间:" + result[6]);}// 退款失败else if (result[0].equals("N")) {return Dict.create().set("code", 500).set("msg", "退款失败:" + result[1] + result[2]);}// 退款发生错误else {return Dict.create().set("code", 500).set("msg", "退款错误:" + result[1] + result[2]);}}// 查询失败else {return Dict.create().set("code", 500).set("msg", "退款错误:" + query[1] + query[2]);}}/*** 查询接口** @param outTradeNo 商家订单号* @return 结果*/@PostMapping(value = "/query")@ResponseBodypublic Dict query(String outTradeNo) {// 查询订单String[] query = weChatPayService.query(null, outTradeNo);// 查询成功if (query[0].equals("Y")) {return Dict.create().set("code", 200).set("msg", "查询成功!" +" <br>商户单号:" + query[1] +" <br>微信单号:" + query[2] +" <br>订单金额:" + query[3] + "分" +" <br>交易时间:" + query[4] +" <br>交易状态:" + query[5] +" <br>交易描述:" + query[6]);}// 查询失败else if (query[0].equals("N")) {return Dict.create().set("code", 500).set("msg", "查询结果:" + query[1] + query[2]);}// 查询发送异常else {return Dict.create().set("code", 500).set("msg", "查询失败:" + query[1] + query[2]);}}
}

到此为止,我们千辛万苦费劲拿到的范围值,仅仅是为了一个prepay_id,这个prepay_id是在微信支付接口返回的qrcode就是prepay_id

5、调起支付

看看前台都需接收哪些值吧
https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
在这里插入图片描述
Java代码展示:

import cn.hutool.core.io.file.FileReader;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.poi.util.IOUtils;
import org.junit.Test;
import org.springframework.stereotype.Repository;
import org.springframework.util.ResourceUtils;import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Base64;@Repository
public class CreateSign {public String getToken(String appid,String prepay_id) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {String randomOnce = RandomStringUtils.randomAlphanumeric(32);//随机字符串String nonceStr = randomOnce;//真!随机字符串//时间戳long timestamp = System.currentTimeMillis() / 1000;//从下往上依次生成String message = buildMessage(appid, timestamp, nonceStr, prepay_id);//签名String signature = sign(message.getBytes("utf-8"));JSONObject param = new JSONObject();param.put("appId",appid);param.put("timeStamp",timestamp);param.put("nonceStr",randomOnce);param.put("package",prepay_id);param.put("signType","RSA");param.put("paySign",signature);return  param.toString() ;}public String sign(byte[] message) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {//签名方式Signature sign = Signature.getInstance("SHA256withRSA");//在本地环境运行使用 私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名sign.initSign(MyPrivatekey.getPrivateKey("E:\\微信支付\\Cert\\apiclient_key.pem"));//在服务器中使用这种方式//FileReader fileReader = new FileReader("\\usr\\local\\WXCertUtil\\cert\\1621641850_20220614_cert\\apiclient_key.pem");sign.initSign(MyPrivatekey.getPrivateKey(fileReader.readString()));sign.update(message);return Base64.getEncoder().encodeToString(sign.sign());}/***  按照前端签名文档规范进行排序,\n是换行* @param appid* @param timestamp* @param nonceStr* @param prepay_id* @return*/public String buildMessage(String appid, long timestamp,String nonceStr,String prepay_id) {return appid + "\n"+ timestamp + "\n"+ nonceStr + "\n"+ "prepay_id="+prepay_id + "\n";}
}

补充上面代码用到的MyPrivateKey工具类

import org.springframework.stereotype.Repository;import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;@Repository
public class MyPrivatekey {/*** 获取私钥。** @param filename 私钥文件路径  (required)* @return 私钥对象*/public static PrivateKey getPrivateKey(String filename) throws IOException {String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");try {String privateKey = filename.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");KeyFactory kf = KeyFactory.getInstance("RSA");return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(privateKey)));} catch (NoSuchAlgorithmException e) {throw new RuntimeException("当前Java环境不支持RSA", e);} catch (InvalidKeySpecException e) {throw new RuntimeException("无效的密钥格式");}}
}

Service层代码:

@Override
public String rsaPaySign(String prepayId) {String appId = "XXXXXXX";try {String getToken = createSign.getToken(appId, prepayId);return getToken;} catch (IOException e) {e.printStackTrace();} catch (SignatureException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();}return "签名错误";
}

前端控制器Controller层代码:

     /*** 调起支付对sign加密* @param prepayId* @return*/@GetMapping("rsaPaySign")public String rsaPaySign(@RequestParam("prepayId")String prepayId){System.out.println("prepayId:"+prepayId);String rsaPaySign = weChatPayService.rsaPaySign(prepayId);return rsaPaySign;}

6、前端调起支付部分代码(Vue)

function onBridgeReady() {WeixinJSBridge.invoke('getBrandWCPayRequest', {"appId":value.appId,     //公众号ID,由商户传入     "timeStamp": value.timeStamp+"",     //时间戳,自1970年以来的秒数     "nonceStr": value.nonceStr,      //随机串     "package": "prepay_id="+value.package,"signType": value.signType,     //微信签名方式:     "paySign": value.paySign},function(res) {if (res.err_msg == "get_brand_wcpay_request:ok") {//支付成功							} else if (res.err_msg == "get_brand_wcpay_request:cancel") {} else if (res.err_msg == "get_brand_wcpay_request:fail") {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。});
}
if (typeof WeixinJSBridge == "undefined") {if (document.addEventListener) {document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);} else if (document.attachEvent) {document.attachEvent('WeixinJSBridgeReady', onBridgeReady);document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);}
} else {onBridgeReady();
}

http://chatgpt.dhexx.cn/article/UMf4k42C.shtml

相关文章

微信 JSAPI 支付流程

微信支付分为5种&#xff1a; Jsapi支付&#xff0c;二维码支付&#xff0c;H5支付&#xff0c;小程序支付&#xff0c;App支付 Jsapi支付流程&#xff1a; (1) 通过oauth协议获取open_id a.第一步&#xff1a;用户同意授权&#xff0c;获取code 在确保微信公众账号拥有授权…

微信小程序微信支付《JSAPI支付》APIV3详细教程

文章目录 前提整体介绍我的maven依赖1、整体流程2、openid 的获取3、统一下单Controller&#xff08;预支付订单&#xff09;4、配置类和配置文件5、工具类6、前端接收到必要的参数&#xff0c;进行调起支付页面7、微信支付通知&#xff0c;notify_url的回调Controller8、前端小…

java对接微信支付:JSAPI支付(微信公众号支付)

本文是【微信JSAPI支付】文章&#xff0c;主要讲解商户对接微信支付&#xff0c;简洁版测试 文章目录 一、JSAPI支付接入前准备 二、代码片段 1.引入Maven依赖 2.后端业务请求接口 3.前端调起支付请求方法 总结 一、JSAPI支付接入前准备 1、JSAPI支付首先需要注册、认证一个公…

微信 JSAPI 支付流程

微信支付&#xff0c;开发文档地址&#xff1a; https://pay.weixin.qq.com/wiki/doc/api/index.html JSAPI支付文档地址&#xff1a; https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter9_2 微信支付分为5种&#xff1a; Jsapi支付&#xff0c;二维码支付&#xf…

微信公众号支付(JSAPI)

六年代码两茫茫&#xff0c;不思量&#xff0c;自难忘 6年资深前端主管一枚&#xff0c;只分享技术干货&#xff0c;项目实战分享 关注博主不迷路~ 文章目录 JSAPI支付简介应用场景支付的对接准备工作开发流程必填参数预支付统一下单公众号授权获取用户信息微信支付&#xff08…

微信支付之JSAPI支付

首先看一下&#xff0c;微信支付关于jsapi的官方文档&#xff0c;相关接口&#xff0c;一共有下单&#xff0c;查询订单&#xff0c;关闭订单&#xff0c;调起jsapi支付&#xff0c;支付结果通知&#xff0c;申请退款&#xff0c;查询单笔退款&#xff0c;退款结果通知&#xf…

微信支付JSAPI

一、什么是JSAPI支付 JSAPI支付是指商户通过调用微信支付提供的JSAPI接口&#xff0c;在支付场景中调起微信支付模块完成收款。 应用场景有&#xff1a; 线下场所&#xff1a;调用接口生成二维码&#xff0c;用户扫描二维码后在微信浏览器中打开页面后完成支付 公众号场景&…

微信支付之JSAPI支付开发流程

JSAPI支付 前言准备开发1.流程说明2.下单&#xff08;预支付&#xff09;3.前端调起支付4.支付结果异步通知5.退款申请6.退款结果异步通知 结语 前言 最近项目涉及到微信支付的功能&#xff0c;在这里简单分享下整体的开发流程,这里要介绍的是JSAPI支付。 JSAPI网页支付&#…

FME是一个好东东

FME产品分为三个层次&#xff1a; 一、入门级 1、特点&#xff1a;支持常见的GIS软件的数据交换&#xff0c;如 MapInfo TAB, DGN, DXF, DWG, SDTS, SHP, and TIGER&#xff1b;可以运行大部分函数&#xff08;Funtion和Factory&#xff09;&#xff1b;不支持由Plug-in开发的第…

黑马程序员Maven学习笔记

前言 这里是黑马程序员Maven学习笔记分享&#xff0c;这是视频链接。 我还有其它前端内容的笔记&#xff0c;有需要可以查看。 文章目录 前言基础Maven简介Maven是什么Maven的作用 Maven的下载Maven的基础概念仓库坐标本地仓库配置远程仓库的配置 第一个Maven项目Maven的项目…

MAEKDOWN

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

谷粒商城前端笔记

前端笔记 JavaScript ES6 ECMAScript6.0&#xff08;以下简称ES6&#xff0c;ECMAScript是一种由Ecma国际通过ECMA-262标准化的脚本&#xff09;&#xff0c;是JavaScript语言的下一代标准&#xff0c;2015年6月正式发布&#xff0c;从ES6开始的版本号采用年号&#xff0c;如…

谷粒商城-分布式基础【业务编写】

谷粒商城-分布式基础篇【环境准备】谷粒商城-分布式基础【业务编写】谷粒商城-分布式高级篇【业务编写】持续更新谷粒商城-分布式高级篇-ElasticSearch谷粒商城-分布式高级篇-分布式锁与缓存项目托管于gitee 一、三级分类 此处三级分类最起码得启动renren-fast、nacos、gate…

1. 谷粒商城架构

架构图 解析 客户通过任意客户端&#xff08;app/Web&#xff09;向服务器发送请求&#xff0c; 请求首先来到Nginx集群&#xff0c;Nginx将请求转交给Api网关&#xff08;SpringCloud Gateway&#xff09;, Api网关&#xff1a; 可以根据当前请求&#xff0c;动态路由到指…

谷粒商城详细笔记

前言 mysql安装在腾讯云 redis安装在本地虚拟机master上 运行时&#xff0c;renren-fast这个项目要到单独开个idea窗口打开。 一、项目简介 1、项目微服务架构图 微服务&#xff1a;拒绝大型单体应用&#xff0c;基于业务边界进行服务微化拆分&#xff0c;各个服务独立部…

谷粒商城之分布式基础(二)

6 商品服务 6.1 三级分类 商城的商品页面展示是一个三级分类的。有一级分类、二级分类、三级分类。这就是我们接下来要进行的操作。 6.1.1 数据库 首先我们在gulimall_pms这个数据库中的pms_category这个表下插入数据 商品三级分类SQL代码 6.1.2 查出所有分类及其子分类 1…

麦克

品牌&#xff1a;InvenSense Kingstate RS PRO 灵敏度&#xff1a;-27到-44db之间 方向性&#xff1a;全方位、单向性、噪声消除 标准操作电压&#xff1a;1.5V到3.3V均有 安装方式&#xff1a;导线、表面贴装、通孔 输出阻抗大小&#xff1a;1.8K、2.2K、200欧、350欧…

Kubeedge Beehive 模块源码分析

文章目录 概述结构Model --- 消息模型Header --- 消息头Router --- 消息路由资源操作资源类型 Context --- 上下文ModuleContext --- 模块上下文MessageContext --- 消息上下文GlobalContext --- 全局上下文方法 Channel Context数据结构方法ModuleContext 接口实现AddModuleAd…

谷粒商城简介(1~5集)

谷粒商城简介&#xff08;1~5集&#xff09; 一、项目简介 1、项目背景 1&#xff09;、电商模式 市面上有 5 种常见的电商模式 B2B、B2C、C2B、C2C、O2O&#xff1b; 1、B2B 模式 B2B (Business to Business)&#xff0c; 是指商家与商家建立的商业关系。 如&#xff1a;阿…

谷粒商城:分布式基础概念(2)

微服务 微服务架构风格&#xff0c;就像是把一个单独的应用程序开发为一套小服务&#xff0c;每个小服务运行在自 己的进程中&#xff0c;并使用轻量级机制通信&#xff0c;通常是 HTTP API。这些服务围绕业务能力来构建&#xff0c; 并通过完全自动化部署机制来独立部署。这些…