PHP实现微信支付功能开发代码分享

小云云
小云云 原创
2023-03-21 06:46:02 5645浏览

本文主要和大家详细介绍了PHP微信支付开发过程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能帮助到大家。

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

2.需要相关文件和权限
微信支付需申请开通
微信公众平台开发者文档:http://mp.weixin.qq.com/wiki/home/index.html
微信支付开发者文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
微信支付SDK下载地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

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

这里写图片描述

这里写图片描述

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

这里写图片描述

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

这里写图片描述

然后填写js接口安全域。

这里写图片描述

最后设置网页授权

这里写图片描述

这里写图片描述

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

这里写图片描述

4.微信支付配置

这里写图片描述

把相关配置填写正确。




[php] view plain copy


  1. /**

  2. * 配置账号信息

  3. */

  4. class WxPayConfig

  5. {

  6. //=======【基本信息设置】=====================================

  7. //

  8. /**

  9. * TODO: 修改这里配置为您自己申请的商户信息

  10. * 微信公众号信息配置

  11. *

  12. * APPID:绑定支付的APPID(必须配置,开户邮件中可查看)

  13. *

  14. * MCHID:商户号(必须配置,开户邮件中可查看)

  15. *

  16. * KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置)

  17. * 设置地址:https://pay.weixin.qq.com/index.php/account/api_cert

  18. *

  19. * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置),

  20. * 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN

  21. * @var string

  22. */

  23. const APPID = '';

  24. const MCHID = '';

  25. const KEY = '';

  26. const APPSECRET = '';

  27. //=======【证书路径设置】=====================================

  28. /**

  29. * TODO:设置商户证书路径

  30. * 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载,

  31. * API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书)

  32. * @var path

  33. */

  34. const SSLCERT_PATH = '../cert/apiclient_cert.pem';

  35. const SSLKEY_PATH = '../cert/apiclient_key.pem';

  36. //=======【curl代理设置】===================================

  37. /**

  38. * TODO:这里设置代理机器,只有需要代理的时候才设置,不需要代理,请设置为0.0.0.0和0

  39. * 本例程通过curl使用HTTP POST方法,此处可修改代理服务器,

  40. * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此时不开启代理(如有需要才设置)

  41. * @var unknown_type

  42. */

  43. const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";

  44. const CURL_PROXY_PORT = 0;//8080;

  45. //=======【上报信息配置】===================================

  46. /**

  47. * TODO:接口调用上报等级,默认紧错误上报(注意:上报超时间为【1s】,上报无论成败【永不抛出异常】,

  48. * 不会影响接口调用流程),开启上报之后,方便微信监控请求调用的质量,建议至少

  49. * 开启错误上报。

  50. * 上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报

  51. * @var int

  52. */

  53. const REPORT_LEVENL = 1;

  54. }

现在开始贴出代码:




[php] view plain copy


  1. namespace Wechat\Controller;

  2. use Think\Controller;

  3. /**

  4. * 父类控制器,需要继承

  5. * @file ParentController.class.php

  6. * @author Gary <lizhiyong2204@sina.com>

  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. * 获取access_token

  23. * @return mixed|boolean|unknown

  24. */

  25. public function getToken(){

  26. $cache_token = S('exp_wechat_pay_token');

  27. if(!empty($cache_token)){

  28. return $cache_token;

  29. }

  30. $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s';

  31. $url = sprintf($url,$this->options['appid'],$this->options['appsecret']);

  32. $result = $this->http_get($url);

  33. $result = json_decode($result,true);

  34. if(empty($result)){

  35. return false;

  36. }

  37. S('exp_wechat_pay_token',$result['access_token'],array('type'=>'file','expire'=>3600));

  38. return $result['access_token'];

  39. }

  40. /**

  41. * 发送客服消息

  42. * @param array $data 消息结构{"touser":"OPENID","msgtype":"news","news":{...}}

  43. */

  44. public function sendCustomMessage($data){

  45. $token = $this->getToken();

  46. if (empty($token)) return false;

  47. $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s';

  48. $url = sprintf($url,$token);

  49. $result = $this->http_post($url,self::json_encode($data));

  50. if ($result)

  51. {

  52. $json = json_decode($result,true);

  53. if (!$json || !empty($json['errcode'])) {

  54. $this->errCode = $json['errcode'];

  55. $this->errMsg = $json['errmsg'];

  56. return false;

  57. }

  58. return $json;

  59. }

  60. return false;

  61. }

  62. /**

  63. * 发送模板消息

  64. * @param unknown $data

  65. * @return boolean|unknown

  66. */

  67. public function sendTemplateMessage($data){

  68. $token = $this->getToken();

  69. if (empty($token)) return false;

  70. $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";

  71. $url = sprintf($url,$token);

  72. $result = $this->http_post($url,self::json_encode($data));

  73. if ($result)

  74. {

  75. $json = json_decode($result,true);

  76. if (!$json || !empty($json['errcode'])) {

  77. $this->errCode = $json['errcode'];

  78. $this->errMsg = $json['errmsg'];

  79. return false;

  80. }

  81. return $json;

  82. }

  83. return false;

  84. }

  85. public function getFileCache($name){

  86. return S($name);

  87. }

  88. /**

  89. * 微信api不支持中文转义的json结构

  90. * @param array $arr

  91. */

  92. static function json_encode($arr) {

  93. $parts = array ();

  94. $is_list = false;

  95. //Find out if the given array is a numerical array

  96. $keys = array_keys ( $arr );

  97. $max_length = count ( $arr ) - 1;

  98. if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1

  99. $is_list = true;

  100. for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position

  101. if ($i != $keys [$i]) { //A key fails at position check.

  102. $is_list = false; //It is an associative array.

  103. break;

  104. }

  105. }

  106. }

  107. foreach ( $arr as $key => $value ) {

  108. if (is_array ( $value )) { //Custom handling for arrays

  109. if ($is_list)

  110. $parts [] = self::json_encode ( $value ); /* :RECURSION: */

  111. else

  112. $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */

  113. } else {

  114. $str = '';

  115. if (! $is_list)

  116. $str = '"' . $key . '":';

  117. //Custom handling for multiple data types

  118. if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)

  119. $str .= $value; //Numbers

  120. elseif ($value === false)

  121. $str .= 'false'; //The booleans

  122. elseif ($value === true)

  123. $str .= 'true';

  124. else

  125. $str .= '"' . addslashes ( $value ) . '"'; //All other things

  126. // :TODO: Is there any more datatype we should be in the lookout for? (Object?)

  127. $parts [] = $str;

  128. }

  129. }

  130. $json = implode ( ',', $parts );

  131. if ($is_list)

  132. return '[' . $json . ']'; //Return numerical JSON

  133. return '{' . $json . '}'; //Return associative JSON

  134. }

  135. /**

  136. +----------------------------------------------------------

  137. * 生成随机字符串

  138. +----------------------------------------------------------

  139. * @param int $length 要生成的随机字符串长度

  140. * @param string $type 随机码类型:0,数字+大小写字母;1,数字;2,小写字母;3,大写字母;4,特殊字符;-1,数字+大小写字母+特殊字符

  141. +----------------------------------------------------------

  142. * @return string

  143. +----------------------------------------------------------

  144. */

  145. static public function randCode($length = 5, $type = 2){

  146. $arr = array(1 => "0123456789", 2 => "abcdefghijklmnopqrstuvwxyz", 3 => "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4 => "~@#$%^&*(){}[]|");

  147. if ($type == 0) {

  148. array_pop($arr);

  149. $string = implode("", $arr);

  150. } elseif ($type == "-1") {

  151. $string = implode("", $arr);

  152. } else {

  153. $string = $arr[$type];

  154. }

  155. $count = strlen($string) - 1;

  156. $code = '';

  157. for ($i = 0; $i < $length; $i++) {

  158. $code .= $string[rand(0, $count)];

  159. }

  160. return $code;

  161. }

  162. /**

  163. * GET 请求

  164. * @param string $url

  165. */

  166. private function http_get($url){

  167. $oCurl = curl_init();

  168. if(stripos($url,"https://")!==FALSE){

  169. curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);

  170. curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);

  171. curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1

  172. }

  173. curl_setopt($oCurl, CURLOPT_URL, $url);

  174. curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );

  175. $sContent = curl_exec($oCurl);

  176. $aStatus = curl_getinfo($oCurl);

  177. curl_close($oCurl);

  178. if(intval($aStatus["http_code"])==200){

  179. return $sContent;

  180. }else{

  181. return false;

  182. }

  183. }

  184. /**

  185. * POST 请求

  186. * @param string $url

  187. * @param array $param

  188. * @param boolean $post_file 是否文件上传

  189. * @return string content

  190. */

  191. private function http_post($url,$param,$post_file=false){

  192. $oCurl = curl_init();

  193. if(stripos($url,"https://")!==FALSE){

  194. curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);

  195. curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);

  196. curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1

  197. }

  198. if (is_string($param) || $post_file) {

  199. $strPOST = $param;

  200. } else {

  201. $aPOST = array();

  202. foreach($param as $key=>$val){

  203. $aPOST[] = $key."=".urlencode($val);

  204. }

  205. $strPOST = join("&", $aPOST);

  206. }

  207. curl_setopt($oCurl, CURLOPT_URL, $url);

  208. curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );

  209. curl_setopt($oCurl, CURLOPT_POST,true);

  210. curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);

  211. $sContent = curl_exec($oCurl);

  212. $aStatus = curl_getinfo($oCurl);

  213. curl_close($oCurl);

  214. if(intval($aStatus["http_code"])==200){

  215. return $sContent;

  216. }else{

  217. return false;

  218. }

  219. }

  220. }




[php] view plain copy


  1. namespace Wechat\Controller;

  2. use Wechat\Controller\ParentController;

  3. /**

  4. * 微信支付测试控制器

  5. * @file TestController.class.php

  6. * @author Gary <lizhiyong2204@sina.com>

  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. public function index(){

  19. //①、获取用户openid

  20. $tools = new \JsApiPay();

  21. $openId = $tools->GetOpenid();

  22. //②、统一下单

  23. $input = new \WxPayUnifiedOrder();

  24. //商品描述

  25. $input->SetBody($this->_order_body);

  26. //附加数据,可以添加自己需要的数据,微信回异步回调时会附加这个数据

  27. $input->SetAttach('xxx');

  28. //商户订单号

  29. $out_trade_no = \WxPayConfig::MCHID.date("YmdHis");

  30. $input->SetOut_trade_no($out_trade_no);

  31. //总金额,订单总金额,只能为整数,单位为分

  32. $input->SetTotal_fee(1);

  33. //交易起始时间

  34. $input->SetTime_start(date("YmdHis"));

  35. //交易结束时间

  36. $input->SetTime_expire(date("YmdHis", time() + 600));

  37. //商品标记

  38. $input->SetGoods_tag($this->_order_goods_tag);

  39. //通知地址,接收微信支付异步通知回调地址 SITE_URL=http://test.paywechat.com/Charge

  40. $notify_url = SITE_URL.'/index.php/Test/notify.html';

  41. $input->SetNotify_url($notify_url);

  42. //交易类型

  43. $input->SetTrade_type("JSAPI");

  44. $input->SetOpenid($openId);

  45. $order = \WxPayApi::unifiedOrder($input);

  46. $jsApiParameters = $tools->GetJsApiParameters($order);

  47. //获取共享收货地址js函数参数

  48. $editAddress = $tools->GetEditAddressParameters();

  49. $this->assign('openId',$openId);

  50. $this->assign('jsApiParameters',$jsApiParameters);

  51. $this->assign('editAddress',$editAddress);

  52. $this->display();

  53. }

  54. /**

  55. * 异步通知回调方法

  56. */

  57. public function notify(){

  58. require_once ROOT_PATH."Api/lib/notify.php";

  59. $notify = new \PayNotifyCallBack();

  60. $notify->Handle(false);

  61. //这里的IsSuccess是我自定义的一个方法,后面我会贴出这个文件的代码,供参考。

  62. $is_success = $notify->IsSuccess();

  63. $bdata = $is_success['data'];

  64. //支付成功

  65. if($is_success['code'] == 1){

  66. $news = array(

  67. 'touser' => $bdata['openid'],

  68. 'msgtype' => 'news',

  69. 'news' => array (

  70. 'articles'=> array (

  71. array(

  72. 'title' => '订单支付成功',

  73. 'description' => "支付金额:{$bdata['total_fee']}\n".

  74. "微信订单号:{$bdata['transaction_id']}\n"

  75. 'picurl' => '',

  76. 'url' => ''

  77. )

  78. )

  79. )

  80. );

  81. //发送微信支付通知

  82. $this->sendCustomMessage($news);

  83. }else{//支付失败

  84. }

  85. }

  86. /**

  87. * 支付成功页面

  88. * 不可靠的回调

  89. */

  90. public function ajax_PaySuccess(){

  91. //订单号

  92. $out_trade_no = I('post.out_trade_no');

  93. //支付金额

  94. $total_fee = I('post.total_fee');

  95. /*相关逻辑处理*/

  96. }

贴上模板HTML




[xhtml] view plain copy


  1. <html>

  2. <head>

  3. <meta http-equiv="content-type" content="text/html;charset=utf-8"/>

  4. <meta name="viewport" content="width=device-width, initial-scale=1"/>

  5. <title>微信支付样例-支付</title>

  6. <script type="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. function callpay()

  29. {

  30. if (typeof WeixinJSBridge == "undefined"){

  31. if( document.addEventListener ){

  32. document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);

  33. }else if (document.attachEvent){

  34. document.attachEvent('WeixinJSBridgeReady', jsApiCall);

  35. document.attachEvent('onWeixinJSBridgeReady', jsApiCall);

  36. }

  37. }else{

  38. jsApiCall();

  39. }

  40. }

  41. //获取共享地址

  42. function editAddress()

  43. {

  44. WeixinJSBridge.invoke(

  45. 'editAddress',

  46. {$editAddress},

  47. function(res){

  48. var value1 = res.proviceFirstStageName;

  49. var value2 = res.addressCitySecondStageName;

  50. var value3 = res.addressCountiesThirdStageName;

  51. var value4 = res.addressDetailInfo;

  52. var tel = res.telNumber;

  53. alert(value1 + value2 + value3 + value4 + ":" + tel);

  54. }

  55. );

  56. }

  57. window.onload = function(){

  58. if (typeof WeixinJSBridge == "undefined"){

  59. if( document.addEventListener ){

  60. document.addEventListener('WeixinJSBridgeReady', editAddress, false);

  61. }else if (document.attachEvent){

  62. document.attachEvent('WeixinJSBridgeReady', editAddress);

  63. document.attachEvent('onWeixinJSBridgeReady', editAddress);

  64. }

  65. }else{

  66. editAddress();

  67. }

  68. };

  69. </script>

  70. </head>

  71. <body>

  72. <br/>

  73. <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">1分</span></b></font><br/><br/>

  74. <p align="center">

  75. <button style="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()" >立即支付</button>

  76. </p>

  77. </body>

  78. </html>

notify.php文件代码,这里有在官方文件里新添加的一个自定义方法。




[php] view plain copy


  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. $logHandler= new \CLogFileHandler(ROOT_PATH."/logs/".date('Y-m-d').'.log');

  6. $log = \Log::Init($logHandler, 15);

  7. class PayNotifyCallBack extends WxPayNotify

  8. {

  9. protected $para = array('code'=>0,'data'=>'');

  10. //查询订单

  11. public function Queryorder($transaction_id)

  12. {

  13. $input = new \WxPayOrderQuery();

  14. $input->SetTransaction_id($transaction_id);

  15. $result = \WxPayApi::orderQuery($input);

  16. \Log::DEBUG("query:" . json_encode($result));

  17. if(array_key_exists("return_code", $result)

  18. && array_key_exists("result_code", $result)

  19. && $result["return_code"] == "SUCCESS"

  20. && $result["result_code"] == "SUCCESS")

  21. {

  22. return true;

  23. }

  24. $this->para['code'] = 0;

  25. $this->para['data'] = '';

  26. return false;

  27. }

  28. //重写回调处理函数

  29. public function NotifyProcess($data, &$msg)

  30. {

  31. \Log::DEBUG("call back:" . json_encode($data));

  32. $notfiyOutput = array();

  33. if(!array_key_exists("transaction_id", $data)){

  34. $msg = "输入参数不正确";

  35. $this->para['code'] = 0;

  36. $this->para['data'] = '';

  37. return false;

  38. }

  39. //查询订单,判断订单真实性

  40. if(!$this->Queryorder($data["transaction_id"])){

  41. $msg = "订单查询失败";

  42. $this->para['code'] = 0;

  43. $this->para['data'] = '';

  44. return false;

  45. }

  46. $this->para['code'] = 1;

  47. $this->para['data'] = $data;

  48. return true;

  49. }

  50. /**

  51. * 自定义方法 检测微信端是否回调成功方法

  52. * @return multitype:number string

  53. */

  54. public function IsSuccess(){

  55. return $this->para;

  56. }

  57. }

到这里基本上完成,可以在微信端打开http://test.paywechat.com/Charge/index.php/Test/index/
相关推荐:

nodejs实现微信支付功能实例详解

Thinkphp整合微信支付功能

怎么给PC端网站添加这种微信支付功能

以上就是PHP实现微信支付功能开发代码分享的详细内容,更多请关注php中文网其它相关文章!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。