Chinaunix首页 | 论坛 | 博客
  • 博客访问: 89688
  • 博文数量: 49
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 450
  • 用 户 组: 普通用户
  • 注册时间: 2016-11-28 15:27
文章分类

全部博文(49)

分类: PHP

2017-02-21 17:05:40

支付开发过程,分享给大家,供大家参考,具体内容如下

1.开发环境 
Thinkphp 3.2.3 
:服务号,已认证 
开发域名: (自定义的域名,外网不可访问)

2.需要相关文件和权限 
微信支付需申请开通 
微信公众平台开发者文档:
微信支付开发者文档:
微信支付SDK下载地址:

3.开发 
下载好微信支付PHP版本的SDK,文件目录为下图:

这里写图片描述

这里写图片描述 

把微信支付SDK的Cert和Lib目录放入Thinkphp,目录为 

这里写图片描述 

现在介绍微信支付授权目录问题,首先是微信支付开发配置里面的支付授权目录填写, 

这里写图片描述

然后填写JS接口安全域。 

这里写图片描述

最后设置网页授权 

这里写图片描述

这里写图片描述

这些设置完,基本完成一半,注意设置的目录和我thinkphp里面的目录。 

这里写图片描述

4.微信支付配置

这里写图片描述

把相关配置填写正确。


[python] view plain copy
 
 
  1. /**  
  2. * 配置账号信息  
  3. */  
  4.    
  5. class WxPayConfig  
  6. {  
  7.  //=======【基本信息设置】=====================================  
  8.  //  
  9.  /**  
  10.  * TODO: 修改这里配置为您自己申请的商户信息  
  11.  * 微信公众号信息配置  
  12.  *   
  13.  * APPID:绑定支付的APPID(必须配置,开户邮件中可查看)  
  14.  *   
  15.  * MCHID:商户号(必须配置,开户邮件中可查看)  
  16.  *   
  17.  * KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置)  
  18.  * 设置地址:
  19.  *   
  20.  * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置),  
  21.  * 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN  
  22.  * @var string  
  23.  */  
  24.  const APPID = '';  
  25.  const MCHID = '';  
  26.  const KEY = '';  
  27.  const APPSECRET = '';  
  28.    
  29.  //=======【证书路径设置】=====================================  
  30.  /**  
  31.  * TODO:设置商户证书路径  
  32.  * 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载,  
  33.  * API证书下载地址:,下载之前需要安装商户操作证书)  
  34.  * @var path  
  35.  */  
  36.  const SSLCERT_PATH = '../cert/apiclient_cert.pem';  
  37.  const SSLKEY_PATH = '../cert/apiclient_key.pem';  
  38.    
  39.  //=======【curl代理设置】===================================  
  40.  /**  
  41.  * TODO:这里设置代理机器,只有需要代理的时候才设置,不需要代理,请设置为0.0.0.00  
  42.  * 本例程通过curl使用HTTP POST方法,此处可修改代理服务器,  
  43.  * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此时不开启代理(如有需要才设置)  
  44.  * @var unknown_type  
  45.  */  
  46.  const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";  
  47.  const CURL_PROXY_PORT = 0;//8080;  
  48.    
  49.  //=======【上报信息配置】===================================  
  50.  /**  
  51.  * TODO:接口调用上报等级,默认紧错误上报(注意:上报超时间为【1s】,上报无论成败【永不抛出异常】,  
  52.  * 不会影响接口调用流程),开启上报之后,方便微信监控请求调用的质量,建议至少  
  53.  * 开启错误上报。  
  54.  * 上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报  
  55.  * @var int  
  56.  */  
  57.  const REPORT_LEVENL = 1;  
  58. }  

现在开始贴出代码:


[php] view plain copy
 
 
  1. namespace Wechat\Controller;  
  2. use Think\Controller;  
  3. /** 
  4.  * 父类控制器,需要继承 
  5.  * @file ParentController.class.php 
  6.  * @author Gary  
  7.  * @date 2015年8月4日 
  8.  * @todu 
  9.  */  
  10. class ParentController extends Controller {   
  11.  protected $options = array (  
  12.  'token' => ''// 填写你设定的key  
  13.  'encodingaeskey' => ''// 填写加密用的EncodingAESKey  
  14.  'appid' => ''// 填写高级调用功能的app id  
  15.  'appsecret' => ''// 填写高级调用功能的密钥  
  16.  'debug' => false,  
  17.  'logcallback' => ''  
  18.  );   
  19.  public $errCode = 40001;   
  20.  public $errMsg = "no access";   
  21.    
  22.  /** 
  23.  * 获取access_token 
  24.  * @return mixed|boolean|unknown 
  25.  */  
  26.  public function getToken(){  
  27.  $cache_token = S('exp_wechat_pay_token');  
  28.  if(!empty($cache_token)){  
  29.  return $cache_token;  
  30.  }  
  31.  $url = '%s&secret=%s';  
  32.  $url = sprintf($url,$this->options['appid'],$this->options['appsecret']);   
  33.  $result = $this->http_get($url);  
  34.  $result = json_decode($result,true);   
  35.  if(empty($result)){  
  36.  return false;  
  37.  }   
  38.  S('exp_wechat_pay_token',$result['access_token'],array('type'=>'file','expire'=>3600));  
  39.  return $result['access_token'];  
  40.  }  
  41.    
  42.  /** 
  43.  * 发送客服消息 
  44.  * @param array $data 消息结构{"touser":"OPENID","msgtype":"news","news":{...}} 
  45.  */  
  46.  public function sendCustomMessage($data){  
  47.  $token = $this->getToken();  
  48.  if (empty($token)) return false;   
  49.  $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s';  
  50.  $url = sprintf($url,$token);  
  51.  $result = $this->http_post($url,self::json_encode($data));  
  52.  if ($result)  
  53.  {  
  54.  $json = json_decode($result,true);  
  55.  if (!$json || !empty($json['errcode'])) {  
  56.  $this->errCode = $json['errcode'];  
  57.  $this->errMsg = $json['errmsg'];  
  58.  return false;  
  59.  }  
  60.  return $json;  
  61.  }  
  62.  return false;  
  63.  }  
  64.    
  65.  /** 
  66.  * 发送模板消息 
  67.  * @param unknown $data 
  68.  * @return boolean|unknown 
  69.  */  
  70.  public function sendTemplateMessage($data){  
  71.  $token = $this->getToken();  
  72.  if (empty($token)) return false;  
  73.  $url = "%s";  
  74.  $url = sprintf($url,$token);  
  75.  $result = $this->http_post($url,self::json_encode($data));  
  76.  if ($result)  
  77.  {  
  78.  $json = json_decode($result,true);  
  79.  if (!$json || !empty($json['errcode'])) {  
  80.  $this->errCode = $json['errcode'];  
  81.  $this->errMsg = $json['errmsg'];  
  82.  return false;  
  83.  }  
  84.  return $json;  
  85.  }  
  86.  return false;  
  87.  }  
  88.    
  89.    
  90.  public function getFileCache($name){  
  91.  return S($name);  
  92.  }  
  93.    
  94.  /** 
  95.  * 微信api不支持中文转义的json结构 
  96.  * @param array $arr 
  97.  */  
  98.  static function json_encode($arr) {  
  99.  $parts = array ();  
  100.  $is_list = false;  
  101.  //Find out if the given array is a numerical array  
  102.  $keys = array_keys ( $arr );  
  103.  $max_length = count ( $arr ) - 1;  
  104.  if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1  
  105.  $is_list = true;  
  106.  for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position  
  107.  if ($i != $keys [$i]) { //A key fails at position check.  
  108.   $is_list = false; //It is an associative array.  
  109.   break;  
  110.  }  
  111.  }  
  112.  }  
  113.  foreach ( $arr as $key => $value ) {  
  114.  if (is_array ( $value )) { //Custom handling for arrays  
  115.  if ($is_list)  
  116.   $parts [] = self::json_encode ( $value ); /* :RECURSION: */  
  117.  else  
  118.   $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */  
  119.  } else {  
  120.  $str = '';  
  121.  if (! $is_list)  
  122.   $str = '"' . $key . '":';  
  123.  //Custom handling for multiple data types  
  124.  if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)  
  125.   $str .= $value//Numbers  
  126.  elseif ($value === false)  
  127.  $str .= 'false'//The booleans  
  128.  elseif ($value === true)  
  129.  $str .= 'true';  
  130.  else  
  131.   $str .= '"' . addslashes ( $value ) . '"'//All other things  
  132.  // :TODO: Is there any more datatype we should be in the lookout for? (Object?)  
  133.  $parts [] = $str;  
  134.  }  
  135.  }  
  136.  $json = implode ( ','$parts );  
  137.  if ($is_list)  
  138.  return '[' . $json . ']'//Return numerical JSON  
  139.  return '{' . $json . '}'//Return associative JSON  
  140.  }  
  141.    
  142.  /** 
  143.  +---------------------------------------------------------- 
  144.  * 生成随机字符串 
  145.  +---------------------------------------------------------- 
  146.  * @param int $length 要生成的随机字符串长度 
  147.  * @param string $type 随机码类型:0,数字+大小写字母;1,数字;2,小写字母;3,大写字母;4,特殊字符;-1,数字+大小写字母+特殊字符 
  148.  +---------------------------------------------------------- 
  149.  * @return string 
  150.  +---------------------------------------------------------- 
  151.  */  
  152.  static public function randCode($length = 5, $type = 2){  
  153.  $arr = array(1 => "0123456789", 2 => "abcdefghijklmnopqrstuvwxyz", 3 => "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4 => "~@#$%^&*(){}[]|");  
  154.  if ($type == 0) {  
  155.  array_pop($arr);  
  156.  $string = implode(""$arr);  
  157.  } elseif ($type == "-1") {  
  158.  $string = implode(""$arr);  
  159.  } else {  
  160.  $string = $arr[$type];  
  161.  }  
  162.  $count = strlen($string) - 1;  
  163.  $code = '';  
  164.  for ($i = 0; $i < $length$i++) {  
  165.  $code .= $string[rand(0, $count)];  
  166.  }  
  167.  return $code;  
  168.  }   
  169.    
  170.    
  171.  /** 
  172.  * GET 请求 
  173.  * @param string $url 
  174.  */  
  175.  private function http_get($url){  
  176.  $oCurl = curl_init();  
  177.  if(stripos($url,"https://")!==FALSE){  
  178.  curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);  
  179.  curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);  
  180.  curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1  
  181.  }  
  182.  curl_setopt($oCurl, CURLOPT_URL, $url);  
  183.  curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );  
  184.  $sContent = curl_exec($oCurl);  
  185.  $aStatus = curl_getinfo($oCurl);  
  186.  curl_close($oCurl);  
  187.  if(intval($aStatus["http_code"])==200){  
  188.  return $sContent;  
  189.  }else{  
  190.  return false;  
  191.  }  
  192.  }  
  193.    
  194.  /** 
  195.  * POST 请求 
  196.  * @param string $url 
  197.  * @param array $param 
  198.  * @param boolean $post_file 是否文件上传 
  199.  * @return string content 
  200.  */  
  201.  private function http_post($url,$param,$post_file=false){  
  202.  $oCurl = curl_init();  
  203.  if(stripos($url,"https://")!==FALSE){  
  204.  curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);  
  205.  curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);  
  206.  curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1  
  207.  }  
  208.  if (is_string($param) || $post_file) {  
  209.  $strPOST = $param;  
  210.  } else {  
  211.  $aPOST = array();  
  212.  foreach($param as $key=>$val){  
  213.  $aPOST[] = $key."=".urlencode($val);  
  214.  }  
  215.  $strPOST = join("&"$aPOST);  
  216.  }  
  217.  curl_setopt($oCurl, CURLOPT_URL, $url);  
  218.  curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );  
  219.  curl_setopt($oCurl, CURLOPT_POST,true);  
  220.  curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);  
  221.  $sContent = curl_exec($oCurl);  
  222.  $aStatus = curl_getinfo($oCurl);  
  223.  curl_close($oCurl);  
  224.  if(intval($aStatus["http_code"])==200){  
  225.  return $sContent;  
  226.  }else{  
  227.  return false;  
  228.  }  
  229.  }  
  230. }  

[php] view plain copy
 
 
  1. namespace Wechat\Controller;  
  2. use Wechat\Controller\ParentController;  
  3. /** 
  4.  * 微信支付测试控制器 
  5.  * @file TestController.class.php 
  6.  * @author Gary  
  7.  * @date 2015年8月4日 
  8.  * @todu 
  9.  */  
  10. class TestController extends ParentController {  
  11.  private $_order_body = 'xxx';  
  12.  private $_order_goods_tag = 'xxx';  
  13.  public function __construct(){  
  14.  parent::__construct();  
  15.  require_once ROOT_PATH."Api/lib/WxPay.Api.php";  
  16.  require_once ROOT_PATH."Api/lib/WxPay.JsApiPay.php";  
  17.  }  
  18.    
  19.  public function index(){  
  20.  //①、获取用户openid  
  21.  $tools = new \JsApiPay();  
  22.  $openId = $tools->GetOpenid();   
  23.  //②、统一下单  
  24.  $input = new \WxPayUnifiedOrder();   
  25.  //商品描述  
  26.  $input->SetBody($this->_order_body);  
  27.  //附加数据,可以添加自己需要的数据,微信回异步回调时会附加这个数据  
  28.  $input->SetAttach('xxx');  
  29.  //商户订单号  
  30.  $out_trade_no = \WxPayConfig::MCHID.date("YmdHis");  
  31.  $input->SetOut_trade_no($out_trade_no);  
  32.  //总金额,订单总金额,只能为整数,单位为分   
  33.  $input->SetTotal_fee(1);  
  34.  //交易起始时间  
  35.  $input->SetTime_start(date("YmdHis"));  
  36.  //交易结束时间  
  37.  $input->SetTime_expire(date("YmdHis", time() + 600));  
  38.  //商品标记  
  39.  $input->SetGoods_tag($this->_order_goods_tag);  
  40.  //通知地址,接收微信支付异步通知回调地址 SITE_URL=/Charge  
  41.  $notify_url = SITE_URL.'/index.php/Test/notify.html';  
  42.  $input->SetNotify_url($notify_url);  
  43.  //交易类型  
  44.  $input->SetTrade_type("JSAPI");  
  45.  $input->SetOpenid($openId);  
  46.  $order = \WxPayApi::unifiedOrder($input);  
  47.  $jsApiParameters = $tools->GetJsApiParameters($order);  
  48.  //获取共享收货地址js函数参数  
  49.  $editAddress = $tools->GetEditAddressParameters();  
  50.    
  51.  $this->assign('openId',$openId);  
  52.  $this->assign('jsApiParameters',$jsApiParameters);  
  53.  $this->assign('editAddress',$editAddress);  
  54.  $this->display();   
  55.  }  
  56.    
  57.  /** 
  58.  * 异步通知回调方法 
  59.  */  
  60.  public function notify(){  
  61.  require_once ROOT_PATH."Api/lib/notify.php";  
  62.  $notify = new \PayNotifyCallBack();  
  63.  $notify->Handle(false);  
  64.  //这里的IsSuccess是我自定义的一个方法,后面我会贴出这个文件的代码,供参考。  
  65.  $is_success = $notify->IsSuccess();   
  66.  $bdata = $is_success['data'];   
  67.  //支付成功  
  68.  if($is_success['code'] == 1){   
  69.  $news = array(  
  70.   'touser' => $bdata['openid'],  
  71.   'msgtype' => 'news',  
  72.   'news' => array (  
  73.   'articles'=> array (  
  74.    array(  
  75.    'title' => '订单支付成功',  
  76.    'description' => "支付金额:{$bdata['total_fee']}\n".  
  77.    "微信订单号:{$bdata['transaction_id']}\n"  
  78.    'picurl' => '',  
  79.    'url' => ''  
  80.    )  
  81.    
  82.   )  
  83.   )  
  84.  );  
  85.  //发送微信支付通知  
  86.  $this->sendCustomMessage($news);   
  87.  }else{//支付失败  
  88.    
  89.  }  
  90.  }  
  91.    
  92.  /** 
  93.  * 支付成功页面 
  94.  * 不可靠的回调 
  95.  */  
  96.  public function ajax_PaySuccess(){  
  97.  //订单号  
  98.  $out_trade_no = I('post.out_trade_no');  
  99.  //支付金额  
  100.  $total_fee = I('post.total_fee');  
  101.  /*相关逻辑处理*/  
  102.    
  103.  }  
贴上模板HTML


[php] view plain copy
 
 
  1.   
  2.   
  3.  "content-type" content="text/html;charset=utf-8"/>  
  4.  "viewport" content="width=device-width, initial-scale=1"/>   
  5.  微信支付样例-支付  
  6.  "text/javascript">  
  7.  //调用微信JS api 支付  
  8.  function jsApiCall()  
  9.  {  
  10.  WeixinJSBridge.invoke(  
  11.  'getBrandWCPayRequest',  
  12.  {$jsApiParameters},  
  13.  function(res){  
  14.  WeixinJSBridge.log(res.err_msg);  
  15.  //取消支付  
  16.  if(res.err_msg == 'get_brand_wcpay_request:cancel'){  
  17.  //处理取消支付的事件逻辑  
  18.  }else if(res.err_msg == "get_brand_wcpay_request:ok"){  
  19.  /*使用以上方式判断前端返回,微信团队郑重提示: 
  20.  res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 
  21.  这里可以使用Ajax提交到后台,处理一些日志,如Test控制器里面的ajax_PaySuccess方法。 
  22.  */  
  23.  }  
  24.  alert(res.err_code+res.err_desc+res.err_msg);  
  25.  }  
  26.  );  
  27.  }  
  28.    
  29.  function callpay()  
  30.  {  
  31.  if (typeof WeixinJSBridge == "undefined"){  
  32.  if( document.addEventListener ){  
  33.  document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);  
  34.  }else if (document.attachEvent){  
  35.  document.attachEvent('WeixinJSBridgeReady', jsApiCall);   
  36.  document.attachEvent('onWeixinJSBridgeReady', jsApiCall);  
  37.  }  
  38.  }else{  
  39.  jsApiCall();  
  40.  }  
  41.  }  
  42.  //获取共享地址  
  43.  function editAddress()  
  44.  {  
  45.  WeixinJSBridge.invoke(  
  46.  'editAddress',  
  47.  {$editAddress},  
  48.  function(res){  
  49.  var value1 = res.proviceFirstStageName;  
  50.  var value2 = res.addressCitySecondStageName;  
  51.  var value3 = res.addressCountiesThirdStageName;  
  52.  var value4 = res.addressDetailInfo;  
  53.  var tel = res.telNumber;   
  54.  alert(value1 + value2 + value3 + value4 + ":" + tel);  
  55.  }  
  56.  );  
  57.  }  
  58.    
  59.  window.onload = function(){  
  60.  if (typeof WeixinJSBridge == "undefined"){  
  61.  if( document.addEventListener ){  
  62.  document.addEventListener('WeixinJSBridgeReady', editAddress, false);  
  63.  }else if (document.attachEvent){  
  64.  document.attachEvent('WeixinJSBridgeReady', editAddress);   
  65.  document.attachEvent('onWeixinJSBridgeReady', editAddress);  
  66.  }  
  67.  }else{  
  68.  editAddress();  
  69.  }  
  70.  };  
  71.    
  72.    
  73.   
  74.   
  75.  
      
  76.  "#9ACD32">该笔订单支付金额为"color:#f00;font-size:50px">1分

      
  77.  "center">  
  78.  "width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="callpay()" >立即支付  
  79.  
  
  •   
  •   
  • notify.php文件代码,这里有在官方文件里新添加的一个自定义方法。


    1. require_once ROOT_PATH."Api/lib/WxPay.Api.php";  
    2. require_once ROOT_PATH.'Api/lib/WxPay.Notify.php';  
    3. require_once ROOT_PATH.'Api/lib/log.php';  
    4.    
    5. //初始化日志  
    6. $logHandlernew \CLogFileHandler(ROOT_PATH."/logs/".date('Y-m-d').'.log');  
    7. $log = \Log::Init($logHandler, 15);  
    8.    
    9. class PayNotifyCallBack extends WxPayNotify  
    10. {  
    11.  protected $para = array('code'=>0,'data'=>'');  
    12.  //查询订单  
    13.  public function Queryorder($transaction_id)  
    14.  {  
    15.  $input = new \WxPayOrderQuery();  
    16.  $input->SetTransaction_id($transaction_id);  
    17.  $result = \WxPayApi::orderQuery($input);  
    18.  \Log::DEBUG("query:" . json_encode($result));  
    19.  if(array_key_exists("return_code"$result)  
    20.  && array_key_exists("result_code"$result)  
    21.  && $result["return_code"] == "SUCCESS"  
    22.  && $result["result_code"] == "SUCCESS")  
    23.  {  
    24.  return true;  
    25.  }  
    26.  $this->para['code'] = 0;  
    27.  $this->para['data'] = '';  
    28.  return false;  
    29.  }  
    30.    
    31.  //重写回调处理函数  
    32.  public function NotifyProcess($data, &$msg)  
    33.  {  
    34.  \Log::DEBUG("call back:" . json_encode($data));  
    35.  $notfiyOutput = array();  
    36.    
    37.  if(!array_key_exists("transaction_id"$data)){  
    38.  $msg = "输入参数不正确";  
    39.  $this->para['code'] = 0;  
    40.  $this->para['data'] = '';  
    41.  return false;  
    42.  }  
    43.  //查询订单,判断订单真实性  
    44.  if(!$this->Queryorder($data["transaction_id"])){  
    45.  $msg = "订单查询失败";  
    46.  $this->para['code'] = 0;  
    47.  $this->para['data'] = '';  
    48.  return false;  
    49.  }  
    50.    
    51.  $this->para['code'] = 1;  
    52.  $this->para['data'] = $data;  
    53.  return true;  
    54.  }  
    55.    
    56.  /** 
    57.  * 自定义方法 检测微信端是否回调成功方法 
    58.  * @return multitype:number string 
    59.  */  
    60.  public function IsSuccess(){  
    61.  return $this->para;  
    62.  }  
    63. }  


    阅读(1113) | 评论(0) | 转发(0) |
    0

    上一篇:缓存(数据缓存)

    下一篇:MYSQL性能查看

    给主人留下些什么吧!~~