关于连续订阅的业务设计 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
aababc
V2EX    程序员

关于连续订阅的业务设计

  •  1
     
  •   aababc 2024-07-08 10:44:53 +08:00 3676 次点击
    这是一个创建于 459 天前的主题,其中的信息可能已经有所发展或是发生改变。

    老铁们,我又来求助了,公司在做出海的业务,需要对接 apple/google 的自动订阅,订阅的回调每次都会有一个 expires_date_ms 的结束时间告诉我们订阅啥时间结束。为了方便我们会直接使用这个 expires_date_ms 作为用户的到期时间,但是产品又希望能做一些优惠活动比如赠送用户时长。现在遇到的问题就是如何协调 apple/google 订阅的到期时间和自己赠送的时长。我们现在的技术方案是使用两个字段来存储,一个是到期时间一个是赠送的时长,判断如果用户到期了但是又有赠送时长,就使用当前时间+赠送的订阅时长作为用户新的到期时间,这里还需要一个定时脚本来扣减赠送时长。但是个人总觉得这种方案不够优秀,看看大家有没有更好的方案

    27 条回复    2025-02-06 16:21:35 +08:00
    tomatocici2333
        1
    tomatocici2333  
       2024-07-08 11:04:06 +08:00
    不需要定时任务吧,每次去计算到期时间的时候动态计算到期时间,有赠送时间就加上。
    tomatocici2333
        2
    tomatocici2333  
       2024-07-08 11:05:44 +08:00
    @tomatocici2333 要是业务上需要单独去更新赠送时间 直接去动态扣除就好,赠送时间优先级大于正常结束时间
    LucasLee92
        3
    LucasLee92  
       2024-07-08 11:07:08 +08:00
    用户带状态,并且存过期时间,每天轮训一天过期时间更新状态即可
    至于用户时长怎么来的,可以设计一张流水表存下即可(包括支付、赠送的)
    aababc
        4
    aababc  
    OP
       2024-07-08 12:27:31 +08:00
    @tomatocici2333 这里有个问题,假设用户过期很久了,突然送几天时长就有问题
    klo424
        5
    klo424  
       2024-07-08 13:54:39 +08:00
    没接过,想问一下 apple/google 的自动订阅不能服务端主动推给用户赠送时间,也叠加到 expires_date_ms 中吗?
    LoNeZ
        6
    LoNeZ  
       2024-07-08 14:01:21 +08:00
    不要用他们的时间... 自己转一下 赠送了 xx 时长, 区分会员失效期和订阅失效期.
    aababc
        7
    aababc  
    OP
       2024-07-08 14:03:13 +08:00
    @klo424 #5 这个应该是不行的
    fengYH8080
        8
    fengYH8080  
       2024-07-08 14:06:30 +08:00   4
    订阅模式跟赠送时长底层逻辑上就有冲突,逼着别人把订阅关了去用赠送的,不然一直订阅的人根本用不上,而一旦有取消订阅的动机就代表用户已经准备脱坑,要么就是下单订阅然后立刻取消订阅薅羊毛。只能说做产品也是要经过大脑的,还不如搞些优惠券来得实际。
    至于技术方案,这种方式的核心逻辑越单一越好,产品是无时无刻不在变化,维护越复杂的核心逻辑最终会导致产品的发展而写越多的特殊情况。建议只维护一个到期时间,购买订阅或者订阅自动扣费回调等其他所有形式的增加时长通过消费唯一凭证去处理,过期了就按当前时间加,未到期就叠加。
    然而你说的为了方便用 expires_date_ms 作为过期时间,我看不出为啥会方便,订阅的到期时间只是其中一种支付方式的属性,为啥要跟系统的核心体系的过期时间挂钩。
    gfreezy
        9
    gfreezy  
       2024-07-08 14:10:45 +08:00
    @fengYH8080 +1 。我们也是类似的实现,只要订阅没关,所有赠送的时长都永远用不上
    tomatocici2333
        10
    tomatocici2333  
       2024-07-08 14:11:20 +08:00
    @aababc 过期很久送天数,那就不能用之前的时间的,应该用客户登录的日期。把天数赠送触发在长时间未登录第一次登录上 or 手动领取时间
    wetalk
        11
    wetalk  
       2024-07-08 14:26:16 +08:00
    不太清楚 apple 、Google 订阅规则,我们的订阅对接微信和支付宝,用户签约后,每个周期的扣费由我们主动发起的,例如本月已经扣费过了,用户参加活动获得 7 天时长,下次扣费时间顺延 7 天就行。
    用户获得时长的明细,和用户到期时间,二者应该分开,到期时间作为一个指针,在每条明细之间切换。
    linauror
        12
    linauror  
       2024-07-08 14:43:30 +08:00
    只通过 expires_date_ms 计算续费了多久,比如 X 天,然后把天数加到到期日期上,赠送天数也再次加到到期日期上
    aababc
        13
    aababc  
    OP
       2024-07-08 14:55:29 +08:00
    @fengYH8080 关于 expires_date_ms 可以理解为在订阅的场景中,我们直接使用了 这个自动作为自己系统的过期时间,是两个独立属性
    aababc
        14
    aababc  
    OP
       2024-07-08 14:56:05 +08:00
    @gfreezy 是的,现在的设计就是,只要订阅不关闭,赠送的永远也用不上
    aababc
        15
    aababc  
    OP
       2024-07-08 15:04:10 +08:00
    @fengYH8080 #8 你的这个关于 订阅和赠送本质上又冲突,我是认同的,但是奈何产品就是要有赠送这个业务,有时候也是难评。这里最开始确实是想只使用到期时间,但是 apple/google 的业务场景上有 restore(恢复购买), upgrade(升级订阅), 而且我现在看是不能获取到订阅时长(也就是这次订阅是多长时间的)。关于使用 expires_data_ms 作为过期时间,我想表达的意思是在订阅的场景下会使用这个值作为用户的过期时间字段的值。
    aababc
        16
    aababc  
    OP
       2024-07-08 15:07:45 +08:00
    @gfreezy #9 这里有一个问题,是如何处理 restore 和退费的。我最开始也是按照单一的到期时间来处理的,后来发现后续的越来越复杂。
    gfreezy
        17
    gfreezy  
       2024-07-08 16:46:16 +08:00
    restore 并不会影响 expire time 。expire time 只受 3 个因素影响:
    1. 自动扣费成功,延长时间
    2. 退款完成,扣减时间
    3. 其他类似赠送、单次购买等


    如果说切换账号 restore ,这是另外一个问题了。原来的账号要如何处理,是否要作废掉?新账号怎么算?

    @aababc
    htxy1985
        18
    htxy1985  
       2024-07-08 16:58:41 +08:00
    感觉这是一个产品问题
    aababc
        19
    aababc  
    OP
       2024-07-08 17:04:01 +08:00
    @gfreezy #17 看来在这一块经验比较丰富了,我第一次做 apple 的订阅相关的内容。还有几个问题咨询一下,比如我想知道一次订阅的时长应该怎么计算,是否可以使用 expires_date - purchase_date ,还有这个 restore 是不是可以理解只需要处理 latest_receipt_info 中日期最大的时间。还有这个我看 apple 文档上些的 续订的时候会在扣款不成功的时候在几天内重复,那这时候这个 expires_date 的时间会不会变?
    GXD
        20
    GXD  
       2024-07-08 17:14:57 +08:00
    我们的方案是做了两个到期时间,购买的订阅使用 google/apple 提供的 expires_date ,赠送的免费时长叠加到后面。赠送的截止日期根据 购买订阅 的(续订/到期/退款/暂停/宽限期)等不同行为动态调整,然后保存变更历史
    iosyyy
        21
    iosyyy  
       2024-07-08 17:23:23 +08:00
    @aababc #4 那就把过期时间设为 now+赠送时间呗
    也就是说你这个加的时间起点应该是从订阅时间结束算的
    如果订阅时间已经结束代表是一个新的订阅周期开始那就把时间调到现在变成 现在+订阅时间
    gfreezy
        22
    gfreezy  
       2024-07-08 17:34:49 +08:00
    @aababc 不记得细节了,只参与了设计,没写具体的代码。
    但是 expire date 和扣费肯定是一致的,扣费成功 expire date 延长,扣费失败,expire date 不变。

    是否可以使用 expires_date - purchase_date

    这个应该是不行的。purchase date 不是开始时间。自动续费的时候一般会在周期结束前几天扣款。

    我记得我们好像是固定死 31 天,这样实现比较简单(相当于没有 31 天的月份多送用户几天)
    encro
        23
    encro  
       2024-07-08 17:40:31 +08:00
    @gfreezy

    微信读书也就这样,我订阅没关,赠送 vip 已经超过过半年了。没用。。。
    aababc
        24
    aababc  
    OP
       2024-07-08 17:46:15 +08:00
    @gfreezy 感谢,我再梳理一下看看,感觉还是有点糊涂,头大!
    aababc
        25
    aababc  
    OP
       2024-07-08 18:25:46 +08:00
    @GXD #20 感谢,我看看大家的这几种方案,那个比较合适
    diagnostics
        26
    diagnostics  
       2024-07-08 18:35:35 +08:00
    获取赠送时长的时候增加一个 expired_date_ms = 订阅的 expires_date_ms + 赠送时间 ms

    没有订阅的时候就是 expired_date_ms = now + 赠送时间 ms

    判断过期的时候,遍历现有的所有 expired_date_ms 取最大值

    多个赠送的时候,遍历现有的所有 expired_date_ms 取最大值 + 赠送时间的 ms

    中途取消订阅的时候,比较复杂,那就直接取消订阅一起把赠送的取消(产品设计上辅助开发)不就行了吗?不行的话,那就是取出最大赠送时间的 expired_date_ms - 减去当前时间,得到剩余赠送。或者另一个设计就是吧每个赠送时间单独成一个策略主体,这个时候可能需要单独计算每个策略的赠送时间。


    -------------------------

    或者重构设计,过期时间是计数的设计,每到 XX 时候,调度把时间减去 YY 时间。有订阅优先扣订阅,无订阅扣最近的赠送时间。。。
    yuhongtai114514
        27
    yuhongtai114514  
       245 天前
    苹果的扣款周期没法控制啊,就算我想延长下次扣款时间也不行,我也遇到了这个问题,楼主后来是怎么实现的呢
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5812 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 06:18 PVG 14:18 LAX 23:18 JFK 02:18
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86