firebase - 如何使用 Flutter 处理简单的应用内订阅?
问题描述
我正在使用 in_app_purchase 包,我想在 Flutter 中处理订阅。
只有 1 个订阅选项,它遵循一个非常常见的模式......我有一个部分检查用户 firebase 帐户“高级”字段是否为真。如果是,则显示该部分,如果为 false,则显示“立即订阅”按钮。
目前,在 Android 上,该按钮不显示。在 iOS 上,该按钮显示,但当我单击订阅时 - 它正确输出 PurchaseParams 但从未提示我付款。我正在使用沙盒帐户在物理设备上进行测试。
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:wt_flutter/components/trips/blurred_category.dart';
import 'package:wt_flutter/services/globals.dart';
import 'package:wt_flutter/services/models.dart';
import 'package:wt_flutter/shared/loader.dart';
import 'package:wt_flutter/components/trips/initial_airport.dart';
final String proID = 'go_pro_annual';
class TripsScreen extends StatefulWidget {
const TripsScreen({Key key}) : super(key: key);
@override
_TripsScreenState createState() => _TripsScreenState();
}
class _TripsScreenState extends State<TripsScreen> {
// IAP Plugin Interface
InAppPurchaseConnection _iap = InAppPurchaseConnection.instance;
// Is the API available on the device
bool _available = true;
// Subscriptions for sale
List<ProductDetails> _products = [];
// Past purchases
List<PurchaseDetails> _purchases = [];
// Updates to purchases
StreamSubscription _streamSubscription;
@override
void initState() {
_initialize();
super.initState();
}
@override
void dispose() {
_streamSubscription.cancel();
super.dispose();
}
void _initialize() async {
// Check availablility of In App Purchases
_available = await _iap.isAvailable();
if (_available) {
await _getProducts();
await _getPastPurchases();
// List<Future futures = [_getProducts(), _getPastPurchases()];
// await Future.wait(futures);
_verifyPurchase();
// Listen to new purchases
_streamSubscription =
_iap.purchaseUpdatedStream.listen((data) => setState(() {
print('NEW PURCHASE');
_purchases.addAll(data);
_verifyPurchase();
}));
}
}
// Get all products available for sale
Future<void> _getProducts() async {
Set<String> ids = Set.from([proID]);
ProductDetailsResponse response = await _iap.queryProductDetails(ids);
setState(() {
_products = response.productDetails;
});
}
// Gets past purchases
Future<void> _getPastPurchases() async {
QueryPurchaseDetailsResponse response = await _iap.queryPastPurchases();
for (PurchaseDetails purchase in response.pastPurchases) {
if (Platform.isIOS) {
_iap.completePurchase(purchase);
}
}
// Or for consumables
// TODO query the database for state of consumable products
setState(() {
_purchases = response.pastPurchases;
});
}
// Returns purchase of specific product ID
PurchaseDetails _hasPurchased(String productID) {
return _purchases.firstWhere((purchase) => purchase.purchaseID == productID,
orElse: () => null);
}
// Your own business logic to setup a consumable
void _verifyPurchase() {
PurchaseDetails purchase = _hasPurchased(proID);
//TODO serverside verification & record subscription in the database
if (purchase != null && purchase.status == PurchaseStatus.purchased) {
print('Purchase verified');
}
}
/// Purchase a product
void _buyProduct(ProductDetails prod) {
print(prod);
try {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: prod);
// For one time purchase
print(purchaseParam.productDetails.id);
print(purchaseParam.productDetails.price);
print(purchaseParam.productDetails.title);
_iap.buyNonConsumable(purchaseParam: purchaseParam);
print('purchase successful');
} catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
User user = Provider.of<User>(context);
if (user.homeAirport != '') {
return Scaffold(
body: FutureBuilder(
future: Global.tripsRef.getData(),
builder: (BuildContext context, AsyncSnapshot snap) {
if (snap.hasData) {
return Center(
child: Container(
color: Colors.white,
child: ListView(
physics: ClampingScrollPhysics(),
scrollDirection: Axis.vertical,
children: <Widget>[
Text('Wavetrotter Suggestions'),
TripList(),
for (var prod in _products)
_available && !user.premium
? FlatButton(
child: Text('Subscribe'),
onPressed: () => _buyProduct(prod),
color: Colors.green,
)
: SizedBox(height: 0.0),
Text('Trips For You'),
!user.premium ? BlurredCategory() : TripList(),
Text('Biggest Swells'),
!user.premium ? BlurredCategory() : TripList(),
Text('No Wetsuit'),
!user.premium ? BlurredCategory() : TripList(),
],
),
),
);
} else {
return LoadingScreen();
}
},
),
);
} else {
return InitialAirport();
}
}
}
解决方案
推荐阅读
- compiler-construction - 如何将基于堆栈的指令转换为基于寄存器的指令?
- javascript - 太重载或太更新:SPA
- c# - 如何动态创建表达式
> - c - 从C中的文件读取时出现奇怪的循环
- c# - 'Font' 是 System.Drawing.Font 和 iTextSharp.text.Font 之间的模糊引用
- linux - 异常处理程序的实际代码驻留在 Linux 中的什么位置?
- ios - pagehide 和/或 beforeunload 在 IOS Chrome、Safari 中未触发
- java - 我可以在java中重新发送邮件吗?
- javascript - 不允许从 cloudformation 更新用户池架构
- c++builder - 即使使用 2 个 DLL(ssleay32.dll 和 libeay32.dll),SHA-512 也会返回 NULL