背景
接上篇小程序智能聊天机器人(一),
无论何种程序,在我们没有其他收益来源时,用户付费,是让我们回笼成本的重要渠道之一,所以在一个项目中,会员支付就比较重要了,接下来我们继续进行。
会员套餐设置
所谓会员,就要有不同的会员套餐,就像我们用QQ,各类影视平台一样,有一个月,两个月,三个月,等等。所以我们也先来搞个套餐。我们先来建个表:
CREATE TABLE `t_vip_package` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`vip_name` varchar(100) DEFAULT NULL COMMENT '会员名称',
`vip_description` varchar(1000) DEFAULT NULL COMMENT '会员描述',
`original_price` decimal(10,2) DEFAULT NULL COMMENT '原价',
`price` decimal(10,2) DEFAULT NULL COMMENT '价格',
`show_status` char(1) DEFAULT NULL COMMENT '显示状态:0隐藏 1显示',
`vip_type` char(1) DEFAULT NULL COMMENT '会员类型:0天 1月 2年 3永久',
`duration` int(11) DEFAULT NULL COMMENT '时长仅在永久时可为空',
`day_times` bigint(20) DEFAULT NULL COMMENT '每日提问数',
`choose_status` varchar(100) DEFAULT NULL COMMENT '默认选中:0未选中 1选中',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='会员套餐';
表创建完了,自己加几个套餐数据进去用来测试
获取套餐
我们开放接口给小程序端获取所有的套餐列表:
@PostMapping("getAllVipPackages")
public AjaxResult getAllVipPackages(){
startPage();
List<VipPackage> vipPackages = vipPackageService.list(new QueryWrapper<VipPackage>().eq("show_status", "1"));
vipPackages.forEach(vipPackage -> {
//这里我们计算一下每天多少钱,用于前台展示
vipPackage.setDayPrice(new BigDecimal("0"));
if(!"3".equals(vipPackage.getVipType())){
String vipType = vipPackage.getVipType();
BigDecimal o = new BigDecimal("1");
if ("1".equals(vipType)){
o = new BigDecimal("30");
}
if ("2".equals(vipType)){
o = new BigDecimal("365");
}
BigDecimal totalDay = new BigDecimal(vipPackage.getDuration()).multiply(o);
vipPackage.setDayPrice(vipPackage.getPrice().divide(totalDay, BigDecimal.ROUND_CEILING));
}
});
return AjaxResult.success(getDataTable(vipPackages));
}
前台效果是这样:
订单创建
小程序端选中套餐之后,我们后台提供一个订单生成接口,存储一些基本信息,然后把订单主键返回给前端。这里我们也可以直接向微信官方发起支付申请,因为小程序内只能进行微信支付,因为我的项目不仅在小程序端,所以进行了这一步操作。
@PostMapping("createOrder")
@RestApi
public AjaxResult createOrder(Long vipId){
Long userId = JwtTokenUtil.getUserId;
VipPackage vipPackage = vipPackageService.getById(vipId);
if (StringUtils.isNull(vipPackage)){
return AjaxResult.error("该套餐不存在");
}
Order order = new Order();
order.setVipName("充值"+vipPackage.getVipName());
order.setPrice(vipPackage.getPrice());
order.setVipId(vipId);
order.setUserId(userId);
order.setDuration(vipPackage.getDuration());
order.setUnits(vipPackage.getVipType());
order.setIsAll("3".equals(vipPackage.getVipType()) ? "1" : "0");
orderService.save(order);
Map<String, Long> retObj = new HashMap<>();
retObj.put("orderId", order.getId());
return AjaxResult.success("订单创建成功!", retObj);
}
支付订单申请
前端拿到我们的主键之后,可申请发起支付,这个时候,我们再向微信发起支付申请:
@PostMapping("/toPay")
@ApiOperation("支付订单")
@RestApi
public AjaxResult toPay(Long orderId, String payType){
Order order=orderService.getById(orderId);
if (StringUtils.isNull(order)){
return AjaxResult.error("订单不存在");
}
if (order.getStatus().equals("1")){
return AjaxResult.error("该订单已支付");
}
order.setOrderNo(RandomUtil.randomNumbers(13));
order.setPayType(payType);
orderService.updateById(order);
switch (payType){
case "1": //支付宝
return aliPayConfig.AliPay(order);
case "2"://微信支付,参考官方api进行一些参数配置就好。
return wxPayConfig.WxPay(order);
default:
return AjaxResult.error("请选择支付方式");
}
}
把官方的数据,返回给前端,前端就可以拉起支付了。
支付回调
用户支付完成之后,微信官方会给我们回调通知,也就是告诉我们支付的结果,我们需要在拿到成功支付结果后,再更新一下用户的会员权益
扫描二维码关注公众号,回复:
14771102 查看本文章
public String wxPayNotify(HttpServletRequest request) {
String xmlMsg = HttpKit.readData(request);
log.info("微信支付通知=" + xmlMsg);
Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
String returnCode = params.get("return_code");
//获取创建时候的 商户订单号(唯一就行)
String orderNo = params.get("out_trade_no");
Map<String, String> xml;
// 注意此处签名方式需与统一下单的签名类型一致
// if (WxPayKit.verifyNotify(params, "初始密钥", SignType.HMACSHA256)) {
if (WxPayKit.verifyNotify(params,PaySetConfig.KEY, SignType.HMACSHA256)) {
if (WxPayKit.codeIsOk(returnCode)) {
// 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
Order order = orderService.getOne(new QueryWrapper<Order>().lambda().eq(Order::getOrderNo, orderNo));
if (order.getStatus().equals("1")) {
xml = new HashMap<>(2);
xml.put("return_code", "-1");
xml.put("return_msg", "订单状态失效或已支付,请重新下单");
return WxPayKit.toXml(xml);
}
// 更新订单信息
aliPayConfig.updateOrder(order);
// 发送通知等
xml = new HashMap<>(2);
xml.put("return_code", "200");
xml.put("return_msg", "回调成功");
return WxPayKit.toXml(xml);
}
}
return null;
}
我们先给官方回复个消息,告诉他,我收到通知了!
更新用户权益
接下来更新下用户权益,整个流程就完成了:
/** 修改订单及业务逻辑*/
public void updateOrder(Order order){
order.setStatus("1");
// AppUser appUser=appUserService.getOne(new QueryWrapper<AppUser>().eq("userName", order.getUserId()));
AppUser appUser = appUserService.getById(order.getUserId());
if(StringUtils.isNull(appUser) || appUser.getUserType().equals("3")){
orderService.updateById(order);
return;
}
VipPackage vipPackage = vipPackageService.getById(order.getVipId());
appUser.setUserType("1");//会员
appUser.setDayLimitTimes(vipPackage.getDayTimes());
//充值套餐
if (vipPackage.getVipType().equals("3")) {
//是否永久
appUser.setUserType("3");//永久
} else {
//过期时间 TODO
//用户过期时间
appUser.setVipExpTime(getUserExpTime(StringUtils.isNull(appUser.getVipExpTime()) ? new Date(): appUser.getVipExpTime(),vipPackage.getDuration(), vipPackage.getVipType()));
//该会员过期时间
order.setExpTime(getUserExpTime(new Date(), vipPackage.getDuration(), vipPackage.getVipType()));
//其他会员顺延
setOtherVipExpTime(appUser.getId(), order.getId(), vipPackage.getDuration(), vipPackage.getVipType());
}
if(StringUtils.isNull(appUser.getVipExpTime()) || appUser.getVipExpTime().before(new Date())){
//处理下生效时间,若是已失效,则从当前开始
appUser.setVipEffTime(new Date());
}
orderService.updateById(order);
appUserService.updateById(appUser);
// order.set(getUserExpTime();
// AsyncManager.me().execute(MyMessageFactory.socketSendToAll(appUser,"expTime"));
}
总结
文章内我对整个流程代码没有做细节的讲述,我觉得开发主要是你针对项目的业务场景,有个大概的流程框架,这个东西具备之后,你可以慢慢往里面填写你的业务代码,反正我每次都是先这么干的,先评估整个项目大概需要后端做哪些接口,然后再去把接口中的业务逻辑慢慢填充起来。重要的就是思路要清晰,每一步要做什么先搞清楚!