Showing
6 changed files
with
132 additions
and
55 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']; |
| 58 | + if (TextUtil.isEmpty(orderId)) { | ||
| 59 | + onFailed?.call(); | ||
| 60 | + return; | ||
| 61 | + } | ||
| 62 | + final PurchaseParam purchaseParam = PurchaseParam(productDetails: details, applicationUserName: orderId); | ||
| 56 | if (_isConsumable(details)) { | 63 | if (_isConsumable(details)) { |
| 57 | InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam); | 64 | InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam); |
| 58 | } else { | 65 | } else { |
| 59 | InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam); | 66 | InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam); |
| 60 | } | 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 | + } | ||
| 102 | + | ||
| 103 | + void _handleInvalidPurchase(PurchaseDetails purchaseDetails) { | ||
| 104 | + onFailed?.call(); | ||
| 105 | + } | ||
| 90 | 106 | ||
| 91 | - void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {} | 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