時(shí)間:2018.6
Unity版本:5.6.2
平臺(tái):Unity+iOS
微信SDK版本:2018.6月 1.8.2版本
適用人群:unity 開發(fā)爽蝴,oc基礎(chǔ)較弱
食用前提:請(qǐng)確保已經(jīng)清楚官方的整個(gè)支付流程奉瘤。以及unity與ios的基礎(chǔ)交互谷市。
官方支付流程鏈接:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3
寫作原因:目前百度上能搜索到的資料看來看去就那幾個(gè)人的資料继准,有的雖然看似也能跑通舶沿,但是要么是講解很不清楚阱州,要么是版本老班缰,而且很多情況沒將清楚,讓新人特別是沒有oc基礎(chǔ)的新人非常難受蜘矢。雖然是這么說狂男,但是我也是收益與上述這種不太清楚的攻略才艱難的完成了支付模塊。這也是寫這篇文章的原因品腹。
支付情況:1.整個(gè)模塊都在客戶端完成的岖食。也就是官方流程圖中統(tǒng)一下單也在客戶端完成的。個(gè)人是不建議這樣做的舞吭,因?yàn)楹懿话踩堇浯慰蛻舳舜a也會(huì)稍微復(fù)雜點(diǎn),再加上如果unity開發(fā)人員不懂oc羡鸥,幾乎不能調(diào)試錯(cuò)誤蔑穴。百度能查到的一些有源碼的很多都是整個(gè)一起的。
2.客戶端二次簽名的惧浴,上述情況已經(jīng)包含著一種了存和,因?yàn)橄穹?wù)器請(qǐng)求已經(jīng)在客戶端了,二次簽名當(dāng)然也在客戶端衷旅。還有一種是服務(wù)器端返回了客戶端需要的參數(shù)捐腿,客戶端再次簽名,然后調(diào)用支付接口api的柿顶,這種要注意的是茄袖,客戶端的簽名方法一定要和服務(wù)器端相同,否則就會(huì)出現(xiàn)簽名錯(cuò)誤的提示九串。(跳轉(zhuǎn)到app后绞佩,跳轉(zhuǎn)到微信寺鸥,只有一個(gè)錯(cuò)誤提示,點(diǎn)擊確定會(huì)再回到原程序內(nèi))
3.服務(wù)器端進(jìn)行二次簽名品山,客戶端只需要傳參數(shù)的胆建,這種是最簡單的,不過這種需要后臺(tái)的配合肘交,讓服務(wù)器調(diào)用統(tǒng)一下單api后笆载,再返回客戶端需要參數(shù)前,再進(jìn)行一次簽名涯呻,這樣客戶端就只需要很簡單的傳參數(shù)就可以凉驻。
本文主要作為引導(dǎo),以及分享一些本人遇到的問題上的解決复罐。
正式開始:
1.確保你的unity工程發(fā)布成Xcode后可以在真機(jī)跑通涝登。
2.如果是情況1的話,你需要知道所有微信相關(guān)的參數(shù)效诅。例如:appid ,商戶id,商戶迷鑰等胀滚。
3.如果是情況3的話,你需要知道appid乱投。其他參數(shù)讓后臺(tái)發(fā)送即可咽笼。
4.把微信sdk放入到,unity/plugins/ios/下
5.在unity工程中的playerSetting-otherSetting-Supported URL schemes中加入微信支付appid戚炫。
6.編寫unity調(diào)用iOS的代碼:
[DllImport("__Internal")]
private static extern void _startUp(string appid, string partnerid, string prepayid, string noncster,string time,string sign);
ps:這句的代碼意思是有一個(gè)_startUp函數(shù)是外部函數(shù)剑刑,他需要這些參數(shù)。_statUp即是要在Xcode中實(shí)現(xiàn)的調(diào)用微信sdk的方法双肤。
7.寫一個(gè)回調(diào)函數(shù) payCallBack(string info)施掏,記下這個(gè)腳本所掛載的物體,iOS端再調(diào)用了微信支付后需要將參數(shù)傳遞給unity端茅糜,用來方便后續(xù)的邏輯處理其监。
到此。unity 端的工作就完成了限匣。
8:配置
我們可以發(fā)布xcode代碼了,unity發(fā)布xcode時(shí)有兩個(gè)選項(xiàng)毁菱,一個(gè)是append 還有一個(gè)replace 一個(gè)是增量一個(gè)是替換米死,因?yàn)槲⑿胖Ц对趚code端還有很多參數(shù)和屬性要陪著,多數(shù)情況下贮庞,unity發(fā)布的工程峦筒,不會(huì)一次就ok。如果每次都要重新配置那些屬性和引用的框架窗慎,怕不是要瘋综慎。所以建議,再后續(xù)的發(fā)布時(shí)選擇append吆玖。這樣珊豹,添加好的框架和所需要填寫的linker就不需要每次配置。
發(fā)布到xcode后获洲,要按照官方的教程進(jìn)行配置。
這里不偷懶,也再寫一下帆精。[圖片上傳失敗...(image-18bb14-1528966115678)]
步驟名對(duì)應(yīng)官方教程,不清楚的隧魄,還可以到官方再次確認(rèn)卓练。
3]微信開放平臺(tái)新增了微信模塊用戶統(tǒng)計(jì)功能,便于開發(fā)者統(tǒng)計(jì)微信功能模塊的用戶使用和活躍情況购啄。開發(fā)者需要在工程中鏈接上:SystemConfiguration.framework, libz.dylib, libsqlite3.0.dylib, libc++.dylib, Security.framework, CoreTelephony.framework, CFNetwork.framework襟企。
4.在你的工程文件中選擇Build Setting,在"Other Linker Flags"中加入"-Objc -all_load"狮含,在Search Paths中添加 libWeChatSDK.a 顽悼,WXApi.h,WXApiObject.h辉川,文件所在位置(如下圖所示)表蝙。
Ps:官方寫的是兩個(gè)-Objc 與 -all_load,但我在使用中如果兩個(gè)都加的話乓旗,編譯時(shí)會(huì)爆linker error -v錯(cuò)誤府蛇,刪掉 -all_load就好了,查了百度可能是引用添加重復(fù)導(dǎo)致的屿愚。
5汇跨,在Xcode中,選擇你的工程設(shè)置項(xiàng)妆距,選中“TARGETS”一欄穷遂,在“info”標(biāo)簽欄的“URL type“添加“URL scheme”為你所注冊(cè)的應(yīng)用程序id(如下圖所示)。
6. 在Xcode中娱据,選擇你的工程設(shè)置項(xiàng)蚪黑,選中“TARGETS”一欄,在“info”標(biāo)簽欄的“LSApplicationQueriesSchemes“添加weixin(如下圖所示)中剩。
Ps:5忌穿,6提到的TARGETS 就是我們發(fā)布到Xcode中的Info.plist文件。
9结啼,引用頭文件
在UnityAppController.h 添加wxapi的回掉 如下圖
10.在 UnityAppController.mm 中完成掠剑,首先找到文件中 didFinishLaunchingWithOptions 函數(shù)的位置,加入
[WXApi registerApp:@"wxa72a15850b99c773"];
代碼郊愧。
這里寫你自己的app ID朴译,千萬別忘了改井佑。
11.添加支付成功后的回掉方法,以及兩個(gè)官方要求的方法眠寿。
//iOS9 之后使用這個(gè)回調(diào)方法躬翁。
- (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:
//服務(wù)器端查詢支付通知或查詢API返回的結(jié)果再提示成功
NSLog(@"\n\n\n\n\n支付成功\n\n\n\n\n\n");
UnitySendMessage("AppManager", "OnGetPayResponse","0");
//發(fā)送通知給帶有微信支付功能的視圖控制器,告訴他支付成功了澜公,請(qǐng)求后臺(tái)訂單狀態(tài)姆另,如果后臺(tái)返回的訂單也是成功的狀態(tài),那么可以進(jìn)行下一步操作
[[NSNotificationCenter defaultCenter] postNotificationName:@"WeiXinPaysucceed" object:nil userInfo:nil];
break;
default:
/*
resp.errCode = 2 用戶取消支付
resp.errCode = -1 錯(cuò)誤
*/
NSLog(@"支付失敗坟乾,retcode=%d ---- %@",resp.errCode,resp.errStr);
break;
}
}
}//微信支付成功的回調(diào)方法(回調(diào)函數(shù))
Ps:UnitySendMessage("AppManager", "OnGetPayResponse","0");
這個(gè)就是ios 給unity 傳值的方法迹辐。一個(gè)參數(shù)為接受方法的物體名,第二個(gè)是物體所掛載腳本的函數(shù)名甚侣,第三個(gè)是參數(shù)明吩。
注意。這3個(gè)方法都要寫在
@implementation UnityAppController
@end
實(shí)現(xiàn)內(nèi)殷费。
11印荔,編寫支付方法_startUp
針對(duì)文章開始分的三種情況,這里分別給出代碼详羡。但是我本人只驗(yàn)證過情況二和情況三仍律。沒有驗(yàn)證過情況一,不過應(yīng)該也沒有問題实柠。
情況一:(直接搬運(yùn)的下面鏈接博文的代碼水泉,需要修改的較多,不過也是很容易改的)
void wxpaytest(char* orderN,char* orderP)
{
payRequsestHandler *handle = [[payRequsestHandler alloc]init];
if ( [handle init:APP_id mch_id:MCH_id]) {
NSLog(@"初始化成功");
}
//設(shè)置商戶密鑰
[handle setKey:PARTNER_id];
//提交預(yù)支付窒盐,獲得prepape_id
NSString *order_name = [[NSString alloc] initWithUTF8String:orderN]; //訂單標(biāo)題
NSString *order_price = [[NSString alloc] initWithUTF8String:orderP];//測試價(jià)格 分為單位
NSString *nocify_URL = nocify_url; //回調(diào)借口
NSString *noncestr = [NSString stringWithFormat:@"%d", rand()]; //隨機(jī)串
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,//商戶訂單號(hào):這個(gè)必須用后臺(tái)的訂單號(hào)
@"spbill_create_ip":@"8.8.8.8",
@"total_fee":order_price}mutableCopy];
//提交預(yù)支付兩次簽名得到預(yù)支付訂單的id(每次的請(qǐng)求得到的預(yù)支付訂單id都不同)
NSString *prepate_id = [handle sendPrepay:params];
//提交預(yù)訂單成功
if (prepate_id != nil) {
PayReq *request = [[PayReq alloc]init];
//商家id
request.partnerId = MCH_id;
//訂單id
request.prepayId = prepate_id;
//擴(kuò)展字段(官方文檔:暫時(shí)填寫固定值)
request.package = @"Sign=WXPay";
//隨機(jī)字符串
request.nonceStr = noncestr;
//時(shí)間戳
request.timeStamp = (UInt32)[[NSDate date] timeIntervalSince1970];
//sign參數(shù)(很經(jīng)常出現(xiàn)的問題:就是調(diào)起支付到微信那邊只出現(xiàn)一個(gè)確定按鈕草则,單擊確認(rèn)按鈕直接返回到app,出現(xiàn)這個(gè)問題100%是sign參數(shù)的問題)
/*
參數(shù)依次是: appid_key蟹漓、partnerid_key炕横、prepayid_key、固定值Sign=WXPay葡粒、預(yù)支付的隨機(jī)數(shù)(跟上面得到預(yù)支付訂單的隨機(jī)數(shù)要一致)份殿、支付時(shí)間(秒)
*/
//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是小寫則產(chǎn)生的md5也是小寫,x是大寫則md5是大寫嗽交,這里只能用大寫伯铣,微信的大小寫驗(yàn)證很逗
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:@"您還未安裝微信客戶端,請(qǐng)前往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];
//擴(kuò)展字段(官方文檔:暫時(shí)填寫固定值)
request.package = @"Sign=WXPay";
//隨機(jī)字符串
request.nonceStr = [NSString stringWithUTF8String:noncster];
//時(shí)間戳
//request.timeStamp = (UInt32)[[NSDate date] timeIntervalSince1970];
NSMutableString *stampmp =[[NSString stringWithUTF8String:time] mutableCopy];
request.timeStamp = stampmp.intValue;
//NSLog(@"時(shí)間戳=%@",(UInt32)[[NSDate date] timeIntervalSince1970]);
//sign參數(shù)(很經(jīng)常出現(xiàn)的問題:就是調(diào)起支付到微信那邊只出現(xiàn)一個(gè)確定按鈕,單擊確認(rèn)按鈕直接返回到app轮纫,出現(xiàn)這個(gè)問題100%是sign參數(shù)的問題)
/*
參數(shù)依次是: appid_key、partnerid_key焚鲜、prepayid_key掌唾、固定值Sign=WXPay放前、預(yù)支付的隨機(jī)數(shù)(跟上面得到預(yù)支付訂單的隨機(jī)數(shù)要一致)、支付時(shí)間(秒)
*/
//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是小寫則產(chǎn)生的md5也是小寫糯彬,x是大寫則md5是大寫凭语,這里只能用大寫,微信的大小寫驗(yàn)證很逗
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:@"您還未安裝微信客戶端,請(qǐng)前往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];
//擴(kuò)展字段(官方文檔:暫時(shí)填寫固定值)
request.package = @"Sign=WXPay";
//隨機(jī)字符串
request.nonceStr = [NSString stringWithUTF8String:noncster];
//時(shí)間戳
//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:@"您還未安裝微信客戶端,請(qǐng)前往Appstore下載或者選擇其他支付方式!" delegate:nil cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil]show];
}
}
最后撩扒,本人不是ios開發(fā)似扔,所以可能會(huì)再某些地方描述錯(cuò)誤。而且我相信如果你對(duì)oc也不是很了解的話搓谆。在目前我這一定是最新最詳細(xì)的教程了炒辉。如果有錯(cuò)誤,或者幫助了你泉手,千萬不要吝嗇評(píng)論噢黔寇。
最最后,還是要感謝下面兩個(gè)博客的作者斩萌。
推薦博文:
情況一接入流程:https://blog.csdn.net/qq_24189773/article/details/78360099
Ps: 他文章的一些代碼需要根據(jù)自己的情況來改缝裤。
情況三接入流程:
https://fengyu.name/article/449
ps:寫的很清楚明白。[圖片上傳失敗...(image-5eddc9-1528966603827)]