Destiny 1 год назад
Родитель
Сommit
ab40da8a8d
36 измененных файлов с 1385 добавлено и 175 удалено
  1. BIN
      assets/images/icon_store_free.webp
  2. 141 0
      lib/base/base_request.dart
  3. 21 0
      lib/base/base_response.dart
  4. 34 0
      lib/data/api/atmob_api.dart
  5. 21 0
      lib/data/api/request/order_pay_request.dart
  6. 18 0
      lib/data/api/request/order_status_request.dart
  7. 47 0
      lib/data/api/response/order_pay_response.dart
  8. 14 0
      lib/data/api/response/order_status_response.dart
  9. 20 0
      lib/data/api/response/store_index_response.dart
  10. 23 0
      lib/data/api/response/user_info_response.dart
  11. 17 0
      lib/data/bean/member_info.dart
  12. 20 0
      lib/data/bean/payment_way.dart
  13. 55 0
      lib/data/bean/store_item.dart
  14. 50 3
      lib/data/consts/constants.dart
  15. 33 0
      lib/data/repositories/store_repository.dart
  16. 168 0
      lib/device/atmob_platform_info.dart
  17. 12 0
      lib/device/device_info_util.dart
  18. 32 0
      lib/device/platform_ios_info.dart
  19. 16 0
      lib/main.dart
  20. 0 0
      lib/module/store/payment_status_manager.dart
  21. 42 0
      lib/module/store/store_controller.dart
  22. 147 169
      lib/module/store/store_view.dart
  23. 35 0
      lib/sdk/pay/agile_pay.dart
  24. 7 0
      lib/sdk/pay/applepay/apple_pay.dart
  25. 5 0
      lib/sdk/pay/applepay/apple_pay_info.dart
  26. 33 0
      lib/sdk/pay/assist/agile_pay_state_info.dart
  27. 149 0
      lib/sdk/pay/assist/apple_or_google_pay.dart
  28. 17 0
      lib/sdk/pay/assist/apple_or_google_pay_info.dart
  29. 4 0
      lib/sdk/pay/assist/product_type.dart
  30. 52 0
      lib/sdk/pay/code/agile_pay_code.dart
  31. 50 0
      lib/sdk/pay/listener/agile_pay_state.dart
  32. 7 0
      lib/sdk/pay/listener/i_agile_pay.dart
  33. 21 0
      lib/utils/app_info_util.dart
  34. 14 0
      lib/utils/expand.dart
  35. 51 3
      pubspec.lock
  36. 9 0
      pubspec.yaml

BIN
assets/images/icon_store_free.webp


+ 141 - 0
lib/base/base_request.dart

@@ -0,0 +1,141 @@
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+import '../device/atmob_platform_info.dart';
+import '../utils/app_info_util.dart';
+
+part 'base_request.g.dart';
+
+@JsonSerializable()
+class BaseRequest {
+  /// 平台类型: 1-Android 2-iOS 3-移动H5 4-PC_WEB 5-微信小程序 6-微信小游戏
+  /// 7-微信公众号 8-抖音小程序 9-抖音小游戏 10-鸿蒙APP
+  @JsonKey(name: "appPlatform")
+  late int appPlatform = 0;
+
+  /// 操作系统:android、ios、mac、windows、linux、harmony
+  @JsonKey(name: "os")
+  late String os = "unknown";
+  @JsonKey(name: "osVersion")
+  late String osVersion;
+
+  /// 包信息
+  @JsonKey(name: "packageName")
+  String? packageName;
+  @JsonKey(name: "appVersionName")
+  String? appVersionName;
+  @JsonKey(name: "appVersionCode")
+  int? appVersionCode;
+
+  /// 渠道信息, iOS没有渠道信息, Android通过渠道包工具获取
+  @JsonKey(name: "channelName")
+  String? channelName;
+  @JsonKey(name: "appId")
+  int? appId;
+  @JsonKey(name: "tgPlatform")
+  int? tgPlatform;
+
+  /// 设备信息
+  /// Android特有
+  @JsonKey(name: "oaid")
+  String? oaid;
+  @JsonKey(name: "aaid")
+  String? aaid;
+  @JsonKey(name: "androidId")
+  String? androidId;
+  @JsonKey(name: "imei")
+  String? imei;
+  @JsonKey(name: "simImei0")
+  String? simImei0;
+  @JsonKey(name: "simImei1")
+  String? simImei1;
+  @JsonKey(name: "mac")
+  String? mac;
+
+  /// iOS特有
+  @JsonKey(name: "idfa")
+  String? idfa;
+  @JsonKey(name: "idfv")
+  String? idfv;
+
+  /// 公共设备信息
+  @JsonKey(name: "machineId")
+  String? machineId; //给web使用
+  @JsonKey(name: "brand")
+  String? brand;
+  @JsonKey(name: "model")
+  String? model;
+  @JsonKey(name: "wifiName")
+  String? wifiName;
+
+  /// 地理位置信息
+  @JsonKey(name: "region")
+  String? region;
+  @JsonKey(name: "locLng")
+  double? locLng;
+  @JsonKey(name: "locLat")
+  double? locLat;
+
+  BaseRequest() {
+    initPlatformOS();
+    initPackageInfo();
+    initChannelInfo();
+    initDeviceInfo();
+  }
+
+  Map<String, dynamic> toJson() => _$BaseRequestToJson(this);
+
+  void initPlatformOS() {
+    if (Platform.isAndroid) {
+      appPlatform = 1;
+      os = "android";
+    } else if (Platform.isIOS) {
+      appPlatform = 2;
+      os = "ios";
+    } else if (Platform.isMacOS) {
+      os = "mac";
+    } else if (Platform.isWindows) {
+      os = "windows";
+    } else if (Platform.isLinux) {
+      os = "linux";
+    } else if (Platform.isFuchsia) {
+      os = "fuchsia";
+    }
+    osVersion = Platform.operatingSystemVersion;
+
+    debugPrint("os: $os, osVersion: $osVersion");
+  }
+
+  void initPackageInfo() {
+    packageName = appInfoUtil.packageName;
+    appVersionName = appInfoUtil.appVersionName;
+    appVersionCode = appInfoUtil.appVersionCode;
+  }
+
+  void initChannelInfo() {
+    channelName = atmobPlatformInfo.channelName;
+    appId = atmobPlatformInfo.appId;
+    tgPlatform = atmobPlatformInfo.tgPlatform;
+  }
+
+  void initDeviceInfo() {
+    oaid = atmobPlatformInfo.oaid;
+    aaid = atmobPlatformInfo.aaid;
+    androidId = atmobPlatformInfo.androidId;
+    imei = atmobPlatformInfo.imei;
+    simImei0 = atmobPlatformInfo.simImei0;
+    simImei1 = atmobPlatformInfo.simImei1;
+    mac = atmobPlatformInfo.mac;
+    idfa = atmobPlatformInfo.idfa;
+    idfv = atmobPlatformInfo.idfv;
+    machineId = atmobPlatformInfo.machineId;
+    brand = atmobPlatformInfo.brand;
+    model = atmobPlatformInfo.model;
+    wifiName = atmobPlatformInfo.wifiName;
+    region = atmobPlatformInfo.region;
+    locLng = atmobPlatformInfo.locLng;
+    locLat = atmobPlatformInfo.locLat;
+  }
+}

+ 21 - 0
lib/base/base_response.dart

@@ -0,0 +1,21 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'base_response.g.dart';
+
+@JsonSerializable(genericArgumentFactories: true)
+class BaseResponse<T> {
+  @JsonKey(name: 'data')
+  T? data;
+
+  @JsonKey(name: 'code')
+  int? code;
+
+  @JsonKey(name: 'msg')
+  String? message;
+
+  BaseResponse(this.data, this.code, this.message);
+
+  factory BaseResponse.fromJson(
+      Map<String, dynamic> json, T Function(Object? json) fromJsonT) =>
+      _$BaseResponseFromJson(json, fromJsonT);
+}

+ 34 - 0
lib/data/api/atmob_api.dart

@@ -0,0 +1,34 @@
+import 'package:clean/data/api/request/order_pay_request.dart';
+import 'package:clean/data/api/request/order_status_request.dart';
+import 'package:clean/data/api/response/order_pay_response.dart';
+import 'package:clean/data/api/response/order_status_response.dart';
+import 'package:clean/data/api/response/store_index_response.dart';
+import 'package:clean/data/api/response/user_info_response.dart';
+import 'package:dio/dio.dart';
+import 'package:retrofit/http.dart';
+
+import '../../base/base_request.dart';
+import '../../base/base_response.dart';
+
+part 'atmob_api.g.dart';
+
+@RestApi()
+abstract class AtmobApi {
+  factory AtmobApi(Dio dio, {String baseUrl}) = _AtmobApi;
+
+  @POST("/project/clean/v1/user/info")
+  Future<BaseResponse<UserInfoResponse>> userInfo(
+      @Body() BaseRequest request);
+
+  @POST("/project/clean/v1/item/list")
+  Future<BaseResponse<StoreIndexResponse>> storeIndex(
+      @Body() BaseRequest request);
+
+  @POST("/project/clean/v1/order/pay")
+  Future<BaseResponse<OrderPayResponse>> orderPay(
+      @Body() OrderPayRequest request);
+
+  @POST("/project/clean/v1/order/status")
+  Future<BaseResponse<OrderStatusResponse>> orderStatus(
+      @Body() OrderStatusRequest request);
+}

+ 21 - 0
lib/data/api/request/order_pay_request.dart

@@ -0,0 +1,21 @@
+import 'package:clean/base/base_request.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+part 'order_pay_request.g.dart';
+
+@JsonSerializable()
+class OrderPayRequest extends BaseRequest {
+  @JsonKey(name: 'itemId')
+  int itemId;
+
+  @JsonKey(name: 'payPlatform')
+  int payPlatform;
+
+  @JsonKey(name: 'payMethod')
+  int payMethod;
+
+  OrderPayRequest(this.itemId, this.payPlatform, this.payMethod);
+
+  @override
+  Map<String, dynamic> toJson() => _$OrderPayRequestToJson(this);
+}

+ 18 - 0
lib/data/api/request/order_status_request.dart

@@ -0,0 +1,18 @@
+import 'package:clean/base/base_request.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+part 'order_status_request.g.dart';
+
+@JsonSerializable()
+class OrderStatusRequest extends BaseRequest {
+  @JsonKey(name: 'outTradeNo')
+  String outTradeNo;
+
+  @JsonKey(name: 'receiptData')
+  String? receiptData;
+
+  OrderStatusRequest(this.outTradeNo, this.receiptData);
+
+  @override
+  Map<String, dynamic> toJson() => _$OrderStatusRequestToJson(this);
+}

+ 47 - 0
lib/data/api/response/order_pay_response.dart

@@ -0,0 +1,47 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'order_pay_response.g.dart';
+
+@JsonSerializable()
+class OrderPayResponse {
+  @JsonKey(name: 'outTradeNo')
+  String outTradeNo;
+
+  @JsonKey(name: 'wechatPayQrcodeUrl')
+  String? wechatPayQrcodeUrl;
+
+  @JsonKey(name: 'wechatPayRedirectUrl')
+  String? wechatPayRedirectUrl;
+
+  @JsonKey(name: 'wechatPayPrepayJson')
+  String? wechatPayPrepayJson;
+
+  @JsonKey(name: 'alipayQrcodeHtml')
+  String? alipayQrcodeHtml;
+
+  @JsonKey(name: 'aliPaySubmitHtml')
+  String? aliPaySubmitHtml;
+
+  @JsonKey(name: 'alipayOrderString')
+  String? alipayOrderString;
+
+  @JsonKey(name: 'appAccountToken')
+  String? appAccountToken;
+
+  @JsonKey(name: 'wechatPayH5Url')
+  String? wechatPayH5Url;
+
+  OrderPayResponse(
+      this.outTradeNo,
+      this.wechatPayQrcodeUrl,
+      this.wechatPayRedirectUrl,
+      this.wechatPayPrepayJson,
+      this.alipayQrcodeHtml,
+      this.aliPaySubmitHtml,
+      this.alipayOrderString,
+      this.appAccountToken,
+      this.wechatPayH5Url);
+
+  factory OrderPayResponse.fromJson(Map<String, dynamic> json) =>
+      _$OrderPayResponseFromJson(json);
+}

+ 14 - 0
lib/data/api/response/order_status_response.dart

@@ -0,0 +1,14 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'order_status_response.g.dart';
+
+@JsonSerializable()
+class OrderStatusResponse {
+  @JsonKey(name: 'payStatus')
+  int payStatus;
+
+  OrderStatusResponse(this.payStatus);
+
+  factory OrderStatusResponse.fromJson(Map<String, dynamic> json) =>
+      _$OrderStatusResponseFromJson(json);
+}

+ 20 - 0
lib/data/api/response/store_index_response.dart

@@ -0,0 +1,20 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/payment_way.dart';
+import '../../bean/store_item.dart';
+
+part 'store_index_response.g.dart';
+
+@JsonSerializable()
+class StoreIndexResponse {
+  @JsonKey(name: 'list')
+  List<StoreItem> items = [];
+
+  @JsonKey(name: "payOptions")
+  List<PaymentWay> paymentWays = [];
+
+  StoreIndexResponse(this.items, this.paymentWays);
+
+  factory StoreIndexResponse.fromJson(Map<String, dynamic> json) =>
+      _$StoreIndexResponseFromJson(json);
+}

+ 23 - 0
lib/data/api/response/user_info_response.dart

@@ -0,0 +1,23 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/member_info.dart';
+
+part 'user_info_response.g.dart';
+
+@JsonSerializable()
+class UserInfoResponse {
+  @JsonKey(name: 'ssid')
+  String ssid;
+
+  @JsonKey(name: 'deviceId')
+  String? deviceId;
+
+  @JsonKey(name: 'memberInfo')
+  MemberInfo? memberInfo;
+
+  UserInfoResponse(
+      this.ssid, this.deviceId, this.memberInfo);
+
+  factory UserInfoResponse.fromJson(Map<String, dynamic> json) =>
+      _$UserInfoResponseFromJson(json);
+}

+ 17 - 0
lib/data/bean/member_info.dart

@@ -0,0 +1,17 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'member_info.g.dart';
+
+@JsonSerializable()
+class MemberInfo {
+  @JsonKey(name: 'isMember')
+  bool isMember;
+
+  @JsonKey(name: 'endTimestamp')
+  int endTimestamp;
+
+  MemberInfo(this.isMember, this.endTimestamp);
+
+  factory MemberInfo.fromJson(Map<String, dynamic> json) =>
+      _$MemberInfoFromJson(json);
+}

+ 20 - 0
lib/data/bean/payment_way.dart

@@ -0,0 +1,20 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'payment_way.g.dart';
+
+@JsonSerializable()
+class PaymentWay {
+  @JsonKey(name: 'id')
+  late int id;
+  @JsonKey(name: 'payMethod')
+  late int payMethod;
+  @JsonKey(name: 'payPlatform')
+  late int payPlatform;
+  @JsonKey(name: 'title')
+  late String title;
+
+  PaymentWay(this.id, this.payMethod, this.payPlatform, this.title);
+
+  factory PaymentWay.fromJson(Map<String, dynamic> json) =>
+      _$PaymentWayFromJson(json);
+}

+ 55 - 0
lib/data/bean/store_item.dart

@@ -0,0 +1,55 @@
+
+
+import 'package:clean/utils/expand.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+part 'store_item.g.dart';
+
+@JsonSerializable()
+class StoreItem {
+  @JsonKey(name: "id")
+  late int id;
+  @JsonKey(name: "sort")
+  late int sort;
+  @JsonKey(name: "name")
+  late String name;
+  @JsonKey(name: "appleGoodsId")
+  late String appleGoodsId;
+  @JsonKey(name: "subscribable")
+  late int subscribable;
+  @JsonKey(name: "amount")
+  late int amount;
+  @JsonKey(name: "originalAmount")
+  late int originalAmount;
+  @JsonKey(name: "auth")
+  late String auth;
+  @JsonKey(name: "subscriptionMillis")
+  late int subscriptionMillis;
+  @JsonKey(name: "content")
+  late String content;
+  @JsonKey(name: "priceDesc")
+  late String priceDesc;
+  @JsonKey(name: "coefficient")
+  late int coefficient;
+
+  StoreItem(
+      {required this.id,
+        required this.sort,
+        required this.name,
+        required this.appleGoodsId,
+        required this.subscribable,
+        required this.amount,
+        required this.originalAmount,
+        required this.auth,
+        required this.subscriptionMillis,
+        required this.content,
+        required this.priceDesc,
+        required this.coefficient});
+
+  get amountText => (amount / 100).toFormattedString(2);
+
+  get originalAmountText => (originalAmount / 100).toFormattedString(2);
+
+  factory StoreItem.fromJson(Map<String, dynamic> json) =>
+      _$StoreItemFromJson(json);
+}

+ 50 - 3
lib/data/consts/constants.dart

@@ -1,13 +1,61 @@
-import 'package:clean/utils/mmkv_util.dart';
-
 class Constants {
   Constants._();
 
+  static const String env = envProd;
+
+  static const String envDev = 'dev';
+
+  static const String envTest = 'test';
+
+  static const String envProd = 'prod';
+
+  static const String _devBaseUrl = "http://192.168.10.230:8880";
+
+  static const String _testBaseUrl = "http://42.193.245.11";
+
+  static const String _prodBaseUrl = "https://project-api.atmob.com";
+
+  static String baseUrl = getBaseUrl();
+
+  static const String appDefaultChannel = "Android";
+  static const int appDefaultAppId = 0;
+  static const int appDefaultTgPlatformId = 0;
+
+  //渠道
+  static const String appChanelName = "app_channel_name";
+  static const String appChannelId = "app_channel_id";
+  static const String appTgPlatformId = "app_tg_platform_id";
 
   static const String firstOpenPhotoPreview = 'firstOpenPhotoPreview';
 
+}
+
+String getBaseUrl() {
+  switch (Constants.env) {
+    case Constants.envDev:
+      return Constants._devBaseUrl;
+    case Constants.envTest:
+      return Constants._testBaseUrl;
+    case Constants.envProd:
+      return Constants._prodBaseUrl;
+    default:
+      return Constants._devBaseUrl;
+  }
+}
+
+class PayPlatform {
+  static const int android = 1;
+  static const int apple = 2;
+}
 
+class PayMethod {
+  static const int alipay = 1;
+  static const int wechat = 2;
+  static const int apple = 3;
+  static const int google = 4;
+  static const int douYin = 5;
 }
+
 bool isFirstOpenPhotoPreview() {
   return KVUtil.getBool(Constants.firstOpenPhotoPreview, true);
 }
@@ -15,4 +63,3 @@ void setFirstOpenPhotoPreview(bool firstTime) {
   KVUtil.putBool(Constants.firstOpenPhotoPreview, firstTime);
 }
 
-

+ 33 - 0
lib/data/repositories/store_repository.dart

@@ -0,0 +1,33 @@
+
+
+class StoreRepository {
+  StoreRepository._();
+
+  factory StoreRepository() {
+    return storeRepository;
+  }
+
+  // Future<StoreIndexResponse> storeIndex() {
+  //   return atmobApi
+  //       .storeIndex(AppBaseRequest())
+  //       .then(HttpHandler.handle(false));
+  // }
+  //
+  // Future<OrderPayResponse> orderPay(
+  //     int itemId, int payPlatform, int payMethod) {
+  //   return atmobApi
+  //       .orderPay(OrderPayRequest(itemId, payPlatform, payMethod))
+  //       .then(HttpHandler.handle(false));
+  // }
+  //
+  // Future<int> orderStatus(String outTradeNo, {String? receiptData}) {
+  //   return atmobApi
+  //       .orderStatus(OrderStatusRequest(outTradeNo, receiptData))
+  //       .then(HttpHandler.handle(false))
+  //       .then((data) {
+  //     return data.payStatus;
+  //   });
+  // }
+}
+
+final StoreRepository storeRepository = StoreRepository._();

+ 168 - 0
lib/device/atmob_platform_info.dart

@@ -0,0 +1,168 @@
+class AtmobPlatformInfo {
+  AtmobPlatformInfo();
+
+  /// 设备信息
+  /// Android特有
+  String? _oaid;
+  String? _aaid;
+  String? _androidId;
+  String? _imei;
+  String? _simImei0;
+  String? _simImei1;
+  String? _mac;
+
+  //渠道信息
+  String? _channelName;
+  int? _appId;
+  int? _tgPlatform;
+
+  /// iOS特有
+  String? _idfa;
+  String? _idfv;
+
+  /// 公共设备信息
+  String? _machineId; //给web使用
+  String? _brand;
+  String? _model;
+  String? _wifiName;
+
+  /// 地理位置信息
+  String? _region;
+  double? _locLng;
+  double? _locLat;
+
+  AtmobPlatformInfo setChannelName(String? channelName) {
+    _channelName = channelName;
+    return this;
+  }
+
+  AtmobPlatformInfo setTgPlatform(int? tgPlatform) {
+    _tgPlatform = tgPlatform;
+    return this;
+  }
+
+  AtmobPlatformInfo setAppId(int? appId) {
+    _appId = appId;
+    return this;
+  }
+
+  AtmobPlatformInfo setOaid(String? oaid) {
+    _oaid = oaid;
+    return this;
+  }
+
+  AtmobPlatformInfo setAaid(String? aaid) {
+    _aaid = aaid;
+    return this;
+  }
+
+  AtmobPlatformInfo setAndroidId(String? androidId) {
+    _androidId = androidId;
+    return this;
+  }
+
+  AtmobPlatformInfo setImei(String? imei) {
+    _imei = imei;
+    return this;
+  }
+
+  AtmobPlatformInfo setSimImei0(String? simImei0) {
+    _simImei0 = simImei0;
+    return this;
+  }
+
+  AtmobPlatformInfo setSimImei1(String? simImei1) {
+    _simImei1 = simImei1;
+    return this;
+  }
+
+  AtmobPlatformInfo setMac(String? mac) {
+    _mac = mac;
+    return this;
+  }
+
+  AtmobPlatformInfo setIdfa(String? idfa) {
+    _idfa = idfa;
+    return this;
+  }
+
+  AtmobPlatformInfo setIdfv(String? idfv) {
+    _idfv = idfv;
+    return this;
+  }
+
+  AtmobPlatformInfo setMachineId(String? machineId) {
+    _machineId = machineId;
+    return this;
+  }
+
+  AtmobPlatformInfo setBrand(String? brand) {
+    _brand = brand;
+    return this;
+  }
+
+  AtmobPlatformInfo setModel(String? model) {
+    _model = model;
+    return this;
+  }
+
+  AtmobPlatformInfo setWifiName(String? wifiName) {
+    _wifiName = wifiName;
+    return this;
+  }
+
+  AtmobPlatformInfo setRegion(String? region) {
+    _region = region;
+    return this;
+  }
+
+  AtmobPlatformInfo setLocLng(double? locLng) {
+    _locLng = locLng;
+    return this;
+  }
+
+  AtmobPlatformInfo setLocLat(double? locLat) {
+    _locLat = locLat;
+    return this;
+  }
+
+  double? get locLat => _locLat;
+
+  double? get locLng => _locLng;
+
+  String? get region => _region;
+
+  String? get wifiName => _wifiName;
+
+  String? get model => _model;
+
+  String? get brand => _brand;
+
+  String? get machineId => _machineId;
+
+  String? get idfv => _idfv;
+
+  String? get idfa => _idfa;
+
+  String? get mac => _mac;
+
+  String? get simImei1 => _simImei1;
+
+  String? get simImei0 => _simImei0;
+
+  String? get imei => _imei;
+
+  String? get androidId => _androidId;
+
+  String? get aaid => _aaid;
+
+  String? get oaid => _oaid;
+
+  String? get channelName => _channelName;
+
+  int? get tgPlatform => _tgPlatform;
+
+  int? get appId => _appId;
+}
+
+final AtmobPlatformInfo atmobPlatformInfo = AtmobPlatformInfo();

+ 12 - 0
lib/device/device_info_util.dart

@@ -0,0 +1,12 @@
+import 'package:clean/device/platform_ios_info.dart';
+
+class DeviceInfoUtil {
+  DeviceInfoUtil._();
+
+  init() async {
+    // await PlatformAndroidInfo.init();
+    await PlatformIosInfo.init();
+  }
+}
+
+final deviceInfoUtil = DeviceInfoUtil._();

+ 32 - 0
lib/device/platform_ios_info.dart

@@ -0,0 +1,32 @@
+import 'dart:io';
+
+import 'package:app_tracking_transparency/app_tracking_transparency.dart';
+import 'package:device_info_plus/device_info_plus.dart';
+
+import 'atmob_platform_info.dart';
+
+class PlatformIosInfo {
+  static Future<void> init() async {
+    if (Platform.isIOS) {
+      DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
+      IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
+      atmobPlatformInfo
+          .setModel(iosInfo.model)
+          .setIdfv(iosInfo.identifierForVendor);
+
+      final TrackingStatus status =
+      await AppTrackingTransparency.trackingAuthorizationStatus;
+      if (status == TrackingStatus.notDetermined) {
+        final TrackingStatus newStatus =
+        await AppTrackingTransparency.requestTrackingAuthorization();
+        if (newStatus == TrackingStatus.authorized) {
+          atmobPlatformInfo.setIdfa(
+              await AppTrackingTransparency.getAdvertisingIdentifier());
+        }
+      } else if (status == TrackingStatus.authorized) {
+        atmobPlatformInfo
+            .setIdfa(await AppTrackingTransparency.getAdvertisingIdentifier());
+      }
+    }
+  }
+}

+ 16 - 0
lib/main.dart

@@ -2,6 +2,7 @@ import 'package:clean/resource/colors.gen.dart';
 import 'package:clean/resource/string.gen.dart';
 import 'package:clean/resource/string_source.dart';
 import 'package:clean/router/app_pages.dart';
+import 'package:clean/utils/app_info_util.dart';
 import 'package:clean/utils/mmkv_util.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_localizations/flutter_localizations.dart';
@@ -10,13 +11,28 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:get/get_navigation/src/root/get_material_app.dart';
 import 'package:pull_to_refresh/pull_to_refresh.dart';
 
+import 'device/device_info_util.dart';
+
 Future<void> main() async {
 
   await KVUtil.init();
 
+  //初始化
+  await initAfterGrant();
+
   runApp(const MyApp());
 }
 
+Future<void> initAfterGrant() async {
+  // if (!isAgreePrivacyPolicy()) {
+  //   return;
+  // }
+  //获取包信息
+  await appInfoUtil.init();
+  //获取设备信息
+  await deviceInfoUtil.init();
+}
+
 class MyApp extends StatelessWidget {
   const MyApp({super.key});
 

+ 0 - 0
lib/module/store/payment_status_manager.dart


+ 42 - 0
lib/module/store/store_controller.dart

@@ -1,6 +1,48 @@
+import 'package:get/get_rx/src/rx_types/rx_types.dart';
+
 import '../../base/base_controller.dart';
+import '../../data/bean/payment_way.dart';
+import '../../data/bean/store_item.dart';
+import '../../utils/toast_util.dart';
 
 class StoreController extends BaseController {
 
+  final RxList<StoreItem> storeItems = <StoreItem>[].obs;
+
+  final RxList<PaymentWay> paymentWays = <PaymentWay>[].obs;
+
+  final Rxn<StoreItem> currentSelectedStoreItem = Rxn<StoreItem>();
+
+  final Rxn<PaymentWay> currentSelectedPaymentWay = Rxn<PaymentWay>();
+
+  @override
+  void onInit() {
+    // TODO: implement onInit
+    StoreItem item1 = StoreItem(id: 1, sort: 1, name: "11111", appleGoodsId: "1111", subscribable: 1, amount: 100, originalAmount: 100, auth: "auth", subscriptionMillis: 1, content: "content", priceDesc: "priceDesc", coefficient: 1);
+    StoreItem item2 = StoreItem(id: 2, sort: 1, name: "11111", appleGoodsId: "1111", subscribable: 1, amount: 100, originalAmount: 100, auth: "auth", subscriptionMillis: 1, content: "content", priceDesc: "priceDesc", coefficient: 1);
+    StoreItem item3 = StoreItem(id: 3, sort: 1, name: "11111", appleGoodsId: "1111", subscribable: 1, amount: 100, originalAmount: 100, auth: "auth", subscriptionMillis: 1, content: "content", priceDesc: "priceDesc", coefficient: 1);
+
+    storeItems.add(item1);
+    storeItems.add(item2);
+    storeItems.add(item3);
+
+    currentSelectedStoreItem.value = item1;
+  }
+
+  void onBuyClick() async {
 
+    StoreItem? storeItem = currentSelectedStoreItem.value;
+    if (storeItem == null) {
+      // ToastUtil.showToast(StringName.storeChoiceGoods.tr);
+      return;
+    }
+    PaymentWay? paymentWay = currentSelectedPaymentWay.value;
+    if (paymentWay == null) {
+      // ToastUtil.showToast(StringName.storeChoicePayment.tr);
+      return;
+    }
+    int payPlatform = paymentWay.payPlatform;
+    int payMethod = paymentWay.payMethod;
+    LoadingDialog.show(StringName.storePayLoading.tr);
+  }
 }

+ 147 - 169
lib/module/store/store_view.dart

@@ -1,4 +1,5 @@
 import 'package:clean/base/base_page.dart';
+import 'package:clean/data/bean/store_item.dart';
 import 'package:clean/module/store/store_controller.dart';
 import 'package:clean/utils/expand.dart';
 import 'package:flutter/Material.dart';
@@ -150,18 +151,17 @@ class StorePage extends BasePage<StoreController> {
                 ),
                 SizedBox(height: 14.h),
                 Expanded(
-                  child: ListView.builder(
-                    itemCount: 3,
-                    itemBuilder: (context, index) {
-                      return _buildSubscriptionOption(
-                        title: '3-DAY FREE TRIAL',
-                        price: '\$6.99/week',
-                        perDay: 'only \$0.64/day',
-                        isSelected: true,
-                        showTag: true,
-                      );
-                    },
-                  ),
+                  child: Obx(() {
+                    return ListView.builder(
+                      itemCount: controller.storeItems.length,
+                      itemBuilder: (context, index) {
+                        var item = controller.storeItems[index];
+                        return _buildSubscriptionOption(
+                          item: item,
+                        );
+                      },
+                    );
+                  }),
                 ),
                 Center(
                   child: Text(
@@ -200,7 +200,9 @@ class StorePage extends BasePage<StoreController> {
                   ),
                 ),
                 // // 底部链接
-                SizedBox(height: 8.h,),
+                SizedBox(
+                  height: 8.h,
+                ),
                 Row(
                   mainAxisAlignment: MainAxisAlignment.center,
                   children: [
@@ -233,91 +235,6 @@ class StorePage extends BasePage<StoreController> {
                     ),
                   ],
                 ),
-                //
-                // // 订阅选项
-                // _buildSubscriptionOption(
-                //   title: '3-DAY FREE TRIAL',
-                //   price: '\$6.99/week',
-                //   perDay: 'only \$0.64/day',
-                //   isSelected: true,
-                //   showTag: true,
-                // ),
-                //
-                // _buildSubscriptionOption(
-                //   title: '1 Month',
-                //   price: '\$19.99',
-                //   perDay: 'only \$0.64/day',
-                // ),
-                //
-                // _buildSubscriptionOption(
-                //   title: '1 Year',
-                //   price: '\$69.99',
-                //   perDay: 'only \$0.19/day',
-                // ),
-                //
-                // // 底部文本
-                // Padding(
-                //   padding: EdgeInsets.symmetric(vertical: 16.h),
-                //   child: Center(
-                //     child: Text(
-                //       'Auto-renewable.Cancel anytime.',
-                //       style: TextStyle(
-                //         color: Colors.white54,
-                //         fontSize: 12.sp,
-                //       ),
-                //     ),
-                //   ),
-                // ),
-                //
-                // // 继续按钮
-                // Padding(
-                //   padding: EdgeInsets.all(20.w),
-                //   child: ElevatedButton(
-                //     style: ElevatedButton.styleFrom(
-                //       backgroundColor: Colors.blue,
-                //       minimumSize: Size(double.infinity, 50.h),
-                //       shape: RoundedRectangleBorder(
-                //         borderRadius: BorderRadius.circular(25.r),
-                //       ),
-                //     ),
-                //     child: Text(
-                //       'CONTINUE',
-                //       style: TextStyle(
-                //         fontSize: 16.sp,
-                //         fontWeight: FontWeight.bold,
-                //       ),
-                //     ),
-                //     onPressed: () {},
-                //   ),
-                // ),
-                //
-                // // 底部链接
-                // Padding(
-                //   padding: EdgeInsets.symmetric(horizontal: 20.w),
-                //   child: Row(
-                //     mainAxisAlignment: MainAxisAlignment.center,
-                //     children: [
-                //       TextButton(
-                //         child: Text(
-                //           'Privacy Policy',
-                //           style: TextStyle(color: Colors.white54),
-                //         ),
-                //         onPressed: () {},
-                //       ),
-                //       Text(
-                //         ' | ',
-                //         style: TextStyle(color: Colors.white54),
-                //       ),
-                //       TextButton(
-                //         child: Text(
-                //           'User Agreement',
-                //           style: TextStyle(color: Colors.white54),
-                //         ),
-                //         onPressed: () {},
-                //       ),
-                //     ],
-                //   ),
-                // ),
               ],
             ),
           ),
@@ -348,84 +265,145 @@ class StorePage extends BasePage<StoreController> {
   }
 
   Widget _buildSubscriptionOption({
-    required String title,
-    required String price,
-    required String perDay,
-    bool isSelected = false,
-    bool showTag = false,
+    required StoreItem item,
   }) {
-    return Container(
-      margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 5.h),
-      decoration: BoxDecoration(
-        color: Colors.white.withOpacity(0.06),
-        borderRadius: BorderRadius.circular(14.r),
-        border: isSelected ? Border.all(color: Colors.blue, width: 2.w) : null,
-      ),
-      child: Stack(
-        children: [
-          Padding(
-            padding: EdgeInsets.all(16.w),
-            child: Row(
-              mainAxisAlignment: MainAxisAlignment.spaceBetween,
-              children: [
-                Column(
-                  crossAxisAlignment: CrossAxisAlignment.start,
-                  children: [
-                    Text(
-                      title,
-                      style: TextStyle(
-                        color: Colors.white,
-                        fontSize: 16.sp,
-                        fontWeight: FontWeight.bold,
-                      ),
-                    ),
-                    Text(
-                      perDay,
-                      style: TextStyle(
-                        color: Colors.white54,
-                        fontSize: 12.sp,
-                      ),
-                    ),
-                  ],
-                ),
-                Text(
-                  price,
-                  style: TextStyle(
-                    color: Colors.white,
-                    fontSize: 18.sp,
-                    fontWeight: FontWeight.bold,
-                  ),
-                ),
-              ],
-            ),
-          ),
-          if (showTag)
-            Positioned(
-              right: 0,
-              top: 0,
+    bool isSelected = controller.currentSelectedStoreItem.value?.id == item.id;
+    return Obx(() {
+      return Container(
+        height: 80.h,
+        margin: EdgeInsets.symmetric(horizontal: 20.w),
+        child: Stack(
+          children: [
+            GestureDetector(
+              onTap: () {
+                controller.currentSelectedStoreItem.value = item;
+              },
               child: Container(
-                padding: EdgeInsets.symmetric(
-                  horizontal: 8.w,
-                  vertical: 4.h,
-                ),
+                margin: EdgeInsets.only(top: 12.h),
+                height: 74.h,
                 decoration: BoxDecoration(
-                  color: Colors.red,
-                  borderRadius: BorderRadius.only(
-                    topRight: Radius.circular(12.r),
-                    bottomLeft: Radius.circular(12.r),
-                  ),
+                  borderRadius: BorderRadius.circular(14.r),
+                  gradient:
+                      controller.currentSelectedStoreItem.value?.id == item.id
+                          ? LinearGradient(
+                              begin: Alignment.centerRight,
+                              end: Alignment.centerLeft,
+                              colors: [
+                                '#63CEFF'.color,
+                                '#0279FB'.color,
+                                "#047AFB".color
+                              ],
+                            )
+                          : null,
+                  border:
+                      controller.currentSelectedStoreItem.value?.id == item.id
+                          ? Border.all(color: Colors.transparent, width: 0.w)
+                          : Border.all(
+                              color: Colors.white.withOpacity(0.2),
+                              width: 2.w,
+                            ),
                 ),
-                child: Text(
-                  'No payment now!',
-                  style: TextStyle(
-                    color: Colors.white,
-                    fontSize: 12.sp,
+                child: Container(
+                  margin:
+                      controller.currentSelectedStoreItem.value?.id == item.id
+                          ? EdgeInsets.all(2.w)
+                          : null,
+                  decoration: BoxDecoration(
+                    color: "#05050D".color,
+                    borderRadius: BorderRadius.circular(13.r),
+                  ),
+                  child: Container(
+                    color: Colors.white.withOpacity(0.06),
+                    child: Stack(
+                      children: [
+                        Padding(
+                          padding: EdgeInsets.only(
+                              left: 12.w, top: 10.h, bottom: 12.h, right: 20.w),
+                          child: Row(
+                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                            children: [
+                              Column(
+                                mainAxisAlignment:
+                                    MainAxisAlignment.spaceBetween,
+                                crossAxisAlignment: CrossAxisAlignment.start,
+                                children: [
+                                  Text(
+                                    item.name,
+                                    style: TextStyle(
+                                      color: Colors.white,
+                                      fontSize: 16.sp,
+                                      fontWeight: FontWeight.bold,
+                                    ),
+                                  ),
+                                  Text(
+                                    item.name,
+                                    style: TextStyle(
+                                      color: Colors.white60,
+                                      fontSize: 12.sp,
+                                    ),
+                                  ),
+                                ],
+                              ),
+                              Text(
+                                item.priceDesc,
+                                style: TextStyle(
+                                  color: Colors.white,
+                                  fontSize: 16.sp,
+                                  fontWeight: FontWeight.bold,
+                                ),
+                              ),
+                            ],
+                          ),
+                        ),
+                        if (isSelected)
+                          Positioned(
+                            right: 0,
+                            top: 0,
+                            child: Container(
+                              padding: EdgeInsets.only(
+                                left: 8.w,
+                                right: 8.w,
+                                top: 2.h,
+                                bottom: 5.h,
+                              ),
+                              decoration: BoxDecoration(
+                                gradient: LinearGradient(
+                                  begin: Alignment.centerRight,
+                                  end: Alignment.centerLeft,
+                                  colors: ['#63CEFF'.color, '#0279FB'.color],
+                                ),
+                                borderRadius: BorderRadius.only(
+                                  topRight: Radius.circular(14.r),
+                                  bottomLeft: Radius.circular(14.r),
+                                ),
+                                // border: Border.all(color: Colors.transparent, width: 2.w),
+                              ),
+                              child: Text(
+                                'No payment now!',
+                                style: TextStyle(
+                                  color: Colors.white,
+                                  fontSize: 11.sp,
+                                  fontWeight: FontWeight.bold,
+                                ),
+                              ),
+                            ),
+                          ),
+                      ],
+                    ),
                   ),
                 ),
               ),
             ),
-        ],
-      ),
-    );
+            if (false)
+              Positioned(
+                right: 120.w,
+                top: 0.h,
+                child: Assets.images.iconStoreFree
+                    .image(width: 30.w, height: 28.h),
+              ),
+          ],
+        ),
+      );
+    });
   }
 }

+ 35 - 0
lib/sdk/pay/agile_pay.dart

@@ -0,0 +1,35 @@
+import 'applepay/apple_pay.dart';
+import 'applepay/apple_pay_info.dart';
+import 'code/agile_pay_code.dart';
+import 'listener/agile_pay_state.dart';
+import 'listener/i_agile_pay.dart';
+
+class AgilePay {
+  static IAgilePay? realPay;
+
+  static void startPay(dynamic payInfo,
+      {required AgilePaySuccess success,
+        required AgilePayError payError,
+        required AgileError error,
+        AgilePayBefore? before}) {
+    IAgilePay? iAgilePay;
+
+    if (payInfo is ApplePayInfo) {
+      iAgilePay = ApplePay(payInfo);
+    }
+
+    realPay = iAgilePay;
+    if (iAgilePay != null) {
+      iAgilePay.setPayListener(AgilePayStateImpl(
+          paySuccessListener: success,
+          payErrorListener: payError,
+          errorListener: error,
+          payBeforeListener: before));
+      iAgilePay.pay();
+    } else {
+      payError(AgilePayCode.payCodeNotSupport,
+          AgilePayCode.getMessageByCode(AgilePayCode.payCodeNotSupport));
+    }
+  }
+
+}

+ 7 - 0
lib/sdk/pay/applepay/apple_pay.dart

@@ -0,0 +1,7 @@
+
+import '../assist/apple_or_google_pay.dart';
+import '../listener/i_agile_pay.dart';
+
+class ApplePay extends AppleOrGooglePay implements IAgilePay {
+  ApplePay(super.payInfo);
+}

+ 5 - 0
lib/sdk/pay/applepay/apple_pay_info.dart

@@ -0,0 +1,5 @@
+import '../assist/apple_or_google_pay_info.dart';
+
+class ApplePayInfo extends AppleOrGooglePayInfo {
+  ApplePayInfo(super.productId, super.type, super.accountToken);
+}

+ 33 - 0
lib/sdk/pay/assist/agile_pay_state_info.dart

@@ -0,0 +1,33 @@
+import '../listener/agile_pay_state.dart';
+
+abstract class AgilePayStateInfo {
+  AgilePayState? mPay;
+
+  void sendError(int errno, String? error) {
+    if (mPay != null) {
+      mPay?.error(errno, error);
+    }
+  }
+
+  void sendPaySuccess(String? result) {
+    if (mPay != null) {
+      mPay?.paySuccess(result);
+    }
+  }
+
+  void sendPayError(int errno, String? error) {
+    if (mPay != null) {
+      mPay?.payError(errno, error);
+    }
+  }
+
+  void sendPayBefore() {
+    if (mPay != null) {
+      mPay?.payBefore();
+    }
+  }
+
+  void setPayListener(AgilePayState pay) {
+    mPay = pay;
+  }
+}

+ 149 - 0
lib/sdk/pay/assist/apple_or_google_pay.dart

@@ -0,0 +1,149 @@
+import 'dart:async';
+
+import 'package:clean/sdk/pay/assist/product_type.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:in_app_purchase/in_app_purchase.dart';
+
+import '../code/agile_pay_code.dart';
+import 'agile_pay_state_info.dart';
+import 'apple_or_google_pay_info.dart';
+
+abstract class AppleOrGooglePay extends AgilePayStateInfo {
+  final AppleOrGooglePayInfo _payInfo;
+
+  late final StreamSubscription<List<PurchaseDetails>>
+  _purchaseUpdatedSubscription;
+  late final InAppPurchase _iap;
+
+  final Duration timeout = const Duration(seconds: 10);
+
+  AppleOrGooglePay(this._payInfo) {
+    _iap = InAppPurchase.instance;
+    _purchaseUpdatedSubscription =
+        _iap.purchaseStream.listen((purchaseDetailsList) {
+          listenToPurchaseUpdated(purchaseDetailsList);
+        }, onDone: () {
+          dispose();
+        }, onError: (error) {});
+  }
+
+  void pay() async {
+    sendPayBefore();
+    try {
+      final bool isAe = await isAvailable();
+      if (!isAe) {
+        sendError(AgilePayCode.payCodeNotConnectStore,
+            AgilePayCode.getMessageByCode(AgilePayCode.payCodeNotConnectStore));
+        return;
+      }
+      final ProductDetailsResponse response =
+      await queryProductDetails({_payInfo.productId});
+      if (response.notFoundIDs.isNotEmpty || response.productDetails.isEmpty) {
+        sendError(
+            AgilePayCode.payCodeProductNotFindStore,
+            AgilePayCode.getMessageByCode(
+                AgilePayCode.payCodeProductNotFindStore));
+        return;
+      }
+      List<ProductDetails> products = response.productDetails;
+      for (var element in products) {
+        bool isSend;
+        if (_payInfo.type == ProductType.consumable) {
+          isSend = await buyConsumable(
+              purchaseParam: PurchaseParam(
+                  productDetails: element,
+                  applicationUserName: _payInfo.accountToken));
+        } else {
+          isSend = await buyNonConsumable(
+              purchaseParam: PurchaseParam(
+                  productDetails: element,
+                  applicationUserName: _payInfo.accountToken));
+        }
+        if (!isSend) {
+          sendError(
+              AgilePayCode.payCodeRequestSendError,
+              AgilePayCode.getMessageByCode(
+                  AgilePayCode.payCodeRequestSendError));
+          return;
+        }
+        return;
+      }
+    } catch (e) {
+      if (e is TimeoutException) {
+        sendError(AgilePayCode.payCodeNotConnectStore,
+            AgilePayCode.getMessageByCode(AgilePayCode.payCodeNotConnectStore));
+        return;
+      }
+      sendError(AgilePayCode.payCodeOtherError,
+          AgilePayCode.getMessageByCode(AgilePayCode.payCodeOtherError));
+      return;
+    }
+  }
+
+  Future<bool> isAvailable() {
+    return _iap.isAvailable().timeout(timeout);
+  }
+
+  Future<ProductDetailsResponse> queryProductDetails(Set<String> identifiers) {
+    return InAppPurchase.instance.queryProductDetails(identifiers);
+  }
+
+  Future<bool> buyConsumable({required PurchaseParam purchaseParam}) {
+    return _iap.buyConsumable(purchaseParam: purchaseParam);
+  }
+
+  Future<bool> buyNonConsumable({required PurchaseParam purchaseParam}) {
+    return _iap.buyNonConsumable(purchaseParam: purchaseParam);
+  }
+
+  void dispose() {
+    _purchaseUpdatedSubscription.cancel();
+  }
+
+  void listenToPurchaseUpdated(
+      List<PurchaseDetails> purchaseDetailsList) async {
+    // try {
+    for (var purchaseDetails in purchaseDetailsList) {
+      debugPrint(
+          'agilePay-purchasePay--PurchaseUpdated-> ${purchaseDetails.toString()}');
+      if (purchaseDetails.status == PurchaseStatus.pending) {
+        verifyPendingPurchase(purchaseDetails);
+      } else {
+        if (purchaseDetails.status == PurchaseStatus.error) {
+          verifyErrorPurchase(purchaseDetails);
+        } else if (purchaseDetails.status == PurchaseStatus.purchased) {
+          verifySuccessPurchase(purchaseDetails);
+        } else if (purchaseDetails.status == PurchaseStatus.canceled) {
+          verifyCancelPurchase(purchaseDetails);
+        }
+
+        if (purchaseDetails.pendingCompletePurchase) {
+          await InAppPurchase.instance.completePurchase(purchaseDetails);
+          dispose();
+        }
+      }
+    }
+    // } catch (e) {
+    //   sendError(AgilePayCode.payCodeOtherError,
+    //       AgilePayCode.getMessageByCode(AgilePayCode.payCodeOtherError));
+    // } finally {
+    //   dispose();
+    // }
+  }
+
+  void verifyPendingPurchase(PurchaseDetails purchaseDetails) {}
+
+  void verifyCancelPurchase(PurchaseDetails purchaseDetails) {
+    sendError(AgilePayCode.payCodeCancelError,
+        AgilePayCode.getMessageByCode(AgilePayCode.payCodeCancelError));
+  }
+
+  void verifyErrorPurchase(PurchaseDetails purchaseDetails) {
+    sendError(AgilePayCode.payCodePayError,
+        AgilePayCode.getMessageByCode(AgilePayCode.payCodePayError));
+  }
+
+  void verifySuccessPurchase(PurchaseDetails purchaseDetails) {
+    sendPaySuccess(purchaseDetails.verificationData.serverVerificationData);
+  }
+}

+ 17 - 0
lib/sdk/pay/assist/apple_or_google_pay_info.dart

@@ -0,0 +1,17 @@
+import 'package:clean/sdk/pay/assist/product_type.dart';
+
+class AppleOrGooglePayInfo {
+  final String _productId;
+
+  final ProductType _type;
+
+  final String? _accountToken;
+
+  AppleOrGooglePayInfo(this._productId, this._type, this._accountToken);
+
+  String get productId => _productId;
+
+  ProductType get type => _type;
+
+  String? get accountToken => _accountToken;
+}

+ 4 - 0
lib/sdk/pay/assist/product_type.dart

@@ -0,0 +1,4 @@
+enum ProductType {
+  consumable,
+  nonConsumable,
+}

+ 52 - 0
lib/sdk/pay/code/agile_pay_code.dart

@@ -0,0 +1,52 @@
+class AgilePayCode {
+  AgilePayCode._();
+
+  static const int payCodeQqwalletNotSupport = 50000;
+  static const int payCodeAlipaySuccess = 9000;
+  static const int payCodeOtherError = 99999;
+  static const int payCodeTokenError = 80000;
+  static const int payCodeTokenFormatError = 80001;
+  static const int payCodeWxEnvError = 80002;
+  static const int payCodeWxNoResultError = 80003;
+  static const int payCodeWxNoTokenError = 80004;
+  static const int payCodeWxNoPayperidError = 80005;
+  static const int payCodeParamsError = 80006;
+  static const int payCodeSystemBusy = 4000;
+  static const int payCodeOrderInfoError = 4001;
+  static const int payCodeCancelError = 6001;
+  static const int payCodeNetError = 6002;
+  static const int payCodeShengKeyNotMatch = 70000;
+  static const int payCodePayError = 70001;
+  static const int payCodeSystemError = 70002;
+  static const int payCodeNotSupport = 70003;
+  static const int payCodeNotConnectStore = 70004;
+  static const int payCodeProductNotFindStore = 70005;
+  static const int payCodeRequestSendError = 70006;
+
+  static final Map<int, String> resultStatus = {
+    payCodeSystemError: "系统异常",
+    payCodeNotSupport: "不支持该支付类型",
+    payCodeSystemBusy: "系统繁忙,请稍候再试",
+    payCodeOrderInfoError: "订单参数错误",
+    payCodeCancelError: "取消支付",
+    payCodeNetError: "网络连接异常",
+    payCodeTokenError: "Token获取失败",
+    payCodeTokenFormatError: "Token格式错误",
+    payCodeWxEnvError: "微信未安装或微信版本不支持",
+    payCodeWxNoResultError: "微信支付无返回值",
+    payCodeWxNoTokenError: "微信支付获取access_token错误",
+    payCodeWxNoPayperidError: "微信支付获取payperid错误",
+    payCodeParamsError: "参数错误",
+    payCodePayError: "支付失败",
+    payCodeShengKeyNotMatch: "证书不匹配",
+    payCodeQqwalletNotSupport: "QQ未安装或QQ版本不支持",
+    payCodeOtherError: "其他问题",
+    payCodeNotConnectStore: "无法连接到商店",
+    payCodeProductNotFindStore: "商品未找到",
+    payCodeRequestSendError: "请求支付发送失败",
+  };
+
+  static String getMessageByCode(int code) {
+    return resultStatus[code] ?? "Unknown code";
+  }
+}

+ 50 - 0
lib/sdk/pay/listener/agile_pay_state.dart

@@ -0,0 +1,50 @@
+abstract class AgilePayState {
+  void error(int errno, String? error);
+
+  void payError(int error, String? errorMessage);
+
+  void paySuccess(String? result);
+
+  void payBefore();
+}
+
+typedef AgilePayBefore = void Function();
+typedef AgilePaySuccess = void Function(String? result);
+typedef AgilePayError = void Function(int error, String? errorMessage);
+typedef AgileError = void Function(int errno, String? error);
+
+class AgilePayStateImpl implements AgilePayState {
+  AgileError errorListener;
+
+  AgilePayError payErrorListener;
+
+  AgilePaySuccess paySuccessListener;
+
+  AgilePayBefore? payBeforeListener;
+
+  AgilePayStateImpl(
+      {required this.errorListener,
+        required this.payErrorListener,
+        required this.paySuccessListener,
+        this.payBeforeListener});
+
+  @override
+  void error(int errno, String? error) {
+    errorListener.call(errno, error);
+  }
+
+  @override
+  void payError(int error, String? errorMessage) {
+    payErrorListener.call(error, errorMessage);
+  }
+
+  @override
+  void paySuccess(String? result) {
+    paySuccessListener.call(result);
+  }
+
+  @override
+  void payBefore() {
+    payBeforeListener?.call();
+  }
+}

+ 7 - 0
lib/sdk/pay/listener/i_agile_pay.dart

@@ -0,0 +1,7 @@
+import 'agile_pay_state.dart';
+
+abstract class IAgilePay {
+  void pay();
+
+  void setPayListener(AgilePayState agilePayState);
+}

+ 21 - 0
lib/utils/app_info_util.dart

@@ -0,0 +1,21 @@
+import 'package:package_info_plus/package_info_plus.dart';
+
+class AppInfoUtil {
+  PackageInfo? _packageInfo;
+
+  AppInfoUtil._();
+
+  init() async {
+    _packageInfo = await PackageInfo.fromPlatform();
+  }
+
+  String? get appName => _packageInfo?.appName;
+
+  String? get packageName => _packageInfo?.packageName;
+
+  String? get appVersionName => _packageInfo?.version;
+
+  int? get appVersionCode => int.tryParse(_packageInfo?.buildNumber ?? '');
+}
+
+final appInfoUtil = AppInfoUtil._();

+ 14 - 0
lib/utils/expand.dart

@@ -10,4 +10,18 @@ extension HexColor on String {
     }
     return Color(int.parse(hex, radix: 16));
   }
+}
+
+extension DoubleExtension on double {
+  String toFormattedString(int fractionDigits) {
+    if (this == toInt()) {
+      return toInt().toString();
+    } else {
+      return toStringAsFixed(fractionDigits);
+    }
+  }
+
+  double toFormattedDouble(int fractionDigits) {
+    return double.parse(toStringAsFixed(fractionDigits));
+  }
 }

+ 51 - 3
pubspec.lock

@@ -302,7 +302,7 @@ packages:
     source: hosted
     version: "7.0.2"
   dio:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: dio
       sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
@@ -378,6 +378,14 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_card_swiper:
+    dependency: "direct main"
+    description:
+      name: flutter_card_swiper
+      sha256: "1eacbfab31b572223042e03409726553aec431abe48af48c8d591d376d070d3d"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "7.0.2"
   flutter_gen_core:
     dependency: transitive
     description:
@@ -529,6 +537,38 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.2.0"
+  in_app_purchase:
+    dependency: "direct main"
+    description:
+      name: in_app_purchase
+      sha256: "960f26a08d9351fb8f89f08901f8a829d41b04d45a694b8f776121d9e41dcad6"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.2.0"
+  in_app_purchase_android:
+    dependency: transitive
+    description:
+      name: in_app_purchase_android
+      sha256: b7cc194f183a97d71e6da1edb4a4095466ca0c22533615ecad021bdf95a5ecdc
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.3.6+13"
+  in_app_purchase_platform_interface:
+    dependency: transitive
+    description:
+      name: in_app_purchase_platform_interface
+      sha256: "1d353d38251da5b9fea6635c0ebfc6bb17a2d28d0e86ea5e083bf64244f1fb4c"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.4.0"
+  in_app_purchase_storekit:
+    dependency: transitive
+    description:
+      name: in_app_purchase_storekit
+      sha256: "52caf1051fec8018adabd2d1e775a44cb389972ed868a746c4fc28a301734d41"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.3.20+4"
   intl:
     dependency: "direct main"
     description:
@@ -554,7 +594,7 @@ packages:
     source: hosted
     version: "0.7.1"
   json_annotation:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: json_annotation
       sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
@@ -881,6 +921,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.5.1"
+  pretty_dio_logger:
+    dependency: "direct main"
+    description:
+      name: pretty_dio_logger
+      sha256: "36f2101299786d567869493e2f5731de61ce130faa14679473b26905a92b6407"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.4.0"
   protobuf:
     dependency: transitive
     description:
@@ -922,7 +970,7 @@ packages:
     source: hosted
     version: "2.0.0"
   retrofit:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: retrofit
       sha256: c6cc9ad3374e6d07008343140a67afffaaa34cdf6bf08d4847d91417a99dcf45

+ 9 - 0
pubspec.yaml

@@ -34,6 +34,12 @@ dependencies:
   # 状态管理
   get: ^4.6.6
 
+  # 网络请求
+  dio: ^5.6.0
+  retrofit: '>=4.0.0 <5.0.0'
+  pretty_dio_logger: ^1.4.0
+  json_annotation: ^4.8.1
+
   #获取设备信息
   device_info_plus: ^10.1.2
   android_id: ^0.4.0
@@ -58,6 +64,9 @@ dependencies:
   #权限申请
   permission_handler: ^11.3.1
 
+  #支付
+  in_app_purchase: ^3.2.0
+
   # 弹窗
   flutter_smart_dialog: ^4.9.8