php实现微信支付之退款功能


Posted in PHP onMay 30, 2018

网上的很多PHP微信支付接入教程都颇为复杂,且需要配置和引入较多的文件,本人通过整理后给出一个单文件版的,希望可以给各位想接入微信支付的带来些许帮助和借鉴意义。

直接运行该文件即可给指定的微信用户退款。

需要注意的事项:

1.微信退款到零钱要求必传证书,需要到这里账户中心->账户设置->API安全->下载证书,然后修改代码中的证书路径
2.该文件需放到支付授权目录下,可以在微信支付商户平台->产品中心->开发配置中设置。
3.如提示签名错误可以通过微信支付签名验证工具进行验证:微信公众平台支付接口调试工具
4.错误码参照:参照地址

代码如下:

<?php
/**
 * 关于微信退款的说明
 * 1.微信退款要求必传证书,需要到https://pay.weixin.qq.com 账户中心->账户设置->API安全->下载证书,证书路径在第119行和122行修改
 * 2.错误码参照 :https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
 */
header('Content-type:text/html; Charset=utf-8');
$mchid = 'xxxxx';     //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
$appid = 'xxxxx'; //微信支付申请对应的公众号的APPID
$apiKey = 'xxxxx';  //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
$orderNo = '';           //商户订单号(商户订单号与微信订单号二选一,至少填一个)
$wxOrderNo = '';           //微信订单号(商户订单号与微信订单号二选一,至少填一个)
$totalFee = 0.01;          //订单金额,单位:元
$refundFee = 0.01;         //退款金额,单位:元
$refundNo = 'refund_'.uniqid();    //退款订单号(可随机生成)
$wxPay = new WxpayService($mchid,$appid,$apiKey);
$result = $wxPay->doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo,$orderNo);
if($result===true){
  echo 'refund success';exit();
}
echo 'refund fail';
class WxpayService
{
  protected $mchid;
  protected $appid;
  protected $apiKey;
  public $data = null;
  public function __construct($mchid, $appid, $key)
  {
    $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号
    $this->appid = $appid; //微信支付申请对应的公众号的APPID
    $this->apiKey = $key;  //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
  }
  /**
   * 退款
   * @param float $totalFee 订单金额 单位元
   * @param float $refundFee 退款金额 单位元
   * @param string $refundNo 退款单号
   * @param string $wxOrderNo 微信订单号
   * @param string $orderNo 商户订单号
   * @return string
   */
  public function doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo='',$orderNo='')
  {
    $config = array(
      'mch_id' => $this->mchid,
      'appid' => $this->appid,
      'key' => $this->apiKey,
    );
    $unified = array(
      'appid' => $config['appid'],
      'mch_id' => $config['mch_id'],
      'nonce_str' => self::createNonceStr(),
      'total_fee' => intval($totalFee * 100),    //订单金额  单位 转为分
      'refund_fee' => intval($refundFee * 100),    //退款金额 单位 转为分
      'sign_type' => 'MD5',      //签名类型 支持HMAC-SHA256和MD5,默认为MD5
      'transaction_id'=>$wxOrderNo,        //微信订单号
      'out_trade_no'=>$orderNo,    //商户订单号
      'out_refund_no'=>$refundNo,    //商户退款单号
      'refund_desc'=>'商品已售完',   //退款原因(选填)
    );
    $unified['sign'] = self::getSign($unified, $config['key']);
    $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/secapi/pay/refund', self::arrayToXml($unified));
    $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
    if ($unifiedOrder === false) {
      die('parse xml error');
    }
    if ($unifiedOrder->return_code != 'SUCCESS') {
      die($unifiedOrder->return_msg);
    }
    if ($unifiedOrder->result_code != 'SUCCESS') {
      die($unifiedOrder->err_code);
    }
    return true;
  }
  public static function curlGet($url = '', $options = array())
  {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    if (!empty($options)) {
      curl_setopt_array($ch, $options);
    }
    //https请求 不验证证书和host
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
  }
  public function curlPost($url = '', $postData = '', $options = array())
  {
    if (is_array($postData)) {
      $postData = http_build_query($postData);
    }
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
    if (!empty($options)) {
      curl_setopt_array($ch, $options);
    }
    //https请求 不验证证书和host
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    //第一种方法,cert 与 key 分别属于两个.pem文件
    //默认格式为PEM,可以注释
    curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
    curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert/apiclient_cert.pem');
    //默认格式为PEM,可以注释
    curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
    curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/cert/apiclient_key.pem');
    //第二种方式,两个文件合成一个.pem文件
//    curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem');
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
  }
  public static function createNonceStr($length = 16)
  {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $str = '';
    for ($i = 0; $i < $length; $i++) {
      $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
    }
    return $str;
  }
  public static 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;
  }
  public static function getSign($params, $key)
  {
    ksort($params, SORT_STRING);
    $unSignParaString = self::formatQueryParaMap($params, false);
    $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
    return $signStr;
  }
  protected static function formatQueryParaMap($paraMap, $urlEncode = false)
  {
    $buff = "";
    ksort($paraMap);
    foreach ($paraMap as $k => $v) {
      if (null != $v && "null" != $v) {
        if ($urlEncode) {
          $v = urlencode($v);
        }
        $buff .= $k . "=" . $v . "&";
      }
    }
    $reqPar = '';
    if (strlen($buff) > 0) {
      $reqPar = substr($buff, 0, strlen($buff) - 1);
    }
    return $reqPar;
  }
}
?>

github下载地址

相关文章:

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

PHP 相关文章推荐
《APMServ 5.1.2》使用图解
Oct 23 PHP
PHP中文分词的简单实现代码分享
Jul 17 PHP
应用开发中涉及到的css和php笔记分享
Aug 02 PHP
php FLEA中二叉树数组的遍历输出
Sep 26 PHP
php结合ajax实现赞、顶、踩功能实例
May 12 PHP
CI框架给视图添加动态数据
Dec 01 PHP
yii2 数据库读写分离配置示例
Feb 10 PHP
PHP利用二叉堆实现TopK-算法的方法详解
Apr 24 PHP
PHP简单实现合并2个数字键数组值的方法
May 30 PHP
php实现的三个常用加密解密功能函数示例
Nov 06 PHP
详解no input file specified 三种解决方法
Nov 29 PHP
PHP7 foreach() 函数修改
Mar 09 PHP
分享5个非常有用的Laravel Blade指令
May 30 #PHP
php实现微信支付之企业付款
May 30 #PHP
ThinkPHP5框架缓存查询操作分析
May 30 #PHP
PHP实现通过CURL上传文件功能示例
May 30 #PHP
php实现微信支付之现金红包
May 30 #PHP
php微信支付之公众号支付功能
May 30 #PHP
php实现微信原生支付(扫码支付)功能
May 30 #PHP
You might like
Cannot modify header information错误解决方法
2008/10/08 PHP
PHP中删除变量时unset()和null的区别分析
2011/01/27 PHP
ThinkPHP视图查询详解
2014/06/30 PHP
PHP导入导出Excel代码
2015/07/07 PHP
thinkPHP分组后模板无法加载问题解决方法
2016/07/12 PHP
laravel excel 上传文件保存到本地服务器功能
2019/11/14 PHP
javascript正则表达式之search()用法实例
2015/01/19 Javascript
基于jQuery实现Div窗口震动特效代码-代码简单
2015/08/28 Javascript
jquery插件jquery.dragscale.js实现拖拽改变元素大小的方法(附demo源码下载)
2016/02/25 Javascript
原生javascript实现的一个简单动画效果
2016/03/30 Javascript
Jquery和Js获得元素标签名称的方法总结
2016/10/08 Javascript
bootstrap select插件封装成Vue2.0组件
2017/04/17 Javascript
关于JavaScript中forEach和each用法浅析
2017/07/27 Javascript
详谈vue+webpack解决css引用图片打包后找不到资源文件的问题
2018/03/06 Javascript
vue权限路由实现的方法示例总结
2018/07/29 Javascript
微信小程序在其他页面监听globalData中值的变化
2019/07/15 Javascript
使用JS监听键盘按下事件(keydown event)
2019/11/07 Javascript
Python psutil模块简单使用实例
2015/04/28 Python
Python求两个文本文件以行为单位的交集、并集与差集的方法
2015/06/17 Python
Python数据类型之Tuple元组实例详解
2019/05/08 Python
python networkx 根据图的权重画图实现
2019/07/10 Python
python递归法实现简易连连看小游戏
2020/03/25 Python
Python基于pandas绘制散点图矩阵代码实例
2020/06/04 Python
使用Django的JsonResponse返回数据的实现
2021/01/15 Python
浅析两列自适应布局的3种思路
2016/05/03 HTML / CSS
利用HTML5中的Canvas绘制一张笑脸的教程
2015/05/07 HTML / CSS
约瑟夫·特纳男装:Joseph Turner
2017/10/10 全球购物
Carter’s OshKosh加拿大:购买婴幼儿服装和童装
2018/11/27 全球购物
俄罗斯园林植物网上商店:Garshinka
2020/07/16 全球购物
护理专业自荐信
2013/12/03 职场文书
护理专业学生的求职信范文
2013/12/11 职场文书
DIY手工制作经营店创业计划书
2014/02/01 职场文书
政治思想表现评语
2014/05/04 职场文书
教师党员公开承诺事项
2014/05/28 职场文书
Java 关于String字符串原理上的问题
2022/04/07 Java/Android
利用Python实现模拟登录知乎
2022/05/25 Python