新增:微信Native支付

This commit is contained in:
2024-06-30 13:32:42 +08:00
parent 9e3ac0a152
commit b473efc094
8 changed files with 402 additions and 27 deletions
@@ -0,0 +1,20 @@
package com.gxwebsoft.common.core.utils;
import com.wechat.pay.java.core.Config;
import java.util.HashMap;
import java.util.Map;
public class WxNativeUtil {
private static final Map<Integer, Config> tenantConfigs = new HashMap<>();
public static void addConfig(Integer tenantId, Config config) {
tenantConfigs.put(tenantId, config);
}
public static Config getConfig(Integer tenantId) {
return tenantConfigs.get(tenantId);
}
}
@@ -275,4 +275,47 @@ public class MainController extends BaseController {
tokenExpireTime, configProperties.getTokenKey());
return success("登录成功", new LoginResult(access_token, user));
}
@ApiOperation("会员注册")
@PostMapping("/register")
public ApiResult<LoginResult> register(@RequestBody LoginParam param, HttpServletRequest request) {
final String phone = param.getPhone();
final Integer tenantId = param.getTenantId();
final String code = param.getCode();
User user = userService.getByUsername(phone, tenantId);
// 验证码校验
String key = "code:" + param.getPhone();
if(!code.equals(redisUtil.get(key))){
String message = "验证码不正确";
loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, tenantId,request);
return fail(message, null);
}
if (user == null) {
String message = "账号不存在";
loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, tenantId,request);
return fail(message, null);
}
if (!user.getStatus().equals(0)) {
String message = "账号被冻结";
loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, tenantId, request);
return fail(message, null);
}
loginRecordService.saveAsync(phone, LoginRecord.TYPE_LOGIN, null, tenantId, request);
// 设置过期时间
Long tokenExpireTime = configProperties.getTokenExpireTime();
final JSONObject register = cacheClient.getSettingInfo("register", tenantId);
if(register != null){
final String ExpireTime = register.getString("tokenExpireTime");
if (ExpireTime != null) {
tokenExpireTime = Long.valueOf(ExpireTime);
}
}
// 签发token
String access_token = JwtUtil.buildToken(new JwtSubject(phone, tenantId),
tokenExpireTime, configProperties.getTokenKey());
return success("登录成功", new LoginResult(access_token, user));
}
}
@@ -0,0 +1,90 @@
package com.gxwebsoft.common.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* 支付方式
*
* @author 科技小王子
* @since 2024-05-11 12:39:11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "Payment对象", description = "支付方式")
@TableName("sys_payment")
public class Payment implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "ID")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "支付方式")
private String name;
@ApiModelProperty(value = "类型")
private Integer type;
@ApiModelProperty(value = "标识")
private String code;
@ApiModelProperty(value = "支付图标")
private String image;
@ApiModelProperty(value = "微信商户号类型 1普通商户2子商户")
private Integer wechatType;
@ApiModelProperty(value = "应用ID")
private String appId;
@ApiModelProperty(value = "商户号")
private String mchId;
@ApiModelProperty(value = "设置APIv3密钥")
private String apiKey;
@ApiModelProperty(value = "证书文件 (CERT)")
private String apiclientCert;
@ApiModelProperty(value = "证书文件 (KEY)")
private String apiclientKey;
@ApiModelProperty(value = "商户证书序列号")
private String merchantSerialNumber;
@ApiModelProperty(value = "支付结果通知")
private String notifyUrl;
@ApiModelProperty(value = "备注")
private String comments;
@ApiModelProperty(value = "文章排序(数字越小越靠前)")
private Integer sortNumber;
@ApiModelProperty(value = "状态, 0未启用, 1启用")
private Boolean status;
@ApiModelProperty(value = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
@ApiModelProperty(value = "租户id")
private Integer tenantId;
@ApiModelProperty(value = "注册时间")
private Date createTime;
@ApiModelProperty(value = "修改时间")
private Date updateTime;
}
@@ -143,7 +143,6 @@ public class OrderController extends BaseController {
return fail("核销失败",null);
}
@PreAuthorize("hasAuthority('sys:order:save')")
@ApiOperation("下单")
@PostMapping()
public ApiResult<?> save(@RequestBody Order order, HttpServletRequest request) {
@@ -172,13 +171,13 @@ public class OrderController extends BaseController {
// 已下过订单
if(order.getOrderId() != null){
Order order1 = orderService.getByIdRel(order.getOrderId());
order1.setPayType(order.getPayType());
order1.setOpenid(loginUser.getOpenid());
order.setOrderNo(Long.toString(IdUtil.getSnowflakeNextId()));
System.out.println("已下过订单 = " + order1);
// 微信统一下单接口
if (order.getPayType().equals(1)) {
return success("下单成功", orderService.createWxOrder(order1));
}
order1.setOrderNo(Long.toString(IdUtil.getSnowflakeNextId()));
order1.setAccessToken(access_token);
order1.setLoginUser(loginUser);
// 调起支付
return onPay(order1);
}
// 创建订单
@@ -190,6 +189,8 @@ public class OrderController extends BaseController {
order.setRealName(loginUser.getRealName());
order.setPhone(loginUser.getPhone());
order.setUserId(loginUser.getUserId());
order.setLoginUser(loginUser);
order.setAccessToken(access_token);
order.setExpirationTime(DateUtil.offset(DateUtil.date(), DateField.MINUTE, 10));
if (order.getOrderInfo() != null) {
order.setTotalNum(order.getOrderInfo().size());
@@ -257,29 +258,39 @@ public class OrderController extends BaseController {
userCardService.save(userCard);
order.setCardId(userCard.getId());
}
// TODO 余额支付
if (order.getPayType().equals(0)) {
order.setAccessToken(access_token);
order.setLoginUser(loginUser);
return success("下单成功", orderService.payByBalance(order));
}
// TODO 微信统一下单接口
if (order.getPayType().equals(1)) {
return success("下单成功", orderService.createWxOrder(order));
}
// TODO 会员卡支付
if(order.getPayType().equals(2)){
order.setAccessToken(access_token);
order.setLoginUser(loginUser);
return success("下单成功", orderService.payByUserCard(order));
}
// 调起支付
return onPay(order);
}
return fail("下单失败");
}
private ApiResult<?> onPay(Order order) {
// TODO 余额支付
if (order.getPayType().equals(0)) {
order.setAccessToken(order.getAccessToken());
return success("下单成功", orderService.payByBalance(order));
}
// TODO 微信统一下单接口
if (order.getPayType().equals(1)) {
return success("下单成功", orderService.createWxOrder(order));
}
// // TODO 微信Native支付
// if(order.getPayType().equals(102)){
// order.setAccessToken(access_token);
// order.setLoginUser(loginUser);
// return success("下单成功", orderService.payByWxNative(order));
// }
// TODO 会员卡支付
if(order.getPayType().equals(6)){
order.setAccessToken(order.getAccessToken());
return success("下单成功", orderService.payByUserCard(order));
}
return fail("支付失败");
}
@ApiOperation("修改订单")
@PutMapping()
public ApiResult<?> update(@RequestBody Order order) {
@@ -0,0 +1,162 @@
package com.gxwebsoft.shop.controller;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.utils.CommonUtil;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.utils.WxNativeUtil;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.entity.Setting;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.entity.Order;
import com.gxwebsoft.common.system.entity.Payment;
import com.gxwebsoft.common.system.param.SettingParam;
import com.gxwebsoft.shop.service.OrderService;
import com.gxwebsoft.common.system.service.SettingService;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Map;
@Api(tags = "微信Native支付接口")
@RestController
@RequestMapping("/api/shop/wx-native-pay")
public class WxNativePayController extends BaseController {
@Value("${spring.profiles.active}")
String active;
public static String merchantId = "1246610101";
public static String privateKeyPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/apiclient_key.pem";
public static String merchantSerialNumber = "48749613B40AA8F1D768583FC352358E13EB5AF0";
public static String apiV3Key = "zGufUcqa7ovgxRL0kF5OlPr482EZwtn9";
@Resource
private RedisUtil redisUtil;
private Config wxPayConfig;
@Resource
private ConfigProperties config;
@Resource
private SettingService settingService;
@Resource
private OrderService orderService;
@ApiOperation("生成付款码")
@PostMapping("/codeUrl")
public ApiResult<?> getCodeUrl(@RequestBody Order order) {
final User loginUser = getLoginUser();
if(loginUser == null){
return fail("请先登录");
}
String key = "Payment:wxPay:".concat(getTenantId().toString());
final Payment payment = redisUtil.get(key, Payment.class);
if (payment == null) {
return fail("微信支付未配置");
}
// 获取微信小程序配置信息
final String string = redisUtil.get("mp-weixin:".concat(getTenantId().toString()));
if(string == null){
return fail("微信小程序未配置");
}
final JSONObject jsonObject = JSONObject.parseObject(string);
final String appId = jsonObject.getString("appId");
// 处理订单数据
order.setTenantId(loginUser.getTenantId());
order.setUserId(loginUser.getUserId());
order.setRealName(loginUser.getRealName());
order.setPhone(loginUser.getPhone());
order.setExpirationTime(DateUtil.offset(DateUtil.date(), DateField.MONTH, order.getTotalNum()));
// 使用自动更新平台证书的RSA配置
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
// 构建service
NativePayService service = new NativePayService.Builder().config(this.getWxPayConfig()).build();
// request.setXxx(val)设置所需参数,具体参数可见Request定义
PrepayRequest request = new PrepayRequest();
// 计算金额
order.setMoney(new BigDecimal("0.01")); // order.getPayPrice().toString()
long orderNo = IdUtil.getSnowflakeNextId();
order.setOrderNo(Long.toString(orderNo));
BigDecimal decimal = order.getMoney();
final BigDecimal multiply = decimal.multiply(new BigDecimal(100));
// 将 BigDecimal 转换为 Integer
Integer money = multiply.intValue();
Amount amount = new Amount();
amount.setTotal(money);
request.setAmount(amount);
request.setAppid(appId);
request.setMchid(payment.getMchId());
request.setDescription(order.getComments());
request.setNotifyUrl("https://modules.gxwebsoft.com/api/shop/wx-native-pay/notify/" + getTenantId());
request.setOutTradeNo(order.getOrderNo());
// 调用下单方法,得到应答
PrepayResponse response = service.prepay(request);
// 使用微信扫描 code_url 对应的二维码,即可体验Native支付
// System.out.println(response.getCodeUrl());
// 生成指定url对应的二维码到文件,宽和高都是300像素
// QrCodeUtil.generate(response.getCodeUrl(), 300, 300, FileUtil.file("/Users/gxwebsoft/Documents/uploads/wx-native-qrcode.jpg"));
orderService.save(order);
return success("生成付款码", response.getCodeUrl());
}
private Config getWxPayConfig() {
// 获取租户ID
final Integer tenantId = getTenantId();
Config build = WxNativeUtil.getConfig(tenantId);
if (build != null) {
return build;
}
String key = "Payment:wxPay:".concat(tenantId.toString());
final Payment payment = redisUtil.get(key, Payment.class);
System.out.println("payment = " + payment);
String apiclientKey = config.getUploadPath().concat("/file").concat(payment.getApiclientKey());
// 开发环境配置
if(active.equals("dev")){
apiclientKey = privateKeyPath;
}
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(payment.getMchId())
.privateKeyFromPath(apiclientKey)
.merchantSerialNumber(payment.getMerchantSerialNumber())
.apiV3Key(payment.getApiKey())
.build();
System.out.println("config = " + config);
WxNativeUtil.addConfig(tenantId, config);
return config;
}
@ApiModelProperty("异步通知")
@PostMapping("/notify/{tenantId}")
public String wxNotify(@RequestHeader Map<String, String> header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) {
System.out.println("异步通知*************** = ");
System.out.println("request header = " + header);
System.out.println("request body = " + body);
System.out.println("tenantId = " + tenantId);
// 获取支付配置信息用于解密
final SettingParam param = new SettingParam();
param.setSettingKey("payment");
param.setTenantId(tenantId);
final String uploadPath = config.getUploadPath(); // 服务器本地路径
return "fail";
}
}
@@ -50,4 +50,6 @@ public interface OrderService extends IService<Order> {
Boolean payByBalance(Order order);
Boolean payByUserCard(Order order);
// String payByWxNative(Order order);
}
@@ -41,6 +41,8 @@ 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.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
@@ -90,6 +92,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
@Resource
private UserBalanceLogService userBalanceLogService;
@Resource
private SettingService settingService;
@Resource
private RedisUtil redisUtil;
public static String privateKeyPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/apiclient_key.pem";
public static String privateCertPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/apiclient_cert.pem";
@@ -370,5 +374,48 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
orderService.updateById(order);
return true;
}
//
// @Override
// public String payByWxNative(Order order) {
// String key = "Payment:wxPay:".concat(order.getTenantId().toString());
// final Payment payment = redisUtil.get(key, Payment.class);
// if (payment == null) {
// throw new BusinessException("微信支付未配置");
// }
// // 获取微信小程序配置信息
// JSONObject setting = settingService.getBySettingKey("mp-weixin");
// final String appId = setting.getString("appId");
// final String appSecret = setting.getString("appSecret");
//
// // 使用自动更新平台证书的RSA配置
// // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
//
// // 构建service
//// NativePayService service = new NativePayService.Builder().config(this.getWxPayConfig()).build();
// // request.setXxx(val)设置所需参数,具体参数可见Request定义
// com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest request = new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
// // 计算金额
// order.setMoney(new BigDecimal(0.01));
// order.setOrderNo(CommonUtil.createOrderNo());
// BigDecimal decimal = order.getMoney();
// final BigDecimal multiply = decimal.multiply(new BigDecimal(100));
// // 将 BigDecimal 转换为 Integer
// Integer money = multiply.intValue();
// com.wechat.pay.java.service.payments.nativepay.model.Amount amount = new com.wechat.pay.java.service.payments.nativepay.model.Amount();
// amount.setTotal(money);
// request.setAmount(amount);
// request.setAppid(appId);
// request.setMchid(payment.getMchId());
// request.setDescription("企业版-1年授权");
//// request.setNotifyUrl("https://server.gxwebsoft.com/api/system/wx-native-pay/notify/" + getTenantId());
// request.setOutTradeNo(order.getOrderNo());
// // 调用下单方法,得到应答
//// PrepayResponse response = service.prepay(request);
// // 使用微信扫描 code_url 对应的二维码,即可体验Native支付
//// System.out.println(response.getCodeUrl());
// // 生成指定url对应的二维码到文件,宽和高都是300像素
//// QrCodeUtil.generate(response.getCodeUrl(), 300, 300, FileUtil.file("/Users/gxwebsoft/Documents/uploads/wx-native-qrcode.jpg"));
//// return response.getCodeUrl();
// }
}
@@ -29,7 +29,7 @@ public class OrderTask extends BaseController {
/**
* 自动处理过期订单任务
*/
@Scheduled(fixedDelay = 2000, initialDelay = 2000)
// @Scheduled(fixedDelay = 2000, initialDelay = 2000)
@Transactional(rollbackFor = {Exception.class})
public void settledExpirationOrder() {
final OrderParam p = new OrderParam();