提交 0143537b 编写于 作者: G Gadfly

feat: 微信支付V3实现

由于退款接口本来也没有,所以只做了个退款的回调实现。顺便把wxjava的版本升级到了4.1.5.B。
wxjava已知一个bug是由于换行符问题,在windows下对v3接口的签名与验签是错误的,我已经就此向wxjava项目提交了pr,在他们合并之前想在windows上进行签名的话可以参考 Wechat-Group/WxJava:#2246 ,自行实现签名算法。
上级 0933f45d
......@@ -35,7 +35,11 @@ CREATE TABLE `oms_order` (
`freight_amount` bigint(0) NOT NULL DEFAULT 0 COMMENT '运费金额(分)',
`pay_amount` bigint(0) NOT NULL DEFAULT 0 COMMENT '应付总额(分)',
`pay_time` datetime(0) NULL DEFAULT NULL COMMENT '支付时间',
`pay_type` tinyint(0) NULL DEFAULT NULL COMMENT '支付方式【1->支付宝;2->微信;3->银联; 4->货到付款;】',
`pay_type` tinyint(4) NULL DEFAULT NULL COMMENT '支付方式【1->微信jsapi;2->支付宝;3->余额; 4->微信app;】',
`out_trade_no` varchar(32) NULL DEFAULT NULL COMMENT '微信支付等第三方支付平台的商户订单号',
`transaction_id` varchar(32) NULL DEFAULT NULL COMMENT '微信支付订单号',
`out_refund_no` varchar(32) NULL DEFAULT NULL COMMENT '商户退款单号',
`refund_id` varchar(32) NULL DEFAULT NULL COMMENT '微信退款单号',
`delivery_time` datetime(0) NULL DEFAULT NULL COMMENT '发货时间',
`receive_time` datetime(0) NULL DEFAULT NULL COMMENT '确认收货时间',
`comment_time` datetime(0) NULL DEFAULT NULL COMMENT '评价时间',
......@@ -43,7 +47,11 @@ CREATE TABLE `oms_order` (
`gmt_create` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `index_order_sn`(`order_sn`) USING BTREE COMMENT '订单号唯一索引'
UNIQUE INDEX `index_order_sn`(`order_sn`) USING BTREE COMMENT '订单号唯一索引',
UNIQUE INDEX `index_otn`(`out_trade_no`) USING BTREE COMMENT '商户订单号唯一索引',
UNIQUE INDEX `index_ti`(`transaction_id`) USING BTREE COMMENT '商户支付单号唯一索引',
UNIQUE INDEX `index_orn`(`out_refund_no`) USING BTREE COMMENT '商户退款单号唯一索引',
UNIQUE INDEX `index_ri`(`refund_id`) USING BTREE COMMENT '退款单号唯一索引'
) ENGINE = InnoDB AUTO_INCREMENT = 1351548262424821912 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '订单详情表' ROW_FORMAT = Dynamic;
-- ----------------------------
......
......@@ -16,7 +16,7 @@ public enum OrderStatusEnum {
USER_CANCEL(102,"用户取消"),
AUTO_CANCEL(103,"系统自动取消"),
PAID(201,"已支付"),
PAYED(201,"已支付"),
APPLY_REFUND(202,"申请退款"),
REFUNDED(203,"已退款"),
......
......@@ -12,10 +12,11 @@ import lombok.Getter;
@AllArgsConstructor
public enum PayTypeEnum {
WEIXIN(1,"微信支付"),
WEIXIN_JSAPI(1,"微信JSAPI支付"),
ALIPAY(2,"支付宝支付"),
BALANCE(3,"会员余额支付")
;
BALANCE(3,"会员余额支付"),
WEIXIN_APP(1,"微信JSAPI支付");
@Getter
private Integer code;
......
......@@ -75,9 +75,25 @@ public class OmsOrder extends BaseEntity {
*/
private Date payTime;
/**
* 支付方式【1->支付宝;2->微信;3->银联; 4->货到付款;】
* 支付方式【1->微信jsapi;2->支付宝;3->余额; 4->微信app;】
*/
private Integer payType;
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 微信支付订单号
*/
private String transactionId;
/**
* 商户退款单号
*/
private String outRefundNo;
/**
* 微信支付退款单号
*/
private String refundId;
/**
* 发货时间
*/
......
package com.youlai.mall.oms.pojo.vo;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author Gadfly
* @since 2021-06-04 15:18
*/
@Data
@Accessors(chain = true)
public class WxPayResponseVO {
private String code;
private String message;
}
......@@ -113,6 +113,11 @@
<version>${youlai.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
</dependency>
</dependencies>
<build>
......
package com.youlai.mall.oms.config;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 微信支付配置
*
* @author Gadfly
*/
@Slf4j
@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
@AllArgsConstructor
public class WxPayConfiguration {
private final WxPayProperties properties;
@Bean
@ConditionalOnMissingBean
public WxPayService wxPayService() {
if (StringUtils.isBlank(properties.getMchId())) {
log.error("微信商户号配置无效,请检查!");
return new WxPayServiceImpl();
}
WxPayService wxPayService = new WxPayServiceImpl();
WxPayConfig payConfig = new WxPayConfig();
payConfig.setMchId(StringUtils.trimToNull(properties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(properties.getMchKey()));
payConfig.setSubAppId(StringUtils.trimToNull(properties.getSubAppId()));
payConfig.setSubMchId(StringUtils.trimToNull(properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(properties.getKeyPath()));
payConfig.setApiV3Key(StringUtils.trimToNull(properties.getApiV3Key()));
payConfig.setCertSerialNo(StringUtils.trimToNull(properties.getCertSerialNo()));
payConfig.setPrivateKeyPath(StringUtils.trimToNull(properties.getPrivateKeyPath()));
payConfig.setPrivateCertPath(StringUtils.trimToNull(properties.getPrivateCertPath()));
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(BooleanUtils.toBoolean(properties.getSandboxEnabled()));
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
package com.youlai.mall.oms.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* wxpay pay properties.
* 示例配置:
* <pre>
* # 微信支付配置
* wx:
* pay:
* # 微信支付商户号
* mchId: 12345678
* # 微信支付商户密钥
* mchKey: abcdefghijklmn
* # path除了classpath也可以用url
* keyPath: classpath:wxcert/apiclient_cert.p12
* privateKeyPath: classpath:wxcert/apiclient_key.pem
* privateCertPath: classpath:wxcert/apiclient_cert.pem
* apiV3Key: abcdefghijklmn
* certSerialNo: abcdefghijklmn
* sandboxEnabled: false
* payNotifyUrl: https://exmaple.com/callback-api/v1/wx-pay/notify-order-v3
* refundNotifyUrl: https://example.com/callback-api/v1/wx-pay/notify-refund-v3
* </pre>
*
* @author Gadfly
*/
@Data
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
/**
* 微信支付商户号
*/
private String mchId;
/**
* 微信支付商户密钥
*/
private String mchKey;
/**
* 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除
*/
private String subAppId;
/**
* 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除
*/
private String subMchId;
/**
* apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定
*/
private String keyPath;
/**
* apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径.
*/
private String privateKeyPath;
/**
* apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径.
*/
private String privateCertPath;
/**
* apiV3 秘钥值.
*/
private String apiV3Key;
/**
* apiV3 证书序列号值
*/
private String certSerialNo;
/**
* 是否为沙盒环境
*/
private Boolean sandboxEnabled;
/**
* 支付通知url
*/
private String payNotifyUrl;
/**
* 退款通知url
*/
private String refundNotifyUrl;
}
......@@ -10,6 +10,8 @@ public interface OmsConstants {
String ORDER_TOKEN_PREFIX = "order:token:";
String ORDER_SN_PREFIX = "order:sn:";
/**
* 释放锁lua脚本
*/
......
......@@ -72,18 +72,15 @@ public class OrderController {
@PostMapping("/{orderId}/_pay")
@ApiImplicitParams({
@ApiImplicitParam(name = "orderId", value = "订单ID", paramType = "path", dataType = "Long"),
@ApiImplicitParam(name = "payType", value = "支付方式", paramType = "query", dataType = "Integer")
@ApiImplicitParam(name = "payType", value = "支付方式", paramType = "query", dataType = "Integer"),
@ApiImplicitParam(name = "appId", value = "小程序appId", paramType = "query", dataType = "String")
})
public Result pay(@PathVariable Long orderId, Integer payType) {
public <T> Result<T> pay(@PathVariable Long orderId, Integer payType, String appId) {
PayTypeEnum payTypeEnum = PayTypeEnum.getByCode(payType);
switch (payTypeEnum) {
case BALANCE:
orderService.pay(orderId);
break;
default:
return Result.failed("系统暂不支持该支付方式~");
if (payTypeEnum == null) {
return Result.failed("系统暂不支持该支付方式~");
}
return Result.success();
return Result.success(orderService.pay(orderId, appId, payTypeEnum));
}
@ApiOperation("订单删除")
......
package com.youlai.mall.oms.controller.callback;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.youlai.mall.oms.pojo.vo.WxPayResponseVO;
import com.youlai.mall.oms.service.IOrderService;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 回调接口
*
* @author Gadfly
* @since 2021-05-27 14:24
*/
@Api(tags = "【回调】微信支付回调接口")
@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/callback-api/v1/wx-pay")
public class WxPayCallbackController {
private final IOrderService orderService;
/**
* 微信下单支付结果回调
*
* @param notifyData 加密数据
* @param headers 请求头
* @return {"code": "SUCCESS", "message": "成功"}
*/
@PostMapping("/notify-order-v3")
public WxPayResponseVO wxPayOrderNotify(@RequestBody String notifyData,
@RequestHeader HttpHeaders headers) throws WxPayException {
SignatureHeader signatureHeader = getSignatureHeaderByHttpHeaders(headers);
orderService.handleWxPayOrderNotify(signatureHeader, notifyData);
return new WxPayResponseVO()
.setCode(WxPayConstants.ResultCode.SUCCESS)
.setMessage("成功");
}
/**
* 微信退款结果回调
*
* @param notifyData 加密数据
* @param headers 请求头
* @return {"code": "SUCCESS", "message": "成功"}
*/
@PostMapping("/notify-refund-v3")
public WxPayResponseVO wxPayRefundNotify(@RequestBody String notifyData,
@RequestHeader HttpHeaders headers) throws WxPayException {
SignatureHeader signatureHeader = getSignatureHeaderByHttpHeaders(headers);
orderService.handleWxPayRefundNotify(signatureHeader, notifyData);
return new WxPayResponseVO()
.setCode(WxPayConstants.ResultCode.SUCCESS)
.setMessage("成功");
}
private SignatureHeader getSignatureHeaderByHttpHeaders(HttpHeaders headers) {
SignatureHeader signatureHeader = new SignatureHeader();
signatureHeader.setSignature(headers.getFirst("Wechatpay-Signature"));
signatureHeader.setSerial(headers.getFirst("Wechatpay-Serial"));
signatureHeader.setTimeStamp(headers.getFirst("Wechatpay-Timestamp"));
signatureHeader.setNonce(headers.getFirst("Wechatpay-Nonce"));
return signatureHeader;
}
}
......@@ -4,6 +4,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.youlai.mall.oms.enums.PayTypeEnum;
import com.youlai.mall.oms.pojo.entity.OmsOrder;
import com.youlai.mall.oms.pojo.dto.OrderConfirmDTO;
import com.youlai.mall.oms.pojo.vo.OrderConfirmVO;
......@@ -37,8 +40,7 @@ public interface IOrderService extends IService<OmsOrder> {
/**
* 订单支付
*/
boolean pay(Long orderId);
<T> T pay(Long orderId, String appId, PayTypeEnum payTypeEnum);
/**
* 系统关闭订单
......@@ -57,5 +59,23 @@ public interface IOrderService extends IService<OmsOrder> {
IPage<OmsOrder> list(Page<OmsOrder> omsOrderPage, OmsOrder order);
/**
* 处理微信支付成功回调
*
* @param signatureHeader 签名头
* @param notifyData 加密通知
* @throws WxPayException 微信异常
*/
void handleWxPayOrderNotify(SignatureHeader signatureHeader, String notifyData) throws WxPayException;
/**
* 处理微信退款成功回调
*
* @param signatureHeader 签名头
* @param notifyData 加密通知
* @throws WxPayException 微信异常
*/
void handleWxPayRefundNotify(SignatureHeader signatureHeader, String notifyData) throws WxPayException;
}
......@@ -2,15 +2,28 @@ package com.youlai.mall.oms.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.youlai.common.enums.BusinessTypeEnum;
import com.youlai.common.redis.component.BusinessNoGenerator;
import com.youlai.common.result.Result;
import com.youlai.common.web.exception.BizException;
import com.youlai.common.web.util.JwtUtils;
import com.youlai.mall.oms.config.WxPayProperties;
import com.youlai.mall.oms.enums.OrderStatusEnum;
import com.youlai.mall.oms.enums.OrderTypeEnum;
import com.youlai.mall.oms.enums.PayTypeEnum;
......@@ -33,11 +46,13 @@ import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
import com.youlai.mall.ums.api.MemberAddressFeignClient;
import com.youlai.mall.ums.api.MemberFeignClient;
import com.youlai.mall.ums.pojo.entity.UmsAddress;
import com.youlai.mall.ums.pojo.entity.UmsMember;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
......@@ -49,6 +64,7 @@ import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.youlai.mall.oms.constant.OmsConstants.*;
......@@ -58,17 +74,20 @@ import static com.youlai.mall.oms.constant.OmsConstants.*;
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> implements IOrderService {
private final WxPayProperties wxPayProperties;
private final ICartService cartService;
private final SkuFeignClient skuFeignService;
private final MemberAddressFeignClient addressFeignService;
private final IOrderItemService orderItemService;
private final RabbitTemplate rabbitTemplate;
private final StringRedisTemplate redisTemplate;
private final RedissonClient redissonClient;
private final ThreadPoolExecutor threadPoolExecutor;
private final MemberFeignClient memberFeignClient;
private final BusinessNoGenerator businessNoGenerator;
private SeataTccOrderService seataTccOrderService;
private final SeataTccOrderService seataTccOrderService;
private final WxPayService wxPayService;
/**
* 订单确认
......@@ -127,7 +146,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
}, threadPoolExecutor);
CompletableFuture.allOf(orderItemsCompletableFuture, addressesCompletableFuture, orderTokenCompletableFuture).join();
log.info("订单确认响应:{}", orderConfirmVO.toString());
log.info("订单确认响应:{}", orderConfirmVO);
return orderConfirmVO;
}
......@@ -211,7 +230,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
OrderSubmitVO submitVO = new OrderSubmitVO();
submitVO.setOrderId(order.getId());
submitVO.setOrderSn(order.getOrderSn());
log.info("订单提交响应:{}", submitVO.toString());
log.info("订单提交响应:{}", submitVO);
return submitVO;
}
......@@ -268,7 +287,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
OrderSubmitVO submitVO = new OrderSubmitVO();
submitVO.setOrderId(order.getId());
submitVO.setOrderSn(order.getOrderSn());
log.info("订单提交响应:{}", submitVO.toString());
log.info("订单提交响应:{}", submitVO);
return submitVO;
}
......@@ -277,41 +296,112 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
* 订单支付
*
* @param orderId
* @param appId
* @return
*/
@Override
@SuppressWarnings("unchecked")
@GlobalTransactional(rollbackFor = Exception.class)
public boolean pay(Long orderId) {
public <T> T pay(Long orderId, String appId, PayTypeEnum payTypeEnum) {
OmsOrder order = this.getById(orderId);
if (order != null && !OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
throw new BizException("支付失败,请检查订单状态");
if (order == null) {
throw new BizException("订单不存在");
}
RLock lock = redissonClient.getLock(ORDER_SN_PREFIX + order.getOrderSn());
try {
lock.tryLock(0L, 10L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
throw new BizException("订单不可重复支付");
}
// 扣减余额
if (!OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
throw new BizException("支付失败,请检查订单状态");
}
Long userId = JwtUtils.getUserId();
Long payAmount = order.getPayAmount();
Result deductBalanceResult = memberFeignClient.deductBalance(userId, payAmount);
if (!Result.isSuccess(deductBalanceResult)) {
throw new BizException("扣减账户余额失败");
T result;
switch (payTypeEnum) {
case WEIXIN_JSAPI:
result = (T) wxJsapiPay(appId, order, userId);
break;
default:
case BALANCE:
result = (T) balancePay(order, userId);
}
// 扣减库存
Result deductStockResult = skuFeignService.deductStock(order.getOrderSn());
Result<?> deductStockResult = skuFeignService.deductStock(order.getOrderSn());
if (!Result.isSuccess(deductStockResult)) {
throw new BizException("扣减商品库存失败");
}
lock.unlock();
// 支付成功删除购物车已勾选的商品
cartService.removeCheckedItem();
return result;
}
private Boolean balancePay(OmsOrder order, Long userId) {
// 扣减余额
Long payAmount = order.getPayAmount();
Result<?> deductBalanceResult = memberFeignClient.deductBalance(userId, payAmount);
if (!Result.isSuccess(deductBalanceResult)) {
throw new BizException("扣减账户余额失败");
}
// 更新订单状态
order.setStatus(OrderStatusEnum.PAID.getCode());
order.setStatus(OrderStatusEnum.PAYED.getCode());
order.setPayType(PayTypeEnum.BALANCE.getCode());
order.setPayTime(new Date());
this.updateById(order);
// 支付成功删除购物车已勾选的商品
cartService.removeCheckedItem();
return Boolean.TRUE;
}
private WxPayUnifiedOrderV3Result.JsapiResult wxJsapiPay(String appId, OmsOrder order, Long userId) {
Result<UmsMember> userInfoResult = memberFeignClient.getUserEntityById(userId);
if (!Result.isSuccess(userInfoResult)) {
throw new BizException("用户查询失败");
}
UmsMember userInfo = userInfoResult.getData();
Long payAmount = order.getPayAmount();
// 如果已经有outTradeNo了就先进行关单
if (PayTypeEnum.WEIXIN_JSAPI.getCode().equals(order.getPayType()) && StrUtil.isNotBlank(order.getOutTradeNo())) {
try {
wxPayService.closeOrderV3(order.getOutTradeNo());
} catch (WxPayException e) {
log.error(e.getMessage(), e);
throw new BizException("微信关单异常");
}
}
// 用户id前补零保证五位,对超出五位的保留后五位
String userIdFilledZero = String.format("%05d", userId);
String fiveDigitsUserId = userIdFilledZero.substring(userIdFilledZero.length() - 5);
// 在前面加上wxo(weixin order)等前缀是为了人工可以快速分辨订单号是下单还是退款、来自哪家支付机构等
// 将时间戳+3位随机数+五位id组成商户订单号,规则参考自<a href="https://tech.meituan.com/2016/11/18/dianping-order-db-sharding.html">大众点评</a>
String outTradeNo = "wxo_" + System.currentTimeMillis() + RandomUtil.randomNumbers(3) + fiveDigitsUserId;
log.info("商户订单号拼接完成:{}", outTradeNo);
// 更新订单状态
order.setPayType(PayTypeEnum.WEIXIN_JSAPI.getCode());
order.setOutTradeNo(outTradeNo);
this.updateById(order);
return true;
WxPayUnifiedOrderV3Request wxRequest = new WxPayUnifiedOrderV3Request()
.setOutTradeNo(outTradeNo)
.setAppid(appId)
.setNotifyUrl(wxPayProperties.getPayNotifyUrl())
.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(Math.toIntExact(payAmount)))
.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(userInfo.getOpenid()))
.setDescription("赅买-订单编号" + order.getOrderSn());
WxPayUnifiedOrderV3Result.JsapiResult jsapiResult;
try {
jsapiResult = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, wxRequest);
} catch (WxPayException e) {
log.error(e.getMessage(), e);
throw new BizException("微信统一下单异常");
}
return jsapiResult;
}
@Override
......@@ -369,4 +459,45 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
page.setRecords(list);
return page;
}
@Override
public void handleWxPayOrderNotify(SignatureHeader signatureHeader, String notifyData) throws WxPayException {
log.info("开始处理支付结果通知");
// 解密支付通知内容
final WxPayOrderNotifyV3Result.DecryptNotifyResult result =
this.wxPayService.parseOrderNotifyV3Result(notifyData, signatureHeader).getResult();
log.debug("支付通知解密成功:[{}]", result.toString());
// 根据商户订单号查询订单
QueryWrapper<OmsOrder> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(OmsOrder::getOutTradeNo, result.getOutTradeNo());
OmsOrder orderDO = this.getOne(wrapper);
// 支付成功处理
if (WxPayConstants.WxpayTradeStatus.SUCCESS.equals(result.getTradeState())) {
orderDO.setStatus(OrderStatusEnum.PAYED.getCode());
orderDO.setTransactionId(result.getTransactionId());
orderDO.setPayTime(new Date());
this.updateById(orderDO);
}
log.info("账单更新成功");
}
@Override
public void handleWxPayRefundNotify(SignatureHeader signatureHeader, String notifyData) throws WxPayException {
log.info("开始处理退款结果通知");
// 解密支付通知内容
final WxPayRefundNotifyV3Result.DecryptNotifyResult result =
this.wxPayService.parseRefundNotifyV3Result(notifyData, signatureHeader).getResult();
log.debug("退款通知解密成功:[{}]", result.toString());
// 根据商户退款单号查询订单
QueryWrapper<OmsOrder> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(OmsOrder::getOutTradeNo, result.getOutTradeNo());
OmsOrder orderDO = this.getOne(wrapper);
// 退款成功处理
if (WxPayConstants.RefundStatus.SUCCESS.equals(result.getRefundStatus())) {
orderDO.setStatus(OrderStatusEnum.REFUNDED.getCode());
orderDO.setRefundId(result.getRefundId());
this.updateById(orderDO);
}
log.info("账单更新成功");
}
}
......@@ -10,12 +10,10 @@ import org.springframework.web.bind.annotation.*;
public interface MemberFeignClient {
@PostMapping("/app-api/v1/members")
Result<Long > add(@RequestBody UmsMember member);
Result<Long> add(@RequestBody UmsMember member);
@PutMapping("/app-api/v1/members/{id}")
Result update(@PathVariable Long id,@RequestBody UmsMember member);
<T> Result<T> update(@PathVariable Long id,@RequestBody UmsMember member);
/**
* 获取会员信息
......@@ -23,6 +21,11 @@ public interface MemberFeignClient {
@GetMapping("/app-api/v1/members/{id}")
Result<MemberDTO> getUserById(@PathVariable Long id);
/**
* 获取会员信息
*/
@GetMapping("/app-api/v1/members/detail/{id}")
Result<UmsMember> getUserEntityById(@PathVariable Long id);
/**
* 获取认证会员信息
......@@ -34,22 +37,19 @@ public interface MemberFeignClient {
* 修改会员积分
*/
@PutMapping("/app-api/v1/members/{id}/points")
Result updatePoint(@PathVariable Long id, @RequestParam Integer num);
<T> Result<T> updatePoint(@PathVariable Long id, @RequestParam Integer num);
/**
* 扣减会员余额
*/
@PutMapping("/app-api/v1/members/{id}/deduct-balance")
Result deductBalance(@PathVariable Long id, @RequestParam Long balance);
<T> Result<T> deductBalance(@PathVariable Long id, @RequestParam Long balance);
/**
* 获取会员余额
*/
@GetMapping("/app-api/v1/members/{id}/balance")
Result<Long> getBalance(@PathVariable Long id);
}
......@@ -18,6 +18,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import static com.youlai.common.constant.GlobalConstants.STATUS_YES;
......@@ -38,7 +39,7 @@ public class MemberController {
@ApiImplicitParam(name = "nickname", value = "会员昵称", paramType = "query", dataType = "String")
})
@GetMapping
public Result list(
public Result<List<UmsMember>> list(
String queryMode,
Integer page,
Integer limit,
......@@ -58,7 +59,7 @@ public class MemberController {
@ApiOperation(value = "会员详情")
@ApiImplicitParam(name = "id", value = "会员ID", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{id}")
public Result getMemberById(
public Result<UmsMember> getMemberById(
@PathVariable Long id
) {
UmsMember user = iUmsMemberService.getById(id);
......@@ -71,7 +72,7 @@ public class MemberController {
@ApiImplicitParam(name = "member", value = "实体JSON对象", required = true, paramType = "body", dataType = "UmsMember")
})
@PutMapping(value = "/{id}")
public Result update(
public <T> Result<T> update(
@PathVariable Long id,
@RequestBody UmsMember member) {
boolean status = iUmsMemberService.updateById(member);
......@@ -84,7 +85,7 @@ public class MemberController {
@ApiImplicitParam(name = "member", value = "实体JSON对象", required = true, paramType = "body", dataType = "UmsMember")
})
@PatchMapping("/{id}")
public Result patch(@PathVariable Long id, @RequestBody UmsMember user) {
public <T> Result<T> patch(@PathVariable Long id, @RequestBody UmsMember user) {
LambdaUpdateWrapper<UmsMember> updateWrapper = new LambdaUpdateWrapper<UmsMember>().eq(UmsMember::getId, id);
updateWrapper.set(user.getStatus() != null, UmsMember::getStatus, user.getStatus());
boolean status = iUmsMemberService.update(updateWrapper);
......@@ -94,7 +95,7 @@ public class MemberController {
@ApiOperation(value = "删除会员")
@ApiImplicitParam(name = "ids", value = "id集合", required = true, paramType = "query", dataType = "String")
@DeleteMapping("/{ids}")
public Result delete(@PathVariable String ids) {
public <T> Result<T> delete(@PathVariable String ids) {
boolean status = iUmsMemberService.update(new LambdaUpdateWrapper<UmsMember>()
.in(UmsMember::getId, Arrays.asList(ids.split(",")))
.set(UmsMember::getDeleted, STATUS_YES));
......
......@@ -29,7 +29,7 @@ public class MemberController {
@ApiOperation(value = "获取会员信息")
@ApiImplicitParam(name = "id", value = "会员ID", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{id}")
public Result getById(
public Result<MemberDTO> getById(
@PathVariable Long id
) {
MemberDTO memberDTO = new MemberDTO();
......@@ -44,10 +44,23 @@ public class MemberController {
return Result.success(memberDTO);
}
@ApiOperation(value = "获取会员实体信息")
@ApiImplicitParam(name = "id", value = "会员ID", required = true, paramType = "path", dataType = "Long")
@GetMapping("/detail/{id}")
public Result<UmsMember> getMemberEntityById(
@PathVariable Long id
) {
UmsMember user = iUmsMemberService.getById(id);
if (user == null) {
return Result.failed(ResultCode.USER_NOT_EXIST);
}
return Result.success(user);
}
@ApiOperation(value = "根据openid获取会员信息")
@ApiImplicitParam(name = "openid", value = "微信身份唯一标识", required = true, paramType = "path", dataType = "String")
@GetMapping("/openid/{openid}")
public Result getByOpenid(
public Result<UmsMember> getByOpenid(
@PathVariable String openid
) {
UmsMember member = iUmsMemberService.getOne(new LambdaQueryWrapper<UmsMember>()
......@@ -73,14 +86,14 @@ public class MemberController {
@ApiOperation(value = "修改会员")
@ApiImplicitParam(name = "member", value = "实体JSON对象", required = true, paramType = "body", dataType = "UmsMember")
@PutMapping("/{id}")
public Result add(@PathVariable Long id, @RequestBody UmsMember user) {
public <T> Result<T> add(@PathVariable Long id, @RequestBody UmsMember user) {
boolean status = iUmsMemberService.updateById(user);
return Result.judge(status);
}
@ApiOperation(value = "获取登录会员信息")
@GetMapping("/me")
public Result getMemberInfo() {
public Result<MemberDTO> getMemberInfo() {
Long userId = JwtUtils.getUserId();
UmsMember member = iUmsMemberService.getById(userId);
if (member == null) {
......@@ -98,7 +111,7 @@ public class MemberController {
@ApiImplicitParam(name = "num", value = "积分数量", required = true, paramType = "query", dataType = "Integer")
})
@PutMapping("/{id}/points")
public Result updatePoint(@PathVariable Long id, @RequestParam Integer num) {
public <T> Result<T> updatePoint(@PathVariable Long id, @RequestParam Integer num) {
UmsMember user = iUmsMemberService.getById(id);
user.setPoint(user.getPoint() + num);
boolean result = iUmsMemberService.updateById(user);
......@@ -111,7 +124,7 @@ public class MemberController {
@ApiImplicitParam(name = "balance", value = "会员余额", required = true, paramType = "query", dataType = "Long")
})
@PutMapping("/{id}/deduct-balance")
public Result updateBalance(@PathVariable Long id, @RequestParam Long balance) {
public <T> Result<T> updateBalance(@PathVariable Long id, @RequestParam Long balance) {
boolean result = iUmsMemberService.update(new LambdaUpdateWrapper<UmsMember>()
.setSql("balance = balance - " + balance)
.eq(UmsMember::getId, id)
......@@ -123,12 +136,11 @@ public class MemberController {
@ApiImplicitParam(name = "id", value = "会员ID", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{id}/balance")
public Result<Long> updateBalance(@PathVariable Long id) {
Long balance = 0l;
Long balance = 0L;
UmsMember user = iUmsMemberService.getById(id);
if (user != null) {
balance = user.getBalance();
}
return Result.success(balance);
}
}
......@@ -40,7 +40,7 @@
<druid.version>1.2.4</druid.version>
<mybatis-plus.version>3.4.3</mybatis-plus.version>
<minio.version>7.1.0</minio.version>
<weixin-java-miniapp.version>4.1.0</weixin-java-miniapp.version>
<weixin-java.version>4.1.5.B</weixin-java.version>
<hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
<seata.version>1.4.1</seata.version>
<commons-pool2.version>2.9.0</commons-pool2.version>
......@@ -114,7 +114,13 @@
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>${weixin-java-miniapp.version}</version>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册