博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【微信小程序】退款功能教程(含申请退款和退款回调)
阅读量:5770 次
发布时间:2019-06-18

本文共 15658 字,大约阅读时间需要 52 分钟。

1、一定要区分小程序和公众号的退款,唯一的区别就是 appid不一样,其他的都是一样的。

不废话,直接写代码了啊。 放大招!!!

然后,需要注意的:最好是把证书放在下面的php的同级或者下级。

证书的路径一定要是服务器的根路径,比如E:\tupuu\WWW\XXX。而像http://www.xxx.com/../.. 是不行的,会报58错误。

DEMO1、用来调试退款流程,在浏览器直接访问这个php文件。

 

'homeX20171206155833_1180', 'out_trade_no'=>'homeX20171206155833_1180', 'order_amount'=>'0.02', ); $appid = 'wx60****d';//如果是公众号 就是公众号的appid;小程序就是小程序的appid $mch_id = '126****01'; $KEY = 'xixi***09000908bkj'; $nonce_str = randomkeys(32);//随机字符串 $op_user_id = $mch_id; $out_trade_no = $order_msg['out_trade_no'];//商户传微信的订单号 $out_refund_no = $order_msg['out_trade_no'];//用户请求退款id对应的out_trade_no订单号 $refund_fee =1;//退款金额 $total_fee = 2;//订单总金额 //这里是按照顺序的 因为下面的签名是按照(字典序)顺序 排序错误 肯定出错 $post['appid'] = $appid; $post['mch_id'] = $mch_id; $post['nonce_str'] = $nonce_str;//随机字符串 $post['op_user_id'] = $mch_id; $post['out_refund_no'] = $out_refund_no; $post['out_trade_no'] = $out_trade_no; $post['refund_fee'] = $refund_fee; $post['total_fee'] = $total_fee; //总金额 最低为一分钱 必须是整数 $sign = MakeSign($post,$KEY); //签名 $post_xml = '
'.$appid.'
'.$mch_id.'
'.$nonce_str.'
'.$mch_id.'
'.$out_refund_no.'
'.$out_trade_no.'
'.$refund_fee.'
'.$total_fee.'
'.$sign.'
'; //echo $post_xml; //申请退款接口 $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund'; $xml = curl_post_ssl($url,$post_xml); //POST方式请求http,支付无需证书;申请退款api需要证书校验 $array = xml2array($xml); //将【申请退款】api返回xml数据转换成数组,全要大写 //var_dump($array); if($array['RETURN_CODE'] == 'SUCCESS' && $array['RESULT_CODE'] == 'SUCCESS'){
//退款业务已受理// // $up['order_status'] = 'refunding'; //退款申请中 // $up['order_id'] = $order_id; // $up['order_time'] = time(); //退款申请时间 // if(M("home_order","xxf_witkey_")->save($up)){ // $model_demo = new \Home\Model\HomeorderModel('home_order','xxf_witkey_'); // $res = $model_demo->home_order_list($userwx_info['uid'],19); // if(!$res){ // exit('订单信息有误'); // }else{ // echo json_encode($res);exit; // } // }       // for 调试 echo "业务结果:".$array['RETURN_CODE']."
"; echo "错误代码:".$array['err_code']."
"; echo "错误代码描述:".$array['err_code_des']."
"; echo "公众账号ID:".$array['APPID']."
"; echo "商户号:".$array['MCH_ID']."
"; echo "子商户号:".$array['sub_mch_id']."
"; echo "设备号:".$array['device_info']."
"; echo "签名:".$array['SIGN']."
"; echo "微信订单号:".$array['transaction_id']."
"; echo "商户订单号:".$array['OUT_TRADE_NO']."
"; echo "商户退款单号:".$array['OUT_REFUND_NO']."
"; echo "微信退款单号:".$array['refund_idrefund_id']."
"; echo "订单总金额:".$array['TOTAL_FEE']."
"; echo "退款金额:".$array['REFUND_FEE']."
"; echo "现金券退款金额:".$array['coupon_refund_fee']."
"; }else{ echo "RETURN_CODE:".$array['RETURN_CODE']."
"; echo "RESULT_CODE:".$array['RESULT_CODE']."
"; echo $array['RETURN_MSG']."
"; echo "错误代码:".$array['ERR_CODE']."
"; echo "错误代码描述:".$array['ERR_CODE_DES']."
"; exit;echo $array['RETURN_MSG'];exit; } } /* php获取随机字符(数字+英文)函数,长度可以进行控制 */ function randomkeys($length) { $key = null; $pattern = '1234567890abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLOMNOPQRSTUVWXYZ'; for ($i = 0; $i < $length; $i++) { $key .= $pattern {
mt_rand(0, 35)}; } return $key; } /** * 生成签名, $KEY就是支付key * @return 签名 */ function MakeSign( $params,$KEY){ //签名步骤一:按字典序排序数组参数 ksort($params); $string = ToUrlParams($params); //参数进行拼接key=value&k=v //签名步骤二:在string后加入KEY $string = $string . "&key=".$KEY; //签名步骤三:MD5加密 $string = md5($string); //签名步骤四:所有字符转为大写 $result = strtoupper($string); return $result; } /** * 将参数拼接为url: key=value&key=value * @param $params * @return string */ function ToUrlParams( $params ){ $string = ''; if( !empty($params) ){ $array = array(); foreach( $params as $key => $value ){ $array[] = $key.'='.$value; } $string = implode("&",$array); } return $string; } //获取xml里面数据,转换成array function xml2array($xml){ $p = xml_parser_create(); xml_parse_into_struct($p, $xml, $vals, $index); xml_parser_free($p); $data = ""; foreach ($index as $key=>$value) { if($key == 'xml' || $key == 'XML') continue; $tag = $vals[$value[0]]['tag']; $value = $vals[$value[0]]['value']; $data[$tag] = $value; } return $data; } function curl_post_ssl($url, $vars, $second=30,$aHeader=array()) { $ch = curl_init(); //超时时间 curl_setopt($ch,CURLOPT_TIMEOUT,$second); curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1); //这里设置代理,如果有的话 //curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98'); //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); //以下两种方式需选择一种 //第一种方法,cert 与 key 分别属于两个.pem文件 //默认格式为PEM,可以注释 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert/apiclient_cert.pem');//getcwd()=》当前工作目录,不含最下级的/ //默认格式为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'); if( count($aHeader) >= 1 ){ curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader); } curl_setopt($ch,CURLOPT_POST, 1); curl_setopt($ch,CURLOPT_POSTFIELDS,$vars); $data = curl_exec($ch); //返回xml if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); echo "call faild, errorCode:$error\n"; curl_close($ch); return false; } } ?>
微信安全支付

申请退款:

退款单号: >

退款金额(分): >

返回首页

 

 

DEMO2、在其他的php文件中引用该退款流程

//在这里将订单的id、out_trade_no、out_refund_no、总金额、退款金额都拿到,然后进行下面步骤 include_once S_ROOT.'xxpay/wxzf/refund_miniprogram.php';    //小程序退款
'.$appid.'
'.$mch_id.'
'.$nonce_str.'
'.$mch_id.'
'.$out_refund_no.'
'.$out_trade_no.'
'.$refund_fee.'
'.$total_fee.'
'.$sign.'
'; //echo $post_xml; //申请退款接口 $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund'; $xml = curl_post_ssl($url,$post_xml); //POST方式请求http,支付无需证书;申请退款api需要证书校验 $array = xml2array($xml); //将【申请退款】api返回xml数据转换成数组,全要大写 //var_dump($array); if($array['RETURN_CODE'] == 'SUCCESS' && $array['RESULT_CODE'] == 'SUCCESS'){
//退款业务已受理// //数据库更新操作 /* for 调试 */// echo "业务结果:".$array['RETURN_CODE']."
";// echo "错误代码:".$array['err_code']."
";// echo "错误代码描述:".$array['err_code_des']."
";// echo "公众账号ID:".$array['APPID']."
";// echo "商户号:".$array['MCH_ID']."
";// echo "子商户号:".$array['sub_mch_id']."
";// echo "设备号:".$array['device_info']."
";// echo "签名:".$array['SIGN']."
";// echo "微信订单号:".$array['transaction_id']."
";// echo "商户订单号:".$array['OUT_TRADE_NO']."
";// echo "商户退款单号:".$array['OUT_REFUND_NO']."
";// echo "微信退款单号:".$array['refund_idrefund_id']."
";// echo "订单总金额:".$array['TOTAL_FEE']."
";// echo "退款金额:".$array['REFUND_FEE']."
";// echo "现金券退款金额:".$array['coupon_refund_fee']."
"; }else{ echo "RETURN_CODE:".$array['RETURN_CODE']."
"; echo "RESULT_CODE:".$array['RESULT_CODE']."
"; echo $array['RETURN_MSG']."
"; echo "错误代码:".$array['ERR_CODE']."
"; echo "错误代码描述:".$array['ERR_CODE_DES']."
"; exit; }// } /* php获取随机字符(数字+英文)函数,长度可以进行控制 */ function randomkeys($length) { $key = null; $pattern = '1234567890abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLOMNOPQRSTUVWXYZ'; for ($i = 0; $i < $length; $i++) { $key .= $pattern {
mt_rand(0, 35)}; } return $key; } /** * 生成签名, $KEY就是支付key * @return 签名 */ function MakeSign( $params,$KEY){ //签名步骤一:按字典序排序数组参数 ksort($params); $string = ToUrlParams($params); //参数进行拼接key=value&k=v //签名步骤二:在string后加入KEY $string = $string . "&key=".$KEY; //签名步骤三:MD5加密 $string = md5($string); //签名步骤四:所有字符转为大写 $result = strtoupper($string); return $result; } /** * 将参数拼接为url: key=value&key=value * @param $params * @return string */ function ToUrlParams( $params ){ $string = ''; if( !empty($params) ){ $array = array(); foreach( $params as $key => $value ){ $array[] = $key.'='.$value; } $string = implode("&",$array); } return $string; } //获取xml里面数据,转换成array,key键均大写 function xml2array($xml){ $p = xml_parser_create(); xml_parse_into_struct($p, $xml, $vals, $index); xml_parser_free($p); $data = ""; foreach ($index as $key=>$value) { if($key == 'xml' || $key == 'XML') continue; $tag = $vals[$value[0]]['tag']; $value = $vals[$value[0]]['value']; $data[$tag] = $value; } return $data; } function curl_post_ssl($url, $vars, $second=30,$aHeader=array()) { $ch = curl_init(); //超时时间 curl_setopt($ch,CURLOPT_TIMEOUT,$second); curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1); //这里设置代理,如果有的话 //curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98'); //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); //以下两种方式需选择一种 //第一种方法,cert 与 key 分别属于两个.pem文件 //默认格式为PEM,可以注释 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); //curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert/apiclient_cert.pem');//getcwd()=》当前工作目录,不含最下级的/ curl_setopt($ch,CURLOPT_SSLCERT,S_ROOT.'x**ay/wxzf/cert/apiclient_cert.pem');//这种获取绝对路径,请使用文件根目录E:\putuu\WWW\你的项目放置文件夹名\,而非站点目录 //默认格式为PEM,可以注释 curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); //curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/cert/apiclient_key.pem'); curl_setopt($ch,CURLOPT_SSLKEY,S_ROOT.'x**ay/wxzf/cert/apiclient_key.pem'); //第二种方式,两个文件合成一个.pem文件 //curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem'); if( count($aHeader) >= 1 ){ curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader); } curl_setopt($ch,CURLOPT_POST, 1); curl_setopt($ch,CURLOPT_POSTFIELDS,$vars); $data = curl_exec($ch); //返回xml if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); echo "call faild, errorCode:$error\n"; curl_close($ch); return false; } } ?>

退款部分结束。

下面是退款回调

DEMO3、退款回调

1)首先去:商户平台-交易中心-退款配置中配置notify_url。(和支付回调类似)

2)解密步骤:  

3)上面的加密串A是什么?回答:XML里面的<req_info>字段。

 4)如何获取?==》核心:  refund_decrypt($str, $key)    :其中参数$str是加密串A<req_info>(string类型),参数$key是md5后的商户key。

//明文=refund_decrypt(密文,MD5(商户秘钥));明文格式如下:

 

 5)那么获得xml格式明文后,咋办?再次xml转array,得到里面的out_trade_no,订单号都得到了,你想干嘛干嘛去。至此,整个退款和回调结束。

下面附上代码:

//
SUCCESS
//
//
//
//
// //解密步骤如下: // (1)对加密串A
做base64解码,得到加密串B// (2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )// (3)用key*对加密串B做AES-256-ECB解密//这里测试微信是否访问了当前url//$li = db_factory::execute ( sprintf ( " update %switkey_ho*** set order_status='refunded' where out_trade_no= '%s' ",TABLEPRE,'ho*****2') );//var_dump($li);///-------////商户密钥$weixin_key,解密必要,按照上面步骤(2)(3)解密$weixin_key = '**********************';$post = post_data(); //接受微信POST过来的XML数据$post_data = xml_to_array($post); //XML转数组Array$weixin_post_string = $post_data['req_info']; //微信post过来的加密串A,字符串类型//明文=refund_decrypt(密文,MD5(商户秘钥));返回XML格式明文,包含out_trade_no/out_refund_no等信息$refund_xml_string = refund_decrypt($weixin_post_string, md5($weixin_key));$refundArr = xml_to_array($refund_xml_string);if(isset($refundArr['out_trade_no'])){
//$refundArr['out_trade_no']字符串类型 //数据库操作,将订单状态改为退款成功和其他操作 echo return_msg();}//对参数加密串A
进行AES-256(ECB模式,PKCS7Padding)解密,得到加密前参数。function refund_decrypt($str, $key) { $str = base64_decode($str); $str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB); $block = mcrypt_get_block_size('rijndael_128', 'ecb'); $pad = ord($str[($len = strlen($str)) - 1]); $len = strlen($str); $pad = ord($str[$len - 1]); return substr($str, 0, strlen($str) - $pad);}/* * 微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收微信支付成功post给商户的数据 */function post_data(){ $receipt = $_REQUEST; if($receipt==null){ $receipt = file_get_contents("php://input"); if($receipt == null){ $receipt = $GLOBALS['HTTP_RAW_POST_DATA']; } } return $receipt;}/** * 将xml转为array */function xml_to_array($xml){ if(!$xml){ return false; } //将XML转为array //禁止引用外部xml实体 libxml_disable_entity_loader(true); $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $data;}/* 发送给微信的退款回调消息 -xzz1207 */function return_msg(){ $msg = '
'; return $msg;}?>

 

转载地址:http://zxiux.baihongyu.com/

你可能感兴趣的文章
(step6.1.5)hdu 1233(还是畅通工程——最小生成树)
查看>>
Membership三步曲之进阶篇 - 深入剖析Provider Model
查看>>
huffman编码——原理与实现
查看>>
Linux移植随笔:终于解决Tslib的问题了【转】
查看>>
MyBitis(iBitis)系列随笔之四:多表(多对一查询操作)
查看>>
【leetcode】Longest Common Prefix
查看>>
前端优化及相关要点总结
查看>>
Vue 列表渲染
查看>>
struts2中form提交到action中的中文参数乱码问题解决办法(包括取中文路径)
查看>>
25 个精美的手机网站模板
查看>>
C#反射实例应用--------获取程序集信息和通过类名创建类实例
查看>>
VC中实现文字竖排的简单方法
查看>>
会话标识未更新
查看>>
【设计模式】数据访问对象模式
查看>>
Tomcat8 配置Oracle11g数据源
查看>>
【PHP面向对象(OOP)编程入门教程】8.构造方法__construct()与析构方法__destruct()
查看>>
ThinkPHP常用配置路径
查看>>
阿里架构师:程序员必须掌握的几项核心技术能力
查看>>
基于python的图片修复程序-可用于水印去除
查看>>
程序员常用的六大技术博客类
查看>>