flutter-支付宝、微信支付 和 ios UniversalLink
前言
应用的组件分别为 alipay_kit、wechat_kit,即:支付宝框架,微信框架
当然如果要对接国外的一些支付,则选择 stripe 更好,这里不介绍,有需要的可以使用这个
支付宝支付
这里面主要针对 ios
端设置,android
暂时没发现什么特殊问题
首先添加三方库 alipay_kit
、alipay_kit_ios
,这两个都要添加,前面一个主要准备android
,后面一个主要针对于 ios
flutter pub add alipay_kit
flutter pub add alipay_kit_ios
原生端配置
主要ios,android目前不需要配置,出现了问题,可以参考 alipay_kit 解决
ios端
设置,设置 plist
,主要设置网络、跳转支付宝的scheme
iOS 9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。
<key>LSApplicationQueriesSchemes</key>
<array>
<string>alipay</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
ios端
设置,除了上面的,还要设置自己的 scheme
,用于支付宝跳回我们的 app
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>alipay</string>
<key>CFBundleURLSchemes</key>
<array>
<string>alipay123456</string>
</array>
</dict>
</array>
其实际上,在我们的 info 里面设置一下即可,会自动生成上面的代码,前面名称要用 alipay
, 后面取一个相对比较唯一
的字符串即可
flutter端使用
下面注册支付
和授权登陆
回调,不注册监听,直接使用返回的 Future
也可以,但不确定是否覆盖所有版本或者情况
//导入头文件,无需导入ios的那个库
import 'package:alipay_kit/alipay_kit.dart';
//声明参数用于回调使用
late final StreamSubscription<AlipayResp> _alipaySubs; //用于支付
late final StreamSubscription<AlipayResp> _alipayAuthSubs; //用于授权登陆
//注册支付宝支付和授权结果回调
void registerAlipayResp() {
_alipaySubs = Alipay.instance.payResp().listen(listenAlipayPay);
_alipayAuthSubs = Alipay.instance.authResp().listen(_listenAlipayAuth);
}
//支付成功或者失败回调
void listenAlipayPay(AlipayResp resp) {
final String content = 'pay: ${resp.resultStatus} - ${resp.result}';
print(content);
}
//收取那登陆成功或者失败回调
void _listenAlipayAuth(AlipayResp resp) {
final String content = 'auth: ${resp.resultStatus} - ${resp.result}';
print(content);
}
检测是否安装了支付宝app
//返回的 Future 需要等待
final isInstall = await Alipay.instance.isInstalled();
if (!isInstall) {
print("未安装支付宝app");
return;
}
授权登陆,需要传递授权信息字符串,由服务器返回
Alipay.instance.auth(authInfo: "authInfo-123123");
支付接口,需要传递 订单信息字符串,由服务器返回
Alipay.instance.pay(orderInfo: "orderInfo-123123123123")
看到上面你可能知道为什么客户端不需要 appid
之类的信息了,没错,都在服务器返回的信息字符串里面,也是服务器进行部分加密,因此相对客户端比较安全,也是支付宝推荐
ps
:对接前需要先到 支付宝商家平台 申请我们的应用服务,不然等待就比较浪费时间了,前后端都会卡到这里
微信支付、分享、登陆
微信
大部分内容和支付宝
类似,只不过ios端
额外引出了 UniversalLink
(下一小节专门介绍了) 作为新版本跳转传参方案,因此需要额外做一些操作
需要添加三方库 wechat_kit
,
//flutter 添加 alipay_kit
flutter pub add alipay_kit
原生端配置
设置 plist
,以便于能够跳转到微信(使用该 scheme 支持新老版本的跳转),支付宝和微信都存在的情况,内容合并即可
<array>
<string>weixinULAPI</string>
<string>weixin</string>
<string>alipay</string> //也有支付宝的话就是额外加一条即可,这条注释不要放进去
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
我们自己的 schemes
设置,用于支付宝微信跳回我们的app的,就在 info 里面设置
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>alipay</string>
<key>CFBundleURLSchemes</key>
<array>
<string>alipay123456</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>weixin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>weixin123456</string> //注意:这里面填写自己的微信申请的 key
</array>
</dict>
</array>
flutter 端使用
下面是注册微信支付、分享、登陆等回调
//导入包
import 'package:wechat_kit/wechat_kit.dart';
//声明回调参数
late final StreamSubscription<BaseResp> _wechatRespSubs;
AuthResp? _wechatAuthResp;//保存授权的resp码,用于获取后续获取用户信息
//注册微信支付、分享、授权等回调
void registerWechatResp() {
_wechatRespSubs = Wechat.instance.respStream().listen(listenWechatResp);
}
//监听回调,集多个回调与一身
void listenWechatResp(BaseResp resp) {
final String content = '基础: ${resp.errorCode} ${resp.errorMsg}';
print(content);
if (resp is AuthResp) {
//可以保存下来授权的状态(没必要这个),用来获取用户信息
_wechatAuthResp = resp;
final String content = '登录: ${resp.errorCode} ${resp.errorMsg}';
print(content);
} else if (resp is ShareMsgResp) {
final String content = '分享: ${resp.errorCode} ${resp.errorMsg}';
print(content);
} else if (resp is PayResp) {
final String content = '支付: ${resp.errorCode} ${resp.errorMsg}';
print(content);
} else if (resp is LaunchMiniProgramResp) {
//这里就不加入案例了
final String content = '拉起小程序: ${resp.errorCode} ${resp.errorMsg}';
print(content);
}
}
使用前需要先注册,应用启动时注册一次即可,传入商户平台申请的key
,还是有就是 UniversalLink
(后面讲) 配置后的 link
连,如果只是android
端则不需要传递该参数,该参数用于新版本微信(但必须得处理)
await Wechat.instance.registerApp(
appId: wechatKey,
//不了解需要的话可以搜索ios universalLink 通用链接设置,能够直接通过链接唤起app,我也有相关文章哈
universalLink: "https://help.wechat.com/$wechatKey ",
);
注册微信回调函数,伴随启动时注册即可,否则不回调注册的函数
await Wechat.instance.handleInitialWXReq();
判断是否,安装了微信
final isInstalled = await Wechat.instance.isInstalled();
if (!isInstalled) {
print("没安装微信")
return;
}
微信授权登陆,会返回需要的 id 一般登陆和绑定时使用
Wechat.instance.auth(
scope: <String>[WechatScope.SNSAPI_USERINFO],
state: 'auth',
);
分享图片、文字到微信聊天、朋友圈、收藏
//分享方法shareImage、shareText
//scene类型-- SESSION:聊天界面、TIMELINE:朋友圈、FAVORITE:收藏
Wechat.instance.shareText(
scene: WechatScene.SESSION,
text: '测试文字分享',
);
微信支付,里面提供了必要的一些参数,建议从服务器,校验加密后返回,也可以写到本地(毕竟注册也用到的appId)
//这就算完成支付了,这些东西最好让后台走校验接口返回
Wechat.instance.pay(
appId: 'appId', //微信申请的appid
partnerId: 'partnerId', //合作伙伴id
prepayId: 'prepayId', //预支付id
package: 'package', //微信传递参数
nonceStr: 'nonceStr', //随机字符串
timeStamp: 'timeStamp', //时间戳
sign: 'sign', //微信签名
);
问题和解决方案
在测试ios app
的时候,如果碰到老版本的微信
是没问题的,如果碰到用户是新版本的微信
,那需要配置 UniversalLink
,否则跳转微信时,会提示"未验证引用"
,不配置甚至都不知道注册时的 universalLink
要传递啥,别说不适配新版本微信用户(那么这个功能可能大部分人ios用户使用可能都有问题)
因此只需要在 ios 端
配置好 UniversalLink
即可,此外还需要将json配置文件
放到我们后台服务器
中
下面会介绍到
UniversalLink 配置
这个是 ios 9
就已经推出的UniversalLink 通用连接
,点击该链接时,iOS 设备可以不通过 Safari 或网页,直接打开 App,比如在备忘录中直接打开App
ios
开发实际上默认不是必须的,但是 flutter 想接入微信相关服务
,那就是必须
的了,下面介绍下其接入过程
1
、创建一个名字为 apple-app-site-association
的文件
ps
:内容为json
格式,但是不需要.json为扩展名
,扩展名只是方便在操作系统中,给应用标记类型,方便调用产生的一种策略,因此不是必要的)
{
"applinks": {
"apps": [],
"details": [ //可以有多组,加入我们有多个应用,都可以使用这一个文件
{
"appID": "teamID.bundleID", //这里填写的是我们的 团队id,可以在证书或者开发者账号中查看
"paths": [ "*" ] //path路径为,域名后面的路径,可以使用通配符,这里*表示任意路径都可以
}
{
"appID": "D1I2O3P3PK.com.example.appstore", //案例 teamid bundleid
"paths": [ "/qq_zone/*","/qq/*" ] //通配符表示weixin或者qq路径下,后面任意路径或者参数
},
{
"appID": "D1I2O3P3PP.com.example.enterprise",
"paths": [ "/weixin/*","/wechat/10000000/*", /wx/10010/* ]
}
]
}
}
注意上面里面的每一个 path
不应当重复,这个可能被应用到多个应用中
如果 teamId
还是不知道,登陆一下开发者账号,下面的位置,或者打开钥匙串,查看证书,就在我们证书里面也有
2
、将 apple-app-site-association
放到服务器
上面的 apple-app-site-association
文件,我们需要将其放到我们的服务器中的根目录
或.well-known 目录
(推荐这个,默认先访问这个目录)下
例如:
`https://host/.well-known/apple-app-site-association`
`https://host/apple-app-site-association`
另外
iOS会先请求`.well-known`的域名
`apple-app-site-association`只会在APP第一次启动的时候请求一次,
因此文件的任何更新的验证都需要 APP 重新安装或 App Store 更新
Copy
如果说 apple-app-site-association
为了找到我们的文件 paths
里面设置的路径名就是为了区分app
,因此,不同appId
的 paths
也应当不一样,paths
可以取多个的目的是多个路径都能指向我们的同一个 app
注意
:微信
使用我们的 links
的时候,会在我们路径后面拼接参数
,因此,单个 path 后面必须利用通配符,即使用 /*
收尾
3
设置 Associated Domains
进入开发账号,进入 Identifiers
,设置我们 appIdentifiers
对应的 Associated Domains
,加完后,记得更新我们的 profiles
描述文件
然后 app项目配置
加入 Associated Domains
,如下所示,需要 applinks:
开头,后面是我们的域名 host
,例如:www.百度.com
、mail.qq.com
(一般app会用子域名,就像腾讯一样)
4
、填写 UniversalLink
host / path
就是我们的 UniversalLink
链接了,例如
假设我们的域名是 www.百度.com
//这就是我们的 UniversalLink,路径 wechat 专门给微信使用的
//末尾一定要加上 / ,微信要在后面拼参数传递, 域名 路径(wechat) /
https://www.百度.com/wechat/
//这就是我们qq应用的,域名 路径(qq) /
https://www.百度.com/qq/
//如果我们的域名是 mail.qq.com,域名 路径(qq) /
https://mail.qq.com/qq/
验证我们的QQ、微信APP是否支持
验证当前版本 QQ
是否支持Universal Link
,验证地址:qm.qq.com
验证当前微信版本
是否支持Universal Link
,验证地址:help.wechat.com/app/
下面是尝试结果,我的手机是支持的 ?
原生的部分处理
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler{
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL *webpageURL = userActivity.webpageURL;
if(webpageURL && [TencentOAuth CanHandleUniversalLink:webpageURL]) {
// QQ
return [QQApiInterface handleOpenUniversallink:webpageURL delegate:self] || [TencentOAuth HandleUniversalLink:webpageURL];
}else if ([webpageURL.absoluteString hasPrefix:[NSString stringWithFormat:@"https://app.yourDomain.com/test/%@", WeChatAppId]]){
// 微信
if([WXApi handleOpenUniversalLink:userActivity delegate:self]){
}else{
[WXApi handleOpenURL:webpageURL delegate:self];
}
return YES;
}
// 编写我们自己的判断逻辑
}
return YES;