目录
前端调用后端接口发起微信支付订单
1、引入Maven坐标
<!-- 微信支付api v3 -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
2、配置文件
# 微信支付相关参数
# 商户号
wxpay.mch-id=1558950191
# 商户API证书序列号
wxpay.mch-serial-no=34345964330B66427E0D3D28826C4993C77E631F
# 商户私钥文件
wxpay.private-key-path=apiclient_key.pem
# APIv3密钥
wxpay.api-v3-key=UDuLFDcmy5Eb6o0nTNZdu6ek4DDh4K8B
# APPID
wxpay.appid=wx74862e0dfcf69954
# 微信服务器地址
wxpay.domain=https://api.mch.weixin.qq.com
# 接收结果通知地址
# 回调地址
wxpay.notify-domain=https://500c-219-143-130-12.ngrok.io
# APIv2密钥
wxpay.partnerKey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
3、配置类 WxPayConfig
package com.tuimi.zxdt.wxpay.config;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
/**
* @description: 微信配置
* @author: HK
* @since: 2024/9/26 11:46
*/
@Configuration
@PropertySource("classpath:config/wxPay.application") //读取配置文件
@ConfigurationProperties(prefix="wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
public class WxPayConfig {
// 商户号
private String mchId;
// 商户API证书序列号
private String mchSerialNo;
// 商户私钥文件
private String privateKeyPath;
// APIv3密钥
private String apiV3Key;
// APPID
private String appid;
// 微信服务器地址
private String domain;
// 接收结果通知地址
private String notifyDomain;
// APIv2密钥
private String partnerKey;
/**
* 获取商户的私钥文件
* @param filename
* @return
*/
private PrivateKey getPrivateKey(String filename){
try {
return PemUtil.loadPrivateKey(new FileInputStream(filename));
} catch (FileNotFoundException e) {
throw new RuntimeException("私钥文件不存在", e);
}
}
/**
* 获取签名验证器
* @return
*/
@Bean
public Verifier getVerifier(){
log.info("获取签名验证器");
//获取商户私钥
PrivateKey privateKey = getPrivateKey(privateKeyPath);
//私钥签名对象
PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);
//身份认证对象
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
try {
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));
} catch (IOException | GeneralSecurityException | HttpCodeException e) {
e.printStackTrace();
}
try {
return certificatesManager.getVerifier(mchId);
} catch (NotFoundException e) {
e.printStackTrace();
throw new RuntimeException("获取签名验证器失败");
}
// 使用定时更新的签名验证器,不需要传入证书
// ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
// wechatPay2Credentials,
// apiV3Key.getBytes(StandardCharsets.UTF_8));
//
// return verifier;
}
/**
* 获取http请求对象
* @param verifier
* @return
*/
@Bean(name = "wxPayClient")
public CloseableHttpClient getWxPayClient(Verifier verifier){
log.info("获取httpClient");
//获取商户私钥
PrivateKey privateKey = getPrivateKey(privateKeyPath);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, privateKey)
.withValidator(new WechatPay2Validator(verifier));
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
/**
* 获取HttpClient,无需进行应答签名验证,跳过验签的流程
*/
@Bean(name = "wxPayNoSignClient")
public CloseableHttpClient getWxPayNoSignClient(){
//获取商户私钥
PrivateKey privateKey = getPrivateKey(privateKeyPath);
//用于构造HttpClient
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
//设置商户信息
.withMerchant(mchId, mchSerialNo, privateKey)
//无需进行签名验证、通过withValidator((response) -> true)实现
.withValidator((response) -> true);
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
CloseableHttpClient httpClient = builder.build();
log.info("== getWxPayNoSignClient END ==");
return httpClient;
}
}
4、支付链接枚举 WxApiType
package com.tuimi.zxdt.wxpay.config;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @description: 支付链接枚举
* @author: HK
* @since: 2024/9/26 15:22
*/
@AllArgsConstructor
@Getter
public enum WxApiType {
/**
* 支付
*/
JSAPI_PAY("/v3/pay/transactions/jsapi"),
/**
* 查询订单(商户订单号查询)
*/
ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"),
/**
* 关闭订单
*/
CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),
/**
* 申请退款
*/
DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"),
/**
* 查询单笔退款
*/
DOMESTIC_REFUNDS_QUERY("/v3/refund/domestic/refunds/%s"),
/**
* 申请交易账单
*/
TRADE_BILLS("/v3/bill/tradebill"),
/**
* 申请资金账单
*/
FUND_FLOW_BILLS("/v3/bill/fundflowbill");
/**
* 类型
*/
private final String type;
}
5、发起请求参数封装 WxPayCommon
package com.tuimi.zxdt.wxpay.config;
import com.google.gson.Gson;
import com.tuimi.zxdt.utils.StringUtils;
import com.tuimi.zxdt.wxpay.domain.WxPay;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @description:
* @author: HK
* @since: 2024/9/26 15:00
*/
@Slf4j
public class WxPayCommon {
/**
* 封装基础通用请求数据
* @param wxPayConfig 微信的配置文件
* @param wxPay 微信支付基础请求数据
* @return 封装后的map对象
*/
public static Map<String, Object> getBasePayParams(WxPayConfig wxPayConfig, WxPay wxPay) {
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("appid", wxPayConfig.getAppid());
paramsMap.put("mchid", wxPayConfig.getMchId());
// 如果商品名称过长则截取
String title = wxPay.getGoodsName().length() > 62 ? wxPay.getGoodsName().substring(0, 62) : wxPay.getGoodsName();
paramsMap.put("description",title);
paramsMap.put("out_trade_no", wxPay.getOrderNumber());
paramsMap.put("attach", wxPay.getOrderAttach());
paramsMap.put("notify_url", wxPayConfig.getNotifyDomain());
Map<String, Object> amountMap = new HashMap<>();
amountMap.put("total", wxPay.getOrderMoney());
amountMap.put("currency", "CNY");
paramsMap.put("amount", amountMap);
return paramsMap;
}
/**
* 获取请求对象(Post请求)
* @param paramsMap 请求参数
* @return Post请求对象
*/
public static HttpPost getHttpPost(String type, Map<String, Object> paramsMap) {
// 1.设置请求地址
HttpPost httpPost = new HttpPost(type);
// 2.设置请求数据
Gson gson = new Gson();
String jsonParams = gson.toJson(paramsMap);
// 3.设置请求信息
StringEntity entity = new StringEntity(jsonParams, "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
return httpPost;
}
/**
* 解析响应数据
* @param response 发送请求成功后,返回的数据
* @return 微信返回的参数
*/
public static HashMap<String, String> resolverResponse(CloseableHttpResponse response) {
try {
// 1.获取请求码
int statusCode = response.getStatusLine().getStatusCode();
// 2.获取返回值 String 格式
final String bodyAsString = EntityUtils.toString(response.getEntity());
Gson gson = new Gson();
if (statusCode == 200) {
// 3.如果请求成功则解析成Map对象返回
HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
return resultMap;
} else {
if (StringUtils.isNoneBlank(bodyAsString)) {
log.error("微信支付请求失败,提示信息:{}", bodyAsString);
// 4.请求码显示失败,则尝试获取提示信息
HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
throw new RuntimeException(resultMap.get("message"));
}
log.error("微信支付请求失败,未查询到原因,提示信息:{}", response);
// 其他异常,微信也没有返回数据,这就需要具体排查了
throw new IOException("request failed");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 创建微信支付订单-jsapi方式
* @param wxPayConfig 微信配置信息
* @param openId 基础请求信息,商品标题、商家订单id、订单价格
* @param openId 通过微信小程序或者公众号获取到用户的openId
* @param wxPayClient 微信请求客户端()
* @return 微信支付二维码地址
*/
public static String wxJsApiPay(WxPayConfig wxPayConfig, WxPay wxPay, String openId, CloseableHttpClient wxPayClient) {
// 1.获取请求参数的Map格式
Map<String, Object> paramsMap = getBasePayParams(wxPayConfig, wxPay);
// 1.1 添加支付者信息
Map<String,String> payerMap = new HashMap<>();
payerMap.put("openid",openId);
paramsMap.put("payer",payerMap);
// 2.获取请求对象
HttpPost httpPost = getHttpPost("https://api.mch.weixin.qq.com"+WxApiType.JSAPI_PAY.getType(),paramsMap);
// 3.完成签名并执行请求
CloseableHttpResponse response = null;
try {
response = wxPayClient.execute(httpPost);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("微信支付请求失败");
}
// 4.解析response对象
HashMap<String, String> resultMap = resolverResponse(response);
if (resultMap != null) {
// native请求返回的是二维码链接,前端将链接转换成二维码即可
return resultMap.get("prepay_id");
}
return null;
}
}
6、订单编号、签名工具类 OrderUtils
package com.tuimi.zxdt.wxpay.utils;
import com.tuimi.zxdt.wxpay.domain.SignDto;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.Signature;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.Random;
/**
* @description:
* @author: HK
* @since: 2024/9/26 16:00
*/
public class OrderUtils {
/**
* 获取订单编号
* @return
*/
public static String getOrderNo() {
return "ORDER_" + getNo();
}
/**
* 获取退款单编号
* @return
*/
public static String getRefundNo() {
return "REFUND_" + getNo();
}
/**
* 获取编号
* @return
*/
public static String getNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String newDate = sdf.format(new Date());
String result = "";
Random random = new Random();
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
return newDate + result;
}
/**
* 金额判断
* @param amount
* @return
*/
public static boolean isValidAmount(BigDecimal amount) {
if (amount == null) {
throw new IllegalArgumentException("金额不能为空");
} else if (amount.compareTo(BigDecimal.ZERO) <= 0) {
return false; // 金额小于等于0的情况视为无效
} else {
return true;
}
}
/**
* 获取签名
* @param signDto: appId微信公众号或者小程序等的
* timestamp 时间戳 10位
* nonceStr 随机数
* prepay_id 预支付交易会话ID
* privateKeyPath 商户私钥文件
* @return String 新签名
*/
public static String getSign(SignDto signDto) {
//从下往上依次生成
String message = buildMessage(signDto);
//签名
try {
return sign(message.getBytes("utf-8"), signDto.getPrivateKeyPath());
} catch (IOException e) {
throw new RuntimeException("签名异常,请检查参数或商户私钥");
}
}
private static String sign(byte[] message, String privateKeyPath) {
try {
//签名方式
Signature sign = Signature.getInstance("SHA256withRSA");
//私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
sign.initSign(PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath)));
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (Exception e) {
throw new RuntimeException("签名异常,请检查参数或商户私钥");
}
}
/**
* 按照前端签名文档规范进行排序,\n是换行
* @param signDto: appId微信公众号或者小程序等的
* timestamp 时间戳 10位
* nonceStr 随机数
* prepay_id 预支付交易会话ID
* @return String 新签名
*/
private static String buildMessage(SignDto signDto) {
return signDto.getAppId() + "\n"
+ signDto.getTimestamp() + "\n"
+ signDto.getNonceStr() + "\n"
+ signDto.getPrepayId() + "\n";
}
}
7、微信支付回调工具类 WxPayCallbackUtil
package com.tuimi.zxdt.wxpay.utils;
import com.alibaba.fastjson.JSONObject;
import com.tuimi.zxdt.wxpay.config.WxPayConfig;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @description: 微信支付回调
* @author: HK
* @since: 2024/9/27 14:24
*/
@Component
public class WxPayCallbackUtil {
@Resource
private Verifier verifier;
@Resource
private WxPayConfig wxPayConfig;
/**
* 获取回调数据
* @param request
* @param response
* @return
*/
public Map<String, String> wxChatPayCallback(HttpServletRequest request, HttpServletResponse response) {
//获取报文
String body = getRequestBody(request);
//随机串
String nonceStr = request.getHeader("Wechatpay-Nonce");
//微信传递过来的签名
String signature = request.getHeader("Wechatpay-Signature");
//证书序列号(微信平台)
String serialNo = request.getHeader("Wechatpay-Serial");
//时间戳
String timestamp = request.getHeader("Wechatpay-Timestamp");
//构造签名串 应答时间戳\n,应答随机串\n,应答报文主体\n
String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));
Map<String, String> map = new HashMap<>(2);
try {
//验证签名是否通过
boolean result = verifiedSign(serialNo, signStr, signature);
if(result){
//解密数据
String plainBody = decryptBody(body);
return convertWechatPayMsgToMap(plainBody);
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
/**
* 读取请求数据流
* @param request
* @return
*/
public String getRequestBody(HttpServletRequest request) {
StringBuffer sb = new StringBuffer();
try (ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* 签名验证
* @param serialNo 证书序列号(微信平台)
* @param signStr 构造签名串 应答时间戳\n,应答随机串\n,应答报文主体\n
* @param signature 微信传递过来的签名
* @return
* @throws UnsupportedEncodingException
*/
public boolean verifiedSign(String serialNo, String signStr, String signature) throws UnsupportedEncodingException {
return verifier.verify(serialNo, signStr.getBytes("utf-8"), signature);
}
/**
* 解密body的密文
* @param body
* "resource": {
* "original_type": "transaction",
* "algorithm": "AEAD_AES_256_GCM",
* "ciphertext": "",
* "associated_data": "",
* "nonce": ""
* }
* @return
* @throws UnsupportedEncodingException
* @throws GeneralSecurityException
*/
public String decryptBody(String body) throws UnsupportedEncodingException, GeneralSecurityException {
AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes("utf-8"));
JSONObject object = JSONObject.parseObject(body);
JSONObject resource = object.getJSONObject("resource");
String ciphertext = resource.getString("ciphertext");
String associatedData = resource.getString("associated_data");
String nonce = resource.getString("nonce");
return aesUtil.decryptToString(associatedData.getBytes("utf-8"),nonce.getBytes("utf-8"),ciphertext);
}
/**
* 解析ciphertext密文
* @param plainBody 密文
* @return
*/
public Map<String,String> convertWechatPayMsgToMap(String plainBody){
Map<String,String> paramsMap = new HashMap<>(5);
JSONObject jsonObject = JSONObject.parseObject(plainBody);
// 微信支付订单号
paramsMap.put("transactionId",jsonObject.getString("transaction_id"));
//商户订单号
paramsMap.put("out_trade_no",jsonObject.getString("out_trade_no"));
//交易状态
paramsMap.put("trade_state",jsonObject.getString("trade_state"));
// 支付完成时间
paramsMap.put("success_time",jsonObject.getString("success_time"));
//附加数据
paramsMap.put("attach",jsonObject.getString("attach"));
// if (jsonObject.getJSONObject("attach") != null && !jsonObject.getJSONObject("attach").equals("")){
// paramsMap.put("account_no",jsonObject.getJSONObject("attach").getString("accountNo"));
// }
return paramsMap;
}
}
8、实体类签名参数 SignDto
package com.tuimi.zxdt.wxpay.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description: 签名参数
* @author: HK
* @since: 2024/9/26 16:46
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SignDto {
// 随机字符串,不长于32位。
private String nonceStr;
// 商户申请的公众号对应的appid,由微信支付生成,可在公众号后台查看
private String appId;
// JSAPI下单接口返回的prepay_id参数值
private String prepayId;
// 时间戳
private long timestamp;
// 商户私钥文件
private String privateKeyPath;
}
9、微信支付实体 WxPay
package com.tuimi.zxdt.wxpay.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @description:
* @author: HK
* @since: 2024/9/26 15:04
*/
@Data
public class WxPay {
@ApiModelProperty(value = "商品名称")
private String goodsName;
@ApiModelProperty(value = "订单编号")
private String orderNumber;
@ApiModelProperty(value = "订单附加信息,逗号分割(订单类型和订单Id)")
private String orderAttach;
@ApiModelProperty(value = "订单金额(单位‘分’)")
private Integer orderMoney;
}
10、返回前端参数 WxPayDto
package com.tuimi.zxdt.wxpay.domain;
import lombok.Data;
/**
* @description: 返回前端参数
* @author: HK
* @since: 2024/9/26 15:40
*/
@Data
public class WxPayDto {
/**
* 需要支付的小程序id
*/
private String appid;
/**
* 时间戳(当前的时间)
*/
private String timeStamp;
/**
* 随机字符串,不长于32位。
*/
private String nonceStr;
/**
* 小程序下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
*/
private String prepayId;
/**
* 签名类型,默认为RSA,仅支持RSA。
*/
private String signType;
/**
* 签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值
*/
private String paySign;
}
11、微信支付controller
/**
* 资料 =》微信支付
* @param studyMaterialOrder 参数
* @return 信息提示
*/
@PostMapping("studyMaterialWxPay")
public ResponseEntity<Object> studyMaterialWxPay(@RequestBody StudyMaterialOrder studyMaterialOrder, HttpServletRequest request) {
Integer memberId = (Integer) request.getAttribute("memberId");
studyMaterialOrder.setMemberId(memberId);
WxPayDto wxChatPayDto = service.studyMaterialWxPay(studyMaterialOrder);
return new ResponseEntity<>(wxChatPayDto, HttpStatus.OK);
}
12、微信支付service
@Override
@Transactional
public WxPayDto studyMaterialWxPay(StudyMaterialOrder studyMaterialOrder) {
// 根据用户ID查询openId
Member member = memberService.getById(studyMaterialOrder.getMemberId());
if (member == null || StringUtils.isEmpty(member.getOpenId())) {
throw new BadRequestException("用户信息异常!");
}
// 根据资料ID查询资料名称
StudyMaterial studyMaterial = studyMaterialService.getById(studyMaterialOrder.getMaterialId());
if (studyMaterial == null || StringUtils.isEmpty(studyMaterial.getFileName())) {
throw new BadRequestException("资料信息异常!");
}
if (!OrderUtils.isValidAmount(studyMaterial.getPrice())) {
throw new BadRequestException("金额异常!");
}
// 生成订单编号
String no = OrderUtils.getNo();
studyMaterialOrder.setOrderNumber(no);
// 订单金额
studyMaterialOrder.setOrderMoney(studyMaterial.getPrice());
// 支付状态
studyMaterialOrder.setPayStatus("0");
studyMaterialOrderService.save(studyMaterialOrder);
// 调起微信支付
WxPay wxPay = new WxPay();
wxPay.setGoodsName(studyMaterial.getFileName());
BigDecimal price = studyMaterial.getPrice();
BigDecimal multiply = price.multiply(new BigDecimal(100));
int value = multiply.intValue();
wxPay.setOrderMoney(value);
wxPay.setOrderNumber(no);
wxPay.setOrderAttach("2,"+studyMaterialOrder.getId());
String prepayId = WxPayCommon.wxJsApiPay(wxPayConfig, wxPay, member.getOpenId(), wxPayClient);
String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
WxPayDto wxPayDto = new WxPayDto();
wxPayDto.setAppid(wxPayConfig.getAppid());
wxPayDto.setTimeStamp(timeStamp);
wxPayDto.setNonceStr(nonceStr);
wxPayDto.setPrepayId("prepay_id=" + prepayId);
wxPayDto.setSignType("RSA");
// 获取签名
SignDto signDto = new SignDto(nonceStr,wxPayConfig.getAppid(),prepayId,Long.parseLong(timeStamp), wxPayConfig.getPrivateKeyPath());
wxPayDto.setPaySign(OrderUtils.getSign(signDto));
return wxPayDto;
}
发起成功后返回签名等信息给前端调起支付
WeixinJSBridge内置对象在其他浏览器中无效。
onBridgeReady(value) {
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
'appId': value.appid, // 公众号ID,由商户传入
'timeStamp': value.timeStamp, // 时间戳,自1970年以来的秒数
'nonceStr': value.nonceStr, // 随机串
'package': 'prepay_id=' + value.prepayId,
'signType': value.signType, // 微信签名方式:
'paySign': value.paySign // 微信签名
},
function(res) {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
// 使用以上方式判断前端返回,微信团队郑重提示:
// res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
}
)
},
支付成功后回调支付结果通知
1、微信回调controller
/**
* 微信支付【成功回调】
* @return 信息提示
*/
@PostMapping("payNotify")
public ResponseEntity<Object> payNotify(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> stringMap = service.payNotify(request, response);
return new ResponseEntity<>(stringMap, HttpStatus.OK);
}
2、微信回调service
@Override
@Transactional
public Map<String, String> payNotify(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> map = new HashMap<>(2);
try {
Map<String, String> stringMap = wxChatPayCallback.wxChatPayCallback(request, response);
//支付成功
if (stringMap.get("trade_state").equals("SUCCESS")){
// (通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)
// 支付成功和后的逻辑
// 获取咱们自己生成的订单号 不是微信支付生成的
String out_trade_no = stringMap.get("out_trade_no");
String attach = stringMap.get("attach");
String success_time = stringMap.get("success_time");
if (StringUtils.isEmpty(attach)){
log.warn("微信携带参数失败!");
return map;
}
String[] split = attach.split(",");
if ("2".equals(split[0])){
// 资料订单
StudyMaterialOrder studyMaterialOrder = studyMaterialOrderService.getById(split[1]);
if (out_trade_no.equals(studyMaterialOrder.getOrderNumber())) {
// 修改订单状态
studyMaterialOrder.setPayStatus("1");
Date date = simpleDateFormat.parse(success_time);
studyMaterialOrder.setPayTime(new Timestamp(date.getTime()));
studyMaterialOrderService.updateById(studyMaterialOrder);
// 添加学习资料关联会员表
StudyMaterialMember studyMaterialMember = new StudyMaterialMember();
studyMaterialMember.setMemberId(studyMaterialOrder.getMemberId());
studyMaterialMember.setMaterialId(studyMaterialOrder.getMaterialId());
studyMaterialMemberService.save(studyMaterialMember);
}
}
}
//响应微信
map.put("code", "SUCCESS");
map.put("message", "成功");
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
其中的一些业务逻辑代码需自己替换