PHP实现微信退款的分析与源码实现
原文:https://blog.csdn.net/jason19905/article/details/78628349
网上的很多PHP微信支付接入教程都颇为复杂,且需要配置和引入较多的文件,本人通过整理后给出一个单文件版的,希望可以给各位想接入微信支付的带来些许帮助和借鉴意义。
直接运行该文件即可给指定的微信用户退款。
需要注意的事项:
* 1.微信退款到零钱要求必传证书,需要到https://pay.weixin.qq.com 账户中心->账户设置->API安全->下载证书,然后修改代码中的证书路径
* 2.该文件需放到支付授权目录下,可以在微信支付商户平台->产品中心->开发配置中设置。
* 3.如提示签名错误可以通过微信支付签名验证工具进行验证:https://pay.weixin.qq.com/wiki/tools/signverify/
* 4.错误码参照 :https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
代码如下:
<?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'; ?>
Wxpay.php
<?php
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;
}
} ?>
PHP实现微信退款的分析与源码实现的更多相关文章
- VopSdk一个高逼格微信公众号开发SDK(源码下载)
看之前回复很多说明大家很有热情&文章被误删掉了,不想让有的朋友错失这个高逼格的东西,现在重新发布,这次就直接放出源码,文章最末下载地址. 看之前回复很多说明大家很有热情&文章被误删掉了 ...
- 从微信小程序开发者工具源码看实现原理(四)- - 自适应布局
从前面从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计可以知道,小程序大部分是通过web技术进行渲染的,也就是最终通过浏览器的dom tree + cssom来生成渲染树:既然最终是通 ...
- Android源码分析--CircleImageView 源码详解
源码地址为 https://github.com/hdodenhof/CircleImageView 实际上就是一个圆形的imageview 的自定义控件.代码写的很优雅,实现效果也很好, 特此分析. ...
- 一 分析easyswoole源码(启动服务)
分析easyswoole源码 1以启动为例 //检查是否已经安装 installCheck();//检查锁文件是否存在,不存在结束 //启动服务 serverStart showLogo();//显示 ...
- 分析jQuery源码时记录的一点感悟
分析jQuery源码时记录的一点感悟 1. 链式写法 这是jQuery语法上的最大特色,也许该改改POJO里的set方法,和其他的非get方法什么的,可以把多行代码合并,减去每次 ...
- Linux内核(2) - 分析内核源码如何入手(上)
透过现象看本质,兽兽们无非就是一些人体艺术展示.同样往本质里看过去,学习内核,就是学习内核的源代码,任何内核有关的书籍都是基于内核,而又不高于内核的. 既然要学习内核源码,就要经常对内核代码进行分析, ...
- STM32F103 ucLinux开发之一(BOOT分析及源码)
STM32F103 ucLinux开发BOOT STM3210E-EVAL官方开发板主芯片STM32F103ZET6: 片内512K Flash,地址0x0800 0000 ~ 0x0807 FFFF ...
- 微信小程序一键生成源码 在线制作定制功能强大的微信小程序
微信小程序发展到现在,短短的一年不到的时间(很快就要迎来微信小程序周年庆),在快迎来周年庆之际,百牛信息技术bainiu.ltd特记录一下这个发展的历程,用于将来见证小程序发展的辉煌时刻,我们还能知道 ...
- 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计
使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...
随机推荐
- duilib教程之duilib入门简明教程8.完整的自绘标题栏
看了前面那么多教程,相信对duilib已有基本映像了,我们就快马加鞭,做出一个完整的自绘标题栏吧~ 看到下面这个效果图,小伙伴们是不是有点惊呆了呢~O(∩_∩)O~ duilib实现以 ...
- 16.ajax_case08
# 抓取简书博客总阅读量 # https://www.jianshu.com/u/130f76596b02 import requests import json import re from lxm ...
- Stern-Brocot Tree、伪.GCD 副本
Stern-Brocot Tree.伪.GCD 副本 伪.GCD 问题 1:\(f(a,b,c,n) = \sum_{i=0}^{n} [\frac{ai+b}{c}]\) Case 1: \(a\g ...
- LUA中的冒号、点和self
在Lua编程中,经常会看到有时用点号定义一个table的成员函数,有时却用冒号,在调用的时候也是如此.那么点号和冒号在使用上有什么区别呢?它们与self之间又是什么样的关系呢?稍安勿躁,接下来谜底将一 ...
- CF891D Sloth
题意:给你一棵树,你选择删掉一条边,再加上一条边(也要保证为树),问最后树上的节点能够两两完美匹配的加删边方案数? n<=5e5. 标程: #include<cstdio> #inc ...
- Ignite-Spark
2017.12.02 Ignite-Spark 讲ppt的时候还是很紧张 PPT地址 https://files.cnblogs.com/files/swobble/Spark.pptx
- 廖雪峰Java15JDBC编程-3JDBC接口-3JDBC更新
使用update语句的时候,需要通过JDBC实现update语句的执行,这个时候仍然通过PreparedStatement对象来使用,直接传入update语句,然后通过setObject传入占位符的值 ...
- 边缘节点服务ENS重磅升级 阿里云首次定义“边缘云计算”概念层层深入
随着5G.物联网时代的到来以及云计算应用的逐渐增加,传统集中式的云计算技术已经无法满足终端侧“大连接,低时延,大带宽”的需求.结合边缘计算的概念,云计算将必然发展到下一个技术阶段,也就是将云计算的能力 ...
- 自己整理的一个访问SQLite3数据库的C++类
原文地址:自己整理的一个访问SQLite3数据库的C++类作者:vigra 近日,对SQLite3的使用进行了研究.真不愧是优秀的嵌入数据库,API接口也极其简捷.基本上只要使用以下几个接口就能完成数 ...
- ArcMap10.2 中制作符号库
今天在发布地图服务时,发现地图中的3D符号没法用,出现”00013“错误,如下: