小程序实战之会员支付流程
- 背景
- 会员套餐设置
- 获取套餐
- 订单创建
- 支付订单申请
- 支付回调
- 更新用户权益
- 总结
背景
接上篇小程序智能聊天机器人(一),
无论何种程序,在我们没有其他收益来源时,用户付费,是让我们回笼成本的重要渠道之一,所以在一个项目中,会员支付就比较重要了,接下来我们继续进行。
会员套餐设置
所谓会员,就要有不同的会员套餐,就像我们用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")@RestApipublic 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("支付订单")@RestApipublic 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("请选择支付方式");}}
把官方的数据,返回给前端,前端就可以拉起支付了。
支付回调
用户支付完成之后,微信官方会给我们回调通知,也就是告诉我们支付的结果,我们需要在拿到成功支付结果后,再更新一下用户的会员权益
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"));}
总结
文章内我对整个流程代码没有做细节的讲述,我觉得开发主要是你针对项目的业务场景,有个大概的流程框架,这个东西具备之后,你可以慢慢往里面填写你的业务代码,反正我每次都是先这么干的,先评估整个项目大概需要后端做哪些接口,然后再去把接口中的业务逻辑慢慢填充起来。重要的就是思路要清晰,每一步要做什么先搞清楚!