PHP实现微信提现功能(微信商城)


Posted in PHP onNovember 21, 2019

提现必须得用双向证书、所以大家一定要在微信的商户平台找到相应的地方去设置、因为做这个提现已经有一段时间了、所以设置微信商户平台的那几个地方没有图的情况、也说不清楚、下次再做提现的时候、给大家分享如何设置商户平台那几个地方、不是很难、下面贴代码

注意事项:商户打款时是从商户可用余额中减钱,所以确保商户可用余额充足,同时注意官方文档中的付款规则;

封装提现的方法

function tixian($money){
  $appid = "################";//商户账号appid
  $secret = "##########";//api密码
  $mch_id = "#######";//商户号
  $mch_no = "#######";
  $openid="123456789";//授权用户openid

  $arr = array();
  $arr['mch_appid'] = $appid;
  $arr['mchid'] = $mch_id;
  $arr['nonce_str'] = ugv::randomid(20);//随机字符串,不长于32位
  $arr['partner_trade_no'] = '1298016501' . date("Ymd") . rand(10000, 90000) . rand(10000, 90000);//商户订单号
  $arr['openid'] = $openid;
  $arr['check_name'] = 'NO_CHECK';//是否验证用户真实姓名,这里不验证
  $arr['amount'] = $money;//付款金额,单位为分
  $desc = "###提现";
  $arr['desc'] = $desc;//描述信息
  $arr['spbill_create_ip'] = '192.168.0.1';//获取服务器的ip
  //封装的关于签名的算法
  $notify = new Notify_pub();
  $notify->weixin_app_config = array();
  $notify->weixin_app_config['KEY'] = $mch_no;

  $arr['sign'] = $notify->getSign($arr);//签名

  $var = $notify->arrayToXml($arr);
  $xml = $this->curl_post_ssl('https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', $var, 30, array(), 1);
  $rdata = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
  $return_code = (string)$rdata->return_code;
  $result_code = (string)$rdata->result_code;
  $return_code = trim(strtoupper($return_code));
  $result_code = trim(strtoupper($result_code));

  if ($return_code == 'SUCCESS' && $result_code == 'SUCCESS') {
   $isrr = array(
    'con'=>'ok',
    'error' => 0,
   );
  } else {
   $returnmsg = (string)$rdata->return_msg;
   $isrr = array(
    'error' => 1,
    'errmsg' => $returnmsg,
   );

  }
  return json_encode($isrr);
}

用到的curl_post_ssl()

function curl_post_ssl($url, $vars, $second = 30, $aHeader = array())
 {
  $isdir = "/cert/";//证书位置
  $ch = curl_init();//初始化curl
  curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置执行最长秒数
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
  curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// 终止从服务端进行验证
  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//
  curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//证书类型
  curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem');//证书位置
  curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
  curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem');//证书位置
  curl_setopt($ch, CURLOPT_CAINFO, 'PEM');
  curl_setopt($ch, CURLOPT_CAINFO, $isdir . 'rootca.pem');
  if (count($aHeader) >= 1) {
   curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);//设置头部
  }
  curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
  curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);//全部数据使用HTTP协议中的"POST"操作来发送
  $data = curl_exec($ch);//执行回话
  if ($data) {
   curl_close($ch);
   return $data;
  } else {
   $error = curl_errno($ch);
   echo "call faild, errorCode:$error\n";
   curl_close($ch);
   return false;
  }
}

关于具体签名算法,可参考微信官方文档;

简单示范签名算法:

//将要发送的数据整理为$data
ksort($data);//排序
//使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串
$str='';
foreach($data as $k=>$v) {
 $str.=$k.'='.$v.'&';
}
//拼接API密钥
$str.='key='.$secrect;
$data['sign']=md5($str);//加密

将数组转换成xml格式(简单方法):

//遍历数组方法
function arraytoxml($data){
 $str='<xml>';
 foreach($data as $k=>$v) {
  $str.='<'.$k.'>'.$v.'</'.$k.'>';
 }
 $str.='</xml>';
 return $str;
}

将xml格式转换为数组:

function xmltoarray($xml) { 
  //禁止引用外部xml实体 
 libxml_disable_entity_loader(true); 
 $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); 
 $val = json_decode(json_encode($xmlstring),true); 
 return $val;
}

下面来看看ThinkPHP5封装的提现类。

<?php
namespace Home\Controller;
use Think\Controller;
class TixianController extends Controller{
 //高级功能-》开发者模式-》获取
 private $app_id1 = '';  //appid
 private $app_secret1 = ''; //secreat
 private $apikey1 = ''; //支付秘钥
 private $mchid1 = 's';  //商户号
  private $app_id=null;
  private $app_secret=null;
  private $apikey=null;
  private $mchid=null;
 public $error=0;
 public $state = '';
 //金额,需在实例化时传入
 public $amount = '0';
 //用户订单号,需在实例化时传入
 public $order_sn = '';
 //用户openid,需在实例化时传入
 public $openid = '';
 //微信提现操作接口-------》
 public function actionAct_tixian()
 {
  $this->state=md5(uniqid(rand(), TRUE));
  $this->amount=I('amount');//设置POST过来钱数
  $this->order_sn=rand(100,999).date('YmdHis'); //随机数可以作为单号
  $this->openid= I('openid'); //设置获取POST过来用户的OPENID
  $user_id = I('user_id');
  $this->app_id=$this->app_id1;
  $this->app_secret=$this->app_secret1;
  $this->apikey=$this->apikey1;
  $this->mchid=$this->mchid1;
  $xml=$this->tiXianAction();
  $result=simplexml_load_string($xml);
  if($result->return_code=='SUCCESS' && $result->result_code=='SUCCESS') {
    $cash = D('cash');
    $data['user_id'] = $user_id;
    $data['amount'] = $this->amount;
    $res = $cash->where('user_id="'.$user_id.'"')->find();
    if($res){
     $res2 = $cash->where('user_id="'.$user_id.'"')->setInc('amount',$this->amount);
     $res4 = D('member')->where('user_id="'.$user_id.'"')->setDec('user_balance',$this->amount);
    }else{
     $res3 = $cash->add($data);
    }
   $output = array('code' => 1, 'data' => $result->result_code, 'info' => '提现成功');
   exit(json_encode($output));
  }else{
   $output = array('code' => 2, 'data' => $xml, 'info' => '提现失败');
   exit(json_encode($output));
  }
 }
 /**
 * 提现接口操作,控制器调用
 * @param $openid 用户openid 唯一标示
 * @return
 */
 //提现接口操作
 public function tiXianAction(){
  //获取xml数据
  $data=$this->getdataXml($this->openid);
  $ch = curl_init ();
  //接口地址
  $MENU_URL="https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
  curl_setopt ( $ch, CURLOPT_URL, $MENU_URL );
  curl_setopt ( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
  curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );
  curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, FALSE );
  //证书地址,微信支付下面
  curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
  curl_setopt($ch,CURLOPT_SSLCERT, 'C:\web\www\Home\wx_pay\apiclient_cert.pem'); //证书这块大家把文件放到哪都行、
  curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
  curl_setopt($ch,CURLOPT_SSLKEY, 'C:\web\www\Home\wx_pay\apiclient_key.pem');//注意证书名字千万别写错、
  //$zs1=dirname(dirname(__FILE__)).'\wx_pay\apiclient_cert.pem';
  //$zs2=dirname(dirname(__FILE__)).'\wx_pay\apiclient_key.pem';
  //show_bug($zs1);
  //curl_setopt($ch,CURLOPT_SSLCERT,$zs1);
  //curl_setopt($ch,CURLOPT_SSLKEY,$zs2);
  // curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01;
  // Windows NT 5.0)');
  //curl_setopt ( $ch, CURLOPT_FOLLOWLOCATION, 1 );
  curl_setopt ( $ch, CURLOPT_AUTOREFERER, 1 );
  curl_setopt ( $ch, CURLOPT_POSTFIELDS, $data );
  curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
  $info = curl_exec ( $ch );
  //返回结果
  if($info){
   curl_close($ch);
   return $info;
  } else {
   $error = curl_errno($ch);
   curl_close($ch);
   return "curl出错,错误码:$error";
  }
 }
 /**
 * 获取数据封装为数组
 * @param $openid 用户openid 唯一标示
 * @return xml
 */
 private function getdataXml($openid){
  //封装成数据
  $dataArr=array(
   'amount'=>$this->amount*100,//金额(以分为单位,必须大于100)
   'check_name'=>'NO_CHECK',//校验用户姓名选项,NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
   'desc'=>'提现',//描述
   'mch_appid'=>$this->app_id,
   'mchid'=>$this->mchid,//商户号
   'nonce_str'=>rand(100000, 999999),//不长于32位的随机数
   'openid'=>$openid,//用户唯一标识
   'partner_trade_no'=>$this->order_sn,//商户订单号
   're_user_name'=>'',//用户姓名,check_name为NO_CHECK时为可选项
   'spbill_create_ip'=>$_SERVER["REMOTE_ADDR"],//服务器ip
  );
  //获取签名
  $sign=$this->getSign($dataArr);
  //xml数据
  $data="<xml>
   <mch_appid>".$dataArr['mch_appid']."</mch_appid>
   <mchid>".$dataArr['mchid']."</mchid>
   <nonce_str>".$dataArr['nonce_str']."</nonce_str>
   <partner_trade_no>".$dataArr['partner_trade_no']."</partner_trade_no>
   <openid>".$dataArr['openid']."</openid>
   <check_name>".$dataArr['check_name']."</check_name>
   <re_user_name>".$dataArr['re_user_name']."</re_user_name>
   <amount>".$dataArr['amount']."</amount>
   <desc>".$dataArr['desc']."</desc>
   <spbill_create_ip>".$dataArr['spbill_create_ip']."</spbill_create_ip>
   <sign>".$sign."</sign>
   </xml>";
  return $data;
 }
 /**
 *  作用:格式化参数,签名过程需要使用
 */
 private function formatBizQueryParaMap($paraMap, $urlencode)
 {
  $buff = "";
  ksort($paraMap);
  foreach ($paraMap as $k => $v)
  {
   if($v){
   if($urlencode)
   {
    $v = urlencode($v);
   }
   $buff .= $k . "=" . $v . "&";
   }
  }
  $reqPar=NULL;
  if (strlen($buff) > 0)
  {
   $reqPar = substr($buff, 0, strlen($buff)-1);
  }
  return $reqPar;
 }
 /**
 *  作用:生成签名
 */
 private function getSign($Obj)
 {
  foreach ($Obj as $k => $v)
  {
   $Parameters[$k] = $v;
  }
  //签名步骤一:按字典序排序参数
  ksort($Parameters);
  $String = $this->formatBizQueryParaMap($Parameters, false);
  //echo '【string1】'.$String.'</br>';
  //签名步骤二:在string后加入KEY
  $String = $String."&key=".$this->apikey;
  //echo "【string2】".$String."</br>";
  //签名步骤三:MD5加密
  $String = md5($String);
  //echo "【string3】 ".$String."</br>";
  //签名步骤四:所有字符转为大写
  $result_ = strtoupper($String);
  //echo "【result】 ".$result_."</br>";
  return $result_;
 }
 //-----------
 private function http($url, $method='POST', $postfields = null, $headers = array())
 {
  header("Content-Type:text/html;charset=utf-8");
  $ch = curl_init();
  /* Curl settings */
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_POSTFIELDS, "");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
  curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  switch ($method){
   case 'POST':
   curl_setopt($ch,CURLOPT_POST, true);
   break;
  }
  curl_setopt($ch, CURLOPT_HTTPHEADER,$headers);
  curl_setopt($ch, CURLINFO_HEADER_OUT, true);
  $response = curl_exec($ch);
  $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); //返回请求状态码
  curl_close($ch);
  return array($http_code, $response);
 }
}

总结

以上所述是小编给大家介绍的PHP实现微信提现功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

PHP 相关文章推荐
PHP脚本的10个技巧(6)
Oct 09 PHP
坏狼php学习 计数器实例代码
Jun 15 PHP
PHP 在线翻译函数代码
May 07 PHP
Yii使用find findAll查找出指定字段的实现方法
Sep 05 PHP
PHP正则表达式替换站点关键字链接后空白的解决方法
Sep 16 PHP
PHP实现网页内容html标签补全和过滤的方法小结【2种方法】
Apr 27 PHP
PHP实现模拟http请求的方法分析
Dec 20 PHP
PHP基于双向链表与排序操作实现的会员排名功能示例
Dec 26 PHP
PHP实现的猴王算法(猴子选大王)示例
Apr 30 PHP
PHP simplexml_load_string()函数实例讲解
Feb 03 PHP
PHP实现的文件浏览器功能简单示例
Sep 12 PHP
基于PHP实现生成随机水印图片
Dec 09 PHP
使用PHP+Redis实现延迟任务,实现自动取消订单功能
Nov 21 #PHP
PHP框架实现WebSocket在线聊天通讯系统
Nov 21 #PHP
PHP读取Excel内的图片(phpspreadsheet和PHPExcel扩展库)
Nov 19 #PHP
使用PHP开发留言板功能
Nov 19 #PHP
关于Laravel参数验证的一些疑与惑
Nov 19 #PHP
php传值和传引用的区别点总结
Nov 19 #PHP
php 使用 __call实现重载功能示例
Nov 18 #PHP
You might like
php字符串截取的简单方法
2013/07/04 PHP
PHP图片库imagemagick安装方法
2014/09/23 PHP
Zend Framework教程之Autoloading用法详解
2016/03/08 PHP
总结一些js自定义的函数
2006/08/05 Javascript
比较详细的关于javascript 解析json的代码
2009/12/16 Javascript
JavaScript 代码压缩工具小结
2012/02/27 Javascript
Jquery与Bootstrap实现后台管理页面增删改查功能示例
2017/01/22 Javascript
Ajax和Comet技术总结
2017/02/19 Javascript
基于Bootstrap的网页设计实例
2017/03/01 Javascript
ES6/JavaScript使用技巧分享
2017/12/14 Javascript
vue2.0 自定义组件的方法(vue组件的封装)
2018/06/05 Javascript
webstorm+vue初始化项目的方法
2018/10/18 Javascript
Nuxt.js开启SSR渲染的教程详解
2018/11/30 Javascript
记一次用vue做的活动页的方法步骤
2019/04/11 Javascript
微信小程序封装的HTTP请求示例【附升级版】
2019/05/11 Javascript
layui 数据表格 点击分页按钮 监听事件的实例
2019/09/02 Javascript
JS变量提升原理与用法实例浅析
2020/05/22 Javascript
[47:55]Ti4第二日主赛事败者组 NaVi vs EG 1
2014/07/20 DOTA
[00:33]2018DOTA2亚洲邀请赛TNC出场
2018/04/04 DOTA
[51:44]2018DOTA2亚洲邀请赛 4.3 突围赛 Optic vs iG 第二场
2018/04/04 DOTA
Linux上安装Python的PIL和Pillow库处理图片的实例教程
2016/06/23 Python
Python正则抓取网易新闻的方法示例
2017/04/21 Python
python+django+sql学生信息管理后台开发
2018/01/11 Python
python 日期操作类代码
2018/05/05 Python
对Python通过pypyodbc访问Access数据库的方法详解
2018/10/27 Python
css3中单位px,em,rem,vh,vw,vmin,vmax的区别及浏览器支持情况
2016/12/06 HTML / CSS
索引覆盖(Index Covering)查询含义
2012/02/18 面试题
Linux面试经常问的文件系统操作命令
2016/10/04 面试题
旅游专业职业生涯规划范文
2014/01/13 职场文书
政风行风评议整改方案
2014/09/15 职场文书
2014党的群众路线教育实践活动学习心得体会
2014/10/31 职场文书
学校办公室主任岗位职责
2015/04/01 职场文书
上市公司董事长岗位职责
2015/04/16 职场文书
2015年幼儿园安全工作总结
2015/05/12 职场文书
2015年办税服务厅工作总结
2015/07/23 职场文书
Nginx 负载均衡是什么以及该如何配置
2021/03/31 Servers