iOS微信支付

需要下面第三方支持

备注: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

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: 支付失败”);
    }
    }重点内容
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值