Firebase CloudFunction如何检查IAP从应用程序外部订阅更改?
我从 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 ->
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
}
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
}
@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
}
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 技术交流群。

问题是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的互动几乎无法实现,对于最简单的情况而言。
编辑:几个点要澄清,为将来的读者讲述。
简而言之:执行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.
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.