1. 微信小程序支付-开发者文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml
2. 导入依赖
<!--小程序支付 v3-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
3. 微信支付工具类
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.PrivateKey;
/**
* 微信支付工具类
*/
@Component
public class WxPayUtils {
// 商户号
public static final String mchId = "xxxxxxx";
// AppID(小程序ID)
public static final String appId = "xxxxxxx";
// AppSecret(小程序密钥)
public static final String appSecret = "xxxxxxx";
// 授权
public final static String grantType = "authorization_code";
// APIv3密钥
public final static String apiV3Key = "xxxxxxx";
// 证书序列号 (从p12文件解析)
public final static String serialnumber = "xxxxxxx";
// 证书私钥
public static final String privateKey = "xxxxxxx";
/**
* 获取私钥。
*/
public static PrivateKey getPrivateKey() throws IOException {
// TODO
return merchantPrivateKey;
}
}
4. 微信支付URL工具类
public interface ConstantUtils {
// JSAPI下单
public final static String JSAPI_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
// 支付通知
public final static String NOTIFY_URL = "https://你的线上地址.com";
// 关闭订单
public final static String CLOSE_PAY_ORDER_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close";
// 查询订单 (根据商户订单号查询)
public final static String QUERY_PAY_RESULT_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/";
}
5. 微信支付API v3 HttpClient (自动处理签名和验签)
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 lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
@Slf4j
@Component
@Order(3)
public class WechatPayaHttpclientUtils implements ApplicationRunner {
// 商户API私钥
public static PrivateKey merchantPrivateKey;
// verifier
public static Verifier verifier;
// httpClient
public static CloseableHttpClient httpClient;
@Override
public void run(ApplicationArguments args) {
log.info("----------->构造微信支付API v3 HttpClient");
createHttpClient();
}
/***
* 微信支付API v3 HttpClient
*
* 自动处理签名和验签
*
* @return
*/
public void createHttpClient() {
// TODO
httpClient = builder.build();
}
}
6. controller
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.ruoyils.sy.order.domain.LsOrder;
import com.ruoyi.ruoyils.wx.pay.service.IWxPayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Api(value = "微信-支付", tags = {"微信-支付"})
@RestController
@RequestMapping("/ls/wx/pay")
public class WxPayController extends BaseController {
@Autowired
private IWxPayService wxPayService;
/**
* jsapi下单
*
* @param lsOrder 订单
* @return
*/
@ApiOperation("统一下单")
@PostMapping(value = "/payment")
public AjaxResult payment(@RequestBody LsOrder lsOrder) {
return wxPayService.addOrder(lsOrder);
}
/**
* 支付通知
*
* @param request
* @param response
* @return
*/
@PostMapping("/notifyUrl")
public AjaxResult notifyUrl(HttpServletRequest request, HttpServletResponse response) {
return AjaxResult.success(wxPayService.notifyUrl(request, response));
}
}
7. service
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.ruoyils.exception.MyException;
import com.ruoyi.ruoyils.sy.append.domain.LsAppend;
import com.ruoyi.ruoyils.sy.append.mapper.LsAppendMapper;
import com.ruoyi.ruoyils.sy.order.domain.LsOrder;
import com.ruoyi.ruoyils.sy.order.mapper.LsOrderMapper;
import com.ruoyi.ruoyils.sy.order.service.impl.LsOrderServiceImpl;
import com.ruoyi.ruoyils.sy.product.domain.LsProduct;
import com.ruoyi.ruoyils.sy.product.mapper.LsProductMapper;
import com.ruoyi.ruoyils.utils.*;
import com.ruoyi.ruoyils.wx.pay.service.IWxPayService;
import com.ruoyi.ruoyils.wx.user.domain.LsUser;
import com.ruoyi.ruoyils.wx.user.mapper.LsUserMapper;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import lombok.extern.slf4j.Slf4j;
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.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.Signature;
import java.util.*;
@Order(5)
@Slf4j
@Service
public class WxPayServiceImpl implements IWxPayService {
@Autowired
private LsOrderMapper orderMapper;
@Autowired
private LsUserMapper lsUserMapper;
@Autowired
private LsProductMapper lsProductMapper;
@Autowired
private LsAppendMapper lsAppendMapper;
/**
* 立即下单
*
* @param lsOrder 订单
* @return 结果
*/
@Transactional(rollbackFor = Exception.class)
@Override
public AjaxResult addOrder(LsOrder lsOrder) {
// TODO 订单业务操作
// 生成订单
int i = orderMapper.insertLsOrder(lsOrder);
if (i > 0) {
// 调起支付
AjaxResult payment = this.payment(lsOrder);
return payment;
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return AjaxResult.error("下单失败 !");
}
/**
* 调起支付
*
* @param lsOrder
* @return
*/
@Override
public AjaxResult payment(LsOrder lsOrder) {
try {
JSONObject order = new JSONObject();
// 应用ID
order.put("appid",WxPayUtils.appId);
// 商户号
order.put("mchid",WxPayUtils.mchId);
// 商品描述
order.put("description",lsOrder.getProductName());
// 订单号
order.put("out_trade_no",lsOrder.getOrderNo());
// 通知地址
order.put("notify_url",ConstantUtils.NOTIFY_URL+"/ls/wx/pay/notifyUrl");
/**订单金额*/
JSONObject amount = new JSONObject();
// 总金额 (默认单位分)
// amount.put("total",lsOrder.getTotalPrice().intValue()*100);
amount.put("total",1);
// 货币类型
amount.put("currency","CNY");
order.put("amount",amount);
/**支付者*/
JSONObject payer = new JSONObject();
LsUser user = SpringUtils.getBean(LsUserMapper.class).selectLsUserById(lsOrder.getUserId());
// 用户标识
payer.put("openid",user.getOpenid());
order.put("payer",payer);
// TODO
return null;
}
/**
* 支付通知
*
* @param servletRequest
* @param response
* @return
*/
@Override
public AjaxResult notifyUrl(HttpServletRequest servletRequest, HttpServletResponse response) {
log.info("----------->微信支付回调开始<-----------");
Map<String, String> map = new HashMap<>(12);
String timeStamp = servletRequest.getHeader("Wechatpay-Timestamp");
String nonce = servletRequest.getHeader("Wechatpay-Nonce");
String signature = servletRequest.getHeader("Wechatpay-Signature");
String certSn = servletRequest.getHeader("Wechatpay-Serial");
// TODO
return null;
}
/**
* 关闭订单
*
* @param orderNo
* @return
*/
public boolean closePayOrder(String orderNo) {
JSONObject obj = new JSONObject();
// 直连商户号
obj.put("mchid", WxPayUtils.mchId);
// 请求地址
String closeOrderUrl = ConstantUtils.CLOSE_PAY_ORDER_URL.replace("{out_trade_no}", orderNo);
HttpPost httpPost = new HttpPost(closeOrderUrl);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(obj.toJSONString(), "UTF-8"));
// 微信httpClient
CloseableHttpClient httpClient = WechatPayaHttpclientUtils.httpClient;
try {
if(httpClient == null){
log.info("关闭订单失败,请重试,无法连接微信支付服务器!");
}
//执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
//状态码
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 204) {
//关闭订单成功!
log.info("微信关闭订单成功: {}", orderNo);
}else if(statusCode == 202){
//用户支付中,需要输入密码
log.info("关闭微信订单--用户支付中,需要输入密码,暂时不做处理!");
}else{
log.info("关闭微信订单--关闭支付订单失败,出现未知原因: {}", EntityUtils.toString(response.getEntity()));
}
} catch (IOException e) {
log.info("关闭微信订单--关闭订单失败: {}", e.getMessage());
}
return false;
}
/**
* 订单查询 (定时查询订单, 修改订单状态)
*
* @return
*/
@Scheduled(cron="0/10 * * * * ?")
public void queryOrder() {
// TODO
}
}
8. 返回给前端调起支付的必要参数
{
"msg": "操作成功",
"code": 200,
"data": {
"timeStamp": "1692334223808",
"package": "prepay_id=wx18125024326178678d4e07673e277c0000",
"paySign": "ocI64smXYkantoHEIkv7fibP0Y83pUmoVN1pQAjrJnFRI75sXCmQt09emPMhZJ+ujuemaGinJdJjmvGZv1JFoWvGSaMv8imDxOQV2EBr9QI+gybtUyC57+H2PhjXIR4gF0M8n7yv6Q9TA+7EIfpXOTaMJjDzM4AkFhAwz/quUAAEJVPLuaMsyF1xqi1qSY9AnE309YhqVG6ETDbZeP9/fuGCs9gD1HdD14HF4BndU696wR4TQdoiTzIyOokrE21oZLdK6Tp6sBPj2mGiIFX8viEHxq8GWOEMOIQXlr4NId4hrYA1Nn6xLk2Ka75t2t8L5V//3rWmbGSOaE5nrkeJcg==",
"appId": "xxxxxxxxxxxxxx",
"signType": "RSA",
"nonceStr": "KFW6FBHHDMALH1A39FM07HKXM7I0T1GR"
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/345b0fcc0c1ddd878b18ae28c0978ea7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0563ae735e5d45d99317f17fe1cedd8d.jpeg)
👉 点我获取demo