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 相关文章推荐
了解Joomla 这款来自国外的php网站管理系统
Mar 11 PHP
php生成随机密码的几种方法
Jan 17 PHP
PHP批量生成静态HTML的简单原理和方法
Apr 20 PHP
php事务处理实例详解
Jul 11 PHP
PHP缓存集成库phpFastCache用法
Dec 15 PHP
php实现搜索一维数组元素并删除二维数组对应元素的方法
Jul 06 PHP
PHP使用ODBC连接数据库的方法
Jul 18 PHP
简述php环境搭建与配置
Dec 05 PHP
php使用curl实现简单模拟提交表单功能
May 15 PHP
PHP使用数组实现矩阵数学运算的方法示例
May 29 PHP
PHP实现图的邻接矩阵表示及几种简单遍历算法分析
Nov 24 PHP
php 命名空间(namespace)原理与用法实例小结
Nov 13 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执行速率优化技巧小结
2008/03/15 PHP
Laravel 5 框架入门(三)
2015/04/09 PHP
PHP结合jQuery实现找回密码
2015/07/22 PHP
Yii2主题(Theme)用法详解
2016/07/23 PHP
php外部执行命令函数用法小结
2016/10/11 PHP
PHP使用PDO操作sqlite数据库应用案例
2019/03/07 PHP
PHP创建XML接口示例
2019/07/04 PHP
PHP 7.4中使用预加载的方法详解
2019/07/08 PHP
jquery1.4.2 for Visual studio 2010 模板文件
2010/07/14 Javascript
如何将一个String和多个String值进行比较思路分析
2013/04/22 Javascript
js中单引号与双引号冲突问题解决方法
2013/10/04 Javascript
cocos2dx骨骼动画Armature源码剖析(三)
2015/09/08 Javascript
Bootstrap每天必学之按钮(一)
2015/11/24 Javascript
深入分析jQuery的ready函数是如何工作的(工作原理)
2015/12/17 Javascript
详解Vue基于vue-quill-editor富文本编辑器使用心得
2019/01/03 Javascript
Vue项目移动端滚动穿透问题的实现
2020/05/19 Javascript
[46:55]LGD vs Liquid 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/19 DOTA
从零学python系列之浅谈pickle模块封装和拆封数据对象的方法
2014/05/23 Python
Python工程师面试题 与Python基础语法相关
2016/01/14 Python
总结python实现父类调用两种方法的不同
2017/01/15 Python
Python制作刷网页流量工具
2017/04/23 Python
Python读写压缩文件的方法
2020/07/30 Python
Python模拟登录和登录跳转的参考示例
2020/10/30 Python
Python WebSocket长连接心跳与短连接的示例
2020/11/24 Python
HTML5 Canvas中使用路径描画二阶、三阶贝塞尔曲线
2015/01/01 HTML / CSS
基于HTML5陀螺仪实现ofo首页眼睛移动效果的示例
2017/07/31 HTML / CSS
中国排名第一的外贸销售网站:LightInTheBox.com(兰亭集势)
2016/10/28 全球购物
潘多拉珠宝英国官方网上商店:PANDORA英国
2018/06/12 全球购物
创意爱尔兰礼物:Creative Irish Gifts
2020/01/29 全球购物
鉴定评语大全
2014/05/05 职场文书
银行转正自我鉴定
2014/09/29 职场文书
实习工作表现评语
2014/12/31 职场文书
python生成随机数、随机字符、随机字符串
2021/04/06 Python
HAM-2000摩机图
2021/04/22 无线电
python获取对象信息的实例详解
2021/07/07 Python
PyTorch device与cuda.device用法
2022/04/03 Python