欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS实现微信支付流程详解

程序员文章站 2024-02-11 23:52:58
背景 自微信支付、支付宝支付入世以来,移动端的支付日渐火热。虚拟货币有取代实体货币的趋向(这句纯属扯淡,不用管),支付在app开发中是一项基本的功能,有必要去掌握。从难易...

背景

自微信支付、支付宝支付入世以来,移动端的支付日渐火热。虚拟货币有取代实体货币的趋向(这句纯属扯淡,不用管),支付在app开发中是一项基本的功能,有必要去掌握。从难易程度上讲,不管是微信支付还是支付宝支付都是非常简单的,因为第三方的支付文档非常详细,而且他们内部的安全性也非常高。作为使用这些支付策略的我们,只需要掌握流程,能够实现正常支付的功能即可。为什么要写下这篇博文,原因有二。其一,微信支付流程中有坑,其二,以后忘记了可以拿出来看看。

配置

1.微信支付需要两个账号,财付通和微信开发者,注册完成后需要开通支付功能,这些流程就不用多说了。在所有申请成功后,我们要取出我们支付功能需要的key:

appid、app密钥(微信发给你的邮件中有如何生成密钥的链接)、商户号。

2.在xcode中配置app id,需要设置下白名单,在url中配置app id,不清楚的童鞋可以百度一下。

支付

从此处开始,进入本次的主题,开始支付。支付的过程且分为四个步骤:

第一步  获取订单号

获取订单号的途径可以是客户端自己生成,也可以去服务器生成,不过一般都是服务器生成。假设拿到了订单号设为order_no。

第二步  统一下单

这个过程是非常关键的一步,也是坑常驻的一步。统一下单有的人做法是在服务器操作,有的在客户端,不管在哪下单都是有必要搞懂的。接下来看看统一下单必须要的参数列表:

/*应用id 微信开放平台审核通过的应用app id*/
@property (nonatomic, copy) nsstring *appid;
/*商户号 微信支付分配的商户号*/
@property (nonatomic, copy) nsstring *mch_id;
/*随机字符串 随机字符串,不长于32位*/
@property (nonatomic, copy) nsstring *nonce_str;
/*签名*/
@property (nonatomic, copy) nsstring *sign;
/*商品描述 天天爱消除-游戏充值。*/
@property (nonatomic, copy) nsstring *body;
/*商户订单号*/
@property (nonatomic, copy) nsstring *out_trade_no;
/*总金额 订单总金额,单位为分*/
@property (nonatomic, copy) nsstring *total_fee;
/*终端ip*/
@property (nonatomic, copy) nsstring *spbill_create_ip;
/*通知地址*/
@property (nonatomic, copy) nsstring *notify_url;
/*交易类型*/
@property (nonatomic, copy) nsstring *trade_type;

其中,sign是其他所有参数按照key1=value1&key2=value2...的方式拼接,然后进行加密得到。参数拼接按照字母排序,举个例子,参数为appid=@"id",mch_id=@"mch"得到的字符串应该是:@"appid=id&mch_id=mch",然后对该字符串进行加密,如下述代码

- (instancetype)initwithdicinfo:(nsdictionary *)infodic{

  if (self = [super init]) {

    self.appid = wechat_share_appid;

    self.mch_id = wechat_mch_id;

    self.nonce_str = [appmethod getrandomstring];

    self.body = @"test";

    self.out_trade_no = [infodic formateobjectforkey:@"order_no"];

    self.total_fee = [nsstring stringwithformat:@"%@", [infodic formateobjectforkey:@"amount"]];

    self.spbill_create_ip = [appmethod deviceipadress];

    self.notify_url = [nsstring stringwithformat:@"%@%@", base_url, wechat_noti_url];

    self.trade_type = @"app";

    self.paydic = [nsmutabledictionary dictionary];

    [self.paydic setvalue:self.appid forkey:@"appid"];

    [self.paydic setvalue:self.mch_id forkey:@"mch_id"];

    [self.paydic setvalue:self.nonce_str forkey:@"nonce_str"];

    [self.paydic setvalue:self.body forkey:@"body"];

    [self.paydic setvalue:self.out_trade_no forkey:@"out_trade_no"];

    [self.paydic setvalue:self.total_fee forkey:@"total_fee"];

    [self.paydic setvalue:self.spbill_create_ip forkey:@"spbill_create_ip"];

    [self.paydic setvalue:self.notify_url forkey:@"notify_url"];

    [self.paydic setvalue:self.trade_type forkey:@"trade_type"];

    self.sign = [self partnersignorder:self.paydic];

    [self.paydic setvalue:self.sign forkey:@"sign"];

  }

  return self;

}
- (nsstring *)partnersignorder:(nsdictionary*)paramdic{
  nsarray *keyarray = [paramdic allkeys];
  nsmutablearray *sortedkeyarray = [nsmutablearray arraywitharray:keyarray];
  [sortedkeyarray sortusingcomparator:^nscomparisonresult(nsstring* key1, nsstring* key2) {
    return [key1 compare:key2];
  }];
  nsmutablestring *paramstring = [nsmutablestring stringwithstring:@""];
  // 拼接成 a=b&x=y
  for (nsstring *key in sortedkeyarray){
    if ([paramdic[key] length] != 0){
      [paramstring appendformat:@"&%@=%@", key, paramdic[key]];
    }
  }
  if ([paramstring length] > 1){
    [paramstring deletecharactersinrange:nsmakerange(0, 1)];  // remove first '&'
  }
  [paramstring appendformat:@"&key=%@", wechatpartner_id];//app密钥
  return [[appmethod signstring:paramstring] uppercasestring];
}

appmethod.m
+ (nsstring *)signstring:(nsstring*)origstring{
  const char *original_str = [origstring utf8string];
  unsigned char result[32];
  cc_md5(original_str, (cc_long)strlen(original_str), result);//调用md5
  nsmutablestring *hash = [nsmutablestring string];
  for (int i = 0; i < 16; i++){
    [hash appendformat:@"%02x", result[i]];
  }
  return hash;
}

这样,统一下单的参数已经准备好了,下面开始请求微信的下单接口:,现在坑又来了。按照微信的下单说明,传给微信服务器的参数必须是xml格式的数据。如果你传过这种类型的自然好办,不过我猜大多数童鞋没有传过这种类型的数据,好在af有提供方法,不然就等着哭吧。继续看代码

+ (void)postwechatpaywithurl:(nsstring *)url
           params:(id)params
         andsuccess:(requestsuccessresult)successblock
         andfailure:(requestfailureresult)failureblock{
  nsstring *string = [params xmlstring];//这里需要导入xmldictionary文件,里面有该方法
  afhttpsessionmanager *session = [afhttpsessionmanager manager];
  // 这里传入的xml字符串只是形似xml,但不是正确是xml格式,需要使用af方法进行转义
  session.responseserializer = [[afhttpresponseserializer alloc] init];
  [session.requestserializer setvalue:@"text/xml; charset=utf-8" forhttpheaderfield:@"content-type"];
  [session.requestserializer setvalue:url forhttpheaderfield:@"soapaction"];
  [session.requestserializer setquerystringserializationwithblock:^nsstring *(nsurlrequest *request, nsdictionary *parameters, nserror *__autoreleasing *error) {
    return string;
  }];
  
  [session post:url parameters:params progress:^(nsprogress * _nonnull uploadprogress) {
    
  } success:^(nsurlsessiondatatask * _nonnull task, id _nullable responseobject) {
    successblock(responseobject);
  } failure:^(nsurlsessiondatatask * _nullable task, nserror * _nonnull error) {
    hperror *hperror = [hperror errorwithcode:error.code desc:error.description];
    failureblock(hperror);
  }];
}

按照上述的步骤,到这里就可以正常下单了,如果微信服务器返回给你的数据中有"result_code" = success; "return_code" = success,说明下单成功,这个步骤也到此结束。

第三步  调起微信客户端,并完成支付

如果第二步正常下单,那么微信会返回给你预支付id,这个id在最后的支付中至关重要,下面的代码是比较统一的,大家都这么写。

hpwechatproduct *product = [[hpwechatproduct alloc] initwithdic:result];
payreq *req       = [[payreq alloc] init];
req.partnerid      = product.partnerid;
req.prepayid       = product.prepayid;
req.noncestr       = product.noncestr;
req.timestamp      = [product.timestamp intvalue];
req.package       = product.package;
req.sign         = product.sign;
bool flag = [wxapi sendreq:req];

hpwechatproduct.m
- (instancetype)initwithdic:(nsdictionary *)dic{
  if (self = [super init]) {
    self.appid = wechat_share_appid;
    self.partnerid = mah_id;
    self.prepayid = [dic formateobjectforkey:@"prepay_id"];
    self.package = @"sign=wxpay";
    self.noncestr = [appmethod getrandomstring];
    self.timestamp = [self gettime];
    nsmutabledictionary *dic = [nsmutabledictionary dictionary];
    [dic setvalue:self.appid forkey:@"appid"];
    [dic setvalue:self.partnerid forkey:@"partnerid"];
    [dic setvalue:self.prepayid forkey:@"prepayid"];
    [dic setvalue:self.package forkey:@"package"];
    [dic setvalue:self.noncestr forkey:@"noncestr"];
    [dic setvalue:self.timestamp forkey:@"timestamp"];
    self.sign = [self partnersignorder:dic];
  }
  return self;
}
- (nsstring *)partnersignorder:(nsdictionary*)paramdic{
  nsarray *keyarray = [paramdic allkeys];
  nsmutablearray *sortedkeyarray = [nsmutablearray arraywitharray:keyarray];
  [sortedkeyarray sortusingcomparator:^nscomparisonresult(nsstring* key1, nsstring* key2) {
    return [key1 compare:key2];
  }];
  nsmutablestring *paramstring = [nsmutablestring stringwithstring:@""];
  // 拼接成 a=b&x=y
  for (nsstring *key in sortedkeyarray){
    if ([paramdic[key] length] != 0){
      [paramstring appendformat:@"&%@=%@", key, paramdic[key]];
    }
  }
  if ([paramstring length] > 1){
    [paramstring deletecharactersinrange:nsmakerange(0, 1)];  // remove first '&'
  }
  [paramstring appendformat:@"&key=%@", wechatpartner_id];
  return [[appmethod signstring:paramstring] uppercasestring];
}

- (nsstring *)gettime{
  nstimeinterval interval = [[nsdate date] timeintervalsince1970];
  return [nsstring stringwithformat:@"%ld", (long)interval];
}


appmethod.m
+ (nsstring *)getrandomstring
{
  nsstring *str = [nsstring stringwithformat:@"%s",genrandomstring(32)];
  return str;
}

如果到了这一步,而且跑到了微信并完成了支付,那么微信会有一个回调,告诉你支付成功了。然而真的成功了嘛,请继续看第四步。

第四步  去服务器查询是否支付成功

即使微信告诉你支付成功了,你也不能相信,只有钱真正打到你们的账号里面了,才算支付成功。任何时候都不能以微信的回调的值判断支付是否成功(这是微信文档说的)。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。