zoukankan      html  css  js  c++  java
  • 微信支付最详解教程

    最近要用微信支付功能,在此总结一下!
     
    需要下面第三方支持
     
    备注:JSONKit框架是基于MRC的,如果工程开发环境是ARC的话,请在编译时设定 编译参数 -fno-objc-arc
     
     
     

     
     
    1、首先到微信开放平台上,申请app及与T进行签约、认证
    https://open.weixin.qq.com/ 
    获取到:
    /**
     *  微信开放平台申请得到的 appid, 需要同时添加在info.plist文件中URL schema,用于完成时,回调到app
     */
    #define WXAppId @"wxd930ea5d5a258f4f"
     
    #define WXAppSecret @"db426a9829e4b49a0dcac7b4162da6b6"
     
    以上两个参数用于获取access_token
    access_token是APP的全局唯一票据,APP调用各接口时都需使用access_token。正常情况下access_token有效期为7200秒,重复获取将导致上次获取的access_token失效。
    APP可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在开放平台后台获得。注意调用接口时需使用https协议。
     
    接口调用请求说明
    https://api.weixin.qq.com/cgi-
    bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
     
    返回说明
    正常情况下,微信会返回下述JSON数据包给开发者:
    {"access_token":"ACCESS_TOKEN","expires_in":7200}
     
    参数说明:
     
     
    appkey 、partnerId、partnerKey、paySignKey
     
    appkey:appkey就是Paysignkey,申请支付通过之后由财付通下发。
     
    partnerId:财付通商户身份的标识。审核通过后,在财付通发送的邮件中查看。
     
    partnerKey:财付通商户权限密钥Key。审核通过后,在财付通发送的邮件中查看。
    paySignKey:除了支付请求需要用到paySignKey,公众平台接口API的权限获取所需密钥Key,在使用所有公众平台API时,都需要先用它去换取access_token,然后再进行调用。审核通过后,在微信发送的邮件中查看。
     
    2、代码实现
    @interface SkyAppDelegate : UIResponder <UIApplicationDelegate, WXApiDelegate> //在appDelegate方法中实现WXApiDelegate
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        
        // 向微信终端注册appID
        [WXApi registerApp:WXAppId withDescription:@"weixin demo"];
     /*! @brief WXApi的成员函数,在微信终端程序中注册第三方应用。
     *
     * 需要在每次启动第三方应用程序时调用。第一次调用后,会在微信的可用应用列表中出现。
     * @param appid 微信开发者ID
     * @param appdesc 应用附加信息,长度不超过1024字节
     * @return 成功返回YES,失败返回NO。
     */
        return YES;
    }
     
    //用于完成支付后的程序回调,
    - (BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    {
        NSLog(@"%@",url);//跳转到URL schema中配置的地址
        return [WXApi handleOpenURL:url delegate:self];
    }
     
    //收到一个来自微信的处理结果。调用一次sendReq后会收到onResp。
    - (void)onResp:(BaseResp *)resp
    {
        if ([resp isKindOfClass:[PayResp class]])
        {
            PayResp *response = (PayResp *)resp;
            
            NSString *strTitle = [NSString stringWithFormat:@"支付结果"];
            NSString *strMsg = [NSString stringWithFormat:@"errcode:%d", response.errCode];
            
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:strTitle
                                                            message:strMsg
                                                           delegate:self
                                                  cancelButtonTitle:@"OK"
                                                  otherButtonTitles:nil, nil];
            [alert show];
            
            switch (response.errCode) {
                case WXSuccess: {
                    NSNotification *notification = [NSNotification notificationWithName:ORDER_PAY_NOTIFICATION object:@"success"];
                    [[NSNotificationCenter defaultCenter] postNotification:notification];
                    break;
                }
                    
                default: {
                    NSNotification *notification = [NSNotification notificationWithName:ORDER_PAY_NOTIFICATION object:@"fail"];
                    [[NSNotificationCenter defaultCenter] postNotification:notification];
                    break;
                }
            }
        }
    }
     
    为了保证发送请求的方便,自己封装了两个工具类
    1、http
    typedef void (^BKHttpCallback)(BOOL isSuccessed, NSDictionary *result);
    /**
     *  GET方法请求数据
     *
     *  @param url     请求的URL
     *  @param params  请求参数
     *  @param (BOOL isSuccessed, Result *result))callback  回调方法
     */
    + (void)doGetWithUrl:(NSString *)url path:(NSString *)path params:(NSDictionary *)params callback:(BKHttpCallback) callback;
     
    /**
     *  请求WebService数据
     *
     *  @param baseUrl  请求的基础URL
     *  @param params   请求参数
     *  @param (BOOL isSuccessed, Result *result))callback  回调方法
     */
    + (void)doPostWithUrl:(NSString *)url path:(NSString *)path params:(NSDictionary *)params callback:(BKHttpCallback)callback;
     
    /**
     *  Get方法请求图片
     *
     *  @param url      图片URL
     *  @param (BOOL isSuccessed, Result *result))callback  回调方法
     */
    + (void)getImageWithUrl:(NSString *)url callback:(BKHttpCallback)callback;
     
    .m文件
     
    + (void)doGetWithUrl:(NSString *)url path:(NSString *)path params:(NSDictionary *)params callback:(BKHttpCallback) callback
    {
        AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:url]];
        [httpClient getPath:path
                 parameters:params
                    success:^(AFHTTPRequestOperation *operation, id responseObject){
                        
                        NSString *responseJson = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
                        if (responseJson)
                        {
                            NSDictionary *result = [responseJson objectFromJSONString];
                            callback(YES, result);
                        }
                        else
                        {
                            callback(NO, nil);
                        }
                    }
                    failure:^(AFHTTPRequestOperation *operation, NSError *error){
                        callback(NO, nil);
                    }];
    }
     
    + (void)doPostWithUrl:(NSString *)url path:(NSString *)path params:(NSDictionary *)params callback:(BKHttpCallback)callback
    {
        AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:url]];
        httpClient.parameterEncoding = AFJSONParameterEncoding;
        [httpClient postPath:path
                  parameters:params
                     success:^(AFHTTPRequestOperation *operation, id responseObject){
                        
                         NSString *responseJson = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
                         if (responseJson)
                         {
                             NSDictionary *result = [responseJson objectFromJSONString];
                             callback(YES, result);
                         }
                         else
                         {
                             callback(NO, nil);
                         }
                     }
                     failure:^(AFHTTPRequestOperation *operation, NSError *error){
                        callback(NO, nil);
                    }];
    }
     
    + (void)getImageWithUrl:(NSString *)url callback:(BKHttpCallback)callback
    {
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
        AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:request];
        
        [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            
            UIImage *image = responseObject;
            NSDictionary *result = @{@"image":image};
            callback(YES, result);
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            callback(NO, nil);
        }];
        [requestOperation start];
    }
    2、MD5加密
     
    + (NSString *)md5:(NSString *)input;
     
    + (NSString *)sha1:(NSString *)input;
     
    + (NSString *)getIPAddress:(BOOL)preferIPv4;
     
    + (NSDictionary *)getIPAddresses;
     
    .m文件
    #define IOS_CELLULAR    @"pdp_ip0"
    #define IOS_WIFI        @"en0"
    #define IP_ADDR_IPv4    @"ipv4"
    #define IP_ADDR_IPv6    @"ipv6"
     
    @implementation CommonUtil
     
    + (NSString *)md5:(NSString *)input
    {
        const char *cStr = [input UTF8String];
        unsigned char digest[16];
        CC_MD5( cStr, strlen(cStr), digest ); // This is the md5 call
        
        NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
        
        for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
            [output appendFormat:@"%02x", digest[i]];
        
        return  output;
    }
     
    + (NSString *)sha1:(NSString *)input
    {
        const char *ptr = [input UTF8String];
        
        int i =0;
        int len = strlen(ptr);
        Byte byteArray[len];
        while (i!=len)
        {
            unsigned eachChar = *(ptr + i);
            unsigned low8Bits = eachChar & 0xFF;
            
            byteArray[i] = low8Bits;
            i++;
        }
        
        unsigned char digest[CC_SHA1_DIGEST_LENGTH];
        
        CC_SHA1(byteArray, len, digest);
        
        NSMutableString *hex = [NSMutableString string];
        for (int i=0; i<20; i++)
            [hex appendFormat:@"%02x", digest[i]];
        
        NSString *immutableHex = [NSString stringWithString:hex];
        
        return immutableHex;
    }
     
    + (NSString *)getIPAddress:(BOOL)preferIPv4
    {
        NSArray *searchArray = preferIPv4 ?
        @[ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] :
        @[ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
        
        NSDictionary *addresses = [self getIPAddresses];
        //NSLog(@"addresses: %@", addresses);
        
        __block NSString *address;
        [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
         {
             address = addresses[key];
             if(address) *stop = YES;
         } ];
        return address ? address : @"0.0.0.0";
    }
     
    + (NSDictionary *)getIPAddresses
    {
        NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
        
        // retrieve the current interfaces - returns 0 on success
        struct ifaddrs *interfaces;
        if(!getifaddrs(&interfaces)) {
            // Loop through linked list of interfaces
            struct ifaddrs *interface;
            for(interface=interfaces; interface; interface=interface->ifa_next) {
                if(!(interface->ifa_flags & IFF_UP) || (interface->ifa_flags & IFF_LOOPBACK)) {
                    continue; // deeply nested code harder to read
                }
                const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
                if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
                    NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
                    char addrBuf[INET6_ADDRSTRLEN];
                    if(inet_ntop(addr->sin_family, &addr->sin_addr, addrBuf, sizeof(addrBuf))) {
                        NSString *key = [NSString stringWithFormat:@"%@/%@", name, addr->sin_family == AF_INET ? IP_ADDR_IPv4 : IP_ADDR_IPv6];
                        addresses[key] = [NSString stringWithUTF8String:addrBuf];
                    }
                }
            }
            // Free memory
            freeifaddrs(interfaces);
        }
        
        // The dictionary keys have the form "interface" "/" "ipv4 or ipv6"
        return [addresses count] ? addresses : nil;
    }
    主体代码:
    #define BASE_URL @"https://api.weixin.qq.com"
     
    @interfaceSkyViewController ()
    @property (nonatomic, copy) NSString *timeStamp;
    @property (nonatomic, copy) NSString *nonceStr;
    @property (nonatomic, copy) NSString *traceId;
    @end
     
    @implementation SkyViewController
     
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getOrderPayResult:) name:ORDER_PAY_NOTIFICATION object:nil];//监听一个通知
    }
     
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter]removeObserver:self];//移除通知
    }
     
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
     
    - (IBAction)pay:(id)sender
    {
        [self getAccessToken];//获取access_token
    }
     
    #pragma mark - 主体流程
    // 获取token
    - (void)getAccessToken
    {
        NSString *tokenUrl = @"cgi-bin/token";
        NSDictionary *param = @{@"grant_type":@"client_credential", @"appid":WXAppId, @"secret":WXAppSecret};
        [HttpUtil doGetWithUrl:BASE_URL
                          path:tokenUrl
                        params:param
                      callback:^(BOOL isSuccessed, NSDictionary *result){
                          
                          NSString *accessToken = result[AccessTokenKey];
                          [self getPrepayId:accessToken];
                      }];
    }
     
    // 生成预支付订单
    - (void)getPrepayId:(NSString *)accessToken
    {
        NSString *prepayIdUrl = [NSString stringWithFormat:@"pay/genprepay?access_token=%@", accessToken];
        
        // 拼接详细的订单数据
        NSDictionary *postDict = [self getProductArgs];
        
        [HttpUtil doPostWithUrl:BASE_URL
                           path:prepayIdUrl
                         params:postDict
                       callback:^(BOOL isSuccessed, NSDictionary *result){
                           
                           NSString *prePayId = result[PrePayIdKey];
                           
                           // 获取预支付订单id,调用微信支付sdk
                           if (prePayId)
                           {
                               NSLog(@"--- PrePayId: %@", prePayId);
                               
                               // 调起微信支付
                               PayReq *request   = [[PayReq alloc] init];
                               request.partnerId = WXPartnerId;
                               request.prepayId  = prePayId;
                               request.package   = @"Sign=WXPay";
                               request.nonceStr  = self.nonceStr;
                               request.timeStamp = [self.timeStamp intValue];
                               
                               // 构造参数列表
                               NSMutableDictionary *params = [NSMutableDictionary dictionary];
                               [params setObject:WXAppId forKey:@"appid"];
                               [params setObject:WXAppKey forKey:@"appkey"];
                               [params setObject:request.nonceStr forKey:@"noncestr"];
                               [params setObject:request.package forKey:@"package"];
                               [params setObject:request.partnerId forKey:@"partnerid"];
                               [params setObject:request.prepayId forKey:@"prepayid"];
                               [params setObject:self.timeStamp forKey:@"timestamp"];
                               request.sign = [self genSign:params];
                               
                               // 在支付之前,如果应用没有注册到微信,应该先调用 [WXApi registerApp:appId] 将应用注册到微信
                               [WXApi safeSendReq:request];//发送一个安全请求
                           }
                       }];
    }
     
    #pragma mark - 生成各种参数
    // 获取时间戳
    - (NSString *)genTimeStamp
    {
        return [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970]];
    }
     
    /**
     *  获取32位内的随机串, 防重发
     *
     *  注意:商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一
     */
    - (NSString *)genNonceStr
    {
        return [CommonUtil md5:[NSString stringWithFormat:@"%d", arc4random() % 10000]];
    }
     
    /**
     *  获取商家对用户的唯一标识
     *
     *  traceId 由开发者自定义,可用于订单的查询与跟踪,建议根据支付用户信息生成此id
     *  建议 traceid 字段包含用户信息及订单信息,方便后续对订单状态的查询和跟踪
     */
    - (NSString *)genTraceId
    {
        return [NSString stringWithFormat:@"crestxu_%@", [self genTimeStamp]];
    }
     
    - (NSString *)genOutTradNo
    {
        return [CommonUtil md5:[NSString stringWithFormat:@"%d", arc4random() % 10000]];
    }
     
    // 订单详情
    - (NSString *)genPackage
    {
        // 构造订单参数列表
        NSMutableDictionary *params = [NSMutableDictionary dictionary];
        [params setObject:@"WX" forKey:@"bank_type"];
        [params setObject:@"千足金箍棒" forKey:@"body"];
        [params setObject:@"1" forKey:@"fee_type"];
        [params setObject:@"UTF-8" forKey:@"input_charset"];
        [params setObject:@"http://weixin.qq.com" forKey:@"notify_url"];
        [params setObject:[self genOutTradNo] forKey:@"out_trade_no"];
        [params setObject:WXPartnerId forKey:@"partner"];
        [params setObject:[CommonUtil getIPAddress:YES] forKey:@"spbill_create_ip"];
        [params setObject:@"1" forKey:@"total_fee"];    // 1 == ¥0.01
        
        NSArray *keys = [params allKeys];
        NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            return [obj1 compare:obj2 options:NSNumericSearch];
        }];
        
        // 生成 packageSign
        NSMutableString *package = [NSMutableString string];
        for (NSString *key in sortedKeys) {
            [package appendString:key];
            [package appendString:@"="];
            [package appendString:[params objectForKey:key]];
            [package appendString:@"&"];
        }
        
        [package appendString:@"key="];
        [package appendString:WXPartnerKey]; // 注意:不能hardcode在客户端,建议genPackage这个过程都由服务器端完成
        
        // 进行md5摘要前,params内容为原始内容,未经过url encode处理
        NSString *packageSign = [[CommonUtil md5:[package copy]] uppercaseString];
        package = nil;
        
        // 生成 packageParamsString
        NSString *value = nil;
        package = [NSMutableString string];
        for (NSString *key in sortedKeys)
        {
            [package appendString:key];
            [package appendString:@"="];
            value = [params objectForKey:key];
            
            // 对所有键值对中的 value 进行 urlencode 转码
            value = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)value, nil, (CFStringRef)@"!*'&=();:@+$,/?%#[]", kCFStringEncodingUTF8));
            
            [package appendString:value];
            [package appendString:@"&"];
        }
        NSString *packageParamsString = [package substringWithRange:NSMakeRange(0, package.length - 1)];
        
        NSString *result = [NSString stringWithFormat:@"%@&sign=%@", packageParamsString, packageSign];
        
        NSLog(@"--- Package: %@", result);
        
        return result;
    }
     
    // 签名
    - (NSString *)genSign:(NSDictionary *)signParams
    {
        // 排序
        NSArray *keys = [signParams allKeys];
        NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            return [obj1 compare:obj2 options:NSNumericSearch];
        }];
        
        // 生成
        NSMutableString *sign = [NSMutableString string];
        for (NSString *key in sortedKeys) {
            [sign appendString:key];
            [sign appendString:@"="];
            [sign appendString:[signParams objectForKey:key]];
            [sign appendString:@"&"];
        }
        NSString *signString = [[sign copy] substringWithRange:NSMakeRange(0, sign.length - 1)];
        
        NSString *result = [CommonUtil sha1:signString];
        NSLog(@"--- Gen sign: %@", result);
        return result;
    }
     
    // 构造订单参数列表
    - (NSDictionary *)getProductArgs
    {
        self.timeStamp = [self genTimeStamp];   // 获取时间戳
        self.nonceStr = [self genNonceStr];     // 获取32位内的随机串, 防重发
        self.traceId = [self genTraceId];       // 获取商家对用户的唯一标识
        
        NSMutableDictionary *params = [NSMutableDictionary dictionary];
        [params setObject:WXAppId forKey:@"appid"];
        [params setObject:WXAppKey forKey:@"appkey"];
        [params setObject:self.timeStamp forKey:@"noncestr"];
        [params setObject:self.timeStamp forKey:@"timestamp"];
        [params setObject:self.traceId forKey:@"traceid"];
        [params setObject:[self genPackage] forKey:@"package"];
        [params setObject:[self genSign:params] forKey:@"app_signature"];
        [params setObject:@"sha1" forKey:@"sign_method"];
        
        return params;
    }
     
    #pragma mark - 支付结果
    - (void)getOrderPayResult:(NSNotification *)notification
    {
        if ([notification.object isEqualToString:@"success"])
        {
            NSLog(@"success: 支付成功");
        }
        else
        {
            NSLog(@"fail: 支付失败");
        }
    }
     
    这只是一个简单的使用,里面没有用到数据模型,在使用过程中,里面的有些参数要转成数据模型。
  • 相关阅读:
    全面兼容的Iframe 与父页面交互操作
    Windows Phone 8.1 新特性
    android手电筒源码
    最新android版QQ布局效果 v4.7.0全新UI源码
    点击弹出动态菜单效果ios源码
    本app(仿手机支付宝界面)ios源码
    WP应用开发笔记
    关于移动,爱游,联通三家运营商付费SDK的融合方案
    cocos2d-x学习总结01引言
    新站seo如何进行站内优化
  • 原文地址:https://www.cnblogs.com/fanjing/p/4502986.html
Copyright © 2011-2022 走看看