Google 支付订阅商品服务端设计方案

10,865 阅读10分钟

前言

最近在开发 Google 订阅商品支付,也就是常见的包月VIP、包年VIP商品。之前写过一个 Google 支付一次性购买商品的设计方案,也就是单月VIP这种的,海外支付(GooglePay)服务端设计方案

前期工作

Google 订阅支付是依赖于实时开发者通知(Pub/Sub),需要开通 Pub/Sub功能:设置发布/订阅通知,需要注意的是,这个功能是收费的,需要绑定好银行卡。

使用Google订阅的场景里面应该主要就是处理消息产生的吞吐流量,这部分每月有10GiB的免费梯度,超出部分是40刀/TiB,所以当然一般免费梯度也够了。

还有一点需要注意,Google 内购商品需要在关联API-project之后创建,如果顺序反了,即使最后都配置好了,发送请求去Google验签也还是会报错:projectNotLinked。如果你顺序搞反了,那你就再去创建一个内购商品就好了。

会员包月(周期扣款的续订商品)服务端表设计

对于这种支付会员包月的设计各有不同,我的设计就是两张表(苹果支付方案另外说)。

  • vip_order,会员订单表

    CREATE TABLE `vip_order` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `order_sn` varchar(100) NOT NULL COMMENT '订单编号',
      `third_order_sn` varchar(100) DEFAULT NULL COMMENT '第三方订单编号',
      `origin_order_sn` varchar(100) DEFAULT NULL COMMENT '源订单号,发起退款时关联的订单',
      `type` tinyint(1) DEFAULT NULL COMMENT '订单类型 0 一次性购买 1 周期扣款订单',
      `user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
      `goods_id` bigint(20) DEFAULT NULL COMMENT '商品id',
      `price` bigint(20) DEFAULT '0' COMMENT '订单金额',
      `distribution_channel` varchar(20) DEFAULT NULL COMMENT '分销渠道',
      `payment_method` varchar(50) DEFAULT NULL COMMENT '付款方式:0 Google pay ',
      `status` tinyint(1) DEFAULT NULL COMMENT '订单状态:0:初始化 1:已完成 2 已经取消 3 已退款',
      `deliver_status` tinyint(1) DEFAULT NULL COMMENT '发货状态:0:发货失败 1:发货成功',
      `complete_time` datetime DEFAULT NULL COMMENT '订单完成时间',
      `refund_time` datetime DEFAULT NULL COMMENT '退款时间',
      `pay_time` datetime DEFAULT NULL COMMENT '付款时间',
      `device_id` varchar(255) DEFAULT NULL COMMENT '设备ID',
      `ip` varchar(100) DEFAULT NULL COMMENT 'IP地址',
      `country` varchar(20) DEFAULT NULL COMMENT '国家',
      `origin_info` text COMMENT '第三方支付信息',
      `remark` varchar(200) DEFAULT NULL COMMENT '备注',
      `support_refund` tinyint(1) DEFAULT NULL COMMENT '是否支持退款',
      `refund_reason` varchar(200) DEFAULT NULL COMMENT '退款原因',
      `goods_info` text COMMENT '商品信息',
      `package_name` varchar(500) DEFAULT NULL COMMENT '包名',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      `update_time` datetime DEFAULT NULL COMMENT '更新时间',
      
      PRIMARY KEY (`id`),
      KEY `index_order_sn` (`order_sn`) USING BTREE,
      KEY `index_third_order_sn` (`third_order_sn`) USING BTREE,
      KEY `index_user_id` (`user_id`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='会员订单表';
    
  • vip_renewal_order,会员续订表

    CREATE TABLE `vip_renewal_order` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `order_sn` varchar(100) NOT NULL COMMENT '订单编号',
      `latest_order_sn` varchar(100) NOT NULL COMMENT '上次订单号',
      `subscription_index` int(11) DEFAULT NULL COMMENT '扣款期数',
      `sign_no` varchar(255) DEFAULT NULL COMMENT '订阅号',
      `user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
      `goods_id` bigint(20) DEFAULT NULL COMMENT '商品id',
      `goods_info` varchar(1000) DEFAULT NULL COMMENT '固化商品信息',
      `price` int(11) DEFAULT '0' COMMENT '订单金额,存储美分',
      `sign_channel` varchar(20) DEFAULT NULL COMMENT '签约渠道(支付渠道):google pay 、 paypal、payerMax',
      `sign_status` tinyint(1) DEFAULT NULL COMMENT '订阅状态,0-订阅处理中 1-订阅成功 2-订阅失败 3-订阅取消 4-订阅失效 5-订阅计划暂停',
      `sign_reason` varchar(100) DEFAULT NULL COMMENT '状态原因',
      `deduct_status` tinyint(1) DEFAULT NULL COMMENT '本期扣款状态: 0-待扣款 1-扣款成功 2-扣款失败 3-扣款取消 4-已忽略',
      `renewal_content` text COMMENT '第三方续订内容',
      `start_order_time` datetime DEFAULT NULL COMMENT '订单第一次订阅时间',
      `latest_order_time` datetime DEFAULT NULL COMMENT '上一次订单自动续约时间',
      `next_order_time` datetime DEFAULT NULL COMMENT '理论上下次扣款时间',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      `update_time` datetime DEFAULT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`),
      KEY `index_order_sn` (`order_sn`) USING BTREE,
      KEY `index_latest_order_sn` (`latest_order_sn`) USING BTREE,
      KEY `index_user_id` (`user_id`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='会员自动续订表';
    
  • 两张表关系,我现在的设计是,会员续订表只记录最新的续订情况,会员续订表可以关联到最新的订单,也就是说,一个用户一个签约号只有一条记录,只保存了最新的续订信息。

服务端验证签名流程

服务端流程主要分两块:创建订单验签流程、处理Google回调的续订流程

创建订单验签流程

image.png

流程很简单,就是首先本地签名验证,然后检查订单,根据订单是否续订商品分别调用Google的接口验证小票(使用Google SDK即可),然后就是续订商品创建两个订单(包括续订单子,续订单子可以看出目前用户的续订状态),最后发放会员。

Google 订阅相关API:developers.google.cn/android-pub…

  • purchases.products 非订阅商品api
  • purchases.subscriptions 订阅商品api
  • purchases.voidedpurchases 退款api

代码

对于调用Google API 验证订单,可以直接调用 Google的API接口,但是需要自己获取 token 流程,如果嫌麻烦的话,也可以直接使用 Google 提供的SDK。

<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-androidpublisher</artifactId>
    <version>v3-rev20211125-1.32.1</version>
</dependency>

主体业务逻辑代码:

@Transactional
public void verifyInapp(GooglePayVerifyRequest request) throws UserClientException {
    // 渠道
    String clientType = httpServletRequestHelper.getClientType();
    Long userId =  httpServletRequestHelper.getCurrentUserId();
    String countryIsoCode = httpServletRequestHelper.getCountryIsoCode();

    GooglePayVerifyRequest.SigntureData signtureData = request.getSigntureData();

    // 本地签名验证
    super.googleRSALocalVerifySignature(request, clientType, userId);

    // 检查商品
    MarketVipGoodsVo marketVipGoodsVo = commodityFeign.findMarketGoods2(signtureData.getProductId(), clientType, countryIsoCode).getData();
    if(marketVipGoodsVo == null){
        log.error("商品Code:{} ,ClientCodeType:{}, 国家码:{} 未能找到上架商品", signtureData.getProductId(), clientType, countryIsoCode);
        throw new UserClientException(Constant.VipOrderExceptionConstant.GOODS_NOT_FOUND);
    }

    // 分布式锁
    RLock lock = null;
    try {
        lock = redisUtils.lock(String.format(Constant.GOOGLE_PAY_VERIFY_LOCK, signtureData.getOrderId()));
        // 检查 订单是否存在库里
        VipOrder order = vipOrderRepository.findByThirdOrderSn(signtureData.getOrderId());
        if (order == null) {
            // 订单不存在 新事物 创建订单
            order = vipOrderService.createOrderByGooglePay(request, marketVipGoodsVo);
        } else if (order.checkHandleStatus()) {
            // 检查订单状态
            log.info("订单:{} 不是初始化状态,表示已经处理了" , JSON.toJSONString(order));
            throw new OrderException(CommonErrorCode.create(OrderVerifyEnum.ORDER_HANDLED.getCode(),
                    OrderVerifyEnum.ORDER_HANDLED.getDesc()));
        }

        String originInfo;
        
        // 续订商品,走 Google 订阅SDK获取订单
        if(marketVipGoodsVo.isSubscribed()){
            // 会员 订阅
            SubscriptionPurchase subscriptionPurchase = subscriptionsGoodsCheck(signtureData.getPackageName(), signtureData.getProductId(), signtureData.getPurchaseToken());
            log.info("Google API 校验结果:{}", subscriptionPurchase);
            originInfo = subscriptionPurchase == null ? null : JSON.toJSONString(subscriptionPurchase);
            if (Constant.GooglePay.checkSubscriptionPurchase(subscriptionPurchase)) {
                vipOrderService.updateOrderFail(order.getId(), originInfo);
                // 订单未支付状态
                log.info("Google API 校验订单:{} 不是支付完成状态,结果:{}" , order.getId(), originInfo);
                throw new OrderException(CommonErrorCode.create(OrderVerifyEnum.ORDER_UN_PAID.getCode(),
                        OrderVerifyEnum.ORDER_UN_PAID.getDesc()));
            }

            // 获取到订单价格
            order.setCountry(subscriptionPurchase.getCountryCode());
            // 使用google 订阅的用户不会导致充值错了用户的BUG  修改用户ID
            order.setUserId(Long.valueOf(subscriptionPurchase.getObfuscatedExternalAccountId()));
            vipOrderRepository.save(order);

            // 创建续订单
            vipOrderService.createSubscribeOrder(order, subscriptionPurchase.getObfuscatedExternalProfileId(),
                    PayMethodEnum.generateVipOrderSn(PayMethodEnum.GOOGLE_PAY_PRIMORDIAL), RenewalSignStatusEnum.SUCCESS, RenewalSignChannelEnum.GOOGLE, JSON.toJSONString(subscriptionPurchase));
        } else {
            // 会员 一次性购买
            Long orderId = order.getId();
            originInfo = purchasesGoodsCheck(request,  orderId, (info) -> vipOrderService.updateOrderFail(orderId, info));
        }

        // 更新订单数据
        vipOrderService.updateOrderPaid(order.getId(), originInfo);

        // 通知发放会员
        boolean grantUserVipSuccess = vipOrderService.grantUserVip(order);
        if(grantUserVipSuccess){
           vipOrderService.updateOrderDeliverSuccess(order);
        }
    } finally {
        if (lock != null) {
            lock.unlock();
        }
    }

}

本地验证签名:

protected void googleRSALocalVerifySignature(GooglePayVerifyRequest request, String clientType, Long userId) {
    GooglePayVerifyRequest.SigntureData signtureData = request.getSigntureData();
    String publicKey = googlePublicKeyProperties.getPackageAndPublicKey().get(clientType);
    log.info("clientType:{} ,userId:{}, 包名:{},公钥:{} 准备开始本地RSA校验",clientType,userId,  signtureData.getPackageName(), publicKey);
    if (!Constant.doCheck(request.getSigntureContent(), request.getSignture(), publicKey)) {
        log.info("服务端RSA校验未通过,参数:{}" , request);
        throw new OrderException(CommonErrorCode.create(OrderVerifyEnum.RSA_VERIFY_FAIL.getCode(),
                OrderVerifyEnum.RSA_VERIFY_FAIL.getDesc()));
    }
}


public static boolean doCheck(String content, String sign, String publicKey) {
    try {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] encodedKey = Base64.getDecoder().decode(publicKey);
        PublicKey pubKey = keyFactory
                .generatePublic(new X509EncodedKeySpec(encodedKey));
        Signature signature = Signature.getInstance("SHA1WithRSA");
        signature.initVerify(pubKey);
        signature.update(content.getBytes("utf-8"));
        return signature.verify(Base64.getDecoder().decode(sign));
    } catch (Exception e) {
        log.error("Google pay RSA 校验异常:{}", e);
    }
    return false;
}

会员订阅 Google SDK 验证订单:

private SubscriptionPurchase subscriptionsGoodsCheck(String packageName, String productId, String purchaseToken) {
    SubscriptionPurchase purchaseSub;
    try {
        purchaseSub = androidPublisher.purchases()
                .subscriptions().get(packageName, productId, purchaseToken).execute();
        log.info("调用 GoogleSDK 获取订单数据,参数:{}, {}, {},返回结果:{}", packageName, productId, purchaseToken, JSON.toJSONString(purchaseSub));
    } catch (Exception ex) {
        log.error("调用GoogleSDK校验订单发生异常:{} ", ex);
        throw new RuntimeException("调用GoogleSDK校验订单发生异常");
    }
    return purchaseSub;
}

一次性购买 Google SDK 校验订单:

protected String purchasesGoodsCheck(GooglePayVerifyRequest request, Long orderId, Consumer<String> consumer) {
    String originInfo;
    ProductPurchase productPurchase = null;
    boolean googleServerError = false;
    try {
        GooglePayVerifyRequest.SigntureData signtureData = request.getSigntureData();
        //校验订单 一次性商品
        productPurchase = androidPublisher.purchases().products()
                .get(signtureData.getPackageName(), signtureData.getProductId(), signtureData.getPurchaseToken()).execute();
        originInfo = JSON.toJSONString(productPurchase);
        log.info("调用 GoogleSDK 获取订单数据,参数:{},返回结果:{}", JSON.toJSONString(signtureData), originInfo);

    } catch (Exception ex) {
        log.error("调用GoogleSDK校验订单发生异常:{} , 忽略异常,因为Google本地RSA校验已经通过", ex);
        originInfo = request.getSigntureContent();
        googleServerError = true;
    }
    if (!googleServerError && (productPurchase == null || productPurchase.getPurchaseState() != 0)) {
        consumer.accept(originInfo);
        // 订单未支付状态
        log.info("Google API 校验订单:{} 不是支付完成状态,结果:{}" , orderId, originInfo);
        throw new OrderException(CommonErrorCode.create(OrderVerifyEnum.ORDER_UN_PAID.getCode(),
                OrderVerifyEnum.ORDER_UN_PAID.getDesc()));
    }
    return originInfo;
}

上面代码是 Google 校验订单的业务逻辑,给个参考即可,具体还是要根据自己业务来处理。

处理Google回调的续订流程

image.png

回调数据大概长这样

{
	"message": {
		"data": "eyJ2ZXJzaW9uIjoiMS4wIiwicGFja2FnZU5hbWUiOiJjb20uZGFybWl1LmRvcmFpbWkiLCJldmVudFRpbWVNaWxsaXMiOiIxNjczMTQ4Mjc0NDAyIiwic3Vic2NyaXB0aW9uTm90aWZpY2F0aW9uIjp7InZlcnNpb24iOiIxLjAiLCJub3RpZmljYXRpb25UeXBlIjoyLCJwdXJjaGFzZVRva2VuIjoiamxvbWNhamhkZWRrbGVoZGhpZm5naGRpLkFPLUoxT3gxcm5rVVBWMHJmZGI5QktYZ0ZnZlZFZFhjVmtLaElWc1RUMFEwQUpzUWhTR3hUQTl5YTl5dUNqc3VHRGdMQmYwbDNBaklYRUdITU5TdTd3OTN4R0N5V05tZ09RIiwic3Vic2NyaXB0aW9uSWQiOiJ2aXBfdGVzdF95ZWFyX3N1YnMifX0=",
		"messageId": "6585558511031719",
		"publishTime": "2023-01-08T03:24:34.623Z"
	},
	"subscription": "projects/pc-api-4971789543089929583-125/subscriptions/loklok-vip-subs-sub"
}

通过 Base64 解码后:

{
	"eventTimeMillis": 1673148274402,
	"packageName": "com.darmiu.doraimi",
	"subscriptionNotification": {
		"notificationType": 2,
		"purchaseToken": "jlomcajhdedklehdhifnghdi.AO-J1Ox1rnkUPV0rfdb9BKXgFgfVEdXcVkKhIVsTT0Q0AJsQhSGxTA9ya9yuCjsuGDgLBf0l3AjIXEGHMNSu7w93xGCyWNmgOQ",
		"subscriptionId": "vip_test_year_subs",
		"version": "1.0"
	},
	"version": "1.0"
}

notificationType 表示通知类型,可以具有以下值:

 (1) SUBSCRIPTION_RECOVERED - 从帐号保留状态恢复了订阅。
 (2) SUBSCRIPTION_RENEWED - 续订了处于活动状态的订阅。
 (3) SUBSCRIPTION_CANCELED - 自愿或非自愿地取消了订阅。如果是自愿取消,在用户取消时发送。
 (4) SUBSCRIPTION_PURCHASED - 购买了新的订阅。
 (5) SUBSCRIPTION_ON_HOLD - 订阅已进入帐号保留状态(如果已启用)。
 (6) SUBSCRIPTION_IN_GRACE_PERIOD - 订阅已进入宽限期(如果已启用)。
 (7) SUBSCRIPTION_RESTARTED - 用户已通过 Play > 帐号 > 订阅恢复了订阅。订阅已取消,但在用户恢复时尚未到期。如需了解详情,请参阅 [恢复](/google/play/billing/subscriptions#restore)。
 (8) SUBSCRIPTION_PRICE_CHANGE_CONFIRMED - 用户已成功确认订阅价格变动。
 (9) SUBSCRIPTION_DEFERRED - 订阅的续订时间点已延期。
 (10) SUBSCRIPTION_PAUSED - 订阅已暂停。
 (11) SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED - 订阅暂停计划已更改。
 (12) SUBSCRIPTION_REVOKED - 用户在到期时间之前已撤消订阅。
 (13) SUBSCRIPTION_EXPIRED - 订阅已到期。

很显然回调的数据可以看出续订的状态,但是不知道订单的状态,所以我们还需要通过回调的数据,packageName (包名), subscriptionId(订阅商品ID), purchaseToken(购买订阅时向用户设备提供的令牌)。通过 Google SDK 调用订阅相关API校验订单数据。

调用 Google SDK 获取数据大概长这样:

{
	"acknowledgementState": 1,
	"autoRenewing": true,
	"countryCode": "ID",
	"developerPayload": "",
	"expiryTimeMillis": 1673150190345,
	"kind": "androidpublisher#subscriptionPurchase",
	"obfuscatedExternalAccountId": "6306074",
	"obfuscatedExternalProfileId": "8e858fa068164b6583efc36ff44c4f1b",
	"orderId": "GPA.3380-6685-4071-88443..0",
	"paymentState": 1,
	"priceAmountMicros": 299000000000,
	"priceCurrencyCode": "IDR",
	"purchaseType": 0,
	"startTimeMillis": 1673146472411
}

具体字段解释的文档:developers.google.cn/android-pub… 通过上面的数据以及文档上的字段,我发现没有一个能表示续订号的字段,也就是说,我通过这个回调数据,没办法找到 vip_renewal_order 表里是哪条数据。
所以和客户端商量决定用 obfuscatedExternalProfileId 字段表示订阅号,客户端在发起创建订阅订单是创建。

具体代码参考:

处理Google回调:

public void subscribeCallback(GoogleSubscribeCallback callback) throws RequestParameterException {
    GoogleSubscribeCallback.Message message = callback.getMessage();
    Base64.Decoder decoder = Base64.getDecoder();
    try {
        // Base64 解码 data
        String dataDecodeStr = new String(decoder.decode(message.getData()), "UTF-8");
        GoogleSubscribeCallback.DataDecode dataDecode = JSON.parseObject(dataDecodeStr, GoogleSubscribeCallback.DataDecode.class);
        log.info("google subscribe 解析回调数据:{}", JSON.toJSONString(dataDecode));
        
        GoogleSubscribeCallback.SubscriptionNotification notification = dataDecode.getSubscriptionNotification();
        
        // 调用 Google SDK 获取订阅结果
        SubscriptionPurchase subscriptionPurchase = subscriptionsGoodsCheck(dataDecode.getPackageName(), notification.getSubscriptionId(), notification.getPurchaseToken());


        if(dataDecode.getSubscriptionNotification().newSubscription()){
            log.info("google 支付收到新的订阅,packageName :{}, productId:{}, purchaseToken:{},结果:{}",
                    dataDecode.getPackageName(), notification.getSubscriptionId(),
                    notification.getPurchaseToken(), subscriptionPurchase == null ? null : JSON.toJSONString(subscriptionPurchase));

            subscriptionDeductionSucceeded(subscriptionPurchase, dataDecode);
        } else if(dataDecode.getSubscriptionNotification().subscriptionExpired()){
            log.info("google 订阅过期,packageName :{}, productId:{}, purchaseToken:{},结果:{}",
                    dataDecode.getPackageName(), notification.getSubscriptionId(),
                    notification.getPurchaseToken(), subscriptionPurchase == null ? null : JSON.toJSONString(subscriptionPurchase));

            // 订阅到期
            googleNoSubscription(subscriptionPurchase, RenewalSignStatusEnum.INVALID);

        } else if(dataDecode.getSubscriptionNotification().subscriptionCanceled()){
            log.info("google 取消订阅,packageName :{}, productId:{}, purchaseToken:{},结果:{}",
                    dataDecode.getPackageName(), notification.getSubscriptionId(),
                    notification.getPurchaseToken(), subscriptionPurchase == null ? null : JSON.toJSONString(subscriptionPurchase));

            // 取消订阅
            googleNoSubscription(subscriptionPurchase, RenewalSignStatusEnum.CANCEL);

        } else if(dataDecode.getSubscriptionNotification().subscriptionRevoked()){
            log.info("google 用户在到期时间之前已撤消订阅,packageName :{}, productId:{}, purchaseToken:{},结果:{}",
                    dataDecode.getPackageName(), notification.getSubscriptionId(),
                    notification.getPurchaseToken(), subscriptionPurchase == null ? null : JSON.toJSONString(subscriptionPurchase));

            // 取消订阅
            googleNoSubscription(subscriptionPurchase, RenewalSignStatusEnum.CANCEL);

        } else if(dataDecode.getSubscriptionNotification().subscriptionPaused()){

            log.info("google 订阅已暂停,packageName :{}, productId:{}, purchaseToken:{},结果:{}",
                    dataDecode.getPackageName(), notification.getSubscriptionId(),
                    notification.getPurchaseToken(), subscriptionPurchase == null ? null : JSON.toJSONString(subscriptionPurchase));
            // 暂停订阅
            subscriptionPurchase.setCancelReason(RenewalSignStatusEnum.SUSPEND.getCode());
            googleNoSubscription(subscriptionPurchase, RenewalSignStatusEnum.SUSPEND);
        }
    } catch (UnsupportedEncodingException e) {
        throw new RequestParameterException("订阅回调参数有问题");
    }
}

处理订阅成功回调:

/**google 订阅回调通过订阅号
 *  订阅自动扣款
 * @param  subscriptionPurchase
 */
private void subscriptionDeductionSucceeded(SubscriptionPurchase subscriptionPurchase, GoogleSubscribeCallback.DataDecode dataDecode ) {
   if(Constant.GooglePay.checkSubscriptionPurchase(subscriptionPurchase)){
        // 校验失败
        log.info("Google 回调校验订单失败,结果:{}",  subscriptionPurchase);
        return;
    }

    VipOrder vipOrder = vipOrderRepository.findByThirdOrderSn(subscriptionPurchase.getOrderId());
    if(vipOrder != null){
        // 可能是第一次订阅 ,订单已经生成
        log.info("google 订阅回调订单:{} 已经生成,不需要再次处理", vipOrder.getId());
        return;
    }

    // 获取续订
    VipRenewalOrder vipRenewalOrder = renewalOrderRepository.findBySignNoAndSignChannel(subscriptionPurchase.getObfuscatedExternalProfileId(), RenewalSignChannelEnum.GOOGLE.getCode());
    if(vipRenewalOrder == null){
        // 续订单不存在
        log.info("google 订阅回调通过订阅号:{} 发现续订订单不存在", subscriptionPurchase.getObfuscatedExternalProfileId());
        return;
    }

    // 找到自动续费上一个单子
    VipOrder latestOrder = vipOrderRepository.findByOrderSn(vipRenewalOrder.getLatestOrderSn());
    MarketVipGoodsVo marketVipGoodsVo = JSON.parseObject(vipRenewalOrder.getGoodsInfo(), MarketVipGoodsVo.class);

    // 创建一个新的订单
    VipOrder newOrder = vipOrderService.createOrderByGooglePayCallBack(dataDecode.getPackageName(), PayMethodEnum.GOOGLE_PAY_PRIMORDIAL.getCode(),
            marketVipGoodsVo, latestOrder, subscriptionPurchase);

    // 修改续订
    if(!RenewalSignStatusEnum.SUCCESS.getCode().equals(vipRenewalOrder.getSignStatus())){
        vipRenewalOrder.setSignStatus(RenewalSignStatusEnum.SUCCESS.getCode());
        vipRenewalOrder.setStatusReason(null);
    }
    vipRenewalOrder.setLatestOrderSn(newOrder.getOrderSn());
    vipRenewalOrder.setLatestOrderTime(newOrder.getCreateTime());
    vipRenewalOrder.setDeductStatus(DeductStatusEnum.DEDUCTED_SUCCESS.getCode());
    vipRenewalOrder.setRenewalContent(JSON.toJSONString(subscriptionPurchase));
    vipRenewalOrder.setSubscriptionIndex(Optional.ofNullable(vipRenewalOrder.getSubscriptionIndex()).orElse(0) + 1);
    renewalOrderRepository.save(vipRenewalOrder);

    // 消耗订单
    acknowledgeGoogleOrder(subscriptionPurchase, dataDecode);

    // 通知发放会员
    boolean grantUserVipSuccess = vipOrderService.grantUserVip(newOrder);
    if(grantUserVipSuccess){
        vipOrderService.updateOrderDeliverSuccess(newOrder);
    }

}

处理用户不再订阅的逻辑:

private void googleNoSubscription(SubscriptionPurchase subscriptionPurchase, RenewalSignStatusEnum signStatusEnum) {
    if(subscriptionPurchase != null){
        VipRenewalOrder vipRenewalOrder = renewalOrderRepository.findBySignNoAndSignChannel(subscriptionPurchase.getObfuscatedExternalProfileId(), RenewalSignChannelEnum.GOOGLE.getCode());
        if(vipRenewalOrder == null){
            // 续订单不存在
            log.error("google 订阅回调通过订阅号:{} 发现续订订单不存在", subscriptionPurchase.getObfuscatedExternalProfileId());
            return;
        }
        vipRenewalOrder.setSignStatus(signStatusEnum.getCode());
        vipRenewalOrder.setStatusReason(GoogleSubscribeCancelReasonEnum.getCancelReason(subscriptionPurchase.getCancelReason()));
        vipRenewalOrder.setRenewalContent(JSON.toJSONString(subscriptionPurchase));
        renewalOrderRepository.save(vipRenewalOrder);
    }
}