Showing
6 changed files
with
137 additions
and
60 deletions
1 | +import 'dart:convert'; | ||
2 | + | ||
1 | import 'package:Parlando/apis/api_response.dart'; | 3 | import 'package:Parlando/apis/api_response.dart'; |
2 | import 'package:Parlando/net/dio_utils.dart'; | 4 | import 'package:Parlando/net/dio_utils.dart'; |
5 | +import 'package:common_utils/common_utils.dart'; | ||
3 | import 'package:dio/dio.dart'; | 6 | import 'package:dio/dio.dart'; |
4 | import 'package:flutter/material.dart'; | 7 | import 'package:flutter/material.dart'; |
5 | 8 | ||
6 | class BaseApi { | 9 | class BaseApi { |
7 | - Future<Response<ApiResponse<T>>> post<T>( | 10 | + Future<Response<T>> post<T>( |
8 | String path, { | 11 | String path, { |
9 | data, | 12 | data, |
10 | Map<String, dynamic>? queryParameters, | 13 | Map<String, dynamic>? queryParameters, |
... | @@ -13,7 +16,7 @@ class BaseApi { | ... | @@ -13,7 +16,7 @@ class BaseApi { |
13 | ProgressCallback? onSendProgress, | 16 | ProgressCallback? onSendProgress, |
14 | ProgressCallback? onReceiveProgress, | 17 | ProgressCallback? onReceiveProgress, |
15 | }) { | 18 | }) { |
16 | - return _getDio().post<ApiResponse<T>>( | 19 | + return _getDio().post<T>( |
17 | path, | 20 | path, |
18 | data: data, | 21 | data: data, |
19 | queryParameters: queryParameters, | 22 | queryParameters: queryParameters, | ... | ... |
1 | +import 'dart:convert'; | ||
2 | + | ||
1 | import 'package:Parlando/apis/api_base.dart'; | 3 | import 'package:Parlando/apis/api_base.dart'; |
4 | +import 'package:Parlando/apis/api_response.dart'; | ||
5 | +import 'package:common_utils/common_utils.dart'; | ||
2 | import 'package:dio/dio.dart'; | 6 | import 'package:dio/dio.dart'; |
3 | import 'package:flutter/material.dart'; | 7 | import 'package:flutter/material.dart'; |
4 | 8 | ||
... | @@ -11,7 +15,13 @@ class OrderApi extends BaseApi { | ... | @@ -11,7 +15,13 @@ class OrderApi extends BaseApi { |
11 | return _instance; | 15 | return _instance; |
12 | } | 16 | } |
13 | 17 | ||
14 | - Future<Response> createOrder(String productId) { | 18 | + Future<dynamic> createOrder(String productId) { |
15 | - return post("/order"); | 19 | + var data = {"goods_id": productId}; |
20 | + return post("order", data: data).then((value) { | ||
21 | + if (TextUtil.isEmpty(value.data)) { | ||
22 | + return {}; | ||
23 | + } | ||
24 | + return json.decode(value.data); | ||
25 | + }); | ||
16 | } | 26 | } |
17 | } | 27 | } | ... | ... |
... | @@ -15,10 +15,7 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> { | ... | @@ -15,10 +15,7 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> { |
15 | 15 | ||
16 | Widget buildBody(BuildContext context); | 16 | Widget buildBody(BuildContext context); |
17 | 17 | ||
18 | - void onFirstBuildBody(BuildContext context){ | 18 | + void onFirstBuildBody(BuildContext context) {} |
19 | - | ||
20 | - } | ||
21 | - | ||
22 | 19 | ||
23 | showLoading({String text = 'Loading...'}) { | 20 | showLoading({String text = 'Loading...'}) { |
24 | EasyLoading.show(status: text); | 21 | EasyLoading.show(status: text); |
... | @@ -27,4 +24,8 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> { | ... | @@ -27,4 +24,8 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> { |
27 | hideLoading() { | 24 | hideLoading() { |
28 | EasyLoading.dismiss(); | 25 | EasyLoading.dismiss(); |
29 | } | 26 | } |
27 | + | ||
28 | + toast(String text) { | ||
29 | + EasyLoading.showToast(text); | ||
30 | + } | ||
30 | } | 31 | } | ... | ... |
... | @@ -30,8 +30,7 @@ class MembershipPage extends StatefulWidget { | ... | @@ -30,8 +30,7 @@ class MembershipPage extends StatefulWidget { |
30 | } | 30 | } |
31 | 31 | ||
32 | class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingObserver { | 32 | class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingObserver { |
33 | - | 33 | + final List<ProductDetails> _products = <ProductDetails>[]; |
34 | - final List<Widget> _productWidgets = <Widget>[]; | ||
35 | 34 | ||
36 | Function? connectionCallBack; | 35 | Function? connectionCallBack; |
37 | 36 | ||
... | @@ -40,11 +39,18 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO | ... | @@ -40,11 +39,18 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO |
40 | @override | 39 | @override |
41 | void initState() { | 40 | void initState() { |
42 | super.initState(); | 41 | super.initState(); |
43 | - PaymentSdk.instance.initState(); | 42 | + PaymentSdk.instance.initState(onPaySuccess: () { |
43 | + hideLoading(); | ||
44 | + }, onCancel: () { | ||
45 | + hideLoading(); | ||
46 | + }, onFailed: () { | ||
47 | + hideLoading(); | ||
48 | + }, onPending: () { | ||
49 | + toast("订单Pending"); | ||
50 | + }); | ||
44 | PaymentSdk.instance.queryProducts().then((value) { | 51 | PaymentSdk.instance.queryProducts().then((value) { |
45 | - for (var element in value) { | 52 | + _products.clear(); |
46 | - _productWidgets.add(buildBuyItem(element)); | 53 | + _products.addAll(value); |
47 | - } | ||
48 | setState(() {}); | 54 | setState(() {}); |
49 | }); | 55 | }); |
50 | if (SpUtil.containsKey(Constant.userToken)!) { | 56 | if (SpUtil.containsKey(Constant.userToken)!) { |
... | @@ -76,8 +82,8 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO | ... | @@ -76,8 +82,8 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO |
76 | child: Column( | 82 | child: Column( |
77 | children: [ | 83 | children: [ |
78 | initBackBar(), | 84 | initBackBar(), |
79 | - Spacer(), | 85 | + const Spacer(), |
80 | - // buildCompleteWidget(), | 86 | + buildCompleteWidget(), |
81 | ], | 87 | ], |
82 | ), | 88 | ), |
83 | ), | 89 | ), |
... | @@ -112,20 +118,6 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO | ... | @@ -112,20 +118,6 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO |
112 | ); | 118 | ); |
113 | } | 119 | } |
114 | 120 | ||
115 | - Widget buildBuyItem(ProductDetails element) { | ||
116 | - var style = const TextStyle(color: Colors.white); | ||
117 | - var button = Text("购买", style: style).paddingLeftRight(15).paddingTopBottom(8).click(() { | ||
118 | - PaymentSdk.instance.buy(element); | ||
119 | - }, color: Colors.blue).round(radius: 10); | ||
120 | - return Row( | ||
121 | - mainAxisAlignment: MainAxisAlignment.end, | ||
122 | - children: [ | ||
123 | - Text(element.price, style: style), | ||
124 | - button, | ||
125 | - ], | ||
126 | - ).paddingLeftRight(10).paddingTopBottom(5); | ||
127 | - } | ||
128 | - | ||
129 | Widget initOtherEntrance() { | 121 | Widget initOtherEntrance() { |
130 | return Row( | 122 | return Row( |
131 | mainAxisAlignment: MainAxisAlignment.spaceBetween, | 123 | mainAxisAlignment: MainAxisAlignment.spaceBetween, |
... | @@ -165,7 +157,7 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO | ... | @@ -165,7 +157,7 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO |
165 | children: [ | 157 | children: [ |
166 | Text(mb?.title ?? "一言会员", style: TextStyle(fontSize: 18.px, color: Colors.white)), | 158 | Text(mb?.title ?? "一言会员", style: TextStyle(fontSize: 18.px, color: Colors.white)), |
167 | Gaps.vGap24, | 159 | Gaps.vGap24, |
168 | - _productWidgets.isNotEmpty ? Column(children: _productWidgets) : const GFLoader(), | 160 | + mb?.goodsList?.isNotEmpty == true ? buildProductWidget(mb) : const GFLoader(), |
169 | Gaps.vGap24, | 161 | Gaps.vGap24, |
170 | Text(mb?.intro ?? "一言介绍", style: TextStyle(fontSize: 14.px, color: Colors.white)), | 162 | Text(mb?.intro ?? "一言介绍", style: TextStyle(fontSize: 14.px, color: Colors.white)), |
171 | Gaps.vGap10, | 163 | Gaps.vGap10, |
... | @@ -186,6 +178,55 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO | ... | @@ -186,6 +178,55 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO |
186 | ); | 178 | ); |
187 | } | 179 | } |
188 | 180 | ||
181 | + buildProductWidget(MembershipData? mb) { | ||
182 | + var widgets = mb?.goodsList?.map((e) => buildProductItem(e)).toList() ?? []; | ||
183 | + return Column(children: widgets); | ||
184 | + } | ||
185 | + | ||
186 | + Widget buildProductItem(MembershipDataGoodsList e) { | ||
187 | + ProductDetails? element; | ||
188 | + for (var value in _products) { | ||
189 | + if (value.id == e.membershipId || mapId(value.id, e.id)) { | ||
190 | + element = value; | ||
191 | + break; | ||
192 | + } | ||
193 | + } | ||
194 | + return buildBuyItem(element, e); | ||
195 | + } | ||
196 | + | ||
197 | + Widget buildBuyItem(ProductDetails? element, MembershipDataGoodsList e) { | ||
198 | + var style = const TextStyle(color: Colors.white); | ||
199 | + var button = Text("购买", style: style).paddingLeftRight(15).paddingTopBottom(8).click(() { | ||
200 | + showLoading(); | ||
201 | + if (element != null) { | ||
202 | + PaymentSdk.instance.buy(element, e); | ||
203 | + } | ||
204 | + }, color: Colors.blue).round(radius: 10); | ||
205 | + return Row( | ||
206 | + mainAxisAlignment: MainAxisAlignment.end, | ||
207 | + children: [ | ||
208 | + Text(buildBuyPrice(element, e), style: style), | ||
209 | + button, | ||
210 | + ], | ||
211 | + ).paddingLeftRight(10).paddingTopBottom(5); | ||
212 | + } | ||
213 | + | ||
214 | + String buildBuyPrice(ProductDetails? element, MembershipDataGoodsList e) { | ||
215 | + if (element == null) { | ||
216 | + return "${e.price}/${e.name}"; | ||
217 | + } | ||
218 | + return "${element.price}/${e.name}"; | ||
219 | + } | ||
220 | + | ||
221 | + bool mapId(String? id, int? membershipId) { | ||
222 | + bool year = id == 'yearly_yiyan_vip' && membershipId == 4; | ||
223 | + bool month = id == 'monthly_yiyan_vip' && membershipId == 3; | ||
224 | + if (year || month) { | ||
225 | + print("$id ---- $membershipId"); | ||
226 | + } | ||
227 | + return year || month; | ||
228 | + } | ||
229 | + | ||
189 | @override | 230 | @override |
190 | void dispose() { | 231 | void dispose() { |
191 | PaymentSdk.instance.dispose(); | 232 | PaymentSdk.instance.dispose(); | ... | ... |
... | @@ -122,7 +122,7 @@ class LoggingInterceptor extends Interceptor { | ... | @@ -122,7 +122,7 @@ class LoggingInterceptor extends Interceptor { |
122 | Log.e('ResponseCode: ${response.statusCode}'); | 122 | Log.e('ResponseCode: ${response.statusCode}'); |
123 | } | 123 | } |
124 | // 输出结果 | 124 | // 输出结果 |
125 | - // Log.json(response.data.toString()); | 125 | + Log.json(response.data.toString()); |
126 | Log.d('----------End: $duration 毫秒----------'); | 126 | Log.d('----------End: $duration 毫秒----------'); |
127 | super.onResponse(response, handler); | 127 | super.onResponse(response, handler); |
128 | } | 128 | } | ... | ... |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | 2 | ||
3 | import 'package:Parlando/apis/api_order.dart'; | 3 | import 'package:Parlando/apis/api_order.dart'; |
4 | -import 'package:flutter/material.dart'; | 4 | +import 'package:Parlando/membership/models/membership_entity.dart'; |
5 | +import 'package:common_utils/common_utils.dart'; | ||
5 | import 'package:in_app_purchase/in_app_purchase.dart'; | 6 | import 'package:in_app_purchase/in_app_purchase.dart'; |
6 | 7 | ||
7 | class PaymentSdk { | 8 | class PaymentSdk { |
... | @@ -13,12 +14,25 @@ class PaymentSdk { | ... | @@ -13,12 +14,25 @@ class PaymentSdk { |
13 | return _instance; | 14 | return _instance; |
14 | } | 15 | } |
15 | 16 | ||
16 | - static const Set<String> _kIds = <String>{'yearly_yiyan_vip', 'monthly_yiyan_vip', 'yearly-default'}; | 17 | + static const Set<String> _kIds = <String>{'yearly_yiyan_vip', 'monthly_yiyan_vip'}; |
17 | 18 | ||
18 | StreamSubscription<List<PurchaseDetails>>? _subscription; | 19 | StreamSubscription<List<PurchaseDetails>>? _subscription; |
19 | List<ProductDetails> products = []; | 20 | List<ProductDetails> products = []; |
21 | + Function? onPaySuccess; | ||
22 | + Function? onPending; | ||
23 | + Function? onFailed; | ||
24 | + Function? onCancel; | ||
20 | 25 | ||
21 | - initState() { | 26 | + initState({ |
27 | + Function? onPaySuccess, | ||
28 | + Function? onPending, | ||
29 | + Function? onFailed, | ||
30 | + Function? onCancel, | ||
31 | + }) { | ||
32 | + this.onPaySuccess = onPaySuccess; | ||
33 | + this.onPending = onPending; | ||
34 | + this.onFailed = onFailed; | ||
35 | + this.onCancel = onCancel; | ||
22 | final Stream<List<PurchaseDetails>> purchaseUpdated = InAppPurchase.instance.purchaseStream; | 36 | final Stream<List<PurchaseDetails>> purchaseUpdated = InAppPurchase.instance.purchaseStream; |
23 | _subscription = purchaseUpdated.listen((purchaseDetailsList) { | 37 | _subscription = purchaseUpdated.listen((purchaseDetailsList) { |
24 | _listenToPurchaseUpdated(purchaseDetailsList); | 38 | _listenToPurchaseUpdated(purchaseDetailsList); |
... | @@ -32,41 +46,35 @@ class PaymentSdk { | ... | @@ -32,41 +46,35 @@ class PaymentSdk { |
32 | Future<List<ProductDetails>> queryProducts() async { | 46 | Future<List<ProductDetails>> queryProducts() async { |
33 | final bool available = await InAppPurchase.instance.isAvailable(); | 47 | final bool available = await InAppPurchase.instance.isAvailable(); |
34 | if (!available) { | 48 | if (!available) { |
35 | - print("####### isAvailable false"); | ||
36 | return []; | 49 | return []; |
37 | } | 50 | } |
38 | - final ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails({ | 51 | + final ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails(_kIds); |
39 | - 'yearly_yiyan_vip', | ||
40 | - 'monthly_yiyan_vip', | ||
41 | - 'yearly-default', | ||
42 | - 'test.yiyan.vip.1.month', | ||
43 | - }); | ||
44 | - if (response.notFoundIDs.isNotEmpty) { | ||
45 | - // Handle the error. | ||
46 | - print("####### notFoundIDs"); | ||
47 | - } else { | ||
48 | - } | ||
49 | - print(response.productDetails.length); | ||
50 | return products = response.productDetails; | 52 | return products = response.productDetails; |
51 | } | 53 | } |
52 | 54 | ||
53 | - buy(ProductDetails details) { | 55 | + buy(ProductDetails details, MembershipDataGoodsList e) { |
54 | - // OrderApi.request.createOrder() | 56 | + OrderApi.request.createOrder(e.id.toString()).then((value) { |
55 | - final PurchaseParam purchaseParam = PurchaseParam(productDetails: details); | 57 | + var orderId = value?['data']?['data']?['order_sn']; |
56 | - if (_isConsumable(details)) { | 58 | + if (TextUtil.isEmpty(orderId)) { |
57 | - InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam); | 59 | + onFailed?.call(); |
58 | - } else { | 60 | + return; |
59 | - InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam); | 61 | + } |
60 | - } | 62 | + final PurchaseParam purchaseParam = PurchaseParam(productDetails: details, applicationUserName: orderId); |
63 | + if (_isConsumable(details)) { | ||
64 | + InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam); | ||
65 | + } else { | ||
66 | + InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam); | ||
67 | + } | ||
68 | + }); | ||
61 | } | 69 | } |
62 | 70 | ||
63 | void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) async { | 71 | void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) async { |
64 | for (var purchaseDetails in purchaseDetailsList) { | 72 | for (var purchaseDetails in purchaseDetailsList) { |
65 | if (purchaseDetails.status == PurchaseStatus.pending) { | 73 | if (purchaseDetails.status == PurchaseStatus.pending) { |
66 | - // _showPendingUI(); | 74 | + onPending?.call(); |
67 | } else { | 75 | } else { |
68 | if (purchaseDetails.status == PurchaseStatus.error) { | 76 | if (purchaseDetails.status == PurchaseStatus.error) { |
69 | - // _handleError(purchaseDetails.error!); | 77 | + _handleError(purchaseDetails.error!); |
70 | } else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) { | 78 | } else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) { |
71 | bool valid = await _verifyPurchase(purchaseDetails); | 79 | bool valid = await _verifyPurchase(purchaseDetails); |
72 | if (valid) { | 80 | if (valid) { |
... | @@ -74,6 +82,8 @@ class PaymentSdk { | ... | @@ -74,6 +82,8 @@ class PaymentSdk { |
74 | } else { | 82 | } else { |
75 | _handleInvalidPurchase(purchaseDetails); | 83 | _handleInvalidPurchase(purchaseDetails); |
76 | } | 84 | } |
85 | + } else { | ||
86 | + onCancel?.call(); | ||
77 | } | 87 | } |
78 | if (purchaseDetails.pendingCompletePurchase) { | 88 | if (purchaseDetails.pendingCompletePurchase) { |
79 | await InAppPurchase.instance.completePurchase(purchaseDetails); | 89 | await InAppPurchase.instance.completePurchase(purchaseDetails); |
... | @@ -86,15 +96,27 @@ class PaymentSdk { | ... | @@ -86,15 +96,27 @@ class PaymentSdk { |
86 | return true; | 96 | return true; |
87 | } | 97 | } |
88 | 98 | ||
89 | - void _deliverProduct(PurchaseDetails purchaseDetails) {} | 99 | + void _deliverProduct(PurchaseDetails purchaseDetails) { |
100 | + onPaySuccess?.call(); | ||
101 | + } | ||
90 | 102 | ||
91 | - void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {} | 103 | + void _handleInvalidPurchase(PurchaseDetails purchaseDetails) { |
104 | + onFailed?.call(); | ||
105 | + } | ||
106 | + | ||
107 | + void _handleError(IAPError iapError) { | ||
108 | + onFailed?.call(); | ||
109 | + } | ||
92 | 110 | ||
93 | bool _isConsumable(ProductDetails details) { | 111 | bool _isConsumable(ProductDetails details) { |
94 | return true; | 112 | return true; |
95 | } | 113 | } |
96 | 114 | ||
97 | void dispose() { | 115 | void dispose() { |
116 | + onPaySuccess = null; | ||
117 | + onPending = null; | ||
118 | + onFailed = null; | ||
119 | + onCancel = null; | ||
98 | _subscription?.cancel(); | 120 | _subscription?.cancel(); |
99 | } | 121 | } |
100 | } | 122 | } | ... | ... |
-
Please register or login to post a comment