APP支付接入方法指引
微信公众平台
微信公众平台是微信公众账号申请入口和管理后台。商户可以在公众平台提交基本资料、业务资料、财务资料申请开通微信支付功能。
平台入口:http://mp.weixin.qq.com。
微信商户平台
微信商户平台是微信支付相关的商户功能集合,包括参数配置、支付数据查询与统计、在线退款、代金券或立减优惠运营等功能。
平台入口:http://pay.weixin.qq.com。
sign就是签名,是你提交的数据经过一定规则组和后用md5加密的一个东西(官方文档有介绍)。是用来验证数据是否被第三方篡改的一个凭证。你发送到微信服务器,微信服务器会用你发来的参数生成sign。再和你传过去的sign做对比。
签名算法
签名生成的通用步骤如下:
第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
- ◆ 参数名ASCII码从小到大排序(字典序);
- ◆ 如果参数的值为空不参与签名;
- ◆ 参数名区分大小写;
- ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
- ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API --https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1】。商户系统先调用该接口(URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder)在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再在APP里面调起支付。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明--https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5】
步骤5:商户后台接收支付通知。api参见【支付结果通知API---https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7】
步骤6:商户后台查询支付结果。,api参见【查询订单API---https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_2】
====商户服务器生成支付订单,先调用【统一下单API】生成预付单,获取到prepay_id后将参数再次签名传输给APP发起支付。以下是调起微信支付的关键代码:
PayReq *request = [[[PayReq alloc] init] autorelease];
request.partnerId = @"10000100";
request.prepayId= @"1101000000140415649af9fc314aa427";
request.package = @"Sign=WXPay";
request.nonceStr= @"a462b76e7436e98e0ed6e13c64b4fd1c";
request.timeStamp= @"1397527777";
request.sign= @"582282D72DD2B03AD892830965F428CB16E7A256";
[WXApi sendReq:request];
注意:该sign生成字段名列表见调起支付API
===、支付结果回调
照微信SDK Sample,在类实现onResp函数,支付完成后,微信APP会返回到商户APP并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码,如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意 一定不能以客户端返回作为用户支付的结果,应以服务器端的接收的支付通知或查询API返回的结果为准。代码示例如下:
-(void)onResp:(BaseResp*)resp{ if ([respisKindOfClass:[PayRespclass]]){ PayResp*response=(PayResp*)resp; switch(response.errCode){ caseWXSuccess: //服务器端查询支付通知或查询API返回的结果再提示成功 NSlog(@"支付成功"); break; default: NSlog(@"支付失败,retcode=%d",resp.errCode); break; } } }
#import "AppDelegate.h"
#import "WXApi.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//注册APP,
[WXApiregisterApp:@"wxb4ba3c02aa476ea1"];
returnYES;
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
// if ([url.scheme isEqualToString:@"wx23a1f7f291ef4b3d"])
// {
// return [WXApi handleOpenURL:url delegate:(id<WXApiDelegate>)self];
// }
// 跳转到URL scheme中配置的地址
//NSLog(@"跳转到URL scheme中配置的地址-->%@",url);
return [WXApi handleOpenURL:url delegate:(id<WXApiDelegate>)self];//设置微信代理,并回调结果url,回调成功返回yes
}
//微信回调,有支付结果的时候会回调这个方法
- (void)onResp:(BaseResp *)resp
{
// NSString *strMsg = [NSString stringWithFormat:@"errcode:%d", resp.errCode];
// NSString *strTitle;
// if([resp isKindOfClass:[SendMessageToWXResp class]])
// {
// strTitle = [NSString stringWithFormat:@"发送媒体消息结果"];
// }
// 支付结果回调
if([respisKindOfClass:[PayRespclass]]){
switch (resp.errCode) {
caseWXSuccess:{
//支付返回结果,实际支付结果需要去自己的服务器端查询
NSNotification *notification = [NSNotificationnotificationWithName:@"ORDER_PAY_NOTIFICATION"object:@"success"];
[[NSNotificationCenterdefaultCenter]postNotification:notification];
break;
}
default:{
NSNotification *notification = [NSNotificationnotificationWithName:@"ORDER_PAY_NOTIFICATION"object:@"fail"];
[[NSNotificationCenterdefaultCenter]postNotification:notification];
break;
}
}
}
}
#import "WXApi.h"
#import "NSString+MD5.h"
MD5 算法:
- (id)MD5
{
constchar *cStr = [selfUTF8String];
unsignedchar digest[16];
CC_MD5( cStr,strlen(cStr), digest );// This is the md5 call
NSMutableString *output = [NSMutableStringstringWithCapacity:CC_MD5_DIGEST_LENGTH *2];
for(int i =0; i <CC_MD5_DIGEST_LENGTH; i++)
[output appendFormat:@"%02x", digest[i]];
return output;
}
//商户关键信息 ,微信分配给商户的appID,商户号,商户的密钥
@property (nonatomic,strong)NSString *appId,*mchId,*spKey;
@end
@implementation WechatPayVC
- (void)viewDidLoad {
[superviewDidLoad];
// 判断用户是否安装微信
//如果判断结果一直为NO,可能appid无效,这里的是无效的
// if([WXApi isWXAppInstalled])
{
// 监听一个通知
[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(getOrderPayResult:)name:@"ORDER_PAY_NOTIFICATION"object:nil];
}
}
http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php为测试数据,一般可以从这儿拿到的数据都可以让服务器端去完成,客户端只需获取到然后配置到PayReq,即可吊起微信;
-(void)easyPay {
[AFNgetWithUrl:@"http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php"params:nilsuccess:^(id response) {
NSLog(@"%@",response);
//配置调起微信支付所需要的参数
PayReq *req = [[PayReqalloc]init];
req.partnerId = [responseobjectForKey:@"partnerid"];
req.prepayId = [responseobjectForKey:@"prepayid"];
req.package = [responseobjectForKey:@"package"];
req.nonceStr = [responseobjectForKey:@"noncestr"];
req.timeStamp = [[responseobjectForKey:@"timestamp"]intValue];
req.sign = [responseobjectForKey:@"sign"];
//调起微信支付
if ([WXApisendReq:req]) {
NSLog(@"吊起成功");
}
} fail:^(NSError *error) {
NSLog(@"%@",error);
}];
}
#pragma mark - 收到支付成功的消息后作相应的处理
- (void)getOrderPayResult:(NSNotification *)notification
{
if ([notification.objectisEqualToString:@"success"]) {
NSLog(@"支付成功");
} else {
NSLog(@"支付失败");
}
}
//有的服务器没有对sign字段进行二次签名,需要客户端进行,下面这些是对吊起支付时的sign字段进行二次签名的,这些操作可以和服务器协商全让服务器做了,因为签名算法都是一样的,后台已经进行了第一次的签名,第二次只是多了prePayid,算法都是一样的没必要客户端再写一次算法
//注意:下面的方法不能直接使用,这里只是给出了算法和参数配置,相应的填充数据就行
//创建package签名
-(NSString*) createMd5Sign:(NSMutableDictionary*)dict
{
NSMutableString *contentString =[NSMutableStringstring];
NSArray *keys = [dictallKeys];
//按字母顺序排序
NSArray *sortedArray = [keyssortedArrayUsingComparator:^NSComparisonResult(id obj1,id obj2) {
return [obj1compare:obj2options:NSNumericSearch];
}];
//拼接字符串
for (NSString *categoryIdin sortedArray) {
if ( ![[dictobjectForKey:categoryId]isEqualToString:@""]
&& ![categoryId isEqualToString:@"sign"]
&& ![categoryId isEqualToString:@"key"]
)
{
[contentString appendFormat:@"%@=%@&", categoryId, [dictobjectForKey:categoryId]];
}
}
//添加key字段
[contentString appendFormat:@"key=%@",self.spKey];
//得到MD5 sign签名
NSString *md5Sign =[contentStringMD5];
return md5Sign;
}
- (NSMutableDictionary*)payWithprePayid:(NSString*)prePayid
{
if(prePayid ==nil)
{
NSLog(@"prePayid为空");
returnnil;
}
//获取到prepayid后进行第二次签名
NSString *package, *time_stamp, *nonce_str;
//设置支付参数
time_t now;
time(&now);
time_stamp = [NSStringstringWithFormat:@"%ld", now];
nonce_str = [time_stamp MD5];
//重新按提交格式组包,微信客户端暂只支持package=Sign=WXPay格式,须考虑升级后支持携带package具体参数的情况
//package = [NSString stringWithFormat:@"Sign=%@",package];
package = @"Sign=WXPay";
//第二次签名参数列表
NSMutableDictionary *signParams = [NSMutableDictionarydictionary];
NSLog(@"%@",signParams);
[signParams setObject:self.appId forKey:@"appid"];
[signParams setObject:self.mchId forKey:@"partnerid"];
[signParams setObject: nonce_str forKey:@"noncestr"];
[signParams setObject: package forKey:@"package"];
[signParams setObject: time_stamp forKey:@"timestamp"];
[signParams setObject: prePayid forKey:@"prepayid"];
//生成签名
// NSString *sign = @"7175C293D3F706F4B40EAD092A3FBAB8";
NSString *sign = [selfcreateMd5Sign:signParams];
//添加签名
[signParams setObject: sign forKey:@"sign"];
//返回参数列表
return signParams;
}