import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:fluwx/fluwx.dart'; export 'package:fluwx/fluwx.dart'; import 'package:http/http.dart' as http; import 'package:get/get.dart'; typedef FlWeChatResponseCallback = void Function(WeChatResponse response); /// 支持成功 typedef FlWeChatPayResponseResultCallback = void Function(); /// 支付结果回调 typedef FlWeChatPayResponseCallback = void Function( WeChatPaymentResponse response); /// LaunchFromWX 回调 typedef FlWeChatLaunchFromWXRequestCallback = void Function( WeChatLaunchFromWXRequest response); /// OpenBusinessWebView 回调 typedef FlWeChatOpenBusinessWebViewResponseCallback = void Function( WeChatOpenBusinessWebviewResponse response); /// WeChatOpenBusinessView 回调 typedef FlWeChatOpenBusinessViewResponseCallback = void Function( WeChatOpenBusinessViewResponse response); /// Share 回调 typedef FlWeChatShareResponseCallback = void Function( WeChatShareResponse response); /// Auth 回调 typedef FlWeChatAuthResponseCallback = Function(WeChatAuthResponse response); /// Auth token 回调 typedef FlWeChatAuthResponseTokenCallback = Function( WeChatAuthResponse response, WXTokenModel token); /// Auth userinfo 回调 typedef FlWeChatAuthResponseUserinfoCallback = Function( WeChatAuthResponse response, WXTokenModel token, WXUserModel userInfo, ); /// 显示 toast typedef FlWXToastCallback = void Function(String msg); /// 日志 打印 typedef FlWXLogCallback = void Function(String msg); /// http 请求 typedef FlWXHTTPCallback = Future Function(String url); class FlWX { factory FlWX() => _singleton ??= FlWX._(); FlWX._(); static FlWX? _singleton; final Fluwx fluwx = Fluwx(); String? _appId; /// 获取当前设置的 appId String? get appId => _appId; String? _appSecret; /// 获取当前设置的 appSecret String? get appSecret => _appSecret; String _appName = ''; String token = ''; String refresh_token = ''; String openid = ''; // 参数 说明 // access_token 接口调用凭证 // expires_in access_token 接口调用凭证超时时间,单位(秒) // refresh_token 用户刷新 access_token // openid 授权用户唯一标识 // scope 用户授权的作用域(snsapi_userinfo) // unionid 当且仅当该移动应用已获得该用户的 userinfo 授权时,才会出现该字段 /// callback params FlWXCallbackParams? _params; /// 分享时的缩略图 Uint8List? _shareThumbnail; /// register bool _isRegister = false; Future register({ required String appId, required FlWXCallbackParams params, String? universalLink, bool doOnIOS = true, bool doOnAndroid = true, /// 用于获取用户信息 String? appSecret, /// app名称(用于分享时使用) String appName = '', /// 分享时的缩略图 Uint8List? shareThumbnail, }) { assert(appId.isNotEmpty); if (universalLink != null) assert(universalLink.isNotEmpty); if (appSecret != null) { assert(appSecret.isNotEmpty); } _isRegister = true; _appId = appId; _appSecret = appSecret; _appName = appName; _shareThumbnail = shareThumbnail; _params = params; return fluwx.registerApi( appId: appId, doOnAndroid: doOnAndroid, doOnIOS: doOnIOS, universalLink: universalLink, ); } /// 获取微信 用户token String _getTokenUrl(String code) { print("_getTokenUrl 数据获取${_appId},${_appSecret}"); assert( _appId != null && _appId!.isNotEmpty && _appSecret != null && _appSecret!.isNotEmpty, ); return 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=$_appId&secret=$_appSecret&code=$code&grant_type=authorization_code'; } /// 获取微信 用户token String _getRefreshTokenUrl(String code) { print("_getRefreshTokenUrl 数据获取${_appId},${_appSecret}"); assert( _appId != null && _appId!.isNotEmpty && _appSecret != null && _appSecret!.isNotEmpty, ); return 'https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=$_appId&grant_type=refresh_token&&refresh_token=$refresh_token'; } // GET请求示例 Future> fetchData(String Url) async { try { final response = await http.get(Uri.parse(Url)); if (response.statusCode == 200) { final data = jsonDecode(response.body) as Map; print('GET响应数据: $data'); return data; } else { print('GET请求失败,状态码: ${response.statusCode}'); return {}; } } catch (e) { print('GET请求异常: $e'); return {}; } } // POST请求示例 Future postData() async { try { final response = await http.post( Uri.parse('https://jsonplaceholder.typicode.com/posts'), headers: { 'Content-Type': 'application/json', }, // 设置请求头‌:ml-citation{ref="6" data="citationList"} body: jsonEncode({ // 编码JSON数据‌:ml-citation{ref="3" data="citationList"} 'title': 'foo', 'body': 'bar', 'userId': 1, }), ); if (response.statusCode == 201) { final data = jsonDecode(response.body); print('POST响应数据: $data'); return true; } else { print('POST请求失败,状态码: ${response.statusCode}'); return false; } } catch (e) { print('POST请求异常: $e'); return false; } } /// 获取微信 用户信息 String _getUserInfoUrl({required String openId, required String token}) => 'https://api.weixin.qq.com/sns/userinfo?access_token=$token&openid=$openId'; //检验授权凭证(access_token)是否有效 String _getUserTokenEffertive({ required String openId, required String token, }) => 'https://api.weixin.qq.com/sns/auth?access_token=$token&openid=$openId'; /// 是否安装微信 Future get isInstalled async { assert(_isRegister, '请先调用 FlWX().register()'); final installed = await fluwx.isWeChatInstalled; if (!installed) _params!.onToast?.call('该服务需要先安装微信'); return installed; } /// isSupportOpenBusinessView Future get isSupportOpenBusinessView async { if (!await isInstalled) return false; return await fluwx.isSupportOpenBusinessView; } /// 添加移除监听 FluwxCancelable _onListener(FlWeChatResponseCallback onResponse) { late FluwxCancelable cancelable; subscriber(response) { onResponse(response); Future.delayed(const Duration(milliseconds: 500), () { cancelable.cancel(); }); } return cancelable = fluwx.addSubscriber(subscriber); } Future getExtMsg() async { if (!await isInstalled) return null; return await fluwx.getExtMsg(); } Future attemptToResumeMsgFromWx({ Function(WeChatShowMessageFromWXRequest response)? onResponse, }) async { if (!await isInstalled) return; await fluwx.attemptToResumeMsgFromWx(); if (onResponse != null) { _onListener((response) { if (response is WeChatShowMessageFromWXRequest) onResponse(response); }); } } /// 调用微信登录 Future authBy( AuthType which, { /// 原始数据回调 FlWeChatAuthResponseCallback? onResponse, /// 获取token回调 FlWeChatAuthResponseTokenCallback? onToken, /// 获取userinfo回调 FlWeChatAuthResponseUserinfoCallback? onUserinfo, bool getUserInfo = false, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) async { if (response is WeChatAuthResponse) { onResponse?.call(response); print( "用户点击事件${response.isSuccessful},${response.code},${response.lang},${response.state},${response.country}", ); if (response.isSuccessful && response.code != null) { print("数据获取失败${onToken},${onUserinfo}"); // if (onToken != null || onUserinfo != null) { try { var Istoken = await fetchData( _getTokenUrl(response.code!), ); // 触发GET请求 //token有效期是30天,30天后需要重新授权,需要看看API文档 print("数据获取fetchData${Istoken}"); if (Istoken != {}) { token = Istoken["access_token"].toString(); refresh_token = Istoken["refresh_token"].toString(); openid = Istoken["openid"].toString(); print("数据获取token=${token}"); print("数据获取openid=${openid}"); print("数据获取refresh_token=${refresh_token}"); Istoken = await fetchData(_getRefreshTokenUrl(response.code!)); print("数据获取_getUserTokenEffertive${Istoken}"); Istoken = await fetchData( _getUserTokenEffertive(openId: openid, token: token), ); // 触发GET请求 print("数据获取_getUserTokenEffertive${Istoken}"); Istoken = await fetchData( _getUserInfoUrl(openId: openid, token: token), ); // 触发GET请求 print("数据获取_getUserInfoUrl${Istoken}"); } } catch (e) { _params!.onLog?.call('数据解析失败 $e'); } // } } } else if (response is WeChatAuthByQRCodeFinishedResponse) {} }); final result = await fluwx.authBy(which: which); print("authBy数据获取${result}"); if (!result) { cancelable.cancel(); _params!.onToast?.call('微信授权失败'); } return result; } Future autoDeduct( AutoDeduct data, { FlWeChatResponseCallback? onResponse, }) async { final cancelable = _onListener((response) { onResponse?.call(response); }); final result = await fluwx.autoDeduct(data: data); if (!result) { cancelable.cancel(); _params!.onToast?.call('微信签约失败'); } return result; } /// 调用微信支付 Future pay( PayType which, { FlWeChatPayResponseResultCallback? onSuccess, FlWeChatPayResponseCallback? onResponse, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) { if (response is WeChatPaymentResponse) { onResponse?.call(response); _params!.onLog?.call('微信支付 result code: ${response.errCode} '); if (response.errCode == 0) { _params!.onToast?.call('支付成功'); onSuccess?.call(); } else if (response.errCode == -2) { _params!.onToast?.call('已取消支付'); } else { _params!.onToast?.call('支付失败'); } } }); final result = await fluwx.pay(which: which); if (!result) { cancelable.cancel(); _params!.onToast?.call('支付失败'); } return result; } /// ****************** open ****************** /// Future open(OpenType target) async { if (!await isInstalled) return false; return fluwx.open(target: target); } Future openWeChatApp({ FlWeChatLaunchFromWXRequestCallback? onResponse, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) { if (response is WeChatLaunchFromWXRequest) onResponse?.call(response); }); final result = await fluwx.open(target: WeChatApp()); if (!result) cancelable.cancel(); return result; } Future openBrowser( String url, { FlWeChatOpenBusinessWebViewResponseCallback? onResponse, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) { if (response is WeChatOpenBusinessWebviewResponse) { onResponse?.call(response); } }); final result = await fluwx.open(target: Browser(url)); if (!result) cancelable.cancel(); return result; } Future openRankList() async { if (!await isInstalled) return false; return fluwx.open(target: RankList()); } Future openBusinessView({ required String businessType, required String query, FlWeChatOpenBusinessViewResponseCallback? onResponse, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) { if (response is WeChatOpenBusinessViewResponse) { onResponse?.call(response); } }); final result = await fluwx.open( target: BusinessView(businessType: businessType, query: query), ); if (!result) cancelable.cancel(); return result; } Future openInvoice({ required String appId, required String cardType, String locationId = '', String cardId = '', String canMultiSelect = '', Function(WeChatOpenInvoiceResponse response)? onResponse, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) { if (response is WeChatOpenInvoiceResponse) onResponse?.call(response); }); final result = await fluwx.open( target: Invoice( appId: appId, cardType: cardType, locationId: locationId, cardId: cardId, canMultiSelect: canMultiSelect, ), ); if (!result) cancelable.cancel(); return result; } Future openCustomerServiceChat({ required String corpId, required String url, Function(WeChatOpenCustomerServiceChatResponse response)? onResponse, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) { if (response is WeChatOpenCustomerServiceChatResponse) { onResponse?.call(response); } }); final result = await fluwx.open( target: CustomerServiceChat(corpId: corpId, url: url), ); if (!result) cancelable.cancel(); return result; } Future openMiniProgram( String username, { String? path, Function(WeChatLaunchMiniProgramResponse response)? onResponse, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) { if (response is WeChatLaunchMiniProgramResponse) { onResponse?.call(response); } }); final result = await fluwx.open( target: MiniProgram(username: username, path: path), ); if (!result) { cancelable.cancel(); _params?.onToast?.call('小程序打开失败'); } return result; } Future openSubscribeMessage({ required String appId, required int scene, required String templateId, String? reserved, Function(WeChatSubscribeMsgResponse response)? onResponse, }) async { if (!await isInstalled) return false; final cancelable = _onListener((response) { if (response is WeChatSubscribeMsgResponse) onResponse?.call(response); }); final result = await fluwx.open( target: SubscribeMessage( appId: appId, scene: scene, templateId: templateId, reserved: reserved, ), ); if (!result) cancelable.cancel(); return result; } /// ****************** share ****************** /// Future share( WeChatShareModel what, { FlWeChatShareResponseCallback? onResponse, }) async { if (!await isInstalled) return false; return _shareResponse(fluwx.share(what), onResponse: onResponse); } Future shareText( String source, { String? title, String? description, WeChatScene scene = WeChatScene.session, String? messageExt, String? messageAction, String? mediaTagName, String? msgSignature, Uint8List? thumbData, String? thumbDataHash, FlWeChatShareResponseCallback? onResponse, }) async { if (!await isInstalled) return false; return _shareResponse( fluwx.share( WeChatShareTextModel( source, title: title ?? _appName, description: description ?? _appName, scene: scene, messageExt: messageExt, messageAction: messageAction, mediaTagName: mediaTagName, msgSignature: msgSignature, thumbData: thumbData ?? _shareThumbnail, thumbDataHash: thumbDataHash, ), ), onResponse: onResponse, ); } Future shareMiniProgram( String webPage, { required String webPageUrl, WXMiniProgramType miniProgramType = WXMiniProgramType.release, required String userName, String path = "/", String? title, String? description, bool withShareTicket = false, String? messageExt, String? messageAction, String? mediaTagName, String? msgSignature, Uint8List? thumbData, String? thumbDataHash, FlWeChatShareResponseCallback? onResponse, }) async { if (!await isInstalled) return false; return _shareResponse( fluwx.share( WeChatShareMiniProgramModel( webPageUrl: webPageUrl, userName: userName, title: title ?? _appName, description: description ?? _appName, messageExt: messageExt, messageAction: messageAction, mediaTagName: mediaTagName, msgSignature: msgSignature, thumbData: thumbData ?? _shareThumbnail, thumbDataHash: thumbDataHash, ), ), onResponse: onResponse, ); } Future shareImage( WeChatImageToShare source, { String? title, String? description, WeChatScene scene = WeChatScene.session, String? messageExt, String? messageAction, String? mediaTagName, String? msgSignature, Uint8List? thumbData, String? thumbDataHash, FlWeChatShareResponseCallback? onResponse, }) async { if (!await isInstalled) return false; return _shareResponse( fluwx.share( WeChatShareImageModel( source, title: title ?? _appName, description: description ?? _appName, scene: scene, messageExt: messageExt, messageAction: messageAction, mediaTagName: mediaTagName, msgSignature: msgSignature, thumbData: thumbData ?? _shareThumbnail, thumbDataHash: thumbDataHash, ), ), onResponse: onResponse, ); } Future shareMusic({ String? musicUrl, String? musicDataUrl, String? musicLowBandUrl, String? musicLowBandDataUrl, String? title, String? description, WeChatScene scene = WeChatScene.session, String? messageExt, String? messageAction, String? mediaTagName, String? msgSignature, Uint8List? thumbData, String? thumbDataHash, FlWeChatShareResponseCallback? onResponse, }) async { if (!await isInstalled) return false; return _shareResponse( fluwx.share( WeChatShareMusicModel( musicUrl: musicUrl, musicDataUrl: musicDataUrl, musicLowBandUrl: musicLowBandUrl, musicLowBandDataUrl: musicLowBandDataUrl, title: title ?? _appName, description: description ?? _appName, scene: scene, messageExt: messageExt, messageAction: messageAction, mediaTagName: mediaTagName, msgSignature: msgSignature, thumbData: thumbData ?? _shareThumbnail, thumbDataHash: thumbDataHash, ), ), onResponse: onResponse, ); } Future shareVideo( String videoUrl, { String? title, String? description, WeChatScene scene = WeChatScene.session, String? messageExt, String? messageAction, String? mediaTagName, String? msgSignature, Uint8List? thumbData, String? thumbDataHash, FlWeChatShareResponseCallback? onResponse, }) async { if (!await isInstalled) return false; return _shareResponse( fluwx.share( WeChatShareVideoModel( title: title ?? _appName, videoUrl: videoUrl, description: description ?? _appName, scene: scene, messageExt: messageExt, messageAction: messageAction, mediaTagName: mediaTagName, msgSignature: msgSignature, thumbData: thumbData ?? _shareThumbnail, thumbDataHash: thumbDataHash, ), ), onResponse: onResponse, ); } Future shareWebPage( String webPage, { String? title, String? description, WeChatScene scene = WeChatScene.session, String? messageExt, String? messageAction, String? mediaTagName, String? msgSignature, Uint8List? thumbData, String? thumbDataHash, FlWeChatShareResponseCallback? onResponse, }) async { if (!await isInstalled) return false; return _shareResponse( fluwx.share( WeChatShareWebPageModel( webPage, title: title ?? _appName, description: description ?? _appName, scene: scene, messageExt: messageExt, messageAction: messageAction, mediaTagName: mediaTagName, msgSignature: msgSignature, thumbData: thumbData ?? _shareThumbnail, thumbDataHash: thumbDataHash, ), ), onResponse: onResponse, ); } Future shareFile( File source, { required String suffix, String? title, String? description, WeChatScene scene = WeChatScene.session, String? messageExt, String? messageAction, String? mediaTagName, String? msgSignature, Uint8List? thumbData, String? thumbDataHash, FlWeChatShareResponseCallback? onResponse, }) async { if (!await isInstalled) return false; return _shareResponse( fluwx.share( WeChatShareFileModel( WeChatFile.file(source, suffix: suffix), title: title ?? _appName, description: description ?? _appName, scene: scene, messageExt: messageExt, messageAction: messageAction, mediaTagName: mediaTagName, msgSignature: msgSignature, thumbData: thumbData ?? _shareThumbnail, thumbDataHash: thumbDataHash, ), ), onResponse: onResponse, ); } Future _shareResponse( Future share, { FlWeChatShareResponseCallback? onResponse, }) async { final cancelable = _onListener((response) { if (response is WeChatShareResponse) onResponse?.call(response); }); final result = await share; if (!result) cancelable.cancel(); return result; } } class FlWXCallbackParams { FlWXCallbackParams({this.onToast, this.onLog, this.onHttp}); /// toast 显示 final FlWXToastCallback? onToast; /// 日志 打印 final FlWXLogCallback? onLog; /// http 请求 返回 json final FlWXHTTPCallback? onHttp; } class WXUserModel { final String? openid; final String? nickname; final int? sex; final String? language; final String? city; final String? province; final String? country; final String? headImgUrl; final List? privilege; final String? unionId; WXUserModel.fromJson(Map json) : openid = json['openid'] as String?, nickname = json['nickname'] as String?, sex = json['sex'] as int?, language = json['language'] as String?, city = json['city'] as String?, province = json['province'] as String?, country = json['country'] as String?, headImgUrl = json['headimgurl'] as String?, privilege = json['privilege'] as List?, unionId = json['unionid'] as String?; Map toMap() => { 'openid': openid, 'unionId': unionId, 'nickname': nickname, 'sex': sex, 'headImgUrl': headImgUrl, 'privilege': privilege, 'language': language, 'city': city, 'province': province, 'country': country, }; } class WXTokenModel { final String? accessToken; final int? expiresIn; final String? refreshToken; final String? openid; final String? scope; final String? unionId; WXTokenModel.fromJson(Map json) : accessToken = json['access_token'] as String?, expiresIn = json['expires_in'] as int?, refreshToken = json['refresh_token'] as String?, openid = json['openid'] as String?, scope = json['scope'] as String?, unionId = json['unionid'] as String?; Map toMap() => { 'openid': openid, 'unionId': unionId, 'accessToken': accessToken, 'expiresIn': expiresIn, 'refreshToken': refreshToken, 'scope': scope, }; } extension ExtensionMap on Map { Payment? toPayment() { try { final appId = (this['appId'] ?? this['appid']); final partnerId = (this['partnerId'] ?? this['partnerid'] ?? this['partner']) as String?; final prepayId = (this['prepayId'] ?? this['prepayid'] ?? this['prepay']); final packageValue = (this['packageValue'] ?? this['packagevalue'] ?? this['package']); final nonceStr = (this['nonceStr'] ?? this['noncestr'] ?? this['nonce']); final timestamp = (this['timestamp'] ?? this['timeStamp']); final sign = this['sign']; if (appId != null && partnerId != null && prepayId != null && packageValue != null && nonceStr != null && timestamp != null && sign != null) { return Payment( appId: appId.toString(), partnerId: partnerId.toString(), prepayId: prepayId.toString(), packageValue: packageValue.toString(), nonceStr: nonceStr.toString(), timestamp: int.parse(timestamp!.toString()), sign: sign.toString(), ); } } catch (e) { FlWX()._params?.onLog?.call(e.toString()); } return null; } }