欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

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

程序员文章站 2022-03-27 12:20:22
...
本文主要和大家详细介绍了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,文件目录为下图:

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

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

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

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

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

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

然后填写js接口安全域。

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

最后设置网页授权

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

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

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

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

4.微信支付配置

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

把相关配置填写正确。




[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 代码 开发