支付,提款iOS接下来要研究的知识点

在线APP支付细节:银联、微信、支付宝

2016-11-27  本文已影响96人  何年何月

仅在技术方面讨论,至于什么签约、开户什么的不涉及。总的来说,支付大部分工作在后台,前端很简单。

支付宝##

槽点1:配置密钥,很长一段时间文档看不懂,什么RSA公钥、私钥、支付宝公钥,好像还有个商户公钥,到底什么关系什么用。后来清楚了,如下:

RSA公钥私钥是自己配置的,可按照RSA(SHA1)密钥配置来配置,然后保存好,将公钥上传到支付宝如下图3所指,由此生成支付宝公钥。商户公钥应该是签约开户时生成的,作用于后台回调支付成功状态。

Screen Shot 2016-11-27 at 4.16.47 PM.png

槽点2:支付成功提醒,文档要求是必须确定后台与支付宝回调成功后才能提示支付成功,我这边是当前端提示返回9000成功后通知到界面发起支付状态请求,后台返回支付成功后才提示成功。总感觉有些不舒服,觉得没必要在确定后台成功与否,以为前端成功后💰钱已经扣掉了。

相关代码:APP支付文档

导入SDK,引入依赖库,详见开发文档。

坑点1、导入SDK后一般会报错

支付宝     openssl/asn1.h' file not found

解决方案:###

找到Xcode的Build Settings,搜索Header Search Paths,双击点开,添加 “$(SRCROOT)/工程名/Classes/SDK/支付宝” ,我把支付宝的SDK文件放在了支付宝这个文件夹下。

坑点2、和 'ZBarSDK'第三方SDK冲突###

ld: 1 duplicate symbol for architecture x86_64 clang: error: linker 
command failed with exit code 1 (use -v to see invocation)


解决方案###

我这边是直接把支付宝中的base64.h 和 base64.m两个文件在工程中删除,没有移到垃圾桶。

支付相关代码如下:###


#import <AlipaySDK/AlipaySDK.h>

- (void)zhifubaoPayForGoodsPrice:(NSString *)price orderNO:(NSString *)orderNO notifyURL:(NSString *)notifyURL subject:(NSString *)subject body:(NSString *)body
{
    
    /*
     *商户的唯一的parnter和seller。
     *签约后,支付宝会为每个商户分配一个唯一的 parnter 和 seller。
     */
    
    /*============================================================================*/
    /*=======================需要填写商户app申请的===================================*/
    /*============================================================================*/
    NSString *partner = KPartner;
    NSString *seller = KSeller;
    NSString *privateKey = KPrivateKey2;
    /*============================================================================*/
    /*============================================================================*/
    /*============================================================================*/
    
    //partner和seller获取失败,提示
    if ([partner length] == 0 ||
        [seller length] == 0 ||
        [privateKey length] == 0)
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
                                                        message:@"缺少partner或者seller或者私钥。"
                                                       delegate:self
                                              cancelButtonTitle:@"确定"
                                              otherButtonTitles:nil];
        [alert show];
        return;
    }
    
    /*
     *生成订单信息及签名
     */
    //将商品信息赋予AlixPayOrder的成员变量
    Order *order = [[Order alloc] init];
    order.partner = partner;
    order.sellerID = seller;
    order.outTradeNO = orderNO; //订单ID(由商家自行制定)
    order.subject = subject; //商品标题
    order.body = body; //商品描述
    order.totalFee = price; //商品价格
    order.notifyURL =  notifyURL; //回调URL
    
    order.service = @"mobile.securitypay.pay";
    order.paymentType = @"1";
    order.inputCharset = @"utf-8";
    order.itBPay = @"30m";
    order.showURL = @"m.alipay.com";
    
    //应用注册scheme,在AlixPayDemo-Info.plist定义URL types
    NSString *appScheme = KappSchemes;
    
    //将商品信息拼接成字符串
    NSString *orderSpec = [order description];
    NSLog(@"orderSpec = %@",orderSpec);
    
    //获取私钥并将商户信息签名,外部商户可以根据情况存放私钥和签名,只需要遵循RSA签名规范,并将签名字符串base64编码和UrlEncode
    id<DataSigner> signer = CreateRSADataSigner(privateKey);
    NSString *signedString = [signer signString:orderSpec];
    
    //将签名成功字符串格式化为订单字符串,请严格按照该格式
    NSString *orderString = nil;
    if (signedString != nil) {
        orderString = [NSString stringWithFormat:@"%@&sign=\"%@\"&sign_type=\"%@\"",
                       orderSpec, signedString, @"RSA"];
        DLog(@"orderString:%@",orderString);
        
// 这一步才是吊起支付,上面那么多事生成签名文件来吊起支付的,由于牵涉到了RSA私钥,所以上面的生成签名字符串应该由后台来做才更安全。
        [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
            // 如果用户手机没有安装支付宝,会调起支付宝的web支付界面,回调界面走这一步通知用户支付成功与否。
            NSLog(@"reslut = %@",resultDic);
            
            NSString *resultStatusStr = [resultDic objectForKey:@"resultStatus"];
            NSString* msg = @"";
            if ([resultStatusStr isEqualToString:@"6001"]) {
                msg=@"您中途取消支付";
            }
            if ([resultStatusStr isEqualToString:@"9000"]) {
                msg=@"支付宝支付成功";
            }
            if([resultStatusStr isEqualToString:@"4000"]){
                msg = @"支付宝异常,请尝试重新购买";
            }
            [self alertWithTitle:@"提示" msg:msg];

            
        }];
    }

}

实际开发中把生成支付签名字符串放在了后台,因为把RSA私钥签名放在APP内不安全。

所以:前端需要做的就是传参,请求接口后获取到orderString,再根据orderString调起支付,appScheme是在APP内如下图,我用的Bundle Identifier

Screen Shot 2016-11-27 at 4.36.51 PM.png

 
            [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
                
              // 如果用户手机没有安装支付宝,会调起支付宝的web支付界面,回调界面走这一步通知用户支付成功与否,如果安装了支付宝,回调代码则在AppDelegate中写。

                   DLog(@"reslut = %@",resultDic);
                
                NSString *resultStatusStr = [resultDic objectForKey:@"resultStatus"];
                NSString* msg = @"";
                if ([resultStatusStr isEqualToString:@"6001"]) {
                    msg=@"您中途取消支付";
                }
                if ([resultStatusStr isEqualToString:@"9000"]) {
                    //                msg=@"支付宝支付成功";
                    
                    [[NSNotificationCenter defaultCenter] postNotificationName:@"zhifubaoNotification" object:resultDic];
                    
                    
                    
                }
                if([resultStatusStr isEqualToString:@"4000"]){
                    msg = @"支付宝异常,请尝试重新购买";
                }
                
                if (![msg isEqualToString:@""]) {
                    [self alertWithTitle:@"提示" msg:msg];
                    
                }
                
                
            }];

回调支付状态相关代码######
// NOTE: 9.0以前使用新API接口
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
    
    NSURL *url1 = url;

    
    if ([url.host isEqualToString:@"safepay"]) {
        //跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
            
            NSString *resultStatusStr = [resultDic objectForKey:@"resultStatus"];
            NSString* msg = @"";
            if ([resultStatusStr isEqualToString:@"6001"]) {
                msg=@"您中途取消支付";
            }
            if ([resultStatusStr isEqualToString:@"9000"]) {
                //                msg=@"支付宝支付成功";
                
                [[NSNotificationCenter defaultCenter] postNotificationName:@"zhifubaoNotification" object:resultDic];
                
                
                
            }
            if([resultStatusStr isEqualToString:@"4000"]){
                msg = @"支付宝异常,请尝试重新购买";
            }
            
            if (![msg isEqualToString:@""]) {
                [self alertWithTitle:@"提示" msg:msg];
                
            }


            
        }];
        
        return YES;
        
    }else if ([url.host isEqualToString:@"uppayresult"]){
        
        [[UPPaymentControl defaultControl] handlePaymentResult:url completeBlock:^(NSString *code, NSDictionary *data) {
            
            //结果code为成功时,先校验签名,校验成功后做后续处理
            if([code isEqualToString:@"success"]) {
                
                //判断签名数据是否存在
                if(data == nil){
                    //如果没有签名数据,建议商户app后台查询交易结果
                    [self alertWithTitle:@"提示" msg:@"未获取签名数据,请联系候鸟旅居查询交易结果"];
                    return;
                }
                
                //数据从NSDictionary转换为NSString
                NSData *signData = [NSJSONSerialization dataWithJSONObject:data
                                                                   options:0
                                                                     error:nil];
                NSString *sign = [[NSString alloc] initWithData:signData encoding:NSUTF8StringEncoding];
                
                
                
                [[NSNotificationCenter defaultCenter]postNotificationName:@"yinlianNotification" object:sign];
                
//                //验签证书同后台验签证书
//                //此处的verify,商户需送去商户后台做验签
//                if([self verify:sign]) {
//                    //支付成功且验签成功,展示支付成功提示
//                    
//                    NSLog(@"银联验证支付成功");
//                    [self alertWithTitle:@"提示" msg:@"银联支付成功!"];
//
//                }
//                else {
//                    //验签失败,交易结果数据被篡改,商户app后台查询交易结果
//                    [self alertWithTitle:@"提示" msg:@"验签失败,请联系候鸟旅居查询交易结果"];
//
//                }
            }
            else if([code isEqualToString:@"fail"]) {
                //交易失败
                [self alertWithTitle:@"提示" msg:@"交易失败!"];

            }
            else if([code isEqualToString:@"cancel"]) {
                //交易取消
                [self alertWithTitle:@"提示" msg:@"交易取消!"];

            }
        }];
        
        return YES;
        
    }else{
        
        
        if ([WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]]) {
            
            
            return YES;
            
            
        }
        
        return NO;//这里是微信支付
        
    }
}



//
// NOTE: 9.0以后使用新API接口
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{

    
    NSURL *url1 = url;
    
    if ([url.host isEqualToString:@"safepay"]) {
        //跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
            
            NSLog(@"result = %@",resultDic);
            
            NSString *resultStatusStr = [resultDic objectForKey:@"resultStatus"];
            NSString* msg = @"";
            if ([resultStatusStr isEqualToString:@"6001"]) {
                msg=@"您中途取消支付";
            }
            if ([resultStatusStr isEqualToString:@"9000"]) {
                //                msg=@"支付宝支付成功";
                
                [[NSNotificationCenter defaultCenter] postNotificationName:@"zhifubaoNotification" object:resultDic];
                
                
                
            }
            if([resultStatusStr isEqualToString:@"4000"]){
                msg = @"支付宝异常,请尝试重新购买";
            }
            
            if (![msg isEqualToString:@""]) {
                [self alertWithTitle:@"提示" msg:msg];
                
            }
            
        }];
        
        return YES;
        
    }else if ([url.host isEqualToString:@"uppayresult"]){
        
        [[UPPaymentControl defaultControl] handlePaymentResult:url completeBlock:^(NSString *code, NSDictionary *data) {
            
            //结果code为成功时,先校验签名,校验成功后做后续处理
            if([code isEqualToString:@"success"]) {
                
                //判断签名数据是否存在
                if(data == nil){
                    //如果没有签名数据,建议商户app后台查询交易结果
                    [self alertWithTitle:@"提示" msg:@"未获取签名数据,请联系候鸟旅居查询交易结果"];
                    return;
                }
                
                //数据从NSDictionary转换为NSString
                NSData *signData = [NSJSONSerialization dataWithJSONObject:data
                                                                   options:0
                                                                     error:nil];
                NSString *sign = [[NSString alloc] initWithData:signData encoding:NSUTF8StringEncoding];
                
                DLog(@"sign:%@",sign);
                
                
                [[NSNotificationCenter defaultCenter]postNotificationName:@"yinlianNotification" object:sign];
//                //验签证书同后台验签证书
//                //此处的verify,商户需送去商户后台做验签
//                if([self verify:sign]) {
//                    //支付成功且验签成功,展示支付成功提示
//                    
//                    NSLog(@"银联验证支付成功");
//                    [self alertWithTitle:@"提示" msg:@"银联支付成功!"];
//                    
//                }
//                else {
//                    //验签失败,交易结果数据被篡改,商户app后台查询交易结果
//                    [self alertWithTitle:@"提示" msg:@"验签失败,请联系候鸟旅居查询交易结果"];
//                    
//                }
            }
            else if([code isEqualToString:@"fail"]) {
                //交易失败
                [self alertWithTitle:@"提示" msg:@"交易失败!"];
                
            }
            else if([code isEqualToString:@"cancel"]) {
                //交易取消
                [self alertWithTitle:@"提示" msg:@"交易取消!"];
                
            }
        }];
        
        return YES;
        

        
    }else{
        
        
        if ([WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]]) {
            
            
            return YES;//这里是微信支付

        }
        
        return NO;
    }
    
}

微信##

微信支付方面的demo
APP端开发步骤说明

导入SDK,顺便把官方demo中的几个类也添加到工程。

Screen Shot 2016-11-27 at 5.13.34 PM.png

微信的支付SDK也只有这三个文件,如果项目中导入了友盟的话,微信支付SDK就不需要重复导入了,(友盟瘦身版除外,友盟现在对功能做了分离,所有主要看有没有这个libWeChatSDK.a文件)

Screen Shot 2016-11-27 at 4.57.12 PM.png

上代码:调起微信支付需要下面这些参数,安全起见交给后台来做,看看前端多省事。


PayReq *request = [[PayReq alloc] init];
request.partnerId = @"10000100";
request.prepayId= @"1101000000140415649af9fc314aa427";
request.package = @"Sign=WXPay";
request.nonceStr= @"a462b76e7436e98e0ed6e13c64b4fd1c";
request.timeStamp= @"1397527777";
request.sign= @"582282D72DD2B03AD892830965F428CB16E7A256";
[WXApi sendReq:request];

传参请求数据,根据请求的数据调起支付。

- (void)weixinPayForGoodsOrderno:(NSString *)orderno{
    
    //
    //
    //    BOOL a = [WXApi isWXAppInstalled];
    //    BOOL b = [WXApi isWXAppSupportApi];
    
    
    if ([WXApi isWXAppInstalled]) {
        [KVNProgress showWithStatus:@"正在调起微信..."];

                NSString *userid = [[NSUserDefaults standardUserDefaults] objectForKey:@"userid"];
        //        NSDictionary *dic = @{@"userid":userid,@"orderno":orderno};
//        NSDictionary *dic = @{@"orderno":orderno};
        NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:orderno,@"orderno",userid,@"userid", nil];

        [netoworkTool postWithURLString:接口地址 parameters:dic progress:^(double progress) {
            
        } success:^(id  _Nullable responseObject) {
            [KVNProgress dismiss];
            DLog(@"responseObject:%@",responseObject);
            
            if ([[NSString stringWithFormat:@"%@",responseObject[@"status"]] isEqualToString:@"0"]) {
                
                NSDictionary *dict = responseObject;
                
                NSMutableString *stamp  = [dict objectForKey:@"timestamp"];
                
                //调起微信支付
                PayReq* req             = [[PayReq alloc] init];
                req.partnerId           = [dict objectForKey:@"partnerid"];
                req.prepayId            = [dict objectForKey:@"prepayid"];
                req.nonceStr            = [dict objectForKey:@"noncestr"];
                req.timeStamp           = stamp.intValue;
                req.package             = [dict objectForKey:@"package"];
                req.sign                = [dict objectForKey:@"sign"];
                [WXApi sendReq:req];
                //日志输出
                
                
            }else{
                
                [self alertWithTitle:@"提示" msg:responseObject[@"msg"]];
            }
        } failure:^(NSError * _Nonnull error) {
            [KVNProgress dismiss];
            [self alertWithTitle:@"提示" msg:@"服务器请求出错"];
            
        } fromClassName:NSStringFromClass([self class])];
        
        
        
    }else{
        
        [self alertWithTitle:@"提示" msg:@"你还未安装微信!"];
    }
    

    
}

微信支付的回调有点特殊:用到了引入的那几个类,微信给回调做了封装,所以只需要设置下代理就好,剩下的在WXApiManager中写相关代码

   
    if ([WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]]) {
            
            return YES;//这里是微信支付
        }
        return NO;

#pragma mark - WXApiDelegate
- (void)onResp:(BaseResp *)resp {
    if ([resp isKindOfClass:[SendMessageToWXResp class]]) {
        if (_delegate
            && [_delegate respondsToSelector:@selector(managerDidRecvMessageResponse:)]) {
            SendMessageToWXResp *messageResp = (SendMessageToWXResp *)resp;
            [_delegate managerDidRecvMessageResponse:messageResp];
        }
    } else if ([resp isKindOfClass:[SendAuthResp class]]) {
        if (_delegate
            && [_delegate respondsToSelector:@selector(managerDidRecvAuthResponse:)]) {
            SendAuthResp *authResp = (SendAuthResp *)resp;
            [_delegate managerDidRecvAuthResponse:authResp];
        }
    } else if ([resp isKindOfClass:[AddCardToWXCardPackageResp class]]) {
        if (_delegate
            && [_delegate respondsToSelector:@selector(managerDidRecvAddCardResponse:)]) {
            AddCardToWXCardPackageResp *addCardResp = (AddCardToWXCardPackageResp *)resp;
            [_delegate managerDidRecvAddCardResponse:addCardResp];
        }
    }else if([resp isKindOfClass:[PayResp class]]){
        //支付返回结果,实际支付结果需要去微信服务器端查询
        NSString *strMsg,*strTitle = [NSString stringWithFormat:@"支付结果"];
        
        switch (resp.errCode) {
            case WXSuccess:
                strMsg = @"";

             // 支付成功的通知。
                [[NSNotificationCenter defaultCenter] postNotificationName:@"weixinverifyNotification" object:nil];
                NSLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode);
                break;
            case WXErrCodeUserCancel:
                strMsg = @"取消了支付!";
                break;
            case WXErrCodeSentFail:
                strMsg = @"发送失败!";
                break;
            case WXErrCodeUnsupport:
                strMsg = @"微信不支持!";
                break;
     
            default:
                strMsg = [NSString stringWithFormat:@"支付结果:失败!retcode = %d, retstr = %@", resp.errCode,resp.errStr];
                NSLog(@"错误,retcode = %d, retstr = %@", resp.errCode,resp.errStr);
                break;
        }
        if (![strMsg isEqualToString:@""]) {
            
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:strTitle message:strMsg delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [alert show];

        }
    }

}

银联##

导入SDK

Screen Shot 2016-11-27 at 5.12.47 PM.png

银联前端开发特别简单,需要后台生成一个流水号,根据流水号调起银联支付!


  [KVNProgress showWithStatus:@"正在调起银联..."];
            
            NSString *appScheme = KappSchemes;
            NSString *userid = [[NSUserDefaults standardUserDefaults] objectForKey:@"userid"];
            
            NSDictionary *dict;
            
            dict = [NSDictionary dictionaryWithObjectsAndKeys:self.money,@"money",@"card",@"type",userid,@"userid", nil];
            
            
            
            [netoworkTool postWithURLString:请求地址 parameters:dict progress:^(double progress) {
                
            } success:^(id  _Nullable responseObject) {
                
                [KVNProgress dismiss];
                if ([[NSString stringWithFormat:@"%@",responseObject[@"status"]] isEqualToString:@"0"]) {
                    
                    NSString *tn = responseObject[@"tn"];
                    self.orderno = [NSString stringWithFormat:@"%@",responseObject[@"orderno"]];
                    
                    if (tn != nil && tn.length > 0)
                    {
                        [[UPPaymentControl defaultControl] startPay:tn fromScheme:appScheme mode:@"00" viewController:self];
                        
                    }
                    
                }else{
                    
                    [KVNProgress showErrorWithStatus:responseObject[@"msg"]];
                }
                
            } failure:^(NSError * _Nonnull error) {
                [KVNProgress dismiss];
                [KVNProgress showErrorWithStatus:@"获取流水号失败!"];
                
            } fromClassName:NSStringFromClass([self class])];
            
            
            
        }];
        

回调部分参考支付宝回调。

总结:后台真苦逼!

上一篇下一篇

猜你喜欢

热点阅读