iOS之微信app支付流程解析(一)









  

APP支付接入方法指引

第一步:注册开放平台账号

登录开放平台(open.weixin.qq.com),注册成为微信开放平台开发者。


第二步:认证开放平台并创建APP
开放平台需进行开发者资质认证后才可申请微信支付,认证费:300元/次;
提交APP基本信息,通过开放平台应用审核,以获得AppID。

第三步:提交资料申请微信支付
登录开放平台,点击【管理中心】,选择需要申请支付功能对应的APP,开始填写资料等待审核,审核时间为1-5个工作日内。

第四步:开户成功,登录商户平台进行验证
资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。(查看验证方法http://kf.qq.com/faq/161220mQjmYj161220n6jYN7.html

第五步:在线签署协议
本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。点此提前预览协议(https://pay.weixin.qq.com/index.php/public/apply_sign/protocol)内容。

第六步:启动设计和开发
支付接口已获得,可根据开发文档(https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_2#)进行开发, 也可了解 成功案例界面示意及素材





==============

微信公众平台

微信公众平台是微信公众账号申请入口和管理后台。商户可以在公众平台提交基本资料、业务资料、财务资料申请开通微信支付功能。 

平台入口:http://mp.weixin.qq.com。 

       微信开放平台


微信开放平台是商户APP接入微信支付开放接口的申请入口,通过此平台可申请微信APP支付。 

平台入口:http://open.weixin.qq.com。 

       微信商户平台


微信商户平台是微信支付相关的商户功能集合,包括参数配置、支付数据查询与统计、在线退款、代金券或立减优惠运营等功能。 

平台入口:http://pay.weixin.qq.com。 

===============


商户在微信公众平台(申请扫码支付、公众号支付)或开放平台(申请APP支付)按照相应提示,申请相应微信支付模式。微信支付工作人员审核资料无误后开通相应的微信支付权限。微信支付申请审核通过后,商户在申请资料填写的邮箱中收取到由微信支付小助手发送的邮件,此邮件包含开发时需要使用的支付账户信息。具体见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=3_1

微信移动支付的工作流程:
用户在商户app选择商品下单----》商户app发送到商户后台请求生成支付订单这时商户后台会进行一次签名)---》商户后台调用微信支付系统统一的API,之前商户后台签名的支付订单利用微信支付系统(即微信后台)生成预付单---》微信后台返回预付单信息给商户后台(附带prepay_id等)--》商户后台再次签名生成支付信息(带参数的支付信息,参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。),返回给商户客户端-----》这时用户只要同意支付,商户客户端就会调起微信客户端调起支付----》微信客户端向微信后台发起支付请求----》微信后台验证支付参数----》微信后台验证完成后向微信客户端请求支付授权(就是让用户输入密码)---》密码输入后提交到微信后台----》微信后台验证密码之类的,完成支付----》后台返回支付结果,同时也会异步通知到商户后台。

微信支付微信服务器的验证签名过程:
sign就是签名,是你提交的数据经过一定规则组和后用md5加密的一个东西(官方文档有介绍)。是用来验证数据是否被第三方篡改的一个凭证。你发送到微信服务器,微信服务器会用你发来的参数生成sign。再和你传过去的sign做对比。
 
  

签名算法

签名生成的通用步骤如下: 

第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 

特别注意以下重要规则: 

  1. ◆ 参数名ASCII码从小到大排序(字典序); 
  2. ◆ 如果参数的值为空不参与签名; 
  3. ◆ 参数名区分大小写; 
  4. ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。 
  5. ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段 

第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。

key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置

最主要的是是key,key就就时API秘钥,时自己设置的32位随机字符串(建议到网上找工具生成),保存在商户系统和微信后台中,用来签名验证的。

以下是交互时序图,统一下单API、支付结果通知API和查询订单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;

}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值