Chad

完善支付接口

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 }
......