reason

增加支付

...@@ -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>
......
1 org.gradle.jvmargs=-Xmx1536M 1 org.gradle.jvmargs=-Xmx1536M
2 android.useAndroidX=true 2 android.useAndroidX=true
3 android.enableJetifier=true 3 android.enableJetifier=true
4 +android.bundle.enableUncompressedNativeLibs=false
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -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
......