使用 Stripe Firebase 扩展 Webhook 运行订阅付款未触发

Posted

技术标签:

【中文标题】使用 Stripe Firebase 扩展 Webhook 运行订阅付款未触发【英文标题】:Run Subscription Payments with Stripe Firebase Extension Webhook Not Firing 【发布时间】:2020-12-01 09:50:08 【问题描述】:

我在我的 Firebase/Vue 应用程序中添加了 the Stripe Subscriptions extension,这样我就可以为我的客户管理订阅计划,但我认为 Stripe 网络钩子有问题。

我通过设置 firebase.rules 开始扩展安装,就像他们在教程中所说的那样:

firebase.rules:

rules_version = '2';
service cloud.firestore 
  match /databases/database/documents 
    match /document=** 
      allow read, write;
    
    match /customers/uid 
      allow read, write: if request.auth.uid == uid;

      match /checkout_sessions/id 
        allow read, write: if request.auth.uid == uid;
      
      match /subscriptions/id 
        allow read, write: if request.auth.uid == uid;
      
    

    match /products/id 
      allow read: if true;
      allow write: if false;

      match /prices/id 
        allow read: if true;
        allow write: if false;
      
    
  

我如下配置了 Stripe webhook,并在 firebase 扩展中添加了 webhook secret:

我还在 Stripe 仪表板中创建了一个受限密钥并将其添加到扩展程序中。

当我测试从 Stripe 仪表板创建新产品时,webhook 成功并且在 firebase 中创建了一个集合:

条纹产品仪表板:

Firestore:

现在,问题来了:

当我尝试创建并完成 Stripe 结帐时出现问题。从教程中,看起来我需要调用stripe.redirectToCheckout(),它会为我处理大部分事情,但我似乎找不到结帐会话的 sessionId。这是他们展示的示例:

const docRef = await db
  .collection('customers')
  .doc(currentUser.uid)
  .collection('checkout_sessions')
  .add(
    price: 'price_1GqIC8HYgolSBA35zoTTN2Zl',
    success_url: window.location.origin,
    cancel_url: window.location.origin,
  );
// Wait for the CheckoutSession to get attached by the extension
docRef.onSnapshot((snap) => 
  const  sessionId  = snap.data();
  if (sessionId) 
    // We have a session, let's redirect to Checkout
    // Init Stripe
    const stripe = Stripe('pk_test_1234');
    stripe.redirectToCheckout( sessionId );
  
);

这是我的实现:

 async checkoutv2(item) 
      let currentUser = this.getCurrentUser();
      const stripe = window.Stripe(this.publishableKey);

      const docRef = await db
        .firestore()
        .collection("customers")
        .doc(currentUser.uid)
        .collection("checkout_sessions")
        .add(
          unit_amount: item["unit_amount"],
          plan: item["id"],
          description: item["description"],
          interval: item["interval"],
          currency: item["currency"],
          type: item["type"],
          interval_count: item["interval_count"],
          active: item["active"],
          // allow_promotion_codes: true,
          // tax_rates: ["txr_1HCshzHYgolSBA35WkPjzOOi"],
          success_url: window.location.origin,
          cancel_url: window.location.origin,
          mode: "subscription",
          metadata: ,
        );

      // Wait for the CheckoutSession to get attached by the extension
      docRef.onSnapshot((snap) => 
        const  sessionId  = snap.data();
        if (sessionId) 
          // We have a session, let's redirect to Checkout
          // Init Stripe
          stripe.redirectToCheckout( sessionId );
         else 
          console.log("NOPE");
        
      );
    ,

在将 CheckOutSession 添加到 Firestore 后,它似乎“附加”到扩展,并且创建了一个会话 ID 并将其添加到文档中。但我看不到我的版本会发生这种情况。正在创建 checkout_sessions,但没有 sessionId

snap 数据如下所示:

sessionId 上没有任何内容,但它确实有自己的 id,我相信这是 Firestore 自动为集合创建的 id。

查看我的日志,没有触发任何 webhook。当我测试特定的 webhook 事件时,有些成功,有些没有。

成功:

与价格、产品创建/更新/删除有关的任何事情。 checkout.session.completed customer.subscription.created

失败(错误状态代码 400):

customer.subscription.updated customer.subscription.deleted(日志:类型为 [customer.subscription.deleted] 的 Stripe 事件 [evt_00000000000000] 的 Webhook 处理程序失败:没有此类订阅:'sub_00000000000000')

谁能告诉我发生了什么?我正在使用 Stripe API 版本:2017-06-05。我已将以下内容添加到我的index.html

  <!-- Firebase App (the core Firebase SDK) is always required and must be listed first -->
    <script src="https://www.gstatic.com/firebasejs/7.14.6/firebase.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.14.6/firebase-functions.js"></script>

    <!-- If you enabled Analytics in your project, add the Firebase SDK for Analytics -->
    <script src="https://www.gstatic.com/firebasejs/7.14.5/firebase-analytics.js"></script>

    <!-- Add Firebase products that you want to use -->
    <script src="https://www.gstatic.com/firebasejs/7.14.5/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.14.5/firebase-firestore.js"></script>

    <script src="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.js"></script>
    <link
      type="text/css"
      rel="stylesheet"
      href="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.css"
    />

我的 package.json:


  "name": "mathpath",
  "version": "0.1.0",
  "private": true,
  "description": "## Project setup ``` npm install ```",
  "author": "",
  "scripts": 
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "start": "node index.js",
    "bd": "vue-cli-service build && firebase deploy"
  ,
  "main": "babel.config.js",
  "dependencies": 
    "aws-sdk": "^2.719.0",
    "axios": "^0.19.2",
    "bootstrap": "^4.5.0",
    "bootstrap-vue": "^2.1.0",
    "core-js": "^3.6.5",
    "dns": "^0.2.2",
    "express": "^4.17.1",
    "firebase": "^7.17.1",
    "firebase-admin": "^9.0.0",
    "firebaseui": "^4.6.0",
    "fs": "0.0.1-security",
    "jquery": "^3.5.1",
    "katex": "^0.12.0",
    "markdown-it": "^11.0.0",
    "markdown-it-katex": "^2.0.3",
    "mathjax-node": "^2.1.1",
    "pg": "^8.2.2",
    "pg-hstore": "^2.3.3",
    "sequelize": "^6.3.0",
    "showdown": "^1.9.1",
    "showdown-katex": "^0.8.0",
    "slugify": "^1.4.5",
    "stripe": "^8.83.0",
    "uniqid": "^5.2.0",
    "vue": "^2.6.11",
    "vue-katex": "^0.4.0",
    "vue-router": "^3.3.4",
    "vue-stripe-checkout": "^3.5.7",
    "vue-youtube-embed": "^2.2.2",
    "vuefire": "^2.2.3",
    "vuex": "^3.5.1"
  ,
  "devDependencies": 
    "@babel/polyfill": "^7.7.0",
    "@vue/cli-plugin-babel": "~4.4.0",
    "@vue/cli-plugin-eslint": "~4.4.0",
    "@vue/cli-service": "~4.4.0",
    "babel-eslint": "^10.1.0",
    "bootstrap": "^4.3.1",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "mutationobserver-shim": "^0.3.3",
    "popper.js": "^1.16.0",
    "portal-vue": "^2.1.6",
    "sass": "^1.19.0",
    "sass-loader": "^8.0.0",
    "vue-cli-plugin-bootstrap-vue": "~0.6.0",
    "vue-template-compiler": "^2.6.11"
  ,
  "eslintConfig": 
    "root": true,
    "env": 
      "node": true
    ,
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "parserOptions": 
      "parser": "babel-eslint"
    ,
    "rules": 
      "no-console": "off",
      "no-restricted-syntax": [
        "error",
        
          "selector": "CallExpression[callee.object.name='console'][callee.property.name!=/^(log|warn|error|info|trace)$/]",
          "message": "Unexpected property on console object was called"
        
      ]
    
  ,
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ],
  "license": "ISC"


【问题讨论】:

您对docRef.onSnapshot() 的回调是否被调用?如果是这样,如果您将其注销,snap 中是否有任何内容? @taintedzodiac 好问题,是的,我在 snap 中取回数据。我已经更新了我的问题以包含它的 console.log。 谢谢!确实似乎 sessionId 没有存储在 Firebase 中,因此您无法重定向到它。这都是在 webhook 参与之前。您的应用正在成功创建 CheckoutSessions,因此问题似乎是 Firebase 集合未正确保存 id。 @taintedzodiac 对,我没有在示例中了解添加 checkout_session 集合后如何存在 sessionId。 // Wait for the CheckoutSession to get attached by the extension 的评论让我相信后端发生了一些事情,触发了一个 webhook 将 sessionId 插入到集合中,但也许我错了。 我不清楚是什么导致了这里的问题。可能值得在 github.com/stripe-samples/firebase-subscription-payments 上提交问题,因为它可能在扩展程序本身中。 【参考方案1】:

我也遇到了同样的问题。

这是我在它开始工作之前更改的一些内容,我不确定到底是什么修复了它:

关闭我的 Firebase 模拟器并在生产数据上进行测试。它似乎在修复之前在模拟器中工作(除了原始帖子中提到的问题),但我还没有测试它是否在模拟器后修复中工作。 删除了我在 Stripe 中的所有测试数据和 webhook 并重新开始。 更新了我所有的 firebase 和 vue-stripe-checkout 依赖项:

"firebase": "7.19.1", "firebase-admin": "^9.1.1", “firebase 功能”:“^3.11.0”, “firebase 工具”:“^8.10.0”, "firebaseui": "^4.6.1", "vue-stripe-checkout": "^3.5.7"

将 Stripe 仪表板中的 Stripe API 从 2020 年 3 月 20 日更新到 2020 年 8 月 27 日。 删除了扩展并使用 CLI 而不是控制台重新安装。配置扩展时我唯一更改的是将客户集合设置为“用户”,因为这是我的应用程序中的约定:https://firebase.google.com/products/extensions/firestore-stripe-subscriptions

完成所有这些后,我在 Stripe 中创建了一个测试订阅产品,并看到它出现在我的产品集合中。这在修复之前已经有效。新的是我现在看到 sessionId 作为第二个 snap.data() 响应的一部分。第一个响应看起来就像“修复”之前的响应,我认为它又失败了,但是在等待一两秒后,出现了一个带有额外 sessionId 参数的新响应?

我也在使用 Vue。然后我参加该会话并将其提交以更新状态。我观察这个会话的变化,然后调用 checkoutRef 函数。这一次,用户的收藏将更新为新订阅。

对我来说,这段代码在 store-auth.js 中

async checkoutUser( commit , payload) 
    let userId = firebaseAuth.currentUser.uid;

    const docRef = await firebaseDb
      .collection("users")
      .doc(userId)
      .collection("checkout_sessions")
      .add(
        price: "YOUR_PRICE_ID",
        success_url: window.location.origin,
        cancel_url: window.location.origin,
        mode: "subscription"
      );

    docRef.onSnapshot(snap => 
      const test = snap.data();

      const  sessionId  = snap.data();
      if (sessionId) 
        commit("setSession", sessionId);
      
    );
  ,

无论你在哪里使用 vue-stripe-checkout。对我来说,它在 PagePlans.vue 上

    <stripe-checkout
    ref="checkoutRef"
    :pk="publishableKey"
    :mode="mode"
    :items="items"
    :successUrl="successUrl"
    :cancelUrl="cancelUrl"
    :session-id="sessionId"

  watch: 
    currentSession: 
      handler(newValue, oldValue) 
        this.sessionId = newValue

        this.$refs.checkoutRef.redirectToCheckout(scenarioId: newValue.sessionId)
      
    
  ,
  computed: 
    ...mapGetters("auth", ["currentSession"]),
  ,
  methods: 
    ...mapActions("auth", [
      "checkoutUser",
    ]),
    async checkout() 
      await this.checkoutUser()
    
  

【讨论】:

@ielivs 您如何在 Firebase 中设置产品和价格?您是从条纹手动插入产品吗?还是创建 cron 来从 stripe 获取产品和价格? @Jitendra,在我完成帖子中的步骤后,当我通过 Stripe 的仪表板创建/更新产品/价格时,它会自动添加为 firebase 中的新集合。我在 Stripe 中手动创建产品/价格,插件在 Firebase 中自动创建/更新集合/文档,因为我已经可以使用它了。【参考方案2】:

对于 Firebase 云功能,我查看了日志,发现是访问问题。

编辑您的受限 API 密钥并授予客户权限

Stripe 受限 API 权限:

用于 Stripe 的 Firebase 函数:

【讨论】:

将 API 上的客户权限设置为写入 嗨,欢迎来到 Stack Overflow!如果您想在答案中添加更多信息,请edit 它而不是将其留在 cmets 中。【参考方案3】:

我在使用 stripe firebase 扩展时遇到了同样的问题。在条带 Firebase 扩展配置中,将 checkout_sessions 放在 customers 集合中,在我的代码中我使用的是 users 集合。

我在两个地方都使用了users 后开始工作。

【讨论】:

以上是关于使用 Stripe Firebase 扩展 Webhook 运行订阅付款未触发的主要内容,如果未能解决你的问题,请参考以下文章

使用 Stripe Firebase 扩展 Webhook 运行订阅付款未触发

使用 Firebase 'Run Subscription Payments with Stripe' 扩展购买多个订阅

对如何在我的网络应用程序中使用 Stripe 作为 Firebase 的扩展感到困惑

使用 React Native/Stripe/Firebase 一次性付款

Firebase + stripe + react native 通知客户端 firebase 功能已完成

拿来。 Stripe 集成在本地主机上工作,并在上传到 Firebase 后停止工作