Firebase CloudFunction如何检查IAP从应用程序外部订阅更改?

发布于 01-21 17:09 字数 6028 浏览 3 评论 0原文

我从 codelab 他的项目与他所有的项目一起,步骤在这里 github 。 Codelab为我提供了很多帮助,谢谢! 我采取了步骤,并从教程中删除了所有IAP产品,并仅添加了可订阅的产品。在AppStoreconnect中,我在同一家庭中有两个购买产品“正常”和“ Ultimate”。它运行良好,但是我发现了一个问题:

情况A:

当用户从它们中订阅一个问题时,当用户想从“正常”到“终极”到“终极”或“终极”到“正常”时,用户却订阅了另一个问题。 “在他的验证活动时间里,Firebase Cloud不会更新他的购买(Produkt ID和订单ID仍然是旧ID)。当Firebase不更新时,请不要立即将用户升级到其他订阅。一年后,他到达那里升级到其他购买。

情况B:

同样的问题,但在应用程序之外。 用户订阅了一种产品,然后他在AppStore设置中从应用程序外出,然后更改其订阅产品。 Firebase从Apple获取信息,但是Firebase Cloud不会更新用户的订阅信息。 您可以或您解决这个问题吗?

我从Codelab - >

“ https://github.com/flutter/codelabs/blob/master/master/in_app_purchases/complete/complete/complete/lib/constants.dart”

const cloudRegion = 'europe-west1';

const subscriptionList = ["kunde_1_fahrzeug", "kunde_3_fahrzeug"];

//storeKeySubscription
const subscription_kunde_1_fahrzeug = 'kunde_1_fahrzeug';
const subscription_kunde_3_fahrzeug = 'kunde_3_fahrzeug';


  void updatePurchases() {
 // omitted

      // hasActiveSubscription = purchases.any((element) => element.productId == subscription_kunde_1_fahrzeug && element.status != Status.expired);
      //hasActiveSubscription = purchases.any((element) => element.productId == subscriptionList && element.status != Status.expired);
      hasActiveSubscription = purchases.any((element) => subscriptionList.any((x) => x == element.productId)  && element.status != Status.expired);
      for(PastPurchase x  in purchases){
        print("Gelb hasActiveSubscription IAP-REPO : ${x.productId} - ${x.status}");
      };

      hasUpgrade = purchases.any(
            (element) => subscriptionList.any((x) => x == element.productId),
      );
/*
      hasUpgrade = purchases.any(
            (element) => element.productId == storeKeyUpgrade,
      );
*/
      notifyListeners();

 // omitted
}

” “ rel =“ nofollow noreferrer”


  void purchasesUpdate() {
 // omitted

    if (products.isNotEmpty) {
      // subscriptions = products .where((element) => element.productDetails.id == subscription_kunde_1_fahrzeug) .toList();

      subscriptions = products
          .where((element) => subscriptionList.any((x) => x == element.productDetails.id))
          .toList();

      upgrades = products
          .where((element) => subscriptionList.any((x) => x == element.productDetails.id))
          .toList();

    }

 // omitted
}

  Future<void> loadPurchases() async {
 // omitted

    const ids = <String>{
      subscription_kunde_1_fahrzeug,
      subscription_kunde_3_fahrzeug,
      //storeKeyUpgrade,
    };

 // omitted
}

  Future<void> buy(PurchasableProduct product) async {
 // omitted

    // case storeKeyConsumable:
    // await iapConnection.buyConsumable(purchaseParam: purchaseParam);
    //  break;
      case subscription_kunde_1_fahrzeug:
        await iapConnection.buyNonConsumable(purchaseParam: purchaseParam);
        break;
      case subscription_kunde_3_fahrzeug:
      //case storeKeyUpgrade:
        await iapConnection.buyNonConsumable(purchaseParam: purchaseParam);
        break;
 // omitted
}

  Future<void> _handlePurchase(PurchaseDetails purchaseDetails) async {
 // omitted

      if (validPurchase) {
        // Apply changes locally
        switch (purchaseDetails.productID) {
          case subscription_kunde_1_fahrzeug:
            print("Orange:  ID Produkt:  ${purchaseDetails.productID},  ${purchaseDetails.transactionDate},  ${purchaseDetails.verificationData},  ${purchaseDetails.status},  ${purchaseDetails.purchaseID},  ${purchaseDetails.pendingCompletePurchase}, switch (purchaseDetails.productID)  case: subscription_kunde_1_fahrzeug");
            counter.applyPaidMultiplier_kunde_1_fahrzeug();
            break;
          case subscription_kunde_3_fahrzeug:
            print("Orange: ID Produkt: ${purchaseDetails.productID},  ${purchaseDetails.transactionDate},  ${purchaseDetails.verificationData},  ${purchaseDetails.status},  ${purchaseDetails.purchaseID},  ${purchaseDetails.pendingCompletePurchase},  switch (purchaseDetails.productID)  case: subscription_kunde_3_fahrzeug");
            counter.applyPaidMultiplier_kunde_3_fahrzeug();
            break;
        //   case storeKeyConsumable:
        //    counter.addBoughtDashes(2000);
        //     break;
        /* case storeKeyUpgrade:
            _beautifiedDashUpgrade = true;
            break;

          */
 // omitted
}

“ https://github.com/flutter/codelabs/blob/master/in_app_purchases/complete/complete/complete/lib/lib/logic/logic/dash_purchases.dart //github.com/flutter/codelabs/blob/master/in_app_purchases/complete/complete/complete/lib/model/model/past_purchase.dart“ rel =“ nofollow noreferrer”> pastpurchase


@immutable
class PastPurchase {
 // omitted

  String get title {
    switch (productId) {
      case subscription_kunde_1_fahrzeug:
        return 'Subscription';
      case subscription_kunde_3_fahrzeug:
        return 'Subscription';
      default:
        return productId;
    }
  }
 // omitted
}

.com/flutter/codelabs/blob/master/in_app_purchases/poutter/firebase-backend/functions/src/products.ts“ rel =“ nofollow noreferrer”> firebase backend

export interface ProductData {
  productId: string;
  type: "SUBSCRIPTION" | "NON_SUBSCRIPTION";
}
export const productDataMap: { [productId: string]: ProductData } = {
  "kunde_1_fahrzeug": {
    productId: "kunde_1_fahrzeug",
    type: "SUBSCRIPTION",
  },
  "kunde_3_fahrzeug": {
    productId: "kunde_3_fahrzeug",
    type: "SUBSCRIPTION",
  },
};

I do the tutorial from CodeLab his project with all steps are here Github. Codelab helped me lot, Thanks!
I do the steps and deleted all IAP products from tutorial and added only subscribable products. i have two purchase product "Normal" and "Ultimate" in the same Family in Appstoreconnect. Its working well, but I found a problem:

Situation A:

When the user subscribed one from them its working all fine, but when the user want to subscribe the other like from "Normal" to "Ultimate" or "Ultimate" to "Normal" in his Validate activ time, then Firebase Cloud don't update his Purchase (Produkt ID and Order ID Is still the old ID's). When Firebase don't update, then get the user not his upgrade to other subscribe instantly. He get there upgrade to the other purchase after a year.

Situation B:

The same Problem, but Outside from the App.
User subscribed one product, then he go outside from app in his Appstore settings and change his subscribe product. Firebase get a info from Apple, but Firebase Cloud don't update the subscription information from User.
can u or have u solve this problem?

my changes from Codelab ->

Constant.dart

const cloudRegion = 'europe-west1';

const subscriptionList = ["kunde_1_fahrzeug", "kunde_3_fahrzeug"];

//storeKeySubscription
const subscription_kunde_1_fahrzeug = 'kunde_1_fahrzeug';
const subscription_kunde_3_fahrzeug = 'kunde_3_fahrzeug';

IAPRepo.dart


  void updatePurchases() {
 // omitted

      // hasActiveSubscription = purchases.any((element) => element.productId == subscription_kunde_1_fahrzeug && element.status != Status.expired);
      //hasActiveSubscription = purchases.any((element) => element.productId == subscriptionList && element.status != Status.expired);
      hasActiveSubscription = purchases.any((element) => subscriptionList.any((x) => x == element.productId)  && element.status != Status.expired);
      for(PastPurchase x  in purchases){
        print("Gelb hasActiveSubscription IAP-REPO : ${x.productId} - ${x.status}");
      };

      hasUpgrade = purchases.any(
            (element) => subscriptionList.any((x) => x == element.productId),
      );
/*
      hasUpgrade = purchases.any(
            (element) => element.productId == storeKeyUpgrade,
      );
*/
      notifyListeners();

 // omitted
}

XXX_Purchase


  void purchasesUpdate() {
 // omitted

    if (products.isNotEmpty) {
      // subscriptions = products .where((element) => element.productDetails.id == subscription_kunde_1_fahrzeug) .toList();

      subscriptions = products
          .where((element) => subscriptionList.any((x) => x == element.productDetails.id))
          .toList();

      upgrades = products
          .where((element) => subscriptionList.any((x) => x == element.productDetails.id))
          .toList();

    }

 // omitted
}

  Future<void> loadPurchases() async {
 // omitted

    const ids = <String>{
      subscription_kunde_1_fahrzeug,
      subscription_kunde_3_fahrzeug,
      //storeKeyUpgrade,
    };

 // omitted
}

  Future<void> buy(PurchasableProduct product) async {
 // omitted

    // case storeKeyConsumable:
    // await iapConnection.buyConsumable(purchaseParam: purchaseParam);
    //  break;
      case subscription_kunde_1_fahrzeug:
        await iapConnection.buyNonConsumable(purchaseParam: purchaseParam);
        break;
      case subscription_kunde_3_fahrzeug:
      //case storeKeyUpgrade:
        await iapConnection.buyNonConsumable(purchaseParam: purchaseParam);
        break;
 // omitted
}

  Future<void> _handlePurchase(PurchaseDetails purchaseDetails) async {
 // omitted

      if (validPurchase) {
        // Apply changes locally
        switch (purchaseDetails.productID) {
          case subscription_kunde_1_fahrzeug:
            print("Orange:  ID Produkt:  ${purchaseDetails.productID},  ${purchaseDetails.transactionDate},  ${purchaseDetails.verificationData},  ${purchaseDetails.status},  ${purchaseDetails.purchaseID},  ${purchaseDetails.pendingCompletePurchase}, switch (purchaseDetails.productID)  case: subscription_kunde_1_fahrzeug");
            counter.applyPaidMultiplier_kunde_1_fahrzeug();
            break;
          case subscription_kunde_3_fahrzeug:
            print("Orange: ID Produkt: ${purchaseDetails.productID},  ${purchaseDetails.transactionDate},  ${purchaseDetails.verificationData},  ${purchaseDetails.status},  ${purchaseDetails.purchaseID},  ${purchaseDetails.pendingCompletePurchase},  switch (purchaseDetails.productID)  case: subscription_kunde_3_fahrzeug");
            counter.applyPaidMultiplier_kunde_3_fahrzeug();
            break;
        //   case storeKeyConsumable:
        //    counter.addBoughtDashes(2000);
        //     break;
        /* case storeKeyUpgrade:
            _beautifiedDashUpgrade = true;
            break;

          */
 // omitted
}

PastPurchase


@immutable
class PastPurchase {
 // omitted

  String get title {
    switch (productId) {
      case subscription_kunde_1_fahrzeug:
        return 'Subscription';
      case subscription_kunde_3_fahrzeug:
        return 'Subscription';
      default:
        return productId;
    }
  }
 // omitted
}

Firebase Backend

export interface ProductData {
  productId: string;
  type: "SUBSCRIPTION" | "NON_SUBSCRIPTION";
}
export const productDataMap: { [productId: string]: ProductData } = {
  "kunde_1_fahrzeug": {
    productId: "kunde_1_fahrzeug",
    type: "SUBSCRIPTION",
  },
  "kunde_3_fahrzeug": {
    productId: "kunde_3_fahrzeug",
    type: "SUBSCRIPTION",
  },
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

波浪屿的海角声2025-01-28 17:09:00

问题是CODELAB与订阅不切实际地相关,并且依赖于也无法处理家庭内订阅更改的Node.js软件包。 Apple的订阅更改不提供新的Product_ID,它们将新的订阅ID作为auto_renew_product_id提供,并且product_id从原始交易中保持不变。转动冗长:真正在运行功能时看到它。

因此,要修复,您需要第三个功能来进行应用内订阅更改,您无法验证并从Apple-Receipt-Verify正确返回,因为该软件包没有提供您要切换到的auto_renew_product_id。因此,您需要一种新的方法来验证收据。

对于应用程序之外的更改,您需要修复HandleServerEvent,因为这无效从外部应用程序更改订阅。

我建议Revenuecat。费用很小,您将拥有一家对更新API的既得利益的公司。

您的收入基金会需要坚定,尽管Google Play商店的软件包和服务器端代码的性能对我来说是一尘不染的……它们与Apple的互动几乎无法实现,对于最简单的情况而言。

编辑:几个点要澄清,为将来的读者讲述。

  1. 截至2022年5月,代码实验室有一个错别字。您需要使用Apple的1型通知。
  2. Apple处理组内订阅自身。它将在组中取消订阅并订阅。您还需要用Apple对小组内订阅进行排名。 Android立即改变。
  3. 苹果处理倾斜,跨级和升级方式不同。所有降级都必须在更改之前完成任期。这样可以防止苹果必须退款。因此,当您降级时,该术语必须完成,然后Apple将更改子并启动服务器事件。
  4. 代码实验室逻辑具有Android创建/取消subs,并在Firestore中留下了购买文档的子历史记录跟踪...这是因为每个子更改都是取消和创建的。但是,苹果内部更改潜艇,然后向服务器发射告诉您什么变化。因此,代码实验室没有详细介绍任何订阅历史记录的DOC TRAIL。

简而言之:执行CODELAB,并达到应用程序的功能级别肯定会让您对每个商店的处理方式有很好的了解……以及它们处理方式的不同。由于它与IN_APP_PURCHASE插件,服务器端验证和性能相关...虽然完成了,但这是Codelab和软件包给出的最低指导。

The problem is the codelab is unrealistically simplistic related to subscriptions, and relies on a node.js package that also doesn't handle in-family subscription changes. Subscription changes with apple don't provide the new product_id, they provide the new subscription id as auto_renew_product_id, and the product_id stays the same from the original transaction. Turn verbose: true to see it when running your function.

So, to fix, you'd need a third function for in-app subscription changes, which you can't validate and return properly from apple-receipt-verify because that package doesn't provide the auto_renew_product_id that you're switching to. So you'll need a new way to validate the receipt.

For changes outside the app, you'll need to fix the handleServerEvent, because that doesn't work to change subscriptions from outside app.

I suggest RevenueCat. The cost is minimal, and you'll have a company with a vested interest in updating the API's.

Your revenue foundation needs to be firm, and while the Google Play Store performance of the package and server-side code was spotless for me...their interaction with Apple is barely functional for the simplest of scenarios.

Edit: A couple points to clarify, expound on for future readers.

  1. There is a typo as of May 2022 in the code lab. You need to use Type 1 Notifications from Apple.
  2. Apple handles in-group subscription changes itself. It will unsubscribe and subscribe within a group. You also need to rank your in-group subscriptions with apple. Android just changes right away.
  3. Apple handles down-grade, cross-grade and upgrade differently. All downgrades must finish their term before changing. This prevents apple from having to refund. So when you downgrade, the term must finish, then apple will change the sub and fire the server event.
  4. The code lab logic has Android creating/canceling subs and leaving a sub history trail of purchases documents in firestore...this is because every sub change is a cancel and create. Apple however changes the subs internally, and then fires to the server telling you what changed. So the code lab does not have a doc trail detailing any subscription history..each sub is one doc always...just changes product id and status.

In short: doing the codelab, and getting to a functional level for your app will definitely give you a good understanding of how each store handles subs...and how differently they handle them. As it relates to the in_app_purchase plug-in, server side validation and performance...while elegantly done, it was the bare minimum guidance given by the codelab and package.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文