在接入微信支付之前,需要现在微信支付商户平台入驻,成为商家,才能继续后续的开发。
微信支付商户平台网址:https://pay.weixin.qq.com
不过,个人用户是无法成为商家的,只有以下几种用户类型才可以成为商家。
成为商家之后,需要完成证书申请、秘钥配置、产品申请等操作,具体如下。
进入微信支付商户平台,在账号中心→API安全页面,完成API证书的申请,和API秘钥的配置,如下图所示。
API证书申请的过程稍微复杂,但是官方有详细的申请教程,点击右侧的查看指引按钮,根据教程一步一步来操作即可。
两种API秘钥则是自定义的32个字符的字符串,任意填写并记住即可。
进入微信支付商户平台,在产品中→我的产品页面,可以查看当前商户已开通和未开通的产品,根据项目需求,字形申请开通即可。
进入微信支付商户平台,在产品中心→开发配置页面,记下本商户的商户号,并填写已申请支付产品的各项授权目录、回调链接等信息。
进入微信支付商户平台,在产品中心→APPID账号管理页面,关联诸如服务号、订阅号、小程序、企业微信、移动应用、网站应用等的APPID,如下图所示。
至此,微信支付商户的基本信息配置完毕,总结下来,以下五项信息时必须的:
创建SpringBoot项目的教程太多太多了…比如:https://cxhit.blog.csdn.net/article/details/113782979,所以这里不再赘述。
项目结构如下图所示。
在pom.xml文件中,导入微信支付的第三方SDK依赖:
<dependency>
<groupId>com.github.binarywanggroupId>
<artifactId>weixin-java-payartifactId>
<version>4.3.0version>
dependency>
其中最新版本可前往Maven官方仓库查看。
第一步:用户同意授权,获取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";
//这里是回调的url
String 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){
//获取回调地址中的code
String 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";
//获取openId
String 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;
}
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方法。
以下代码实现了基本的配置和支付、退款。查询功能,有详细的注解。
另外注意:以下代码中使用了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. 填充回调URL
weChatPayEntity.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:支付结果}
* 关于支付结果:
* APP支付、JSAPI支付为[预支付交易会话标识]
* Native支付为[二维码链接]
* 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下的唯一标识。 下单前需获取到用户的Openid
wxPayUnifiedOrderV3Request.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:退款时间}
* 订单异常:{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()
};
}
}
}
一般来说,我们将配置信息放在yaml文件中。这种操作是没有问题的。
但本文中的支付服务类的实现方案,可以实现将微信支付的配置信息,经过加密后,存储在数据库中。
当需要发起支付的时候,从数据库中读取信息后,经过解密,再写入到微信的支付配置类中。
对于本演示项目,其配置信息就直接写在代码里了,如下图所示。
代码中有详细注释,不过多解释
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 {
@Autowired
private WeChatPayService weChatPayService;
/**
* 微信支付接口
*
* @param title 商品名称
* @param price 商品价格
* @return 返回结果
*/
@PostMapping(value = "/pay")
@ResponseBody
public 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")
@ResponseBody
public 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", "退款进行中,稍后到账!" +
"
商户单号:" + result[1] +
"
退款单号:" + result[3] +
"
订单金额:" + result[4] + "分" +
"
退款金额:" + result[5] + "分" +
"
退款时间:" + 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")
@ResponseBody
public Dict query(String outTradeNo) {
// 查询订单
String[] query = weChatPayService.query(null, outTradeNo);
// 查询成功
if (query[0].equals("Y")) {
return Dict.create().set("code", 200).set("msg", "查询成功!" +
"
商户单号:" + query[1] +
"
微信单号:" + query[2] +
"
订单金额:" + query[3] + "分" +
"
交易时间:" + query[4] +
"
交易状态:" + query[5] +
"
交易描述:" + 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
看看前台都需接收哪些值吧
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;
}
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();
}