Chad

完善支付接口

import 'dart:convert';
import 'package:Parlando/apis/api_response.dart';
import 'package:Parlando/net/dio_utils.dart';
import 'package:common_utils/common_utils.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
class BaseApi {
Future<Response<ApiResponse<T>>> post<T>(
Future<Response<T>> post<T>(
String path, {
data,
Map<String, dynamic>? queryParameters,
......@@ -13,7 +16,7 @@ class BaseApi {
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) {
return _getDio().post<ApiResponse<T>>(
return _getDio().post<T>(
path,
data: data,
queryParameters: queryParameters,
......
import 'dart:convert';
import 'package:Parlando/apis/api_base.dart';
import 'package:Parlando/apis/api_response.dart';
import 'package:common_utils/common_utils.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
......@@ -11,7 +15,13 @@ class OrderApi extends BaseApi {
return _instance;
}
Future<Response> createOrder(String productId) {
return post("/order");
Future<dynamic> createOrder(String productId) {
var data = {"goods_id": productId};
return post("order", data: data).then((value) {
if (TextUtil.isEmpty(value.data)) {
return {};
}
return json.decode(value.data);
});
}
}
......
......@@ -15,10 +15,7 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
Widget buildBody(BuildContext context);
void onFirstBuildBody(BuildContext context){
}
void onFirstBuildBody(BuildContext context) {}
showLoading({String text = 'Loading...'}) {
EasyLoading.show(status: text);
......@@ -27,4 +24,8 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
hideLoading() {
EasyLoading.dismiss();
}
toast(String text) {
EasyLoading.showToast(text);
}
}
......
......@@ -30,8 +30,7 @@ class MembershipPage extends StatefulWidget {
}
class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingObserver {
final List<Widget> _productWidgets = <Widget>[];
final List<ProductDetails> _products = <ProductDetails>[];
Function? connectionCallBack;
......@@ -40,11 +39,18 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
@override
void initState() {
super.initState();
PaymentSdk.instance.initState();
PaymentSdk.instance.initState(onPaySuccess: () {
hideLoading();
}, onCancel: () {
hideLoading();
}, onFailed: () {
hideLoading();
}, onPending: () {
toast("订单Pending");
});
PaymentSdk.instance.queryProducts().then((value) {
for (var element in value) {
_productWidgets.add(buildBuyItem(element));
}
_products.clear();
_products.addAll(value);
setState(() {});
});
if (SpUtil.containsKey(Constant.userToken)!) {
......@@ -76,8 +82,8 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
child: Column(
children: [
initBackBar(),
Spacer(),
// buildCompleteWidget(),
const Spacer(),
buildCompleteWidget(),
],
),
),
......@@ -112,20 +118,6 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
);
}
Widget buildBuyItem(ProductDetails element) {
var style = const TextStyle(color: Colors.white);
var button = Text("购买", style: style).paddingLeftRight(15).paddingTopBottom(8).click(() {
PaymentSdk.instance.buy(element);
}, color: Colors.blue).round(radius: 10);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(element.price, style: style),
button,
],
).paddingLeftRight(10).paddingTopBottom(5);
}
Widget initOtherEntrance() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
......@@ -165,7 +157,7 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
children: [
Text(mb?.title ?? "一言会员", style: TextStyle(fontSize: 18.px, color: Colors.white)),
Gaps.vGap24,
_productWidgets.isNotEmpty ? Column(children: _productWidgets) : const GFLoader(),
mb?.goodsList?.isNotEmpty == true ? buildProductWidget(mb) : const GFLoader(),
Gaps.vGap24,
Text(mb?.intro ?? "一言介绍", style: TextStyle(fontSize: 14.px, color: Colors.white)),
Gaps.vGap10,
......@@ -186,6 +178,55 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
);
}
buildProductWidget(MembershipData? mb) {
var widgets = mb?.goodsList?.map((e) => buildProductItem(e)).toList() ?? [];
return Column(children: widgets);
}
Widget buildProductItem(MembershipDataGoodsList e) {
ProductDetails? element;
for (var value in _products) {
if (value.id == e.membershipId || mapId(value.id, e.id)) {
element = value;
break;
}
}
return buildBuyItem(element, e);
}
Widget buildBuyItem(ProductDetails? element, MembershipDataGoodsList e) {
var style = const TextStyle(color: Colors.white);
var button = Text("购买", style: style).paddingLeftRight(15).paddingTopBottom(8).click(() {
showLoading();
if (element != null) {
PaymentSdk.instance.buy(element, e);
}
}, color: Colors.blue).round(radius: 10);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(buildBuyPrice(element, e), style: style),
button,
],
).paddingLeftRight(10).paddingTopBottom(5);
}
String buildBuyPrice(ProductDetails? element, MembershipDataGoodsList e) {
if (element == null) {
return "${e.price}/${e.name}";
}
return "${element.price}/${e.name}";
}
bool mapId(String? id, int? membershipId) {
bool year = id == 'yearly_yiyan_vip' && membershipId == 4;
bool month = id == 'monthly_yiyan_vip' && membershipId == 3;
if (year || month) {
print("$id ---- $membershipId");
}
return year || month;
}
@override
void dispose() {
PaymentSdk.instance.dispose();
......
......@@ -122,7 +122,7 @@ class LoggingInterceptor extends Interceptor {
Log.e('ResponseCode: ${response.statusCode}');
}
// 输出结果
// Log.json(response.data.toString());
Log.json(response.data.toString());
Log.d('----------End: $duration 毫秒----------');
super.onResponse(response, handler);
}
......
import 'dart:async';
import 'package:Parlando/apis/api_order.dart';
import 'package:flutter/material.dart';
import 'package:Parlando/membership/models/membership_entity.dart';
import 'package:common_utils/common_utils.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
class PaymentSdk {
......@@ -13,12 +14,25 @@ class PaymentSdk {
return _instance;
}
static const Set<String> _kIds = <String>{'yearly_yiyan_vip', 'monthly_yiyan_vip', 'yearly-default'};
static const Set<String> _kIds = <String>{'yearly_yiyan_vip', 'monthly_yiyan_vip'};
StreamSubscription<List<PurchaseDetails>>? _subscription;
List<ProductDetails> products = [];
Function? onPaySuccess;
Function? onPending;
Function? onFailed;
Function? onCancel;
initState() {
initState({
Function? onPaySuccess,
Function? onPending,
Function? onFailed,
Function? onCancel,
}) {
this.onPaySuccess = onPaySuccess;
this.onPending = onPending;
this.onFailed = onFailed;
this.onCancel = onCancel;
final Stream<List<PurchaseDetails>> purchaseUpdated = InAppPurchase.instance.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
_listenToPurchaseUpdated(purchaseDetailsList);
......@@ -32,41 +46,35 @@ class PaymentSdk {
Future<List<ProductDetails>> queryProducts() async {
final bool available = await InAppPurchase.instance.isAvailable();
if (!available) {
print("####### isAvailable false");
return [];
}
final ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails({
'yearly_yiyan_vip',
'monthly_yiyan_vip',
'yearly-default',
'test.yiyan.vip.1.month',
});
if (response.notFoundIDs.isNotEmpty) {
// Handle the error.
print("####### notFoundIDs");
} else {
}
print(response.productDetails.length);
final ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails(_kIds);
return products = response.productDetails;
}
buy(ProductDetails details) {
// OrderApi.request.createOrder()
final PurchaseParam purchaseParam = PurchaseParam(productDetails: details);
if (_isConsumable(details)) {
InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
} else {
InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
}
buy(ProductDetails details, MembershipDataGoodsList e) {
OrderApi.request.createOrder(e.id.toString()).then((value) {
var orderId = value?['data']?['data']?['order_sn'];
if (TextUtil.isEmpty(orderId)) {
onFailed?.call();
return;
}
final PurchaseParam purchaseParam = PurchaseParam(productDetails: details, applicationUserName: orderId);
if (_isConsumable(details)) {
InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
} else {
InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
}
});
}
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) async {
for (var purchaseDetails in purchaseDetailsList) {
if (purchaseDetails.status == PurchaseStatus.pending) {
// _showPendingUI();
onPending?.call();
} else {
if (purchaseDetails.status == PurchaseStatus.error) {
// _handleError(purchaseDetails.error!);
_handleError(purchaseDetails.error!);
} else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) {
bool valid = await _verifyPurchase(purchaseDetails);
if (valid) {
......@@ -74,6 +82,8 @@ class PaymentSdk {
} else {
_handleInvalidPurchase(purchaseDetails);
}
} else {
onCancel?.call();
}
if (purchaseDetails.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchaseDetails);
......@@ -86,15 +96,27 @@ class PaymentSdk {
return true;
}
void _deliverProduct(PurchaseDetails purchaseDetails) {}
void _deliverProduct(PurchaseDetails purchaseDetails) {
onPaySuccess?.call();
}
void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {}
void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {
onFailed?.call();
}
void _handleError(IAPError iapError) {
onFailed?.call();
}
bool _isConsumable(ProductDetails details) {
return true;
}
void dispose() {
onPaySuccess = null;
onPending = null;
onFailed = null;
onCancel = null;
_subscription?.cancel();
}
}
......