微信支付 一步一个坑的APP支付!

admin于2018-10-09 23:00:58发布 来源:https://segmentfault.com/a/1190000006886519

准备工作

这首先呢你得注册个开放平台以及商户平台的账号吧,注册完成后呢你会收到一封微信里邮件里面有你的商户号等信息,注册这俩账号完你会拥有商户号,appid,appkey等需要的东西。

开发流程

准备完成后我们来看一下支付的大体流程

商户APP应用与微信支付主要的交互说明:

  1. 用户在商户APP应用(移动端)中选择商品提交订单,支付方式选择微信支付。
  2. 商户APP应用(后台)收到用户支付订单,调用微信支付中的统一下单接口
  3. 商户APP应用(后台)统一下单接口调用成功后,返回的数据中有我们需要的prepay_id,按照签名规范重新生成一个签名,然后把这个重新生成的签名及app需要的数据返回给商户APP应用(移动端)。
  4. 商户APP应用(移动端)收到商户APP应用(后台)的数据调起微信支付,用户进行支付
  5. 商户APP应用(后台)的回调接口会收到微信发来的支付结果通知
  6. 商户APP应用(后台)查询支付结果通知

附:1,4是移动端所要做的事情,2,3,5是我们PHP服务端后台要做的6也是,但我没做,这个根据情况而定如果需要的话就做。

开始干活

步骤1由移动端完成
步骤2. 调用同一下单接口

先要做的是流程中的第二步,调用同一下单接口。官方文档里说了请求的地址与参数,其中有一些是必填参数,有

  • appid

    应用ID 固定值,你申请账号时就给你了
  • mch_id

    商户号 固定值,你申请账号时就给你了
  • nonce_str

    随机字符串 这个是自己写的要求不能长于32位,参见官方给的[标准][8]
  • sign

    签名 我们把这个签名叫做第一次签名,注意这个是个坑,得自己写了,官方只给了如何写的[标准][9]没有代码,这个就比较蛋疼了。好多人掉进这个坑里,写的签名函数不对,老是出错。但不用担心我在文章的最后会贴出代码里面有签名函数直接调用就可以了。(注意看我写的函数使用规则)
  • body

    商品描述 固定值 商品描述交易字段格式根据不同的应用场景按照以下格式:APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
  • out_trade_no

    商户订单号 我们自己定义的订单号,32个字符内、可包含字母。
  • total_fee

    总金额 这个就是你要支付的钱数了,由前端返回。注意一下这里的货币单位是分!
  • spbill_create_ip

    终端IP 这个用户的IP地址,写个取IP地址的函数一调用就行
  • notify_url

    通知地址 这又是一个坑,好多人不理解是干嘛的,这是接收微信支付异步通知回调地址用的,通知url必须为直接可访问的url,不能携带参数! 也可以这样理解,这个是给微信支付的接口,微信来调用的接口,微信调这接口干嘛用呢?就是告诉你用户付款成功啦或者用户付款失败了,然后你就可以在这个接口里通过微信给你返回的信息来做逻辑处理了。
  • trade_type

    固定值   写 “APP” 因为咱写的是APP支付嘛,所以就填APP。 
    

好了就是这些必选参数了,剩下就可以自己选择是否要用的参数了根据自己情况而定。
参数选完了就要发送参数了呗,如何发呢?
我们来调用wechatAppPay类中的unifiedOrder()函数。
啊哈啥!!!!!!??????
(⊙o⊙)?(⊙o⊙)?(⊙o⊙)?(⊙o⊙)?(⊙o⊙)?
wechatAppPay类???unifiedOrder()函数???
对就这这俩东西,不要惊讶,不要着急看最后有代码,有这个类,有代码的O(∩_∩)O哈哈~,
你只需在你的项目中加载这个类就可以调用这个方法了!不要崇拜我( ╯▽╰)(因为这个类不是我写的我也忘了从哪找的了,我从百度搜的然后整理的做了些改动╮(╯▽╰)╭ -_-|||-_-|||-_-!好吧好吧好吧没做改动,只是加了点注释而已,感谢写这个类的大神谢谢O(∩_∩)O谢谢O(∩_∩)O谢谢)
好了抽完疯了,开是干正事!

我们先来new下wechatAppPay类

$wxappid           = 'wx0000000000000';//应用ID 字符串
$mch_id            = '1000000000';//商户号 字符串
$notify_url        = 'http://www.xxx.com/xxxx.php/xxxx/xxxx';//接收微信支付异步通知回调地址 字符串
$wxkey             = '00000000000000000000000';//这个是在商户中心设置的那个值用来生成签名时保证安全的 字符串

$this->wechatAppPay = new wechatAppPay($wxappid, $mch_id, $notify_url, $wxkey);
   

调用wechatAppPay类中的unifiedOrder()函数。unifiedOrder()需要的参数是个数组我们定义为$params

        $params                     = array();
        $params['body']             = 'APP-在线支付';      //必填项 商品描述
        $params['out_trade_no']     =  time()."$member";  //必填项 自定义的订单号
        $params['total_fee']        = ($money*100);       //必填项 订单金额 单位为分所以要*100
        $params['trade_type']       = 'APP';              //必填项 交易类型固定写  APP  
        $params['根据自己情况定的值'] = "根据自己情况定的值" //非必填项 根据自己情况定的值 这个可有好多个可以参看开发文档中的参数
        
        $result = $this->wechatAppPay->unifiedOrder( $params );

注:如果你加了$params['根据自己情况定的值'] wechatAppPay类里要做相应的改动,文章的最后有代码,你一看代码就明白了
现在$result就是我们调用统一下单接口返回的数据了,这个$resutl通过unifiedOrder()函数的处理已经把xml格式变成数组了。$result 里有return_code,return_msg,appid,mch_id,nonce_str,sign,result_code,prepay_id,trade_type。这里面就用一个prepay_id(预支付交易会话ID),其他都不重要了
步骤2完毕

步骤3 把数据返回给商户APP应用(移动端)调起支付接口

现在我们要把调用统一下单接口返回的数据$resutl里的几个值返回给移动端
那几个值呢?这几个:

  • appid

    应用ID 这个是固定的 可以自己写也可以从$resutl里拿 可以让移动端写死 就不用每次返回了
  • partnerid

    商户号 这个也是固定的 可以自己写也可以从$resutl里拿 可以让移动端写死 就不用每次返回了
  • prepayid

    预支付交易会话ID 这个很重要必须返回给移动端 是必须从$resutl里拿的
  • package

    扩展字段 可以自己写也可以从$resutl里拿 暂填写固定值"Sign=WXPay" 可以让移动端写死 就不用每次返回了
  • noncestr

    随机字符串 这个可以自己写也可以从$resutl里拿
  • timestamp

    时间戳 自己生成 标准北京时间,时区为东八区注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字),这里有个坑,ISO端接收的时候好像得强行转化一下,因为返回的是字符串不是数字,还有什么几位的数字之类的,我也不太懂,反正就是写的时候提醒下iOS工程师就行。安卓不清楚。
  • sign

     签名 又来一个坑,我们把这个签名叫做二次签名,但是这个签名不是从$resutl里拿的,而是自己写的,如何写呢,又有坑!因为参与签名的参数值是那几个不清楚,参数名写不对!不怕我有代码!贴给你看!需要参与签名的值有六个!
           $sign_array              = array();
           $sign_array['appid']     = $wx_result['appid'];    //注意 $sign_array['appid'] 里的参数名必须是appid
           $sign_array['partnerid'] = $wx_result['mch_id'];   //注意 $sign_array['partnerid'] 里的参数名必须是partnerid
           $sign_array['prepayid']  = $wx_result['prepay_id'];//注意 $sign_array['prepayid'] 里的参数名必须是prepayid
           $sign_array['package']   = 'Sign=WXPay';           //注意 $sign_array['package'] 里的参数名必须是package
           $sign_array['noncestr']  = $wx_result['nonce_str'];//注意 $sign_array['noncestr'] 里的参数名必须是noncestr
           $sign_array['timestamp'] = time();                 //注意 $sign_array['timestamp'] 里的参数名必须是timestamp
           
           $sign_two = $this->wechatAppPay->MakeSign($sign_array);//调用wechatAppPay类里的MakeSign()函数生成sign
           

    现在就可以把重新生成的sign($sign_two)以及其他参数返回给移动端了,一共返回七个值,有三个之可以让前端写死(appid,partnerid,package),其余四个必须由服务器返回给移动端。

步骤3完毕
步骤4由移动端完成
步骤5 回调接口 支付结果通用通知

还记得步骤2中我们设置的$notify_url吗,对现在就要对这个微信返回到这个接口的数据进行一系列的逻辑处理了
官方是这样写的:

支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。
对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,防止数据泄漏导致出现“假通知”,造成资金损失。

首先来接收数据

$data = $this->wechatAppPay->getNotifyData();//获取数据 用wechatAppPay类里的getNotifyData()方法,这里数据也被getNotifyData()由xml转化成了数组。

然后官方说要采用数据锁进行并发控制,这个我不懂所以没写(如果你懂你会的话请给我留言私信告诉我,在这谢谢了),对数据进行状态检查这个写了,如何写的呢?很简单微信返回的值有好多其中就可以判断result_code(业务结果)和return_code(返回状态码)是否为SUCCESS就可以了代码就不写了。
然后验签,这个很重要因为这是保证数据没有被第三方人为篡改的标准!
如何验签呢?
把返回的数据$data里除去sign剩下的值都参与重新签名我们把这次签名叫做验签签名,验签签名生成后再与$data里的sign对比,如果相同验签通过,否则不通过。这次签名的参数名与二次签名时的参数名不同,$data数组里叫什么参数名就验签时叫什么参数名。听乱了吧?(~ ̄▽ ̄)~(~ ̄▽ ̄)~没关系请看代码

     //假如$data里有如下参数
      $w_sign = array();           //参加验签签名的参数数组                     
      $w_sign['appid']             = $data['appid'];
      $w_sign['bank_type']         = $data['bank_type'];
      $w_sign['cash_fee']          = $data['cash_fee'];
      $w_sign['fee_type']          = $data['fee_type'];
      $w_sign['is_subscribe']      = $data['is_subscribe'];
      $w_sign['mch_id']            = $data['mch_id'];
      $w_sign['nonce_str']         = $data['nonce_str'];
      $w_sign['openid']            = $data['openid'];
      $w_sign['out_trade_no']      = $data['out_trade_no'];
      $w_sign['result_code']       = $data['result_code'];
      $w_sign['return_code']       = $data['return_code'];
      $w_sign['time_end']          = $data['time_end'];
      $w_sign['total_fee']         = $data['total_fee'];
      $w_sign['trade_type']        = $data['trade_type'];
      $w_sign['transaction_id']    = $data['transaction_id'];

      $verify_sign = $this->wechatAppPay->MakeSign($w_sign);//生成验签签名

好了现在假设你的验签已经通过了接下里就是你自己的逻辑处理了

///////////////////////////////////////////////////////
 商户APP应用(后台)处理逻辑代码
//////////////////////////////////////////////////////

自己的逻辑处理已经处理完之后,还得告诉微信一下,别再一直发结果通用通知啦,我已经收到通知并处理完啦!

 $this->wechatAppPay->replyNotify();//商户处理后同步返回给微信参数
 

步骤5结束
步骤6根据自己情况而定