Google内购/H5支付商品优惠方案设计

前言

大家好,我是肥宅阿久,很久没更新文章了,最近,公司的会员商品做了一次升级,所有的会员商品增加自定义商品优惠(按人群拥有不同优惠),因此总结一下设计方案以及实现过程,总的来说,实现并不复杂的;

值得注意的是,本文档中所有的截图以及订单/订阅数据都来自测试环境,数据没有任何意义,请关注设计方案本身。

先简单介绍一下我们VIP会员商品的设计逻辑

首先,VIP底层是产品,一个产品(例如单月)可以在赋予价格之后包装成多个商品,而商品需要在哪个渠道包下的哪个国家投放,投放时会修改价格以及一些营销信息,这就是最终售卖的上架商品。

这么设计最好的一点就是复用的逻辑,上架商品复用商品,而商品又复用产品。

产品属性

如上图,产品定义了类型(固定日期/周期扣款)以及发放的VIP天数。

商品属性

而商品是在产品的基础上赋予了价格/名称等信息。

商品优惠方案属性

这里最重要的就是这个优惠ID,这是和谷歌优惠方案匹配的标识。优惠周期是指的这个商品的优惠价格持续几期(针对续订商品)

上架商品/优惠方案

上架商品顾名思义就是,商品在哪个渠道包下的哪个国家进行售卖,这里除了商品本身的属性,还会分各渠道各国家修改商品价格以及一些营销信息和优惠方案,这里的优惠方案也是从商品里带过来的(在原来商品关联的优惠方案上增加了营销信息,优惠倒计时等属性)。

基于上面的产品逻辑表设计

原来的订单/商品表在之前的博文有过介绍:

传送门:
Google 支付订阅商品服务端设计方案
Google 支付订阅补坑之路

增加如下三张表设计

sql 复制代码
CREATE TABLE `vip_goods_preferential` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `goods_id` bigint(20) NOT NULL COMMENT '商品ID',
  `plan` tinyint(2) DEFAULT NULL COMMENT '优惠资格(计划) 0 新用户首购',
  `plan_code` varchar(100) NOT NULL COMMENT '优惠计划ID',
  `group_tags` varchar(500) NOT NULL COMMENT '群体标签,多个逗号隔开',
  `description` text COMMENT '描述',
  `price_type` tinyint(2) DEFAULT NULL COMMENT '优惠价格类型',
  `settlement_cycle` int(11) DEFAULT NULL COMMENT '结算周期',
  `sale_price_config` text NOT NULL COMMENT '商品各国家优惠价格配置',
  `sort` int(11) DEFAULT NULL COMMENT '顺位',
  `enable` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否启用 0 未启用,1启用',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `index_goods_id` (`goods_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='会员商品优惠方案';
sql 复制代码
CREATE TABLE `market_vip_goods_preferential` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `goods_preferential_id` bigint(20) NOT NULL COMMENT '商品优惠ID',
  `market_vip_goods_id` bigint(20) NOT NULL COMMENT '上架商品ID',
  `goods_id` bigint(20) NOT NULL COMMENT '商品ID',
  `local_price` int(11) DEFAULT NULL COMMENT '本地价格',
  `local_unit` varchar(100) DEFAULT NULL COMMENT '本地币种',
  `dollar_price` int(11) DEFAULT NULL COMMENT '美元价格',
  `subscript` text COMMENT '角标多语言',
  `description` text COMMENT '描述多语言',
  `short_desc` text COMMENT '短描述多语言',
  `popup_desc` text COMMENT '弹窗描述多语言',
  `limited_time` bigint(20) DEFAULT NULL COMMENT '商品限时(时间戳)',
  `sort` int(11) DEFAULT NULL COMMENT '顺位',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `index_goods_id` (`goods_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='会员上架商品优惠方案';
sql 复制代码
CREATE TABLE `user_vip_goods_preferential_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `market_vip_goods_id` bigint(20) NOT NULL COMMENT '商品ID',
  `market_vip_goods_preferential_id` bigint(20) NOT NULL COMMENT '商品优惠ID',
  `business_order_sn` varchar(255) DEFAULT NULL COMMENT '关联业务订单号',
  `settlement_cycle` int(11) DEFAULT NULL COMMENT '优惠周期',
  `start_time` bigint(20) NOT NULL COMMENT '商品优惠计时开始',
  `end_time` bigint(20) DEFAULT NULL COMMENT '商品优惠计时结束',
  `used` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否使用 0 未使用,1使用',
  `preferential_detail` text NOT NULL COMMENT '固化商品优惠详情',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `index_goods_user` (`market_vip_goods_id`,`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户优惠记录';

之前的 vip_order 表需要增加一个 offer_id(优惠ID),有优惠的话谷歌会携带优惠信息。

vip_renewal_order 表增加如下字段: offer_id(优惠ID)、discount_periods(折扣期数)、discount_period_price(折扣期价格)、remain_discount_periods(剩余可用的折扣期数)

简单介绍一下这些字段的作用,offer_id 固话续订使用的是哪个优惠,discount_periods(折扣期数)、discount_period_price(折扣期价格)也都是固化折扣优惠的信息,remain_discount_periods(剩余可用的折扣期数)用来判断优惠的续订价格还能用几期,每续订一期 remain_discount_periods 减一,直到remain_discount_periods为0。

那直接用 discount_periods(折扣期数) 不行吗?

主要是有这样的业务场景,谷歌优惠方案替换,下面再说。

谷歌如何给商品配置优惠方案

目前支持的谷歌后台配置是:

1、资格条件: 由开发者控制 & 阶段类型: 扣款周期/单次扣款;

2、资格条件: 新客户获取 & 阶段类型: 单次付款

在订阅商品里面可以添加优惠:

添加优惠阶段:

类型:周期性付款折扣/单次扣款折扣/免费

以上配置就可以完成谷歌商品的优惠方案了,我们选择的资格条件是 由开发者控制,所以这个时候用户满足哪些优惠方案完全是由我们服务端控制了,不再是谷歌控制的; 服务端会检查用户满足商品的哪个优惠,然后将优惠ID给客户端,安卓拿到我们给的优惠ID去谷歌那边配置的优惠方案中获取优惠价格。 我们的设计是每次只会返回给客户端一个优惠ID。

如果选择的资格条件是 新客户获取,那用户是否有优惠是由谷歌来判断的。

谷歌回调的变化

那用户匹配了优惠下单之后,我们怎么拿到对应的优惠ID呢? 我们拿着谷歌回调的数据去请求谷歌服务器会得到对应的优惠ID,如下图:

通过这个offerId以及商品code,我们可以拿到用户购买的商品以及优惠价格。

但是值得注意的事: 谷歌会携带优惠ID,每期都会带来,即使没有优惠了。比如:我在谷歌后台配置的优惠结算周期是3期,那续订第4期,我们请求谷歌服务器数据还是能得到这个offerId。

所以我们自己续订需要维护优惠折扣几期;

优惠变更问题

假如一个上架商品配置了多种优惠方案,正好整个用户可以使用多种优惠方案,那么当用户使用了一种优惠方案后,再次购买本商品会触发优惠方案的变动;

但是注意,只是优惠方案变动,订阅还是沿用老的订阅。

为了解释清楚,我把谷歌扣款的账单拉出来,如上图,最下面的三笔是一个折扣2期的优惠方案,三期之后,价格恢复,之后用户再次购买本商品,发生优惠方案变更,此时将收到一个账单为0的订阅(服务端也会收到一个新的订阅,本次订阅会员权益的时间并没有增加),然后将以新优惠方案继续订阅。

所以为此,我在 vip_renewal_order 增加 discount_periods(折扣期数)和 remain_discount_periods(剩余可用的折扣期数)两个字段。

原来是通过 续订期数 index 和 discount_periods(折扣期数)比较来判断是否有优惠的,即 index <= discount_periods 就还有优惠,但是发生优惠变更之后需要重置 discount_periods(折扣期数),而 续订期数(index)是不断增加的,这样就无法判断是有优惠了,因此单独维护一个 remain_discount_periods(剩余可用的折扣期数)字段,只要 remain_discount_periods > 0 就还有优惠。

当修改订阅方案时,会重置 vip_renewal_order 的折扣信息。

还有另外一个问题,谷歌的一次性扣款应该是不支持优惠方案的。

h5 优惠方案

我们H5订阅使用了渠道是 payermax 和 pingpoing。

payermax 不支持几期的优惠方案,他们只支持首期优惠,而且他们的订阅业务已经下架了,我们接入的比较早,还是能用的,但是比较局限吧。

pingpong 扣款的时间和扣款的费用完全是由商户决定的,那么通过 vip_renewal_order 的折扣价/可用折扣期数很容易就做到商品优惠的方案了。

好了,谷歌/h5的优惠方案就到此结束了,是不是很简单。

相关推荐
Cachel wood4 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑7 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb421528710 分钟前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶10 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
zfoo-framework18 分钟前
【jenkins插件】
java
风_流沙23 分钟前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch
颜淡慕潇1 小时前
【K8S问题系列 |19 】如何解决 Pod 无法挂载 PVC问题
后端·云原生·容器·kubernetes
ProtonBase1 小时前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
乐之者v1 小时前
leetCode43.字符串相乘
java·数据结构·算法