Unity-IOS-微信支付
时间:2018.6
Unity版本:5.6.2
平台:Unity+iOS
微信SDK版本:2018.6月 1.8.2版本
适用人群:unity 开发,oc基础较弱
食用前提:请确保已经清楚官方的整个支付流程。以及unity与ios的基础交互。
官方支付流程链接:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3
写作原因:目前百度上能搜索到的资料看来看去就那几个人的资料,有的虽然看似也能跑通,但是要么是讲解很不清楚,要么是版本老,而且很多情况没将清楚,让新人特别是没有oc基础的新人非常难受。虽然是这么说,但是我也是收益与上述这种不太清楚的攻略才艰难的完成了支付模块。这也是写这篇文章的原因。
支付情况:1.整个模块都在客户端完成的。也就是官方流程图中统一下单也在客户端完成的。个人是不建议这样做的,因为很不安全,其次客户端代码也会稍微复杂点,再加上如果unity开发人员不懂oc,几乎不能调试错误。百度能查到的一些有源码的很多都是整个一起的。
2.客户端二次签名的,上述情况已经包含着一种了,因为像服务器请求已经在客户端了,二次签名当然也在客户端。还有一种是服务器端返回了客户端需要的参数,客户端再次签名,然后调用支付接口api的,这种要注意的是,客户端的签名方法一定要和服务器端相同,否则就会出现签名错误的提示。(跳转到app后,跳转到微信,只有一个错误提示,点击确定会再回到原程序内)
3.服务器端进行二次签名,客户端只需要传参数的,这种是最简单的,不过这种需要后台的配合,让服务器调用统一下单api后,再返回客户端需要参数前,再进行一次签名,这样客户端就只需要很简单的传参数就可以。
本文主要作为引导,以及分享一些本人遇到的问题上的解决。
正式开始:
1.确保你的unity工程发布成Xcode后可以在真机跑通。
2.如果是情况1的话,你需要知道所有微信相关的参数。例如:appid ,商户id,商户迷钥等。
3.如果是情况3的话,你需要知道appid。其他参数让后台发送即可。
4.把微信sdk放入到,unity/plugins/ios/下
5.在unity工程中的playerSetting-otherSetting-Supported URL schemes中加入微信支付appid。
6.编写unity调用iOS的代码:
[DllImport("__Internal")]
private static extern void _startUp(string appid, string partnerid, string prepayid, string noncster,string time,string sign);
ps:这句的代码意思是有一个_startUp函数是外部函数,他需要这些参数。_statUp即是要在Xcode中实现的调用微信sdk的方法。
7.写一个回调函数 payCallBack(string info),记下这个脚本所挂载的物体,iOS端再调用了微信支付后需要将参数传递给unity端,用来方便后续的逻辑处理。
到此。unity 端的工作就完成了。
8:配置
我们可以发布xcode代码了,unity发布xcode时有两个选项,一个是append 还有一个replace 一个是增量一个是替换,因为微信支付在xcode端还有很多参数和属性要陪着,多数情况下,unity发布的工程,不会一次就ok。如果每次都要重新配置那些属性和引用的框架,怕不是要疯。所以建议,再后续的发布时选择append。这样,添加好的框架和所需要填写的linker就不需要每次配置。
发布到xcode后,要按照官方的教程进行配置。
这里不偷懒,也再写一下。[图片上传失败...(image-18bb14-1528966115678)]
步骤名对应官方教程,不清楚的,还可以到官方再次确认。
3]微信开放平台新增了微信模块用户统计功能,便于开发者统计微信功能模块的用户使用和活跃情况。开发者需要在工程中链接上:SystemConfiguration.framework, libz.dylib, libsqlite3.0.dylib, libc++.dylib, Security.framework, CoreTelephony.framework, CFNetwork.framework。
pic1.jpg
4.在你的工程文件中选择Build Setting,在"Other Linker Flags"中加入"-Objc -all_load",在Search Paths中添加 libWeChatSDK.a ,WXApi.h,WXApiObject.h,文件所在位置(如下图所示)。
pic2.jpg
Ps:官方写的是两个-Objc 与 -all_load,但我在使用中如果两个都加的话,编译时会爆linker error -v错误,删掉 -all_load就好了,查了百度可能是引用添加重复导致的。
5,在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“URL type“添加“URL scheme”为你所注册的应用程序id(如下图所示)。
pic3.jpg6. 在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“LSApplicationQueriesSchemes“添加weixin(如下图所示)。
pic4.jpgPs:5,6提到的TARGETS 就是我们发布到Xcode中的Info.plist文件。
9,引用头文件
在UnityAppController.h 添加wxapi的回掉 如下图
pic5.jpg10.在 UnityAppController.mm 中完成,首先找到文件中 didFinishLaunchingWithOptions 函数的位置,加入
[WXApi registerApp:@"wxa72a15850b99c773"];
代码。
这里写你自己的app ID,千万别忘了改。
11.添加支付成功后的回掉方法,以及两个官方要求的方法。
//iOS9 之后使用这个回调方法。
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
if ([url.host isEqualToString:@"pay"]) {
return [WXApi handleOpenURL:url delegate:self];
}
return YES;
}
#pragma mark - 微信支付的代理方法
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
return [WXApi handleOpenURL:url delegate:self];
}
-(void)onResp:(BaseResp*)resp
{
if ([resp isKindOfClass:[PayResp class]]){
PayResp *response = (PayResp*)resp;
switch(response.errCode){
case WXSuccess:
//服务器端查询支付通知或查询API返回的结果再提示成功
NSLog(@"\n\n\n\n\n支付成功\n\n\n\n\n\n");
UnitySendMessage("AppManager", "OnGetPayResponse","0");
//发送通知给带有微信支付功能的视图控制器,告诉他支付成功了,请求后台订单状态,如果后台返回的订单也是成功的状态,那么可以进行下一步操作
[[NSNotificationCenter defaultCenter] postNotificationName:@"WeiXinPaysucceed" object:nil userInfo:nil];
break;
default:
/*
resp.errCode = 2 用户取消支付
resp.errCode = -1 错误
*/
NSLog(@"支付失败,retcode=%d ---- %@",resp.errCode,resp.errStr);
break;
}
}
}//微信支付成功的回调方法(回调函数)
Ps:UnitySendMessage("AppManager", "OnGetPayResponse","0");
这个就是ios 给unity 传值的方法。一个参数为接受方法的物体名,第二个是物体所挂载脚本的函数名,第三个是参数。
注意。这3个方法都要写在
@implementation UnityAppController
@end
实现内。
11,编写支付方法_startUp
针对文章开始分的三种情况,这里分别给出代码。但是我本人只验证过情况二和情况三。没有验证过情况一,不过应该也没有问题。
情况一:(直接搬运的下面链接博文的代码,需要修改的较多,不过也是很容易改的)
void wxpaytest(char* orderN,char* orderP)
{
payRequsestHandler *handle = [[payRequsestHandler alloc]init];
if ( [handle init:APP_id mch_id:MCH_id]) {
NSLog(@"初始化成功");
}
//设置商户密钥
[handle setKey:PARTNER_id];
//提交预支付,获得prepape_id
NSString *order_name = [[NSString alloc] initWithUTF8String:orderN]; //订单标题
NSString *order_price = [[NSString alloc] initWithUTF8String:orderP];//测试价格 分为单位
NSString *nocify_URL = nocify_url; //回调借口
NSString *noncestr = [NSString stringWithFormat:@"%d", rand()]; //随机串
NSString *orderno = [NSString stringWithFormat:@"%ld",time(0)];
NSMutableDictionary *params = [@{@"appid":APP_id,
@"mch_id":MCH_id,
@"device_info":[[[UIDevice currentDevice] identifierForVendor] UUIDString],
@"nonce_str":noncestr,
@"trade_type":@"APP",
@"body":order_name,
@"notify_url":nocify_URL,
@"out_trade_no":orderno,//商户订单号:这个必须用后台的订单号
@"spbill_create_ip":@"8.8.8.8",
@"total_fee":order_price}mutableCopy];
//提交预支付两次签名得到预支付订单的id(每次的请求得到的预支付订单id都不同)
NSString *prepate_id = [handle sendPrepay:params];
//提交预订单成功
if (prepate_id != nil) {
PayReq *request = [[PayReq alloc]init];
//商家id
request.partnerId = MCH_id;
//订单id
request.prepayId = prepate_id;
//扩展字段(官方文档:暂时填写固定值)
request.package = @"Sign=WXPay";
//随机字符串
request.nonceStr = noncestr;
//时间戳
request.timeStamp = (UInt32)[[NSDate date] timeIntervalSince1970];
//sign参数(很经常出现的问题:就是调起支付到微信那边只出现一个确定按钮,单击确认按钮直接返回到app,出现这个问题100%是sign参数的问题)
/*
参数依次是: appid_key、partnerid_key、prepayid_key、固定值Sign=WXPay、预支付的随机数(跟上面得到预支付订单的随机数要一致)、支付时间(秒)
*/
//request.sign = [selfClass createMD5SingForPay:APP_id partnerid:MCH_id prepayid:prepate_id package:@"Sign=WXPay" noncestr:noncestr timestamp:(UInt32)[[NSDate date] timeIntervalSince1970]];
NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
[signParams setObject:APP_id forKey:@"appid"];
[signParams setObject:noncestr forKey:@"noncestr"];
[signParams setObject:@"Sign=WXPay" forKey:@"package"];
[signParams setObject:MCH_id forKey:@"partnerid"];
[signParams setObject:prepate_id forKey:@"prepayid"];
[signParams setObject:[NSString stringWithFormat:@"%u",(unsigned int)(UInt32)[[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"];
NSMutableString *contentString =[NSMutableString string];
NSArray *keys = [signParams allKeys];
//按字母顺序排序
NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
//拼接字符串
for (NSString *categoryId in sortedArray) {
if ( ![[signParams objectForKey:categoryId] isEqualToString:@""]
&& ![[signParams objectForKey:categoryId] isEqualToString:@"sign"]
&& ![[signParams objectForKey:categoryId] isEqualToString:@"key"]
)
{
[contentString appendFormat:@"%@=%@&", categoryId, [signParams objectForKey:categoryId]];
}
}
//添加商户密钥key字段
[contentString appendFormat:@"key=%@",PARTNER_id];
// NSString *resul = [self md5:contentString];
const char *cStr = [contentString UTF8String];
unsigned char result[16]= "0123456789abcdef";
CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
//这里的x是小写则产生的md5也是小写,x是大写则md5是大写,这里只能用大写,微信的大小写验证很逗
NSString *resul = [NSString stringWithFormat:
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
request.sign = resul;
//带起微信支付
if ([WXApi sendReq:request]) {
}else{
//未安装微信客户端
[[[UIAlertView alloc]initWithTitle:@"测试demo" message:@"您还未安装微信客户端,请前往Appstore下载或者选择其他支付方式!" delegate:nil cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil]show];
}
//接受成功的通知
[[NSNotificationCenter defaultCenter]addObserver:nil selector:@selector(succeed) name:WEIXINPAYSUCCESSED object:nil];
NSLog(@"wanc");
}
情况2:
extern "C" void _startUp(char* appid,char* partnerid,char *prepayid,char *noncster,char *time,char *sign,char *messageName)
{
PayReq *request = [[PayReq alloc]init];
request.openID =[NSString stringWithUTF8String:appid];
//商家id
request.partnerId = [NSString stringWithUTF8String:partnerid];
//订单id
request.prepayId = [NSString stringWithUTF8String:prepayid];
//扩展字段(官方文档:暂时填写固定值)
request.package = @"Sign=WXPay";
//随机字符串
request.nonceStr = [NSString stringWithUTF8String:noncster];
//时间戳
//request.timeStamp = (UInt32)[[NSDate date] timeIntervalSince1970];
NSMutableString *stampmp =[[NSString stringWithUTF8String:time] mutableCopy];
request.timeStamp = stampmp.intValue;
//NSLog(@"时间戳=%@",(UInt32)[[NSDate date] timeIntervalSince1970]);
//sign参数(很经常出现的问题:就是调起支付到微信那边只出现一个确定按钮,单击确认按钮直接返回到app,出现这个问题100%是sign参数的问题)
/*
参数依次是: appid_key、partnerid_key、prepayid_key、固定值Sign=WXPay、预支付的随机数(跟上面得到预支付订单的随机数要一致)、支付时间(秒)
*/
//request.sign = [selfClass createMD5SingForPay:APP_id partnerid:MCH_id prepayid:prepate_id package:@"Sign=WXPay" noncestr:noncestr timestamp:(UInt32)[[NSDate date] timeIntervalSince1970]];
NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
[signParams setObject:[NSString stringWithUTF8String:appid] forKey:@"appid"];
[signParams setObject:[NSString stringWithUTF8String:noncster] forKey:@"noncestr"];
[signParams setObject:@"Sign=WXPay" forKey:@"package"];
[signParams setObject:[NSString stringWithUTF8String:partnerid] forKey:@"partnerid"];
[signParams setObject:[NSString stringWithUTF8String:prepayid] forKey:@"prepayid"];
//[signParams setObject:[NSString stringWithFormat:@"%u",(unsigned int)(UInt32)[[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"];
[signParams setObject:[NSString stringWithFormat:@"%u",stampmp.intValue] forKey:@"timestamp"];
NSMutableString *contentString =[NSMutableString string];
NSArray *keys = [signParams allKeys];
//按字母顺序排序
NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
//拼接字符串
for (NSString *categoryId in sortedArray) {
if ( ![[signParams objectForKey:categoryId] isEqualToString:@""]
&& ![[signParams objectForKey:categoryId] isEqualToString:@"sign"]
&& ![[signParams objectForKey:categoryId] isEqualToString:@"key"]
)
{
[contentString appendFormat:@"%@=%@&", categoryId, [signParams objectForKey:categoryId]];
}
}
//添加商户密钥key字段
[contentString appendFormat:@"key=%@“,@“XXXXXXXXXXXXX”];
// NSString *resul = [self md5:contentString];
const char *cStr = [contentString UTF8String];
//unsigned char result[16]= "0123456789abcdef";
unsigned char result[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
//这里的x是小写则产生的md5也是小写,x是大写则md5是大写,这里只能用大写,微信的大小写验证很逗
NSString *resul = [NSString stringWithFormat:
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
request.sign = resul;
//带起微信支付
if ([WXApi sendReq:request]) {
}else{
//未安装微信客户端
[[[UIAlertView alloc]initWithTitle:@"测试demo" message:@"您还未安装微信客户端,请前往Appstore下载或者选择其他支付方式!" delegate:nil cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil]show];
}
}
情况3:
extern "C" void _startUp(char* appid,char* partnerid,char *prepayid,char *noncster,char *time,char *sign,char *messageName)
{
PayReq *request = [[PayReq alloc]init];
request.openID =[NSString stringWithUTF8String:appid];
//商家id
request.partnerId = [NSString stringWithUTF8String:partnerid];
//订单id
request.prepayId = [NSString stringWithUTF8String:prepayid];
//扩展字段(官方文档:暂时填写固定值)
request.package = @"Sign=WXPay";
//随机字符串
request.nonceStr = [NSString stringWithUTF8String:noncster];
//时间戳
//request.timeStamp = (UInt32)[[NSDate date] timeIntervalSince1970];
NSMutableString *stampmp =[[NSString stringWithUTF8String:time] mutableCopy];
request.timeStamp = stampmp.intValue;
request.sign =[NSString stringWithUTF8String:sign];
//带起微信支付
if ([WXApi sendReq:request]) {
}else{
//未安装微信客户端
[[[UIAlertView alloc]initWithTitle:@"测试demo" message:@"您还未安装微信客户端,请前往Appstore下载或者选择其他支付方式!" delegate:nil cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil]show];
}
}
最后,本人不是ios开发,所以可能会再某些地方描述错误。而且我相信如果你对oc也不是很了解的话。在目前我这一定是最新最详细的教程了。如果有错误,或者帮助了你,千万不要吝啬评论噢。
最最后,还是要感谢下面两个博客的作者。
推荐博文:
情况一接入流程:https://blog.csdn.net/qq_24189773/article/details/78360099
Ps: 他文章的一些代码需要根据自己的情况来改。
情况三接入流程:
https://fengyu.name/article/449
ps:写的很清楚明白。[图片上传失败...(image-5eddc9-1528966603827)]