我正在使用in_app_purchase软件包,并且想在Flutter中处理订阅。
只有一个订阅选项,它遵循一种非常常见的模式... 我有一个部分检查“用户” 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();
}
}
}