2012-06-29 10:23:06 sing_sing 阅读数 89971

由于app内需要用到支付功能,选择了当前最流行的支付宝进行支付。在进行内嵌支付宝功能开发时,被它狠狠的耍了一把。

根据支付宝开发文档,参考demo代码。将相关支付功能加到了自己的代码中。一些根据文档来做,也没有太大的难度。但添加完代码,进行测试时,问题却来了。在app中可以成功调用支付宝app,成功支付并返回。这个时候就出现问题了。在app delegate支付宝回调本app的执行函数中,公钥验证签名成功后,会对支付结果进行提示。根据demo,提示支付的代码如下:

UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:result.statusMessage delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
[alertView release];

问题就出现在这里了。支付成功后,result.statusMessage返回的值却是空的。而我测试取消支付,这个返回值却是正确的,提示“操作取消”之类的文字(具体忘记了)。支付失败没有进行测试。这下麻烦了,不知问题出在哪里。总不能支付成功后,返回一个空的提示吧。或者直接将提示自己写死,显示“支付成功”?那万一是其他信息提示咋办呢?

重复对比了demo和自己程序的代码,发现没有错漏的地方。百思不得其解,于是想是不是支付宝后台的问题?又根据支付宝服务器端的开发文档,仔细检查,反复测试了。还是找不到原因。

这下没撤了,只能打支持电话,加入一个交流群,问了支付宝负责技术的人,反复说明问题后,终于得到了答覆,支付成功时,result.statusMessage是没有返回值的,大概意思是后来取消了这个返回值。

我的妈阿,对返回值作了改变,为啥文档又没说明,让我等苦逼的程序猿白白折磨了一两天,情何以堪呢。客气的将我的想法提了给支付宝的技术支持,回复说:不要太相信demo。shit,这是人话吗,我用你的接口,不是根据你的文档和demo,那我会担心出错的,而且这是涉及到金钱问题,所以更加不能马虎。内心暗骂了一通之后,又将我的一番建议,例如要求再进一步完善文档,对细节再进一步谨慎之类的提交到支付宝技术支持那里。到此,我的痛苦的支付宝接口调用过程就告一段落了。写这篇文章,算是提醒自己或者有需要的人,有不明白的地方,第一时间找支付宝的技术支持,不要自己瞎折腾了,说不定你碰到的问题不是自己的问题,而是出在第三方接口上。

另外说一下,支付宝网站上找技术支持,直接点支持旺旺是没反应的,你需要打电话去问,才能拿到技术支持的旺旺号,这里公布一下支付宝技术支持群的旺旺号,应该不会被支付宝的人骂吧。呵呵

旺旺号:24768316 
密码:alipay2010

补充:

另外,很多人发私信来问,哪里可以下载支付宝demo或者sdk。这个问题自己曾经也痛苦过,在支付宝网站找个demo,不是一般的难,位置隐藏得非常深,特别是改版后,更加难找了。上一次(改版后)实在找不到,只好问支付宝的技术支持。

支付宝集成sdk和demo下载地址:

下载地址:http://download.alipay.com/public/api/base/WS_SECURE_PAY.zip

这个只是无线产品的集成sdk和demo,其他的产品没有用过,所以忽略。

2016-05-16 11:03:35 TianYou_Code 阅读数 2500

根据现在人们的消费习惯越来越多的APP里面都有添加支付宝支付微信支付

github下载地址:https://github.com/TianYou899/AliPay

PS:这篇文章讲解支付宝支付微信支付请看下篇

支付宝:

        1:先与支付宝签约,获得商户ID(partner)和账号ID(seller)

2:下载相应的公钥私钥文件(加密签名用)

3:下载支付宝SDK

       4:导入SDK,添加依赖库,填写与我们有关的东西
       5:调用支付宝客户端或者网页支付
       6:支付完毕后返回支付结果给商户客户端和服务 , SDK里有集成支付宝功能的一个Demo> 集成支付功能的具体操作方式,可以参考Demo
下面是我个人集成的一个支付宝支付:

前面几步就不说了直接代码说起:

   导入SDK、添加依赖库


在需要支付的页面导入:
#import <AlipaySDK/AlipaySDK.h>
然后填写私钥等信息代码如下:
//发起支付
-(void)treasurePay
{
    /*
     *点击获取prodcut实例并初始化订单信息
     */
    Product *product = [[Product alloc] init];
    //将服务器的订单价格赋值
    product.price = 0.01;//价格
    product.subject = @"商品标题";//标题
    product.body = @"商品描述";//描述
    //product.orderId = self.orderID;
    //NSLog(@"product.orderId=%@",product.orderId);
    /*
     *商户的唯一的parnter和seller。
     *签约后,支付宝会为每个商户分配一个唯一的 parnter 和 seller。
     */
    /*============================================================================*/
    /*=======================需要填写商户app申请的===================================*/
    /*============================================================================*/
    
    //合作伙伴
    NSString *partner = @"";
    //卖方
    NSString *seller = @"";
    //私钥
    NSString *privateKey = @"";
    
    NSLog(@"私钥的长度:%lu",(unsigned long)privateKey.length);
    
    /*============================================================================*/
    /*============================================================================*/
    /*============================================================================*/
    
    //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.seller = seller;
    order.tradeNO = @"20160516103651666666"; //订单ID(由商家自行制定)
    order.productName = product.subject; //商品标题
    order.productDescription = product.body; //商品描述
    order.amount = [NSString stringWithFormat:@"%.2f",product.price]; //商品价格
    order.notifyURL =  @"www.baidu.com"; //回调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 = @"zfb2016428";
    
    //将商品信息拼接成字符串
    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"];
        
        [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
            NSLog(@"页面返回的结果 = %@",resultDic);
            //判断
            NSString * resultStatus = resultDic[@"resultStatus"];
            NSLog(@"状态码:%@",resultStatus);
            if ([resultStatus isEqualToString:@"9000"])//成功
            {//跳转成功页面
               
            }else if ([resultStatus isEqualToString:@"6001"])
            {
         <span style="font-family: Arial, Helvetica, sans-serif;">              //用户取消</span>
            }
            
        }];
    }
    
}
效果图如下由于模拟器调起的web页面(真机调起支付宝app):






2018-03-24 11:10:17 Chengliangyao 阅读数 670

ios上使用Ionic1支付宝插件支付后,支付不返回当前APP,反而提示打开使用了同样插件的另一个APP,其主要原因是调用支付宝时指定返回APP的名称没有发生改变,最简单的方法是使用Xcode进行打包时修改如下Schemes即可:

当然,在ios插件中的info-list配置文件中修改也行,但是修改了插件后必须remove平台然后在install一次,而且比较麻烦,还不能保证能修改正确。

2016-11-26 14:33:37 EmulateStep 阅读数 1965

iOS支付宝使用总结

  • 后台签名,前端只负责调起客户端进行支付
  • 前端签名演示,不建议这么做
  • 错误情况总结
  • 调用客户端,防止app被kill

一、在后台进行签名, 前端只负责调起客户端进行支付

(1)导入需要的SDK

如果在签名后台进行,前端只负责调起客户端进行支付,只需要导入下面两个文件即可。

  1. AlipaySDK.bundle
  2. AlipaySDK.framework

添加依赖库

其中,需要注意的是:
如果是Xcode 7.0之后的版本,需要添加libc++.tbd、libz.tbd;
如果是Xcode 7.0之前的版本,需要添加libc++.dylib、libz.dylib。
当然,使用Xcode7.0之前版本的开发者应该没有了吧……..

(3)添加允许网络请求字段和白名单。(具体看下面)

(4)在需要调起客户端的地方,引入支付宝头文件

#import "AlipaySDK/AlipaySDK.h"

(5)加签字符串从后台获取,前端只进行调起客户端操作,这里参数什么都没写,根据需要传给后台,开始代码

 AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
    mgr.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json",nil];
    NSString *appScheme = @"everythingisok";
    [mgr POST:@"xxxx" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // 从后台获取加签后的字符串
        NSString *orderNo = responseObject[@"orderNo"];
        // 调起支付宝客户端进行支付操作
        [[AlipaySDK defaultService] payOrder:orderNo fromScheme:appScheme callback:^(NSDictionary *resultDic) {
            NSLog(@"%@",resultDic);
        }];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

    }];

注意:
appScheme参数可以随便写,但是要和Info -> URL Type -> URL Scheme保持一致,否则支付完成后,找不到app,如图:
这里写图片描述

(6)配置支付宝客户端返回url处理方法。

在AppDelegate.m文件中,增加引用代码,如果跳转支付宝客户端支付,app会被系统kill掉,支付完成后,不会继续支付完成后的操作,此时,要在AppDelegate里面做些操作;这样,在调用支付宝客户端支付的时候,app不会被kill掉

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation
{
    if ([url.host isEqualToString:@"safepay"]) {
        // 支付跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
        }];

        // 授权跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processAuth_V2Result:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
            // 解析 auth code
            NSString *result = resultDic[@"result"];
            NSString *authCode = nil;
            if (result.length>0) {
                NSArray *resultArr = [result componentsSeparatedByString:@"&"];
                for (NSString *subResult in resultArr) {
                    if (subResult.length > 10 && [subResult hasPrefix:@"auth_code="]) {
                        authCode = [subResult substringFromIndex:10];
                        break;
                    }
                }
            }
            NSLog(@"授权结果 authCode = %@", authCode?:@"");
        }];
    }
    return YES;
}

// NOTE: 9.0以后使用新API接口
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
    if ([url.host isEqualToString:@"safepay"]) {
        // 支付跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
        }];

        // 授权跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processAuth_V2Result:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
            // 解析 auth code
            NSString *result = resultDic[@"result"];
            NSString *authCode = nil;
            if (result.length>0) {
                NSArray *resultArr = [result componentsSeparatedByString:@"&"];
                for (NSString *subResult in resultArr) {
                    if (subResult.length > 10 && [subResult hasPrefix:@"auth_code="]) {
                        authCode = [subResult substringFromIndex:10];
                        break;
                    }
                }
            }
            NSLog(@"授权结果 authCode = %@", authCode?:@"");
        }];
    }
    return YES;
}

(7)可能出现的问题

问题1"_OBJC_CLASS_$_CMMotionManager", objc-class-ref in AlipaySDK
解决方法:遇到这样的错误,是因为少了CoreMotion.framework
Build Phases ->Link Binary With Libraries 下添加库: CoreMotion.framework。

问题2:'openssl/asn1.h‘ file not found ras.h
解决方法:这个是因为openssl,a文件,在Xcode里的header search paths 里添加openssl.a文件的路径。
格式如下  $(PROJECT_DIR)/文件夹名     
例如在根目录/tools/alipay/openssl.a,就写成  $(PROJECT_DIR)/tools/alipay 就可以了

问题3"_OBJC_CLASS_$_UTDevice", referenced from:objc-class-ref in AlipaySDK:
解决方法:只要用支付宝demo里面的的AlipaySDK.bundle,和AlipaySDK.framework ,编译就不会出错了.(好奇怪!!)

二、在前端进行签名,调起支付宝(不建议)

在前端进行签名,调起支付宝,但是不建议这么做。查看官方文档

(1)导入相关SDK和加签文件

  1. AlipaySDK.bundle
  2. AlipaySDK.framework

导入加签文件

  1. libcrypto
  2. libssl.a
  3. openssl文件夹
  4. Util文件
  5. Order.h和Order.m

这里写图片描述

(3)可能出现错误情况:

错误1:如果报错"Cannot find interface declaration for 'NSObject', superclass of 'Base64'"或者"NSString not find",引入头文件,#import <Foundation/Foundation.h>

错误2:如果报错" #include <openssl/asn1.h> not found ",在Build Settings -> Header Search Paths 添加路径,路径为openssl文件夹所在的上一级文件夹,找到上一级文件夹的位置,直接拖进去,注意:"$(SRCROOT)/NewAlipay",如果不行,把引号去掉;如图:

这里写图片描述

(4)ios9之后需要再添加Info里面添加网络请求字段,(白名单字段最好也添加,QQ分享,微信分享需要添加白名单字段),如图:

网络请求:
这里写图片描述
白名单:(LSApplicationQueriesSchemes)
这里写图片描述
点击查看更多白名单

(5)在前端进行加签,调起客户端,这样不安全,不建议这么做

//在需要用到支付宝的地方,引入头文件
#import "AlipaySDK/AlipaySDK.h"
#import "Order.h"
#import "DataSigner.h"
- (IBAction)testPay:(UIButton *)sender
{
     //*商户的唯一的parnter和seller。
     //*签约后,支付宝会为每个商户分配一个唯一的 parnter 和 seller。 
    // partner: 2088开头的十六位的纯数字, 商户ID
    NSString *partner = @"商户id";
    // 收款账户
    NSString *seller = @"账户";
    // 私钥:
    NSString *privateKey = @"你的私钥,这里演示在客户端生成签名";
    //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.seller = seller;
    order.tradeNO = @"2015120622"; //订单ID(由商家自行制定)
    order.productName = @"123456"; //商品标题
    order.productDescription = @"654321"; //商品描述
    order.amount = [NSString stringWithFormat:@"%.2f",0.01]; //商品价格
    order.notifyURL =  @"http://www.xiaovl.com"; //回调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 = @"testZFB";

    //将商品信息拼接成字符串
    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"];

        [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
            NSLog(@"reslut = %@",resultDic);
            // 成功,之后调用自己服务器的方法
            /*
            9000 订单支付成功
            8000 正在处理中
            4000 订单支付失败
            6001 用户中途取消
            6002 网络连接出错
           */
        }];  
    }
}

注意:
appScheme参数可以随便写,但是要和Info -> URL Type -> URL Scheme保持一致,否则支付完成后,找不到app,如图:
这里写图片描述

(6)配置支付宝客户端返回url处理方法。

在AppDelegate.m文件中,增加引用代码,如果跳转支付宝客户端支付,app会被系统kill掉,支付完成后,不会继续支付完成后的操作,此时,要在AppDelegate里面做些操作;这样,在调用支付宝客户端支付的时候,app不会被kill掉

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation
{
    if ([url.host isEqualToString:@"safepay"]) {
        // 支付跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
        }];

        // 授权跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processAuth_V2Result:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
            // 解析 auth code
            NSString *result = resultDic[@"result"];
            NSString *authCode = nil;
            if (result.length>0) {
                NSArray *resultArr = [result componentsSeparatedByString:@"&"];
                for (NSString *subResult in resultArr) {
                    if (subResult.length > 10 && [subResult hasPrefix:@"auth_code="]) {
                        authCode = [subResult substringFromIndex:10];
                        break;
                    }
                }
            }
            NSLog(@"授权结果 authCode = %@", authCode?:@"");
        }];
    }
    return YES;
}

// NOTE: 9.0以后使用新API接口
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
    if ([url.host isEqualToString:@"safepay"]) {
        // 支付跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
        }];

        // 授权跳转支付宝钱包进行支付,处理支付结果
        [[AlipaySDK defaultService] processAuth_V2Result:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
            // 解析 auth code
            NSString *result = resultDic[@"result"];
            NSString *authCode = nil;
            if (result.length>0) {
                NSArray *resultArr = [result componentsSeparatedByString:@"&"];
                for (NSString *subResult in resultArr) {
                    if (subResult.length > 10 && [subResult hasPrefix:@"auth_code="]) {
                        authCode = [subResult substringFromIndex:10];
                        break;
                    }
                }
            }
            NSLog(@"授权结果 authCode = %@", authCode?:@"");
        }];
    }
    return YES;
}

参考博客:
1.官方文档,重要事情说三遍
2.支付宝遇到的坑

2019-05-23 09:39:10 super_man_ww 阅读数 263

本文只讨论你已经成功实现 客户端->支付宝 支付流程,仅仅是支付完成后无法返回自己的APP的问题。
如果你的支付流程是 客户端->Safari->支付宝 不在本文讨论范围之类。
iOS支付宝H5支付不在讨论范围内。提供下列参考:
支付宝H5支付官方文档
支付宝H5支付官方Demo下载Demo使用文档

本文前大段在讲楼主是如何解决这个问题的,以及中间遇到的麻烦。赶时间的同学可以直接看最后的 最终解决方案

在讨论之前,我们先看看官方文档上的一句话:

注意:在iOS系统中,唤起支付宝App支付完成后,不会自动回到浏览器或商户APP。用户可手工切回到浏览器或商户APP;支付宝H5收银台会自动跳转回商户return_url指定的页面。

what? 官方直接跟我们说GG了?楼主不甘心,还是想尝试一下。
借鉴之前 iOS微信H5支付无法返回APP解决方案 的经验,先在 return_url 这个参数上做个尝试吧。

支付宝和微信不同的是,支付宝的return_url不需要在支付宝管理后台备案(填写),微信的redirect_url是需要的。

第一次尝试
webView拦截请求https://mapi.alipay.com/gateway.do,修改return_url参数为自己的URLScheme,比如 URLEncode(A.company.com://),结果失败了,进入支付宝报错的页面。仔细看了一下请求的其它参数,居然有个sign!支付宝文档上也有这个参数,居然校验了参数签名的,好吧,客户端篡改参数的方案宣告失败!

第二次尝试
既然不能篡改,那直接下单的时候填吧,请求后台的同学协助一下,在下单的时候把return_url参数改成 URLEncode(A.company.com://),结果还是失败了。后台同学提示我,return_url必须是HTTP/HTTPS开头,支付宝文档上有说明。

第三次尝试
那加上http试试,我又让后台同学把return_url改成 URLEncode(httpA.company.com://),同时把APP的URLSchemes改成 httpA.company.com,再次尝试,调起支付宝返回的时候打开了Safari,在Safari里面显示了我们支付中心的错误页面,return_url方案彻底失败了!

要不就算了,跟产品说支付宝无法实现支付完成后跳回APP,反正支付宝官方文档上都这么写了。
虽然这么想了,但是我还是继续翻看支付宝的文档,希望能找到点蛛丝马迹。

突然看到了这里,手机网站支付转Native支付

支付宝文档


schemeStr,这个关键字让我眼前一亮。
手机网站支付转Native支付:支付宝的意思是,我们提供了一个SDK,你接了之后就可以很方便的实现H5收银台(即在web上输入支付宝账号密码支付)到支付宝APP收银台的过渡。
如果你是一个"正规"APP开发者,至此已经可以解决你的问题了。按照支付宝文档接支付宝SDK即可,就能实现H5支付回调APP了。

 

但,这不是楼主想要的方案,由于工作需求的原因,楼主不想也不敢接支付宝的SDK,怕被苹果爸爸审核扫包啊。如果你的APP"应该"用苹果支付,即使你没有用到支付宝的功能,但是包含了支付宝的SDK,审核被发现了,也可能被拒绝的。

继续寻找解决办法。

下载了支付宝的Demo,试了下确实可以跳回APP。
楼主琢磨着,既然SDK能实现这个功能,说明支付宝还是支持的H5支付完成后返回APP功能的,并不是向文档上说的,"在iOS系统中,唤起支付宝App支付完成后,不会自动回到浏览器或商户APP。用户可手工切回到浏览器或商户APP",只是支付宝不愿意公开而已。

那就以这个支付宝Demo为切入点吧,虽然SDK中有schemeStr这个参数,但是SDK是黑盒啊,你不知道它在里面做了些什么。

/**
 *  支付接口
 *
 *  @param orderStr       订单信息
 *  @param schemeStr      调用支付的app注册在info.plist中的scheme
 *  @param completionBlock 支付结果回调Block,用于wap支付结果回调(非跳转钱包支付)
 */
- (void)payOrder:(NSString *)orderStr
      fromScheme:(NSString *)schemeStr
        callback:(CompletionBlock)completionBlock;

追本溯源,最终打开支付宝APP都会走

[UIApplication sharedApplication] openURL:]

在Demo工程里搜了一下,没有。但我肯定SDK里面肯定有!

whatever —— Method Swizzling

#import <UIKit/UIKit.h>
#import <objc/runtime.h>

@implementation UIApplication (TrackTimer)

+ (void)load
{
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        SEL mySelector = NSSelectorFromString(@"my_openURL:");
        SEL orginalSelector = NSSelectorFromString(@"openURL:");
        Method myMethod = class_getInstanceMethod([self class], mySelector);
        Method orginalMethod = class_getInstanceMethod([self class], orginalSelector);
        BOOL didAddMethod = class_addMethod([self class], orginalSelector, method_getImplementation(myMethod), method_getTypeEncoding(myMethod));
        if (didAddMethod) {
            class_replaceMethod([self class], mySelector, method_getImplementation(orginalMethod), method_getTypeEncoding(orginalMethod));
        }
        else {
            method_exchangeImplementations(myMethod, orginalMethod);
        }
    });
}

- (void)my_openURL:(NSURL*)url
{
    NSLog(@"%@",url);
    [self my_openURL:url];
}

打印结果如下

alipaymatrixbwf0cml3://alipayclient/?%7B%0A%20%20%22fromAppUrlScheme%22%20%3A%20%22alisdkdemo%22%2C%0A%20%20%22requestType%22%20%3A%20%22SafePay%22%2C%0A%20%20%22dataString%22%20%3A%20%22trade_no%3D%5C%222018041921001001270586523089%5C%22%26pay_phase_id%3D%5C%22%5C%22%26biz_type%3D%5C%22trade%5C%22%26biz_sub_type%3D%5C%22TRADE%5C%22%26app_name%3D%5C%22tb%5C%22%26extern_token%3D%5C%221fc77c67c70ef70d58e2bf7c513d91a5%5C%22%26appenv%3D%5C%22%5C%22%26pay_channel_id%3D%5C%22alipay_sdk%5C%22%26bizcontext%3D%5C%22%7B%5C%22av%5C%22%3A%5C%221%5C%22%2C%5C%22sc%5C%22%3A%5C%22h5tonative%5C%22%2C%5C%22ty%5C%22%3A%5C%22ios_lite%5C%22%2C%5C%22appkey%5C%22%3A%5C%222014052600006128%5C%22%2C%5C%22sv%5C%22%3A%5C%22h.a.3.5.3%5C%22%2C%5C%22an%5C%22%3A%5C%22com.antfin.AliSDKDemo%5C%22%7D%5C%22%22%0A%7D

URLDecode

alipaymatrixbwf0cml3://alipayclient/?{
"fromAppUrlScheme" : "alisdkdemo",
"requestType" : "SafePay",
"dataString" : "trade_no="2018041921001001270586523089"&pay_phase_id=""&biz_type="trade"&biz_sub_type="TRADE"&app_name="tb"&extern_token="1fc77c67c70ef70d58e2bf7c513d91a5"&appenv=""&pay_channel_id="alipay_sdk"&bizcontext="{"av":"1","sc":"h5tonative","ty":"ios_lite","appkey":"2014052600006128","sv":"h.a.3.5.3","an":"com.antfin.AliSDKDemo"}""
}

为什么前面会有一点乱码,alipaymatrixbwf0cml3,不管它了。fromAppUrlScheme,是不是很惊喜。

是不是支付宝H5支付都有这个参数呢?楼主赶紧用自己的项目尝试了一下,webView最终会有一个alipay://请求的,拦截它,看看参数

URLDecode以后(部分字段我做了屏蔽)
alipay://alipayclient/?{"dataString":"h5_route_token="xxxxxxxxxxxxx"&is_h5_route="true"","requestType":"SafePay","fromAppUrlScheme":"alipays"}

也有fromAppUrlScheme这个参数,而且默认是支付宝APP的URLScheme: alipays
对比一下发现,SDK中传入的schemeStr参数对应就是alipay:// 中的 fromAppUrlScheme字段。
由此楼主猜测,在 alipay:// 打开支付宝时,传入 fromAppUrlScheme,支付结束后就会跳到对应的APP。

最终,楼主在自己的项目中验证了上述猜测。
总结下吧

最终解决方案

方案一

接支付宝提供的SDK,schemeStr参数传入自己APP的URLScheme即可。 具体的 手机网站支付转Native支付

方案二

不需要接SDK,不需要添加支付宝白名单,webView拦截 alipay://alipayclient 请求,追加或修改参数 fromAppUrlScheme为你自己的URLScheme值(这一步可没那么简单,自己动手吧,偷笑),生成新的NSURL,然后用 [[UIApplication sharedApplication] openURL:] 打开即可。

2018.5.24更新(上面看懂了下面这段可以不用看)

总有人私信我问我怎么追加fromAppUrlScheme。可能是我说的"webView拦截",让初学者不明白吧,简单提一下。
在webView"发起请求的代理方法"里面拦截请求的URL,即如果请求是alipay://alipayclient开头的,说明这个请求就跳转支付宝的请求。替换里面fromAppUrlScheme的值为你自己的scheme值。(当然如果更严谨的话,应该是判断fromAppUrlScheme这个key是否存在,存在则替换其值,不存在则追加)。用替换好的字符串生成一个NSURL,用[[UIApplication sharedApplication] openURL:newURL]跳转支付宝。代理方法返回NO,即终止这次请求。

常见问题

1.支付后没有返回app而是跳到了Safari
原因:你的Xcode配置里面URLSheme没写或者写错了,和支付宝请求参数中的fromAppUrlScheme不一致。
解决:Xcode配置中新增或者修改URLSheme,确保和alipay://alipayclient请求参数中的fromAppUrlScheme一致。

2.并没有看到作者说的alipay: //alipayclient
原因:可能你的app不是在客户端直接和支付宝通信,而是中间加了一道跳转到网页,由你的后台(或前端)调起支付宝。所以你是拦截不到这个请求的。我文中所说的方式是基于,“客户端->支付宝” 这种模式,而你的是“客户端->Safari->支付宝”模式,后者无法按照文中说述实现返回app。
解决:方案1,修改你的支付模式为“客户端->支付宝”,这样改动恐怕有点大,工作量主要在你们后台。方案2,在不改模式的情况下,需要后台(或前端)的同事去解决这个问题,原理和本文差不多,在后台和支付宝通讯的参数中找到一个类似于fromAppUrlScheme的参数,然后将这个参数修改成你的app的URLScheme,至于具体怎处理楼主没做研究,如果你有好的方法,可以分享一下。



作者:CocoaKier
链接:https://www.jianshu.com/p/0d8dd04fe94e
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

没有更多推荐了,返回首页