首页 > 解决方案 > Google 发布/订阅订阅数据与应用不匹配

问题描述

我正在尝试在服务器上侦听我的 Google Play 应用程序的订阅更改(新的和现有的)。这是我正在使用的代码。这使用google/cloud-pubsub作曲家包:

$projectId = 'app-name';
$keyFile = file_get_contents(storage_path('app/app-name.json'));
$pubsub = new PubSubClient([
    'projectId' => $projectId,
    'keyFile' => json_decode($keyFile, true)
]);

$httpPostRequestBody = file_get_contents('php://input');
$requestData = json_decode($httpPostRequestBody, true);

info(json_encode($requestData));

$message = $pubsub->consume($requestData);
info(json_encode($message));

上面的代码有效,但问题是我得到的数据与我在应用程序端得到的数据不匹配。这是一个示例数据:

{
   "message":{
      "data":"eyJ2ZXJ...",
      "messageId":"16797998xxxxxxxxx",
      "message_id":"1679799xxxxxxxxx",
      "publishTime":"2020-12-15T02:09:23.27Z",
      "publish_time":"2020-12-15T02:09:23.27Z"
   },
   "subscription":"projects\/app-name\/subscriptions\/test-subs"
}

如果你base64_decode()的数据,你会得到这样的:

{
version: "1.0",
packageName: "com.dev.app",
eventTimeMillis: "1607997631636",
subscriptionNotification: {
    version: "1.0",
    notificationType: 4,
    purchaseToken: "kmloa....",
    subscriptionId: "app_subs1"
  }
}

这是我期望与purchaseToken我从客户端获得的相同的地方。

这是客户端的代码。我正在使用 Expo 应用内购买来实现订阅:

setPurchaseListener(async ({ responseCode, results, errorCode }) => {
if (responseCode === IAPResponseCode.OK) {
    const { orderId, purchaseToken, acknowledged } = results[0];
    if (!acknowledged) {
        await instance.post("/subscribe", {
            order_id: orderId,
            order_token: purchaseToken,
            data: JSON.stringify(results[0]),
        });

        finishTransactionAsync(results[0], true);
        alert(
            "You're now subscribed! You can now use the full functionality of the app."
        );
    }
}
});

我希望purchaseToken我从中提取results[0]的内容与 Google 服务器在将通知推送到端点时返回的内容相同。但事实并非如此。

更新

我认为我的主要问题是我假设我需要的所有数据都来自 Google Pay,所以当用户在应用程序中订阅时,我只依赖 Google 发布的数据。

这实际上不是发布消息的那个:

await instance.post("/subscribe")

它只是使用购买令牌更新数据库。我可以用它来订阅用户,但不能保证请求是合法的。有人可以根据现有用户构建必要的凭据,他们几乎可以订阅而无需支付任何费用。另外,此方法不能用于保持用户订阅。所以数据真的必须来自谷歌。

根据下面的答案,我现在意识到您应该从自己的服务器触发发布?然后你听听吗?所以当我从客户那里打电话时:

await instance.post("/subscribe", {
  purchaseToken
});

我实际上需要像这样发布包含购买令牌的消息:

$pubsub = new PubSubClient([
  'projectId' => $projectId,
]);
$topic = $pubsub->topic($topicName);
$message = [
  'purchaseToken' => request('purchaseToken')
];
$topic->publish(['data' => $message]);

这就是你说的吗?但是这种方法唯一的问题是如何验证购买令牌是否合法,以及如何在服务器中续订订阅?我有一个需要每月更新的字段,以便用户在服务器眼中保持“订阅”状态。

也许,我只是通过使用 pub/sub 使事情变得过于复杂。如果实际上有一个 API,我可以定期从中提取数据(使用 cron),它允许我保持用户订阅数据的更新,那么这也是可以接受的答案。

标签: phplaravelgoogle-apigoogle-playgoogle-api-php-client

解决方案


首先 - 由于 php PubSubClient,我对 php 和 pubsub 的体验非常糟糕。如果您的脚本只等待推送并检查消息,则删除 pubsub 包并用几行代码处理它。

示例

$message = file_get_contents('php://input');
$message = json_decode($message, true);
if (is_array($message)) {
    $message = (isset($message['message']) && isset($message['message']['data'])) ? base64_decode($message['message']['data']) : false;
    if (is_string($message)) {
        $message = json_decode($message, true);
        if (is_array($message)) {
            $type = (isset($message['type'])) ? $message['type'] : null;
            $data = (isset($message['data'])) ? $message['data'] : [];
        }
    }
}

我不确定你这边的一切是如何运作的,但如果这部分发布了消息:

await instance.post("/subscribe", {
    order_id: orderId,
    order_token: purchaseToken,
    data: JSON.stringify(results[0]),
});

看起来它是发布消息的代理方法。因为与它一起发送的有效负载不像PubSub 描述的架构,并且在最终消息中它看起来不像IAPQueryResponse

如果我遇到你的情况,我会检查几件事来调试问题

  • 我如何向/从 PubSub 发布/读取消息(主题、订阅和消息有效负载)
  • 如果一切设置正确,那么我将比较所有其他消息数据
  • 如果问题仍然存在,那么我将尝试将最少量的数据发布到 PubSub - 只是purchaseToken在开始时检查是什么破坏了消息

为了更容易调试

  • 创建拉取订阅
  • 当您发布消息时,请使用“查看消息”检查拉取订阅消息

对我来说,问题不直接存在于 PubSub 中,而是存在于您发布/接收消息的实现中。

2020 年 12 月 21 日更新:

流动:

如果您需要以下信息:

  • 新订户数
  • 续订次数
  • 活跃订阅计数您可以创建自己的分析应用程序,但如果您需要更复杂的东西,那么您必须选择一个工具来满足您的需求。

您也可以使用“pull”从 pubsub 获取消息,但我遇到的情况很少:

  • 上次我使用 pull pubsub 返回随机数量的消息 - 如果我的限制是 50 并且队列中有超过 50 条消息,我希望收到 50 条消息,但有时 pubsub 给我的消息更少。
  • PubSub 以随机顺序返回消息 - 现在有一个使用排序键的选项,但它是新的东西。
  • 要实现“拉”,您必须运行 crons 或带有“推”的东西,您会尽快收到消息。
  • 使用“拉”,您必须依赖库/包(或任何它被称为的语言),但在“推”上,您只需几行代码就可以处理消息,就像我的 php 示例一样。

推荐阅读