iOS集成H5微信支付宝支付实现跳转与回调的解决方案

西门桃桃 2021-09-01 PM 63℃ 0条

背景

领导希望在iOS客户端内集成H5微信支付跟支付宝支付,所以我们需要实现的效果是:App→支付宝/微信→支付(成功失败或取消)→App(提示用户刷新页面)

方案

1、 添加 URL Scheme 并把支付宝加入白名单

添加 URL Scheme。在 xcodeproj 文件 Info 选项卡最下面的 URL Types内设置。

1、支付宝的这个可以任意设置

2、微信支付设置商户后台填的域名(跟H5同事确认一下就行)

scheme.png

微信支付设置商户后台填的域名:

wechatdomain.png

把支付宝/微信的 URL Scheme alipayalipaysweixinwechat填入项目的白名单。在 xcodeproj 文件 Info 选项卡内的 LSApplicationQueriesSchemes字段里设置。

info.png

2、代理方法拦截链接并跳转支付宝/微信


@property (nonatomic, copy  ) NSString *redirectUrl;

#pragma mark - WKNavigationDelegate -

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURLRequest *request = navigationAction.request;
    NSString *absoluteString = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];
    
    if (!self.redirectUrl) {
        if ([absoluteString hasPrefix:@"https://openapi.alipay.com"]) {
            self.redirectUrl = [self getURLParam:navigationAction.request.URL.absoluteString key:@"return_url"];
        }
        if ([absoluteString hasPrefix:@"https://wx.tenpay.com"]) {
            self.redirectUrl = [self getURLParam:navigationAction.request.URL.absoluteString key:@"redirect_url"];
        }
    }
    
    if ([navigationAction.request.URL.scheme isEqualToString:@"alipay"]) {
        //  1.以?号来切割字符串
        NSArray *urlBaseArr = [navigationAction.request.URL.absoluteString componentsSeparatedByString:@"?"];
        NSString *urlBaseStr = urlBaseArr.firstObject;
        NSString *urlNeedDecode = urlBaseArr.lastObject;
        //  2.将截取以后的Str,做一下URLDecode,方便我们处理数据
        NSMutableString *afterDecodeStr = [NSMutableString stringWithString:[urlNeedDecode urlDecodeString]];
        //  3.替换里面的默认Scheme为自己的Scheme
        NSString *afterHandleStr = [afterDecodeStr stringByReplacingOccurrencesOfString:@"alipays" withString:@"alipayreturn.company.com"];
        //  4.然后把处理后的,和最开始切割的做下拼接,就得到了最终的字符串
        NSString *finalStr = [NSString stringWithFormat:@"%@?%@",urlBaseStr, [afterHandleStr urlEncodeString]];

        //  判断一下,是否安装了支付宝APP
        if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:finalStr]]) {
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:finalStr]];
        } else {
            [KKBUtility showHUDError:@"未安装支付宝,请先安装支付宝!"];
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    // 拦截WKWebView加载的微信支付统一下单链接, 将redirect_url参数修改为唤起自己App的URLScheme
    NSString *domainUrl = @"www.company.com://wxpaycallback/";

    if ([absoluteString hasPrefix:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb"] && ![absoluteString hasSuffix:[NSString stringWithFormat:@"redirect_url=%@",domainUrl]]) {
        NSString *redirectUrl = nil;
        if ([absoluteString containsString:@"redirect_url="]) {
            NSRange redirectRange = [absoluteString rangeOfString:@"redirect_url"];
            redirectUrl = [[absoluteString substringToIndex:redirectRange.location] stringByAppendingString:[NSString stringWithFormat:@"redirect_url=%@",domainUrl]];
        } else {
            redirectUrl = [absoluteString stringByAppendingString:[NSString stringWithFormat:@"redirect_url=%@",domainUrl]];
        }
        NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:redirectUrl] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
        newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
        newRequest.URL = [NSURL URLWithString:redirectUrl];
        [webView loadRequest:newRequest];
    }

    //拦截重定向的跳转微信的 URL Scheme, 打开微信
    if ([absoluteString hasPrefix:@"weixin://"]) {
        if ([[UIApplication sharedApplication] canOpenURL:navigationAction.request.URL]) {
            [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
        } else {
            [KKBUtility showHUDError:@"未安装微信,请先安装微信!"];
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }

    decisionHandler(WKNavigationActionPolicyAllow);
}

- (NSString *)getURLParam:(NSString *)url key:(NSString *)key {
    NSMutableDictionary *paramer = [[NSMutableDictionary alloc]init];
    //创建url组件类
    NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url];
    //遍历所有参数,添加入字典
    [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [paramer setObject:obj.value forKey:obj.name];
    }];
    return [paramer objectForKey:key];
}

- (void)addNotification {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadRedirectUrl) name:@"htmlPaymentNotification" object:nil];
}

- (void)removeNotification {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
// 支付宝/微信返回APP 刷新当前页面 更新支付状态
- (void)reloadRedirectUrl {
    if (self.redirectUrl) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.redirectUrl] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
            [self.webView loadRequest:request];
            self.redirectUrl = nil;
        });
    }
}

NSString 的类目中添加 URL 的 Encode 和 Decode 方法:

- (NSString *)urlEncodeString {
    static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; 
    static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";

    NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
    [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];

    static NSUInteger const batchSize = 50;

    NSUInteger index = 0;
    NSMutableString *escaped = @"".mutableCopy;

    while (index < self.length) {
        NSUInteger length = MIN(self.length - index, batchSize);
        NSRange range = NSMakeRange(index, length);

        // To avoid breaking up character sequences such as 👴🏻👮🏽
        range = [self rangeOfComposedCharacterSequencesForRange:range];

        NSString *substring = [self substringWithRange:range];
        NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        [escaped appendString:encoded];

        index += range.length;
    }

    return escaped;
}

- (NSString *)urlDecodeString {
    NSMutableString *outputStr = [NSMutableString stringWithString:self];
    [outputStr replaceOccurrencesOfString:@"+" withString:@"" options:NSLiteralSearch range:NSMakeRange(0,[outputStr length])];
    return [outputStr stringByRemovingPercentEncoding];
}

3、 AppDelegate 中接收跳转动作

支付宝回调的 host 是固定的 safepay,而微信支付的 host 随意定义:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options{
    //safepay是支付宝H5支付的回调host, 
    if ([url.host isEqualToString:@"wxpaycallback"] || [url.host isEqualToString:@"safepay"]) {
        // 自行操作业务逻辑,比如使用通知请求查询订单状态,popView回上级页面等
        [[NSNotificationCenter defaultCenter] postNotificationName:@"htmlPaymentNotification" object:self userInfo:nil];
    }
}
标签: 支付

非特殊说明,本博所有文章均为博主原创。

评论啦~