Showing
7 changed files
with
98 additions
and
107 deletions
... | @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { | ... | @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { |
32 | } | 32 | } |
33 | 33 | ||
34 | android { | 34 | android { |
35 | - compileSdkVersion 31 | 35 | + compileSdkVersion 32 |
36 | 36 | ||
37 | compileOptions { | 37 | compileOptions { |
38 | sourceCompatibility JavaVersion.VERSION_1_8 | 38 | sourceCompatibility JavaVersion.VERSION_1_8 |
... | @@ -51,7 +51,7 @@ android { | ... | @@ -51,7 +51,7 @@ android { |
51 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). | 51 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). |
52 | applicationId "pub.yiyan.parlando.Parlando" | 52 | applicationId "pub.yiyan.parlando.Parlando" |
53 | minSdkVersion 21 | 53 | minSdkVersion 21 |
54 | - targetSdkVersion 31 | 54 | + targetSdkVersion 32 |
55 | versionCode flutterVersionCode.toInteger() | 55 | versionCode flutterVersionCode.toInteger() |
56 | versionName flutterVersionName | 56 | versionName flutterVersionName |
57 | multiDexEnabled true | 57 | multiDexEnabled true | ... | ... |
... | @@ -2,6 +2,8 @@ | ... | @@ -2,6 +2,8 @@ |
2 | xmlns:tools="http://schemas.android.com/tools" | 2 | xmlns:tools="http://schemas.android.com/tools" |
3 | package="pub.yiyan.parlando.Parlando"> | 3 | package="pub.yiyan.parlando.Parlando"> |
4 | 4 | ||
5 | + <uses-permission android:name="com.android.vending.BILLING" /> | ||
6 | + | ||
5 | <application | 7 | <application |
6 | android:requestLegacyExternalStorage="true" | 8 | android:requestLegacyExternalStorage="true" |
7 | tools:replace="android:label" | 9 | tools:replace="android:label" |
... | @@ -47,8 +49,5 @@ | ... | @@ -47,8 +49,5 @@ |
47 | <meta-data | 49 | <meta-data |
48 | android:name="flutterEmbedding" | 50 | android:name="flutterEmbedding" |
49 | android:value="2" /> | 51 | android:value="2" /> |
50 | - <meta-data | ||
51 | - android:name="com.google.android.gms.wallet.api.enabled" | ||
52 | - android:value="true" /> | ||
53 | </application> | 52 | </application> |
54 | </manifest> | 53 | </manifest> | ... | ... |
... | @@ -23,7 +23,6 @@ import 'util/device_utils.dart'; | ... | @@ -23,7 +23,6 @@ import 'util/device_utils.dart'; |
23 | import 'util/handle_error_utils.dart'; | 23 | import 'util/handle_error_utils.dart'; |
24 | import 'util/log_utils.dart'; | 24 | import 'util/log_utils.dart'; |
25 | import 'util/theme_utils.dart'; | 25 | import 'util/theme_utils.dart'; |
26 | - | ||
27 | /// | 26 | /// |
28 | /// 配置本地化的方法 | 27 | /// 配置本地化的方法 |
29 | /// 1. 安裝Flutter intl插件 | 28 | /// 1. 安裝Flutter intl插件 |
... | @@ -96,7 +95,7 @@ class MyApp extends StatelessWidget { | ... | @@ -96,7 +95,7 @@ class MyApp extends StatelessWidget { |
96 | } | 95 | } |
97 | interceptors.add(AdapterInterceptor()); | 96 | interceptors.add(AdapterInterceptor()); |
98 | configDio( | 97 | configDio( |
99 | - baseUrl: 'http://www.yiyan.pub/api/v1/', | 98 | + baseUrl: 'https://www.yiyan.pub/api/v1/', |
100 | interceptors: interceptors, | 99 | interceptors: interceptors, |
101 | ); | 100 | ); |
102 | } | 101 | } | ... | ... |
1 | +import 'dart:async'; | ||
2 | +import 'dart:io'; | ||
1 | import 'dart:ui'; | 3 | import 'dart:ui'; |
2 | 4 | ||
3 | import 'package:Parlando/apis/api_response.dart'; | 5 | import 'package:Parlando/apis/api_response.dart'; |
4 | import 'package:Parlando/login/login_router.dart'; | 6 | import 'package:Parlando/login/login_router.dart'; |
5 | import 'package:Parlando/membership/models/membership_entity.dart'; | 7 | import 'package:Parlando/membership/models/membership_entity.dart'; |
6 | -import 'package:Parlando/membership/models/order_entity.dart'; | ||
7 | -import 'package:Parlando/membership/models/pay_entity.dart'; | ||
8 | import 'package:Parlando/membership/view_models/membership_view_model.dart'; | 8 | import 'package:Parlando/membership/view_models/membership_view_model.dart'; |
9 | -import 'package:Parlando/net/dio_utils.dart'; | ||
10 | -import 'package:Parlando/net/http_api.dart'; | ||
11 | import 'package:Parlando/res/constant.dart'; | 9 | import 'package:Parlando/res/constant.dart'; |
12 | import 'package:cached_network_image/cached_network_image.dart'; | 10 | import 'package:cached_network_image/cached_network_image.dart'; |
13 | import 'package:flustars/flustars.dart'; | 11 | import 'package:flustars/flustars.dart'; |
... | @@ -15,8 +13,7 @@ import 'package:flutter/material.dart'; | ... | @@ -15,8 +13,7 @@ import 'package:flutter/material.dart'; |
15 | import 'package:Parlando/res/resources.dart'; | 13 | import 'package:Parlando/res/resources.dart'; |
16 | import 'package:Parlando/routers/fluro_navigator.dart'; | 14 | import 'package:Parlando/routers/fluro_navigator.dart'; |
17 | import 'package:Parlando/extension/int_extension.dart'; | 15 | import 'package:Parlando/extension/int_extension.dart'; |
18 | -import 'package:Parlando/util/toast_utils.dart'; | 16 | +import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'; |
19 | -import 'package:flutter_braintree/flutter_braintree.dart'; | ||
20 | import 'package:provider/provider.dart'; | 17 | import 'package:provider/provider.dart'; |
21 | 18 | ||
22 | class MembershipPage extends StatefulWidget { | 19 | class MembershipPage extends StatefulWidget { |
... | @@ -30,6 +27,19 @@ class MembershipPageState extends State<MembershipPage> | ... | @@ -30,6 +27,19 @@ class MembershipPageState extends State<MembershipPage> |
30 | with WidgetsBindingObserver { | 27 | with WidgetsBindingObserver { |
31 | bool _isLoading = false; | 28 | bool _isLoading = false; |
32 | 29 | ||
30 | + late StreamSubscription _purchaseUpdatedSubscription; | ||
31 | + late StreamSubscription _purchaseErrorSubscription; | ||
32 | + late StreamSubscription _conectionSubscription; | ||
33 | + final List<String> _productLists = Platform.isAndroid | ||
34 | + ? [ | ||
35 | + 'test.yiyan.vip.1.month', | ||
36 | + ] | ||
37 | + : ['test.yiyan.vip.1.month']; | ||
38 | + | ||
39 | + String _platformVersion = 'Unknown'; | ||
40 | + List<IAPItem> _items = []; | ||
41 | + List<PurchasedItem> _purchases = []; | ||
42 | + | ||
33 | @override | 43 | @override |
34 | void initState() { | 44 | void initState() { |
35 | super.initState(); | 45 | super.initState(); |
... | @@ -38,6 +48,8 @@ class MembershipPageState extends State<MembershipPage> | ... | @@ -38,6 +48,8 @@ class MembershipPageState extends State<MembershipPage> |
38 | .setSelectedMembership(null); | 48 | .setSelectedMembership(null); |
39 | Provider.of<MembershipViewProvider>(context, listen: false) | 49 | Provider.of<MembershipViewProvider>(context, listen: false) |
40 | .fetchMembershipData('0'); | 50 | .fetchMembershipData('0'); |
51 | + | ||
52 | + initPlatformState(); | ||
41 | } else { | 53 | } else { |
42 | NavigatorUtils.push(context, LoginRouter.loginPage, replace: true); | 54 | NavigatorUtils.push(context, LoginRouter.loginPage, replace: true); |
43 | } | 55 | } |
... | @@ -115,13 +127,11 @@ class MembershipPageState extends State<MembershipPage> | ... | @@ -115,13 +127,11 @@ class MembershipPageState extends State<MembershipPage> |
115 | ), | 127 | ), |
116 | Gaps.vGap24, | 128 | Gaps.vGap24, |
117 | // TODO 如果会员则显示会员详情 | 129 | // TODO 如果会员则显示会员详情 |
118 | - Expanded( | 130 | + SizedBox( |
119 | - child: ListView.builder( | 131 | + width: double.infinity, |
120 | - itemExtent: 48.0, | 132 | + height: 100, |
121 | - itemBuilder: (_, index) { | 133 | + child: Column( |
122 | - return _buildItem(mb.goodsList![index]); | 134 | + children: _renderInApps(), |
123 | - }, | ||
124 | - itemCount: mb.goodsList!.length, | ||
125 | ), | 135 | ), |
126 | ), | 136 | ), |
127 | Gaps.vGap24, | 137 | Gaps.vGap24, |
... | @@ -135,7 +145,7 @@ class MembershipPageState extends State<MembershipPage> | ... | @@ -135,7 +145,7 @@ class MembershipPageState extends State<MembershipPage> |
135 | Gaps.vGap10, | 145 | Gaps.vGap10, |
136 | Row( | 146 | Row( |
137 | mainAxisAlignment: | 147 | mainAxisAlignment: |
138 | - MainAxisAlignment.spaceBetween, | 148 | + MainAxisAlignment.spaceBetween, |
139 | mainAxisSize: MainAxisSize.min, | 149 | mainAxisSize: MainAxisSize.min, |
140 | crossAxisAlignment: CrossAxisAlignment.center, | 150 | crossAxisAlignment: CrossAxisAlignment.center, |
141 | children: [ | 151 | children: [ |
... | @@ -205,95 +215,77 @@ class MembershipPageState extends State<MembershipPage> | ... | @@ -205,95 +215,77 @@ class MembershipPageState extends State<MembershipPage> |
205 | } | 215 | } |
206 | } | 216 | } |
207 | 217 | ||
208 | - Widget _buildItem(MembershipDataGoodsList goods) { | ||
209 | - return Flex( | ||
210 | - direction: Axis.horizontal, | ||
211 | - children: [ | ||
212 | - Text( | ||
213 | - goods.name!, | ||
214 | - style: const TextStyle( | ||
215 | - color: Colors.white, | ||
216 | - fontSize: 15.0, | ||
217 | - ), | ||
218 | - ), | ||
219 | - Gaps.hGap10, | ||
220 | - Expanded( | ||
221 | - flex: 1, | ||
222 | - child: Align( | ||
223 | - alignment: Alignment.centerRight, | ||
224 | - child: Text( | ||
225 | - goods.price!, | ||
226 | - style: const TextStyle( | ||
227 | - color: Colors.white, | ||
228 | - fontSize: 15.0, | ||
229 | - ), | ||
230 | - ), | ||
231 | - ), | ||
232 | - ), | ||
233 | - Gaps.hGap32, | ||
234 | - ElevatedButton( | ||
235 | - child: const Text( | ||
236 | - "开通", | ||
237 | - style: TextStyle(color: Colors.white, fontSize: 15.0), | ||
238 | - ), | ||
239 | - onPressed: () { | ||
240 | - DioUtils.instance.requestNetwork<OrderEntity>( | ||
241 | - Method.post, | ||
242 | - HttpApi.order, | ||
243 | - params: {'goods_id': goods.id}, | ||
244 | - onSuccess: (data) { | ||
245 | - String orderSn = data!.data!.orderSn!; | ||
246 | - DioUtils.instance.requestNetwork<PayEntity>( | ||
247 | - Method.get, | ||
248 | - "${HttpApi.pay}?order_sn=$orderSn&pay_type=paypal", | ||
249 | - params: [], | ||
250 | - onSuccess: (data) { | ||
251 | - String id = data!.data!.id!; | ||
252 | - payPalRequest(id); | ||
253 | - _isLoading = false; | ||
254 | - }, | ||
255 | - onError: (code, msg) { | ||
256 | - Toast.show(msg.toString()); | ||
257 | - _isLoading = false; | ||
258 | - setState(() {}); | ||
259 | - }, | ||
260 | - ); | ||
261 | - _isLoading = false; | ||
262 | - }, | ||
263 | - onError: (code, msg) { | ||
264 | - Toast.show(msg.toString()); | ||
265 | - _isLoading = false; | ||
266 | - setState(() {}); | ||
267 | - }, | ||
268 | - ); | ||
269 | - }, | ||
270 | - ), | ||
271 | - ], | ||
272 | - ); | ||
273 | - } | ||
274 | - | ||
275 | - Future<void> payPalRequest(String key) async { | ||
276 | - final request = BraintreePayPalRequest( | ||
277 | - amount: '0.01', | ||
278 | - currencyCode: 'CNY', | ||
279 | - billingAgreementDescription: '贝宝支付很无敌', | ||
280 | - ); | ||
281 | - BraintreePaymentMethodNonce? result = await Braintree.requestPaypalNonce( | ||
282 | - key, | ||
283 | - request, | ||
284 | - ); | ||
285 | - if (result != null) { | ||
286 | - print('Nonce: ${result.nonce}'); | ||
287 | - } else { | ||
288 | - print('PayPal flow was canceled.'); | ||
289 | - } | ||
290 | - } | ||
291 | - | ||
292 | @override | 218 | @override |
293 | void didChangeAppLifecycleState(AppLifecycleState state) {} | 219 | void didChangeAppLifecycleState(AppLifecycleState state) {} |
294 | 220 | ||
295 | @override | 221 | @override |
296 | void dispose() { | 222 | void dispose() { |
223 | + _conectionSubscription.cancel(); | ||
297 | super.dispose(); | 224 | super.dispose(); |
298 | } | 225 | } |
226 | + | ||
227 | + Future<void> initPlatformState() async { | ||
228 | + String platformVersion; | ||
229 | + // Platform messages may fail, so we use a try/catch PlatformException. | ||
230 | + | ||
231 | + // prepare | ||
232 | + var result = await FlutterInappPurchase.instance.initialize(); | ||
233 | + print('result: $result'); | ||
234 | + | ||
235 | + // If the widget was removed from the tree while the asynchronous platform | ||
236 | + // message was in flight, we want to discard the reply rather than calling | ||
237 | + // setState to update our non-existent appearance. | ||
238 | + if (!mounted) return; | ||
239 | + | ||
240 | + setState(() { | ||
241 | + // _platformVersion = platformVersion; | ||
242 | + }); | ||
243 | + | ||
244 | + // refresh items for android | ||
245 | + try { | ||
246 | + String msg = await FlutterInappPurchase.instance.consumeAll(); | ||
247 | + print('consumeAllItems: $msg'); | ||
248 | + } catch (err) { | ||
249 | + print('consumeAllItems error: $err'); | ||
250 | + } | ||
251 | + | ||
252 | + _conectionSubscription = | ||
253 | + FlutterInappPurchase.connectionUpdated.listen((connected) { | ||
254 | + print('connected: $connected'); | ||
255 | + }); | ||
256 | + | ||
257 | + _purchaseUpdatedSubscription = | ||
258 | + FlutterInappPurchase.purchaseUpdated.listen((productItem) { | ||
259 | + print('purchase-updated: $productItem'); | ||
260 | + }); | ||
261 | + | ||
262 | + _purchaseErrorSubscription = | ||
263 | + FlutterInappPurchase.purchaseError.listen((purchaseError) { | ||
264 | + print('purchase-error: $purchaseError'); | ||
265 | + }); | ||
266 | + | ||
267 | + List<IAPItem> items = | ||
268 | + await FlutterInappPurchase.instance.getSubscriptions(_productLists); | ||
269 | + for (var item in items) { | ||
270 | + print('${item.toString()}'); | ||
271 | + _items.add(item); | ||
272 | + } | ||
273 | + | ||
274 | + setState(() { | ||
275 | + _items = items; | ||
276 | + _purchases = []; | ||
277 | + }); | ||
278 | + } | ||
279 | + | ||
280 | + List<Widget> _renderInApps() { | ||
281 | + List<Widget> widgets = <Widget>[]; | ||
282 | + for (IAPItem item in _items) { | ||
283 | + widgets.add(Text(item.title!)); | ||
284 | + } | ||
285 | + return widgets; | ||
286 | + } | ||
287 | + | ||
288 | + void _requestPurchase(IAPItem item) { | ||
289 | + FlutterInappPurchase.instance.requestPurchase(item.productId!); | ||
290 | + } | ||
299 | } | 291 | } | ... | ... |
This diff is collapsed. Click to expand it.
... | @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev | ... | @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev |
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. | 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. |
16 | # Read more about iOS versioning at | 16 | # Read more about iOS versioning at |
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html | 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html |
18 | -version: 1.0.0+1 | 18 | +version: 1.0.0+2 |
19 | 19 | ||
20 | environment: | 20 | environment: |
21 | sdk: ">=2.16.2 <3.0.0" | 21 | sdk: ">=2.16.2 <3.0.0" |
... | @@ -113,7 +113,7 @@ dependencies: | ... | @@ -113,7 +113,7 @@ dependencies: |
113 | 113 | ||
114 | animated_radial_menu: ^0.0.1 | 114 | animated_radial_menu: ^0.0.1 |
115 | 115 | ||
116 | - flutter_braintree: ^2.3.1 | 116 | + flutter_inapp_purchase: ^5.3.0 |
117 | 117 | ||
118 | dependency_overrides: | 118 | dependency_overrides: |
119 | decimal: 1.5.0 | 119 | decimal: 1.5.0 | ... | ... |
-
Please register or login to post a comment