php如何实现微信小程序支付及退款

互联网 18-5-19
微信小程序支付的主要逻辑集中在后端,前端只需携带支付所需的数据请求后端接口然后根据返回结果做相应成功失败处理即可。本篇文章后端使用的是php,侧重于整个支付的流程和一些细节方面的东西。所以使用其他后端语言的朋友有需要也是可以看一下的。很多时候开发的需求和相应问题的解决真的要跳出语言语法层面,去从系统和流程的角度考虑。

一. 支付

支付主要分为几个步骤:

从这几个步骤可以看出,后端主要的作用就是将支付需要的数据传给微信服务器,再根据微信服务器的响应确定支付是否完成。

1. 前端请求支付

2. 后端请求微信服务器

后端接收到前端发送的支付请求后,可以进行一下相关验证,例如判断一下用户有没有问题,支付金额对不对等等。

小程序 appid。写小程序的大概没有不知道这个的。。。

用户标识 openid。也就是用户的小程序标识,在我上篇博客中说明了如何获取。

商户号 mch_id 。申请开通微信支付商户认证成功后微信发给你的邮件里有

商户订单号 out_trade_no 。商户为这次支付生成的订单号

总金额 total_fee 。订单总金额,很重要的一点是单位是分,要特别注意。

微信服务器回调通知接口地址 notify_url。微信确认钱已经到账后,会往这个地址多次发送消息,告诉你顾客已经付完钱了,你需要返回消息给微信表示你已经收到了通知。。这个地址不能有端口号,同时要能直接接受POST方法请求。

交易类型 trade_type 。微信小程序支付此值统一为 JSAPI

商品信息 Body。类似"腾讯-游戏"这种格式

终端IP地址 spbill_create_ip 。终端地址IP,也就是请求支付的 IP 地址。

随机字符串 nonce_str 。需要后端随机生成的字符串用于保证数据安全。微信要求不长于32位。

在处理好以上所有数据后,将这些数据以 XML 格式整理并以 POST 方法发送到 微信支付统一下单接口

3.后端接受微信服务器返回数据

微信服务器在接收到支付数据之后,如果数据没有问题,其会返回用于支付的相应数据,其中非常重要的是 名称为 prepay_id 的数据字段,需要将此数据返回前端,前端才能继续支付。

因此,在后端接收到微信服务器的返回数据后,需要进行相应的处理,最终返回到前端如下数据:

appid 不需多说

timeStamp 当前时间戳

nonceStr 随机字符串

package 就是上面提到的 prepay_id,不过切记格式如 “prepay_id= prepay_id_item“。否则会导致错误。

signType 加密方式,一般应该是 MD5

paySign 对以上数据进行相应处理并加密。

到这里,后端的支付接口已经完成了接收前端支付请求,并返回了前端支付所需数据的功能。

4. 前端发起支付

前端在接收到返回数据后,使用 wx.requestPayment() 来请求发起支付。此 API 需要的对象参数各项值就是我们上一步返回的各个数据。

5.后端接受微信服务器回调

前端完成支付后,微信服务器确认支付已经完成。就会向第一步中设置的回调地址发送通知。后端的接收回调接口在接收到通知后,就可以判断支付是否完成,从而决定后续动作。

需要注意的是,在接收到微信服务器的回调通知后,根据通知的result_code字段判断支付是否成功。在接受到成功的通知后,后端需要返回success数据向微信服务器告知已得到回调通知。否则微信服务器会不停的向后端发送消息。另外微信的通知是以XML格式发送的,在接受处理时需要注意。

微信的大概支付流程就是这样。以下是PHP语法的微信支付类,可以比照上面的步骤介绍,加深理解。在需要支付时,直接传入参数实例化此类再调用类的 pay 方法即可。

//微信支付类  class WeiXinPay{    //=======【基本信息设置】=====================================    //微信公众号身份的唯一标识    protected $APPID = appid;//填写您的appid。微信公众平台里的    protected $APPSECRET = secret;    //受理商ID,身份标识    protected $MCHID = '11111111';//商户id    //商户支付密钥Key    protected $KEY = '192006250b4c09247ec02edce69f6a2d';    //回调通知接口    protected $APPURL =   'https://smart.afei.com/receivesuc';    //交易类型    protected $TRADETYPE = 'JSAPI';    //商品类型信息    protected $BODY = 'wx/book';    //微信支付类的构造函数    function __construct($openid,$outTradeNo,$totalFee){      $this->openid = $openid; //用户唯一标识      $this->outTradeNo = $outTradeNo; //商品编号      $this->totalFee = $totalFee; //总价    }    //微信支付类向外暴露的支付接口    public function pay(){      $result = $this->weixinapp();      return $result;    }     //对微信统一下单接口返回的支付相关数据进行处理     private function weixinapp(){       $unifiedorder=$this->unifiedorder();       $parameters=array(       'appId'=>$this->APPID,//小程序ID       'timeStamp'=>''.time().'',//时间戳       'nonceStr'=>$this->createNoncestr(),//随机串       'package'=>'prepay_id='.$unifiedorder['prepay_id'],//数据包       'signType'=>'MD5'//签名方式         );       $parameters['paySign']=$this->getSign($parameters);       return $parameters;     }    /*     *请求微信统一下单接口     */    private function unifiedorder(){      $parameters = array(        'appid' => $this->APPID,//小程序id        'mch_id'=> $this->MCHID,//商户id        'spbill_create_ip'=>$_SERVER['REMOTE_ADDR'],//终端ip        'notify_url'=>$this->APPURL, //通知地址        'nonce_str'=> $this->createNoncestr(),//随机字符串        'out_trade_no'=>$this->outTradeNo,//商户订单编号        'total_fee'=>floatval($this->totalFee), //总金额        'open_id'=>$this->openid,//用户openid        'trade_type'=>$this->TRADETYPE,//交易类型        'body' =>$this->BODY, //商品信息      );      $parameters['sign'] = $this->getSign($parameters);      $xmlData = $this->arrayToXml($parameters);      $xml_result = $this->postXmlCurl($xmlData,'https://api.mch.weixin.qq.com/pay/unifiedorder',60);      $result = $this->xmlToArray($xml_result);      return $result;    }    //数组转字符串方法    protected function arrayToXml($arr){      $xml = "<xml>";      foreach ($arr as $key=>$val)      {        if (is_numeric($val)){          $xml.="<".$key.">".$val."</".$key.">";        }else{           $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";        }      }      $xml.="</xml>";      return $xml;    }    protected function xmlToArray($xml){      $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);      return $array_data;    }    //发送xml请求方法    private static function postXmlCurl($xml, $url, $second = 30)    {      $ch = curl_init();      //设置超时      curl_setopt($ch, CURLOPT_TIMEOUT, $second);      curl_setopt($ch, CURLOPT_URL, $url);      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //严格校验      //设置header      curl_setopt($ch, CURLOPT_HEADER, FALSE);      //要求结果为字符串且输出到屏幕上      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);      //post提交方式      curl_setopt($ch, CURLOPT_POST, TRUE);      curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);      curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);      curl_setopt($ch, CURLOPT_TIMEOUT, 40);      set_time_limit(0);      //运行curl      $data = curl_exec($ch);      //返回结果      if ($data) {        curl_close($ch);        return $data;      } else {        $error = curl_errno($ch);        curl_close($ch);        throw new WxPayException("curl出错,错误码:$error");      }    }    /*     * 对要发送到微信统一下单接口的数据进行签名     */    protected function getSign($Obj){       foreach ($Obj as $k => $v){       $Parameters[$k] = $v;       }       //签名步骤一:按字典序排序参数       ksort($Parameters);       $String = $this->formatBizQueryParaMap($Parameters, false);       //签名步骤二:在string后加入KEY       $String = $String."&key=".$this->KEY;       //签名步骤三:MD5加密       $String = md5($String);       //签名步骤四:所有字符转为大写       $result_ = strtoupper($String);       return $result_;     }    /*     *排序并格式化参数方法,签名时需要使用     */    protected function formatBizQueryParaMap($paraMap, $urlencode)    {      $buff = "";      ksort($paraMap);      foreach ($paraMap as $k => $v)      {        if($urlencode)        {          $v = urlencode($v);        }        //$buff .= strtolower($k) . "=" . $v . "&";        $buff .= $k . "=" . $v . "&";      }      $reqPar;      if (strlen($buff) > 0)      {        $reqPar = substr($buff, 0, strlen($buff)-1);      }      return $reqPar;    }    /*     * 生成随机字符串方法     */    protected function createNoncestr($length = 32 ){       $chars = "abcdefghijklmnopqrstuvwxyz0123456789";       $str ="";       for ( $i = 0; $i < $length; $i++ ) {       $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);       }       return $str;       }  }

以上就是微信支付的相关流程。在理清思路后,流程还是比较清晰和简单的。重点在于需要注意一些细节问题,例如数据格式,加密方法等。

下面说一下微信小程序退款的具体实现

二.退款

小程序退款的流程和付款相似,但有一些细节上的不同。

首先退款的步骤通常如下:

用户前端点击退款按钮后,后端接收到用户的退款请求通过商城后台呈现给商户,商户确定允许退款后,后端再发起向微信退款接口的请求来请求退款。

后端向微信退款接口发送请求后,得到响应信息,确定退款是否完成,根据退款是否完成再去进行改变订单状态等业务逻辑。

退款的步骤相对微信支付来说比较简单。

值得注意的有以下两点:

2.退款请求需要在请求服务器安装微信提供的安全证书,也就是说,发起退款请求相比较支付请求在请求时请求方法不能复用,因为微信退款需要携带证书的请求,此证书可在申请微信商户号成功后从微信商户平台自行下载,Linux下的PHP开发环境的证书只需要放在网站根目录的cert文件夹中即可。其他开发环境可能需要导入操作。

下面讲解一下退款的具体步骤

一. 用户发起退款请求

用户在前端发起退款请求,后端接收到退款请求,将相应订单标记为申请退款,展示在后台.商户查看后,如果同意退款再进行相应操作.此后才进入真正的退款流程.

二. 商户发起退款请求

小程序 appid。

商户号 mch_id 。申请开通微信支付商户认证成功后微信发给你的邮件里有

商户订单号 out_trade_no 。退款订单在支付时生成的订单号

退款订单号 out_refund_no 。由后端生成的退款单号,需要保证唯一,因为多个同样的退款单号只会退款一次。

总金额 total_fee 。订单总金额,单位为分。

退款金额 refund_fee 需要退款的金额,单位同样为分

操作员 op_user_id .与商户号相同即可

随机字符串 nonce_str 。同支付请求

签名 sign 。使用上面的所有参数进行相应处理加密生成签名。(具体处理方式与支付相同,可直接复用。)

三. 退款完成

退款因为流程与支付大同小异,因此退款的PHP类我选择了直接继承支付类,

代码如下,注意区分退款请求方法postXmlSSLCurl和支付请求方法postXmlCurl的区别,这也就是上文提到的退款需要的双向证书的使用。

````   class WinXinRefund extends WeiXinPay{    protected \$SSLCERT_PATH = 'cert/apiclient_cert.pem';//证书路径    protected \$SSLKEY_PATH = 'cert/apiclient_key.pem';//证书路径    protected \$opUserId = '1234567899';//商户号  function __construct($openid,$outTradeNo,$totalFee,$outRefundNo,$refundFee){    //初始化退款类需要的变量    $this->openid = $openid;    $this->outTradeNo = $outTradeNo;    $this->totalFee = $totalFee;    $this->outRefundNo = $outRefundNo;    $this->refundFee = $refundFee;  }   public function refund(){    //对外暴露的退款接口    $result = $this->wxrefundapi();    return $result;  }  private function wxrefundapi(){    //通过微信api进行退款流程    $parma = array(      'appid'=> $this->APPID,      'mch_id'=> $this->MCHID,      'nonce_str'=> $this->createNoncestr(),      'out_refund_no'=> $this->outRefundNo,      'out_trade_no'=> $this->outTradeNo,      'total_fee'=> $this->totalFee,      'refund_fee'=> $this->refundFee,      'op_user_id' => $this->opUserId,    );    $parma['sign'] = $this->getSign($parma);    $xmldata = $this->arrayToXml($parma);    $xmlresult = $this->postXmlSSLCurl($xmldata,'https://api.mch.weixin.qq.com/secapi/pay/refund');    $result = $this->xmlToArray($xmlresult);    return $result;  }  //需要使用证书的请求  function postXmlSSLCurl($xml,$url,$second=30)  {    $ch = curl_init();    //超时时间    curl_setopt($ch,CURLOPT_TIMEOUT,$second);    //这里设置代理,如果有的话    //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');    //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);    curl_setopt($ch,CURLOPT_URL, $url);    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);    //设置header    curl_setopt($ch,CURLOPT_HEADER,FALSE);    //要求结果为字符串且输出到屏幕上    curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);    //设置证书    //使用证书:cert 与 key 分别属于两个.pem文件    //默认格式为PEM,可以注释    curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');    curl_setopt($ch,CURLOPT_SSLCERT, $this->SSLCERT_PATH);    //默认格式为PEM,可以注释    curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');    curl_setopt($ch,CURLOPT_SSLKEY, $this->SSLKEY_PATH);    //post提交方式    curl_setopt($ch,CURLOPT_POST, true);    curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);    $data = curl_exec($ch);    //返回结果    if($data){      curl_close($ch);      return $data;    }    else {      $error = curl_errno($ch);      echo "curl出错,错误码:$error"."<br>";      curl_close($ch);      return false;    }  }}

三. 总结

以上就是关于微信支付和退款的流程及相关知识的介绍。文中的 PHP类 均封装直接可用。

以上内容就是关于php如何实现微信小程序支付和退款功能,相信php程序员也有了一定的思路,希望能帮助到大家。

相关推荐:

微信小程序机器人自动客服功能

微信小程序如何实现图片放大预览功能

微信小程序开发入门实例

微信小程序分享时可自定义配图新功能

最全的微信小程序项目实例

以上就是php如何实现微信小程序支付及退款的详细内容,更多内容请关注技术你好其它相关文章!

来源链接:
免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表本站的观点或立场
标签: 退款
上一篇:php获取远程图片并下载保存到本地的方法分析 下一篇:微信小程序机器人自动客服功能

相关资讯