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 相关文章推荐
数据库的日期格式转换
Oct 09 PHP
一键删除顽固的空文件夹 软件下载
Jan 26 PHP
PHP 输出简单动态WAP页面
Jun 09 PHP
PHP操作mysql函数详解,mysql和php交互函数
May 19 PHP
Yii中实现处理前后台登录的新方法
Dec 28 PHP
smarty模板数学运算示例
Dec 11 PHP
PHP常用的三种设计模式
Feb 17 PHP
php的常量和变量实例详解
Jun 27 PHP
php检查函数必传参数是否存在的实例详解
Aug 28 PHP
浅谈PHP中如何实现Hook机制
Nov 14 PHP
thinkphp5.0自定义验证规则使用方法
Nov 16 PHP
laravel邮件发送的实现代码示例
Jan 31 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
PHP 字符串编码截取函数(兼容utf-8和gb2312)
2009/05/02 PHP
PHP explode()函数的几个应用和implode()函数有什么区别
2015/11/05 PHP
PHP中in_array的隐式转换的解决方法
2018/03/06 PHP
jQuery1.6 正式版发布并提供下载
2011/05/05 Javascript
Tab页界面 用jQuery及Ajax技术实现(php后台)
2011/10/12 Javascript
Javascript 中的 call 和 apply使用介绍
2012/02/22 Javascript
简单的Jquery遮罩层代码实例
2013/11/14 Javascript
抛弃Nginx使用nodejs做反向代理服务器
2014/07/17 NodeJs
jQuery简单实现input文本框内灰色提示文本效果的方法
2015/12/02 Javascript
JS实现搜索框文字可删除功能
2016/12/28 Javascript
vue组件中点击按钮后修改输入框的状态实例代码
2017/04/14 Javascript
浅谈AngularJS中使用$resource(已更新)
2017/09/14 Javascript
JS简单获得节点元素的方法示例
2018/02/10 Javascript
VUE中v-on:click事件中获取当前dom元素的代码
2018/08/22 Javascript
nodejs遍历文件夹下并操作HTML/CSS/JS/PNG/JPG的方法
2018/11/01 NodeJs
Vue $mount实战之实现消息弹窗组件
2019/04/22 Javascript
layui实现数据分页功能(ajax异步)
2019/07/27 Javascript
解决vue+router路由跳转不起作用的一项原因
2020/07/19 Javascript
[02:33]DOTA2英雄基础教程 司夜刺客
2013/12/04 DOTA
详解Python中的文本处理
2015/04/11 Python
Python中列表、字典、元组数据结构的简单学习笔记
2016/03/20 Python
Python定义函数时参数有默认值问题解决
2019/12/19 Python
matplotlib quiver箭图绘制案例
2020/04/17 Python
html5拍照功能实现代码(htm5上传文件)
2013/12/11 HTML / CSS
葡萄牙鞋子品牌:Fair
2016/12/10 全球购物
利用异或运算实现两个无符号数的加法运算
2013/12/20 面试题
C#实现启动一个进程
2016/10/01 面试题
大学生暑期实践感言
2014/02/26 职场文书
小学班干部竞选演讲稿
2014/04/24 职场文书
消防安全承诺书
2014/05/22 职场文书
学习“七一”讲话精神体会
2014/07/08 职场文书
医德医魂心得体会
2014/09/11 职场文书
奖励通知
2015/04/22 职场文书
本科毕业论文致谢词
2015/05/14 职场文书
《所见》教学反思
2016/02/23 职场文书
python 爬取华为应用市场评论
2021/05/29 Python