reason

增加支付

......@@ -95,7 +95,7 @@ class MyApp extends StatelessWidget {
}
interceptors.add(AdapterInterceptor());
configDio(
baseUrl: 'https://www.yiyan.pub/api/v1/',
baseUrl: 'https://api.parlando.ink/api/v1/',
interceptors: interceptors,
);
}
......
import 'dart:async';
import 'dart:io';
import 'dart:ui';
import 'package:Parlando/apis/api_response.dart';
import 'package:Parlando/login/login_router.dart';
import 'package:Parlando/membership/models/membership_entity.dart';
import 'package:Parlando/membership/view_models/membership_view_model.dart';
import 'package:Parlando/payment/payment_service.dart';
import 'package:Parlando/res/constant.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flustars/flustars.dart';
......@@ -14,6 +14,7 @@ import 'package:Parlando/res/resources.dart';
import 'package:Parlando/routers/fluro_navigator.dart';
import 'package:Parlando/extension/int_extension.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
import 'package:getwidget/getwidget.dart';
import 'package:provider/provider.dart';
class MembershipPage extends StatefulWidget {
......@@ -27,18 +28,7 @@ class MembershipPageState extends State<MembershipPage>
with WidgetsBindingObserver {
bool _isLoading = false;
late StreamSubscription _purchaseUpdatedSubscription;
late StreamSubscription _purchaseErrorSubscription;
late StreamSubscription _conectionSubscription;
final List<String> _productLists = Platform.isAndroid
? [
'test.yiyan.vip.1.month',
]
: ['test.yiyan.vip.1.month'];
String _platformVersion = 'Unknown';
List<IAPItem> _items = [];
List<PurchasedItem> _purchases = [];
final List<Widget> _productWidgets = <Widget>[];
@override
void initState() {
......@@ -48,8 +38,7 @@ class MembershipPageState extends State<MembershipPage>
.setSelectedMembership(null);
Provider.of<MembershipViewProvider>(context, listen: false)
.fetchMembershipData('0');
initPlatformState();
PaymentService.instance.initConnection();
} else {
NavigatorUtils.push(context, LoginRouter.loginPage, replace: true);
}
......@@ -130,9 +119,11 @@ class MembershipPageState extends State<MembershipPage>
SizedBox(
width: double.infinity,
height: 100,
child: Column(
children: _renderInApps(),
),
child: _productWidgets.isEmpty
? Column(
children: _productWidgets,
)
: const GFLoader(),
),
Gaps.vGap24,
Text(
......@@ -145,7 +136,7 @@ class MembershipPageState extends State<MembershipPage>
Gaps.vGap10,
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
......@@ -220,72 +211,15 @@ class MembershipPageState extends State<MembershipPage>
@override
void dispose() {
_conectionSubscription.cancel();
PaymentService.instance.dispose();
super.dispose();
}
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// prepare
var result = await FlutterInappPurchase.instance.initialize();
print('result: $result');
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
// _platformVersion = platformVersion;
});
// refresh items for android
try {
String msg = await FlutterInappPurchase.instance.consumeAll();
print('consumeAllItems: $msg');
} catch (err) {
print('consumeAllItems error: $err');
}
_conectionSubscription =
FlutterInappPurchase.connectionUpdated.listen((connected) {
print('connected: $connected');
});
_purchaseUpdatedSubscription =
FlutterInappPurchase.purchaseUpdated.listen((productItem) {
print('purchase-updated: $productItem');
});
_purchaseErrorSubscription =
FlutterInappPurchase.purchaseError.listen((purchaseError) {
print('purchase-error: $purchaseError');
});
List<IAPItem> items =
await FlutterInappPurchase.instance.getSubscriptions(_productLists);
for (var item in items) {
print('${item.toString()}');
_items.add(item);
}
setState(() {
_items = items;
_purchases = [];
});
}
List<Widget> _renderInApps() {
List<Widget> widgets = <Widget>[];
for (IAPItem item in _items) {
widgets.add(Text(item.title!));
void _renderInApps() async {
List<IAPItem> items = await PaymentService.instance.products;
for (IAPItem item in items) {
_productWidgets.add(Text(item.title!));
}
return widgets;
}
void _requestPurchase(IAPItem item) {
FlutterInappPurchase.instance.requestPurchase(item.productId!);
setState(() {});
}
}
......
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:Parlando/util/toast_utils.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
class PaymentService {
/// We want singleton object of ``PaymentService`` so create private constructor
///
/// Use PaymentService as ``PaymentService.instance``
PaymentService._internal();
static final PaymentService instance = PaymentService._internal();
/// To listen the status of connection between app and the billing server
late StreamSubscription<ConnectionResult> _connectionSubscription;
/// To listen the status of the purchase made inside or outside of the app (App Store / Play Store)
///
/// If status is not error then app will be notied by this stream
late StreamSubscription<PurchasedItem?> _purchaseUpdatedSubscription;
/// To listen the errors of the purchase
late StreamSubscription<PurchaseResult?> _purchaseErrorSubscription;
/// List of product ids you want to fetch
final List<String> _productIds = ['test.yiyan.vip.1.month'];
/// All available products will be store in this list
late List<IAPItem> _products;
/// All past purchases will be store in this list
late List<PurchasedItem> _pastPurchases;
/// view of the app will subscribe to this to get notified
/// when premium status of the user changes
final ObserverList<Function> _proStatusChangedListeners =
ObserverList<Function>();
/// view of the app will subscribe to this to get errors of the purchase
final ObserverList<Function(String)> _errorListeners =
ObserverList<Function(String)>();
/// logged in user's premium status
bool _isProUser = false;
bool get isProUser => _isProUser;
/// view can subscribe to _proStatusChangedListeners using this method
addToProStatusChangedListeners(Function callback) {
_proStatusChangedListeners.add(callback);
}
/// view can cancel to _proStatusChangedListeners using this method
removeFromProStatusChangedListeners(Function callback) {
_proStatusChangedListeners.remove(callback);
}
/// view can subscribe to _errorListeners using this method
addToErrorListeners(Function(String) callback) {
_errorListeners.add(callback);
}
/// view can cancel to _errorListeners using this method
removeFromErrorListeners(Function(String) callback) {
_errorListeners.remove(callback);
}
/// Call this method to notify all the subsctibers of _proStatusChangedListeners
void _callProStatusChangedListeners() {
for (var callback in _proStatusChangedListeners) {
callback();
}
}
/// Call this method to notify all the subsctibers of _errorListeners
void _callErrorListeners(String error) {
for (var callback in _errorListeners) {
callback(error);
}
}
/// Call this method at the startup of you app to initialize connection
/// with billing server and get all the necessary data
void initConnection() {
var result = FlutterInappPurchase.instance.initialize();
print("___________________________");
print("result:$result");
_connectionSubscription =
FlutterInappPurchase.connectionUpdated.listen((connected) {});
_purchaseUpdatedSubscription =
FlutterInappPurchase.purchaseUpdated.listen(_handlePurchaseUpdate);
_purchaseErrorSubscription =
FlutterInappPurchase.purchaseError.listen(_handlePurchaseError);
_getItems();
_getPastPurchases();
}
/// call when user close the app
void dispose() {
_connectionSubscription.cancel();
_purchaseErrorSubscription.cancel();
_purchaseUpdatedSubscription.cancel();
FlutterInappPurchase.instance.finalize();
}
void _handlePurchaseError(PurchaseResult? purchaseError) {
_callErrorListeners(purchaseError!.message.toString());
}
/// Called when new updates arrives at ``purchaseUpdated`` stream
void _handlePurchaseUpdate(PurchasedItem? productItem) async {
if (Platform.isAndroid) {
await _handlePurchaseUpdateAndroid(productItem!);
} else {
await _handlePurchaseUpdateIOS(productItem!);
}
}
Future<void> _handlePurchaseUpdateIOS(PurchasedItem purchasedItem) async {
switch (purchasedItem.transactionStateIOS) {
case TransactionState.deferred:
// Edit: This was a bug that was pointed out here : https://github.com/dooboolab/flutter_inapp_purchase/issues/234
// FlutterInappPurchase.instance.finishTransaction(purchasedItem);
break;
case TransactionState.failed:
_callErrorListeners("Transaction Failed");
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
break;
case TransactionState.purchased:
await _verifyAndFinishTransaction(purchasedItem);
break;
case TransactionState.purchasing:
break;
case TransactionState.restored:
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
break;
default:
}
}
/// three purchase state https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchaseState
/// 0 : UNSPECIFIED_STATE
/// 1 : PURCHASED
/// 2 : PENDING
Future<void> _handlePurchaseUpdateAndroid(PurchasedItem purchasedItem) async {
switch (purchasedItem.purchaseStateAndroid) {
case PurchaseState.purchased:
if (purchasedItem.isAcknowledgedAndroid == null) {
await _verifyAndFinishTransaction(purchasedItem);
}
break;
default:
_callErrorListeners("Something went wrong");
}
}
/// Call this method when status of purchase is success
/// Call API of your back end to verify the receipt
/// back end has to call billing server's API to verify the purchase token
_verifyAndFinishTransaction(PurchasedItem purchasedItem) async {
bool isValid = false;
try {
// Call API
isValid = await _verifyPurchase(purchasedItem);
} on Exception {
_callErrorListeners("暂时无法购买,请稍后再试!");
return;
}
if (isValid) {
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
_isProUser = true;
// save in sharedPreference here
_callProStatusChangedListeners();
} else {
_callErrorListeners("Verification failed");
}
}
Future<List<IAPItem>> get products async {
if (_products == null) {
await _getItems();
}
return _products;
}
Future<void> _getItems() async {
List<IAPItem> items =
await FlutterInappPurchase.instance.getSubscriptions(_productIds);
_products = [];
for (var item in items) {
_products.add(item);
}
print("############");
print(_products);
}
void _getPastPurchases() async {
// remove this if you want to restore past purchases in iOS
if (Platform.isIOS) {
return;
}
List<PurchasedItem>? purchasedItems =
await FlutterInappPurchase.instance.getAvailablePurchases();
for (var purchasedItem in purchasedItems!) {
bool isValid = false;
if (Platform.isAndroid) {
Map map = json.decode(purchasedItem.transactionReceipt!);
// if your app missed finishTransaction due to network or crash issue
// finish transactins
if (!map['acknowledged']) {
isValid = await _verifyPurchase(purchasedItem);
if (isValid) {
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
_isProUser = true;
_callProStatusChangedListeners();
}
} else {
_isProUser = true;
_callProStatusChangedListeners();
}
}
}
_pastPurchases = [];
_pastPurchases.addAll(purchasedItems);
}
Future<void> buyProduct(IAPItem item) async {
try {
await FlutterInappPurchase.instance
.requestSubscription(item.productId.toString());
} catch (error) {
Toast.show("购买失败!");
}
}
_verifyPurchase(PurchasedItem purchasedItem) {}
}