当前位置:   article > 正文

Thinkphp对接支付宝和微信支付接口封装类_thinkphp alipay wechatpay

thinkphp alipay wechatpay

导读:

最近开发的一套支付系统,需要对接支付宝和微信支付的主流接口,支付宝有沙箱测试环境和沙箱测试账号,对接非常方便,最想吐槽的就是微信支付,连测试商户账号都不提供,叫你自己申请正式的商户,而且申请非常麻烦,没有正规的企业公司资质,个人开发者根本申请不到。如果不是在公司里上班,建议大家还是别折腾微信支付的了,麻烦!给大家看看我的支付系统体验收银台:

我的支付接口代码都是整合在extend扩展目录里,这样封装方便后期维护和在其他项目里直接使用:

直接分享代码给大家,里面有详细注释,只要不是很菜的程序员,相信都能看得懂,这里就不多做解释了。 

支付宝封装类Alipay.php 

  1. <?php
  2. namespace pay;
  3. use \think\Db;
  4. /**
  5. * 支付宝支付类
  6. */
  7. class Alipay {
  8. //是否沙盒环境
  9. private $is_sandbox = false;
  10. //沙盒地址
  11. private $sandurl = 'https://openapi.alipaydev.com/gateway.do';
  12. //正式地址
  13. private $apiurl = 'https://openapi.alipay.com/gateway.do';
  14. //网关地址(设置为公有,外部需要调用)
  15. public $gateway;
  16. //支付宝的APPID
  17. private $appid;
  18. //应用私钥
  19. private $rsaPrivateKey = '商户设置的私钥';
  20. //支付宝公钥
  21. private $alipayPublicKey= '支付宝自动生成的公钥';
  22. private $charset = 'utf-8';
  23. private $scope;
  24. private $auth_code;
  25. public function setAppid($appid)
  26. {
  27. $this->appid = $appid;
  28. }
  29. public function setRsaPrivateKey($rsaPrivateKey)
  30. {
  31. $this->rsaPrivateKey = $rsaPrivateKey;
  32. }
  33. public function setAlipayPublicKey($alipayPublicKey)
  34. {
  35. $this->alipayPublicKey = $alipayPublicKey;
  36. }
  37. public function setScope($scope)
  38. {
  39. $this->scope = $scope;
  40. }
  41. public function setAuthCode($authCode)
  42. {
  43. $this->auth_code = $authCode;
  44. }
  45. //构造方法
  46. public function __construct($account=null){
  47. if($account!=null){
  48. //如果是沙箱测试
  49. if($this->is_sandbox){
  50. $this->gateway = $this->sandurl;
  51. $this->appid = '2021000117612368';
  52. $this->rsaPrivateKey ='MIIEowIBAAKCAQEAgAWVzlJRyrsZQyoVSjInQQZa2NtOlhLpH5osB04qB3PglUP5PdObbgBoq5xZFHeYfw7GBQzqKxGsHu3CPB7Y+SZyBZcORDtcMY9Nmul0GMn5hNqM40hpl1TfAbkkXcuSzAZTZ0n3VkRhUYbhphNlcYFFPPjaqhm0o+VJ1SZ8N6lEcvvSVSL89gTcAjlGfFVxanFpWtvu8tAQLuFQoK/B72yFsjbn+TnmW9v34DSiGvRKz4xlw7qFpOVyNxgrmN1JH1IMwkI0fW2juBdSmu3N1oB7ok5D9WgWFqvNkvWfQcnojwaCNa5/gXPKUgsVgU+LsreZa0Gy1K0JDxgGPcjQXwIDAQABAoIBAD3JdEnFtTARKNofn3LVzdg2RvZOshkAO39DG2jTMrmyFXw0+D/8txFaJSpeJSgZqIH6EVwypXnv55Bt08bMZI+rHHDSPNJ1i8NnlF7luWCValXSXRj8w+mDzrW4M3MJY2oQy4QYUMVT71w3qwRKbDVh3JbWzEI4cdSAPygekOkTmtY7C8rg/CKkL1IK8KX8qCSRfMzIH2dKenxffgANxprXVaucf+d/dwbwtcyIRNOYN3cxRrSXPnUNSyUvJ8IJc96zCMLXqU4LmBgI2OvC8t3OPkp35CTUNC/7L9VsTNL1p7NFxVbbAkBXLbbnp7Tcw8PPuEpSJJLOhxvYLGDI/xECgYEAyrH9pB6xxAdnkBLHYn2geWSnAfi+VCq+cfpqLCsffgDl5S5NiE2T2lbOy7uJ1fypjt24jimBhriBf4yqo8CeL/FmD9zw23/wfwXAyixP+UXy3nt6M9DxEG+bFA59hN6XgXCHVMkVCzPC2dmN+DucpXYJdcqaFVTJDm40InE96o0CgYEAobBen5q3KK7mMA8nR0NpLfLGEh+QEAOCUJjHBebLaG3GD3RWk4ScO7hTkH5cFZfM72RpEo6iDuXSCQQu6Tu08RD88LEP6vwOncNwsylJt3yFSvrYq+O2pUXRYsP4ZomzyA55zGCJhGYW/7wG1G0DfXw4EbVB65QqBYT5ICc2QZsCgYEAwVxwoXrijCWgJvGzIQLE7mkVZK0dK/+ms+Z2GHJ+1V4X8bxtHCUdccEz/2iFQgyI2BAtefigZptmdf4D3rjmbsfzXBih8FMpYpNjabpCBIM1AAZ8+idNgCdnogd8uB78Gu/1Oe2lVI1pSRbf9zku4y0qMyM2QDkkn86Dj4PF1BUCgYASkBEM+dI1VX8P3W2A/PhCUjDzO7RqYjy/zAptXwXTQ2cNtymJXhwhrxPI7vjbccYRZdG6U62/UBtA9b39AOHRG0Yuy+Ur8SwsaJezufXAnwceiJX/hfEeIFndoOoPu5dsTF0nXZ3sGVxil8+g67MO4Qf+4HRk+2mnoJCJZHcSKQKBgD7fWkvqEJ27ii5orxRmrVcmHGArOwN/w1nY7vAfWu8kScp/EDhbMWt+AvH2XFZJhmo2wxSkBamFMAK2kKT7WTviTiERF2iPFnv23Lm1shKTiR5Gz2fIfTDFwYyyhHo2qkKsyIntWp1eW/1m1HxU/oHR/uB6MnmG+jjmY7Jz5mxg';
  53. $this->alipayPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs9AE75V+F242HcN/SlcGUxUNv3zGpXjai4u2ZMkOL1nolr4/v4BGEsE8tLaYq6lecG/of5taQ27WbWXB5PMB3s6emesTEn5yvZPh/HqmKACHi7js+Dtu123J/z0DyCukMm1ZHJe7jT9VEU9w4gIdNi64VxdaRF3ZMT6DXnboUEegyRKGmJc58h+O30P/UYqdCW/Gl+380o80e6Fs0rX33AixkNTRNgnQ3n2er1Nrqan/9sfUCnqqxgpc1+GTT+vyn3x4Xwvch2pRIkcK4BFCrWVVMKVXx/icj1njTVwWm1KYrXDVYrxf4ZLgpMHyW3SfhnXFKuxvSYnvm5EHjxtNEQIDAQAB';
  54. }else{
  55. $this->gateway = $this->apiurl;
  56. $this->appid = $account['appid'];
  57. $this->rsaPrivateKey = $account['privateKey'];
  58. $this->alipayPublicKey = $account['publicKey'];
  59. }
  60. }
  61. }
  62. /**
  63. *
  64. * 1.发起电脑网站支付
  65. * $params 传输的数据
  66. */
  67. public function pcPay($params){
  68. //商品名称
  69. $subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "购买商品";
  70. //请求参数
  71. $requestConfigs = array(
  72. 'out_trade_no'=>$params['pay_id'],//唯一标识,订单编号(必须)
  73. 'product_code'=>'FAST_INSTANT_TRADE_PAY',
  74. 'total_amount'=>$params['money'], //付款金额,单位:元
  75. 'subject'=>$subject, //订单标题
  76. "timeout_express" =>"5m",//该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天
  77. );
  78. $commonConfigs = array(
  79. //公共参数
  80. 'app_id' => $this->appid,
  81. 'method' => 'alipay.trade.page.pay',//接口名称
  82. 'format' => 'JSON',
  83. 'return_url' => $params['return_url'],//同步通知地址
  84. 'charset'=>$this->charset,
  85. 'sign_type'=>'RSA2',
  86. 'timestamp'=>date('Y-m-d H:i:s'),
  87. 'version'=>'1.0',
  88. 'notify_url' => $params['notify_url'],//异步通知地址
  89. 'biz_content'=>json_encode($requestConfigs),
  90. );
  91. $commonConfigs["sign"] = $this->generateSign($commonConfigs, $commonConfigs['sign_type']);
  92. return $this->buildRequestForm($this->gateway,$commonConfigs);
  93. }
  94. /**
  95. *
  96. * 2.发起手机网站支付
  97. * $params 传输的数据
  98. */
  99. public function wapPay($params){
  100. //商品名称
  101. $subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "购买商品";
  102. //请求参数
  103. $requestConfigs = array(
  104. 'out_trade_no'=>$params['pay_id'],//唯一标识,订单编号(必须)
  105. 'product_code'=>'QUICK_WAP_WAY',
  106. 'total_amount'=>$params['money'], //付款金额,单位:元
  107. 'subject'=>$subject, //订单标题
  108. "timeout_express" =>"5m",//该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天
  109. );
  110. $commonConfigs = array(
  111. //公共参数
  112. 'app_id' => $this->appid,
  113. 'method' => 'alipay.trade.wap.pay',//接口名称
  114. 'format' => 'JSON',
  115. 'return_url' => $params['return_url'],//同步通知地址
  116. 'charset'=>$this->charset,
  117. 'sign_type'=>'RSA2',
  118. 'timestamp'=>date('Y-m-d H:i:s'),
  119. 'version'=>'1.0',
  120. 'notify_url' => $params['notify_url'],//异步通知地址
  121. 'biz_content'=>json_encode($requestConfigs),
  122. );
  123. $commonConfigs["sign"] = $this->generateSign($commonConfigs, $commonConfigs['sign_type']);
  124. return $commonConfigs;
  125. }
  126. /**
  127. *
  128. * 3.发起当面付
  129. * $params 传输的数据
  130. */
  131. public function facePay($params){
  132. //商品名称
  133. $subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "购买商品";
  134. //请求参数的集合,参考https://mp.csdn.net/editor/html/113599448
  135. $biz_content = [
  136. "scene" => "bar_code",//支付场景 条码支付,取值:bar_code 声波支付,取值:wave_code(必须)
  137. "out_trade_no" => $params['pay_id'],//唯一标识,订单编号(必须)
  138. //"auth_code" => $params['auth_code'],//支付授权码(商户签约模式时,为必须参数)
  139. "total_amount" => $params['money'],//订单金额(可选)
  140. "subject" => $subject,//商品名称
  141. //"seller_id" => '', //如果该值为空,则默认为商户签约账号对应的支付宝用户ID
  142. "timeout_express" =>"5m",//该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天
  143. ];
  144. //公共请求参数
  145. $param = [
  146. 'app_id' => $this->appid,//支付宝分配给开发者的应用ID
  147. 'method' => 'alipay.trade.precreate',//接口名称
  148. //'format' => 'JSON',//仅支持JSON
  149. 'charset' => 'utf-8',//请求使用的编码格式
  150. 'sign_type' => 'RSA2',//商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
  151. 'sign' => '',//商户请求参数的签名串
  152. 'timestamp' => date('Y-m-d H:i:s', time()),//发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
  153. 'version' => '1.0',//调用的接口版本,固定为1.0
  154. 'notify_url' => $params['notify_url'],//异步通知地址,支付宝服务器主动通知商户服务器里指定的页面http/https路径
  155. //'app_auth_token' => '',//app_auth_token
  156. 'biz_content' => json_encode($biz_content), //请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
  157. ];
  158. //组合生成签名参数
  159. $signdata = [];
  160. $signdata['app_id'] = $param['app_id'];
  161. $signdata['method'] = $param['method'];
  162. $signdata['charset'] = $param['charset'];
  163. $signdata['sign_type'] = $param['sign_type'];
  164. $signdata['timestamp'] = $param['timestamp'];
  165. $signdata['version'] = $param['version'];
  166. $signdata['notify_url'] = $param['notify_url'];
  167. $signdata['biz_content'] = $param['biz_content'];
  168. //生成签名
  169. $sign = $this->generateSign($signdata, 'RSA2');
  170. $param['sign'] = $sign;
  171. //echo "<pre>";
  172. //var_dump($param);die;
  173. //发起请求
  174. $content = $this->file_post($this->gateway,$param);
  175. $alipayData = json_decode($content, true);
  176. //公共响应参数
  177. $responseData = $alipayData['alipay_trade_precreate_response'];
  178. if($responseData['code'] == 10000){
  179. //生成成功,返回结果给前端
  180. $data = [];
  181. $data['out_trade_no'] = $responseData['out_trade_no'];
  182. $data['qr_code'] = $responseData['qr_code'];
  183. return ['code' => 1 , 'msg' => '成功' , 'data' => $data];
  184. }else {
  185. //file_put_contents(LOG_PATH .'alipayFacepay.log', 'err code:' . $responseData['code'] . ', err msg:' . $responseData['msg'] . '\r\n', FILE_APPEND);
  186. return ['code' => 0 , 'msg' => '错误码:' . $responseData['code'] . ',错误信息:' . $responseData['msg']];
  187. }
  188. }
  189. /**
  190. *
  191. * 4.发起APP付(JSAPI)
  192. * $params 传输的数据
  193. */
  194. public function appPay($params){
  195. //商品名称
  196. $subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "购买商品";
  197. //请求参数
  198. $requestConfigs = array(
  199. 'out_trade_no'=>$params['pay_id'],//唯一标识,订单编号(必须)
  200. 'total_amount'=>$params['money'], //付款金额,单位:元
  201. 'subject'=>$subject, //订单标题
  202. 'product_code'=>'QUICK_MSECURITY_PAY', //销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
  203. 'timeout_express'=>'5m',//该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
  204. //'store_id'=>'', //商户门店编号。该参数用于请求参数中以区分各门店,非必传项。
  205. //'extend_params'=>array(
  206. // 'sys_service_provider_id'=>''//系统商编号,该参数作为系统商返佣数据提取的依据,请填写系统商签约协议的PID
  207. // )
  208. );
  209. $commonConfigs = array(
  210. //公共参数
  211. 'app_id' => $this->appid,
  212. 'method' => 'alipay.trade.app.pay',//接口名称
  213. 'format' => 'JSON',
  214. 'charset'=>$this->charset,
  215. 'sign_type'=>'RSA2',
  216. 'timestamp'=>date('Y-m-d H:i:s'),
  217. 'version'=>'1.0',
  218. 'notify_url' => $params['notify_url'],//异步通知地址
  219. 'biz_content'=>json_encode($requestConfigs),
  220. );
  221. $commonConfigs["sign"] = $this->generateSign($commonConfigs, $commonConfigs['sign_type']);
  222. return http_build_query($commonConfigs);
  223. }
  224. /**
  225. *
  226. * 5.支付查询接口
  227. * @param data 支付宝响应的参数集合
  228. * @param status 要验证的状态
  229. * WAIT_BUYER_PAY 交易创建等待买家付款
  230. * TRADE_CLOSED 未付款交易超时关闭或支付完成后全额退款
  231. * TRADE_SUCCESS 交易支付成功
  232. * TRADE_FINISHED 交易结束不可退款
  233. */
  234. public function orderquery($data , $status){
  235. $biz_content = [
  236. 'out_trade_no' => $data['out_trade_no'],
  237. 'trade_no' => $data['trade_no'],
  238. //'org_pid' => '',
  239. ];
  240. $param = [
  241. 'app_id' => $this->appid,
  242. 'method' => 'alipay.trade.query',
  243. 'charset' => 'utf-8',
  244. 'sign_type' => 'RSA2',
  245. 'sign' => '',
  246. 'timestamp' => date('Y-m-d H:i:s', time()),
  247. 'version' => '1.0',
  248. 'biz_content' => json_encode($biz_content),
  249. ];
  250. //组合签名数组
  251. $signdata = [];
  252. $signdata['app_id'] = $param['app_id'];
  253. $signdata['method'] = $param['method'];
  254. $signdata['charset'] = $param['charset'];
  255. $signdata['sign_type'] = $param['sign_type'];
  256. $signdata['timestamp'] = $param['timestamp'];
  257. $signdata['version'] = $param['version'];
  258. $signdata['biz_content'] = $param['biz_content'];
  259. //生成签名
  260. $sign = $this->generateSign($signdata, 'RSA2');
  261. $param['sign'] = $sign;
  262. $content = $this->file_post($this->gateway,$param);
  263. $alipayData = json_decode($content, true);
  264. //公共响应参数
  265. $responseData = $alipayData['alipay_trade_query_response'];
  266. if($responseData['code'] == 10000){
  267. if($responseData['trade_status'] == $status){
  268. return true;
  269. }else {
  270. return false;
  271. }
  272. }else {
  273. return false;
  274. }
  275. }
  276. /**
  277. * 6.获取用户信息
  278. * @return array
  279. */
  280. public function doGetUserInfo($token)
  281. {
  282. $commonConfigs = array(
  283. //公共参数
  284. 'app_id' => $this->appid,
  285. 'method' => 'alipay.user.info.share',//接口名称
  286. 'format' => 'JSON',
  287. 'charset'=>$this->charset,
  288. 'sign_type'=>'RSA2',
  289. 'timestamp'=>date('Y-m-d H:i:s'),
  290. 'version'=>'1.0',
  291. 'auth_token'=>$token,
  292. );
  293. $commonConfigs["sign"] = $this->generateSign($commonConfigs, $commonConfigs['sign_type']);
  294. $result = $this->file_post('https://openapi.alipay.com/gateway.do?charset='.$this->charset,$commonConfigs);
  295. return json_decode($result,true);
  296. }
  297. /**
  298. * 支付宝异步通知
  299. * @param $data 通知的数据
  300. */
  301. public function notify($data){
  302. $falg=false;
  303. $param = $data;
  304. //不参与签名
  305. unset($param['sign']);
  306. unset($param['sign_type']);
  307. $rst = $this->rsaCheck($param, $data['sign'] , $data['sign_type']);
  308. if(!$rst){
  309. //file_put_contents(LOG_PATH .'alipaynotify.log', '验签失败\r\n' , FILE_APPEND );
  310. return false;
  311. }
  312. //查询支付订单状态
  313. try{
  314. $rst = $this->orderquery($data, 'TRADE_SUCCESS');
  315. } catch (\Exception $e) {
  316. //printLog("查询支付订单状态失败:".$e);
  317. }
  318. if($rst){
  319. $falg=true;
  320. }else {
  321. //file_put_contents(LOG_PATH .'alipaynotify.log', '查询订单状态错误\r\n', FILE_APPEND);
  322. $falg=false;
  323. }
  324. return $falg;
  325. }
  326. /**
  327. * 获取access_token和user_id
  328. * @return array
  329. */
  330. public function doAuth()
  331. {
  332. $commonConfigs = array(
  333. //公共参数
  334. 'app_id' => $this->appid,
  335. 'method' => 'alipay.system.oauth.token',//接口名称
  336. 'format' => 'JSON',
  337. 'charset'=>$this->charset,
  338. 'sign_type'=>'RSA2',
  339. 'timestamp'=>date('Y-m-d H:i:s'),
  340. 'version'=>'1.0',
  341. 'grant_type'=>'authorization_code',
  342. 'code'=>$this->auth_code,
  343. );
  344. $commonConfigs["sign"] = $this->generateSign($commonConfigs, $commonConfigs['sign_type']);
  345. $result = $this->file_post('https://openapi.alipay.com/gateway.do?charset='.$this->charset,$commonConfigs);
  346. return json_decode($result,true);
  347. }
  348. /**
  349. * 获取access_token和user_id
  350. */
  351. public function getToken()
  352. {
  353. //通过code获得access_token和user_id
  354. if (!isset($_GET['auth_code'])){
  355. //触发返回code码
  356. $scheme = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on' ? 'https://' : 'http://';
  357. $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']);
  358. if($_SERVER['QUERY_STRING']) $baseUrl = $baseUrl.'?'.$_SERVER['QUERY_STRING'];
  359. $url = $this->createAuthUrlForCode($baseUrl);
  360. Header("Location: $url");
  361. exit();
  362. } else {
  363. //获取code码,以获取openid
  364. $this->setAuthCode($_GET['auth_code']);
  365. return $this->doAuth();
  366. }
  367. }
  368. /**
  369. * 构造获取token的url连接
  370. * @param string $redirectUrl 服务器回跳的url,需要url编码
  371. * @return 返回构造好的url
  372. */
  373. protected function createAuthUrlForCode($redirectUrl)
  374. {
  375. $urlObj["app_id"] = $this->appid;
  376. $urlObj["redirect_uri"] = "$redirectUrl";
  377. $urlObj["scope"] = $this->scope;
  378. $urlObj["state"] = 123456;
  379. $bizString = $this->ToUrlParams($urlObj);
  380. return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?".$bizString;
  381. }
  382. /**
  383. * 拼接签名字符串
  384. * @param array $urlObj
  385. * @return 返回已经拼接好的字符串
  386. */
  387. protected function ToUrlParams($urlObj)
  388. {
  389. $buff = "";
  390. foreach ($urlObj as $k => $v)
  391. {
  392. if($k != "sign") $buff .= $k . "=" . $v . "&";
  393. }
  394. $buff = trim($buff, "&");
  395. return $buff;
  396. }
  397. protected function generateSign($params, $signType = "RSA") {
  398. return $this->sign($this->getSignContent($params), $signType);
  399. }
  400. protected function getSignContent($params) {
  401. ksort($params);
  402. $stringToBeSigned = "";
  403. $i = 0;
  404. foreach ($params as $k => $v) {
  405. if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
  406. // 转换成目标字符集
  407. $v = $this->characet($v, $this->charset);
  408. if ($i == 0) {
  409. $stringToBeSigned .= "$k" . "=" . "$v";
  410. } else {
  411. $stringToBeSigned .= "&" . "$k" . "=" . "$v";
  412. }
  413. $i++;
  414. }
  415. }
  416. unset ($k, $v);
  417. return $stringToBeSigned;
  418. }
  419. /**
  420. * 转换字符集编码
  421. * @param $data
  422. * @param $targetCharset
  423. * @return string
  424. */
  425. protected function characet($data, $targetCharset) {
  426. if (!empty($data)) {
  427. $fileType = $this->charset;
  428. if (strcasecmp($fileType, $targetCharset) != 0) {
  429. $data = mb_convert_encoding($data, $targetCharset, $fileType);
  430. //$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
  431. }
  432. }
  433. return $data;
  434. }
  435. /**
  436. *
  437. * 校验$value是否非空
  438. */
  439. protected function checkEmpty($value) {
  440. if (!isset($value))
  441. return true;
  442. if ($value === null)
  443. return true;
  444. if (trim($value) === "")
  445. return true;
  446. return false;
  447. }
  448. /**
  449. *
  450. * 签名函数
  451. */
  452. protected function sign($data, $signType = "RSA") {
  453. $priKey=$this->rsaPrivateKey;
  454. $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
  455. wordwrap($priKey, 64, "\n", true) .
  456. "\n-----END RSA PRIVATE KEY-----";
  457. ($res) or die('您使用的私钥格式错误,请检查RSA私钥配置');
  458. if ("RSA2" == $signType) {
  459. //OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持
  460. openssl_sign($data, $sign, $res, version_compare(PHP_VERSION,'5.4.0', '<') ? SHA256 : OPENSSL_ALGO_SHA256);
  461. } else {
  462. openssl_sign($data, $sign, $res);
  463. }
  464. $sign = base64_encode($sign);
  465. return $sign;
  466. }
  467. /**
  468. *
  469. * 验签函数(用于查询支付宝数据)
  470. */
  471. protected function rsaCheck($data, $sign,$type = 'RSA'){
  472. $public_key = $this->alipayPublicKey;
  473. $search = [
  474. "-----BEGIN PUBLIC KEY-----",
  475. "-----END PUBLIC KEY-----",
  476. "\n",
  477. "\r",
  478. "\r\n"
  479. ];
  480. $public_key=str_replace($search,"",$public_key);
  481. $public_key=$search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1];
  482. $res=openssl_get_publickey($public_key);
  483. if($res)
  484. {
  485. if($type == 'RSA'){
  486. $result = (bool)openssl_verify($this->getSignContent($data), base64_decode($sign), $res);
  487. }elseif($type == 'RSA2'){
  488. $result = (bool)openssl_verify($this->getSignContent($data), base64_decode($sign), $res,OPENSSL_ALGO_SHA256);
  489. }
  490. openssl_free_key($res);
  491. }else{
  492. return false;
  493. }
  494. return true;
  495. }
  496. /**
  497. * file_get_contents发送post请求
  498. * @param url 请求地址
  499. * @param postData 要传递的post数据
  500. */
  501. protected function file_post($url, $post_data) {
  502. $postdata = http_build_query($post_data);
  503. $options = array('http' => array('method' => 'POST', 'header' => 'Content-type:application/x-www-form-urlencoded', 'content' => $postdata, 'timeout' => 300
  504. // 超时时间(单位:s)
  505. ));
  506. $context = stream_context_create($options);
  507. $result = file_get_contents($url, false, $context);
  508. //去空格
  509. $result = trim($result);
  510. //转换字符编码
  511. $result = mb_convert_encoding($result, 'utf-8', 'UTF-8,GBK,GB2312,BIG5');
  512. //解决返回的json字符串中返回了BOM头的不可见字符(某些编辑器默认会加上BOM头)
  513. $result = trim($result,chr(239).chr(187).chr(191));
  514. return $result;
  515. }
  516. /**
  517. * curl发送post请求
  518. * @param url 请求地址
  519. * @param postData 要传递的post数据
  520. */
  521. protected function curl_post($url = '', $postData = '', $options = array())
  522. {
  523. if (is_array($postData)) {
  524. $postData = http_build_query($postData);
  525. }
  526. $ch = curl_init();
  527. curl_setopt($ch, CURLOPT_URL, $url);
  528. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  529. curl_setopt($ch, CURLOPT_POST, 1);
  530. curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
  531. curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
  532. if (!empty($options)) {
  533. curl_setopt_array($ch, $options);
  534. }
  535. //https请求 不验证证书和host
  536. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  537. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  538. $data = curl_exec($ch);
  539. curl_close($ch);
  540. return $data;
  541. }
  542. /**
  543. * 建立请求,以表单HTML形式构造(默认)
  544. * @param $url 请求地址
  545. * @param $params 请求参数数组
  546. * @return 提交表单HTML文本
  547. */
  548. protected function buildRequestForm($url,$params) {
  549. $sHtml = "正在跳转至支付页面...<form id='alipaysubmit' name='alipaysubmit' action='".$url."?charset=".$this->charset."' method='POST'>";
  550. foreach($params as $key=>$val){
  551. if (false === $this->checkEmpty($val)) {
  552. $val = str_replace("'","&apos;",$val);
  553. $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
  554. }
  555. }
  556. //submit按钮控件请不要含有name属性
  557. $sHtml = $sHtml."<input type='submit' value='ok' style='display:none;''></form>";
  558. $sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
  559. return $sHtml;
  560. }
  561. }
  562. ?>

微信支付封装类Weinxinpay.php

  1. <?php
  2. namespace pay;
  3. use \think\Db;
  4. /**
  5. * 微信支付类
  6. */
  7. class Weinxinpay {
  8. //是否沙盒环境
  9. private $is_sandbox = false;
  10. //沙盒地址
  11. private $sandurl = 'https://api.mch.weixin.qq.com/sandboxnew/pay';
  12. //正式地址
  13. private $apiurl = 'https://api.mch.weixin.qq.com/pay';
  14. private $mchid;//微信支付分配的商户账号PartnerID 通过微信支付商户资料审核后邮件发送
  15. private $appid;//微信支付分配的公众账号ID(企业号corpid即为此appid)
  16. private $appkey;//微信支付申请对应的公众号的APP Key
  17. private $apikey;//微信支付分配的商户密钥
  18. private $wapname;//wap网站名
  19. private $wapurl; //wap网站域名(网址)
  20. //网关地址
  21. private $gateway;
  22. //构造方法
  23. public function __construct($account=null){
  24. if($account!=null){
  25. $this->mchid = $account['mchid'];
  26. $this->appid = $account['appid'];
  27. $this->apikey = $account['apikey'];
  28. if(isset($account['appkey'])){
  29. $this->appkey = $account['appkey'];
  30. }
  31. if(isset($account['wapname'])){
  32. $this->wapname = $account['wapname'];
  33. }
  34. if(isset($account['wapurl'])){
  35. $this->wapurl = $account['wapurl'];
  36. }
  37. }
  38. //如果是沙箱测试
  39. if($this->is_sandbox){
  40. $this->gateway = $this->sandurl;
  41. }else{
  42. $this->gateway = $this->apiurl;
  43. }
  44. }
  45. /**
  46. *
  47. * 1.发起原生支付
  48. * $params 传输的数据
  49. */
  50. public function nativePay($params){
  51. //商品名称
  52. $subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "购买商品";
  53. $config = array(
  54. 'mch_id' => $this->mchid,
  55. 'appid' => $this->appid,
  56. 'key' => $this->apikey,
  57. );
  58. $unified = array(
  59. 'appid' => $config['appid'],
  60. 'attach' => 'pay', //商家数据包,原样返回,如果填写中文,请注意转换为utf-8
  61. 'body' => $subject,//商品名称(商品简单描述)
  62. 'mch_id' => $config['mch_id'],
  63. 'nonce_str' => self::createNonceStr(),//随机字符串,长度要求在32位以内
  64. 'notify_url' => $params['notify_url'],//异步通知地址
  65. 'out_trade_no'=> $params['pay_id'],//唯一标识,订单编号(必须)
  66. 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
  67. 'total_fee' => floatval($params['money']) * 100,//订单金额,单位 转为分
  68. 'trade_type' => 'NATIVE',
  69. );
  70. $unified['sign'] = self::getSign($unified, $config['key']);
  71. $responseXml = self::curl_post($this->gateway.'/unifiedorder', self::arrayToXml($unified));
  72. //禁止引用外部xml实体
  73. libxml_disable_entity_loader(true);
  74. $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
  75. if ($unifiedOrder === false) {
  76. die('parse xml error');
  77. }
  78. if ($unifiedOrder->return_code != 'SUCCESS') {
  79. die($unifiedOrder->return_msg);
  80. }
  81. if ($unifiedOrder->result_code != 'SUCCESS') {
  82. die($unifiedOrder->err_code);
  83. }
  84. $codeUrl = (array)($unifiedOrder->code_url);
  85. if(!$codeUrl[0]) exit('get code_url error');
  86. $timestamp=time();//付款时间
  87. $arr = array(
  88. "appId" => $config['appid'],
  89. "timeStamp" => "$timestamp",//这里是字符串的时间戳,不是int,所以需加引号
  90. "nonceStr" => self::createNonceStr(),//随机字符串,长度要求在32位以内
  91. "package" => "prepay_id=" . $unifiedOrder->prepay_id,
  92. "signType" => 'MD5',
  93. "code_url" => $codeUrl[0],
  94. );
  95. $arr['paySign'] = self::getSign($arr, $config['key']);
  96. return $arr;
  97. }
  98. /**
  99. *
  100. * 2.发起公众号支付
  101. * $openId
  102. * $params 传输的数据
  103. */
  104. public function jsapiPay($openId,$params){
  105. //商品名称
  106. $subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "购买商品";
  107. $config = array(
  108. 'mch_id' => $this->mchid,
  109. 'appid' => $this->appid,
  110. 'key' => $this->apikey,
  111. );
  112. //$orderName = iconv('GBK','UTF-8',$orderName);
  113. $unified = array(
  114. 'appid' => $config['appid'],
  115. 'attach' => 'pay', //商家数据包,原样返回,如果填写中文,请注意转换为utf-8
  116. 'body' => $subject,//商品名称(商品简单描述)
  117. 'mch_id' => $config['mch_id'],
  118. 'nonce_str' => self::createNonceStr(),//随机字符串,长度要求在32位以内
  119. 'notify_url' => $params['notify_url'],//异步通知地址
  120. 'openid' => $openid, //rade_type=JSAPI,此参数必传
  121. 'out_trade_no' => $params['pay_id'],//唯一标识,订单编号(必须)
  122. 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
  123. 'total_fee' => floatval($params['money']) * 100,//订单金额,单位 转为分
  124. 'trade_type' => 'JSAPI',
  125. );
  126. $unified['sign'] = self::getSign($unified, $config['key']);
  127. $responseXml = self::curl_post($this->gateway.'/unifiedorder', self::arrayToXml($unified));
  128. //禁止引用外部xml实体
  129. libxml_disable_entity_loader(true);
  130. $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
  131. if ($unifiedOrder === false) {
  132. die('parse xml error');
  133. }
  134. if ($unifiedOrder->return_code != 'SUCCESS') {
  135. die($unifiedOrder->return_msg);
  136. }
  137. if ($unifiedOrder->result_code != 'SUCCESS') {
  138. die($unifiedOrder->err_code);
  139. }
  140. $timestamp=time();//付款时间
  141. $arr = array(
  142. "appId" => $config['appid'],
  143. "timeStamp" => "$timestamp",//这里是字符串的时间戳,不是int,所以需加引号
  144. "nonceStr" => self::createNonceStr(),//随机字符串,长度要求在32位以内
  145. "package" => "prepay_id=" . $unifiedOrder->prepay_id,
  146. "signType" => 'MD5',
  147. );
  148. $arr['paySign'] = self::getSign($arr, $config['key']);
  149. return $arr;
  150. }
  151. /**
  152. *
  153. * 3.发起H5支付
  154. * $params 传输的数据
  155. */
  156. public function h5Pay($params){
  157. //商品名称
  158. $subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "购买商品";
  159. $config = array(
  160. 'mch_id' => $this->mchid,
  161. 'appid' => $this->appid,
  162. 'key' => $this->apikey,
  163. );
  164. $scene_info = array(
  165. 'h5_info' =>array(
  166. 'type'=>'Wap',
  167. 'wap_url'=>$this->wapurl,
  168. 'wap_name'=>$this->wapname,
  169. )
  170. );
  171. $unified = array(
  172. 'appid' => $config['appid'],
  173. 'attach' => 'pay',//商家数据包,原样返回,如果填写中文,请注意转换为utf-8
  174. 'body' => $subject,//商品名称(商品简单描述)
  175. 'mch_id' => $config['mch_id'],
  176. 'nonce_str' => self::createNonceStr(),//随机字符串,长度要求在32位以内
  177. 'notify_url' => $params['notify_url'],//异步通知地址
  178. 'out_trade_no' => $params['pay_id'],//唯一标识,订单编号(必须)
  179. 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
  180. 'total_fee' => floatval($params['money']) * 100,//订单金额,单位 转为分
  181. 'trade_type' => 'MWEB',
  182. 'scene_info'=>json_encode($scene_info)
  183. );
  184. $unified['sign'] = self::getSign($unified, $config['key']);
  185. $responseXml = self::curl_post($this->gateway.'/unifiedorder', self::arrayToXml($unified));
  186. $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
  187. if ($unifiedOrder->return_code != 'SUCCESS') {
  188. die($unifiedOrder->return_msg);
  189. }
  190. if($unifiedOrder->mweb_url){
  191. return $unifiedOrder->mweb_url.'&redirect_url='.urlencode($params['return_url']);
  192. }
  193. exit('error');
  194. }
  195. /**
  196. * 异步通知
  197. * @param $data 通知的数据
  198. */
  199. public function notify()
  200. {
  201. $config = array(
  202. 'mch_id' => $this->mchid,
  203. 'appid' => $this->appid,
  204. 'key' => $this->apikey,
  205. );
  206. $postStr = file_get_contents('php://input');
  207. //禁止引用外部xml实体
  208. libxml_disable_entity_loader(true);
  209. $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
  210. if ($postObj === false) {
  211. die('parse xml error');
  212. }
  213. if ($postObj->return_code != 'SUCCESS') {
  214. die($postObj->return_msg);
  215. }
  216. if ($postObj->result_code != 'SUCCESS') {
  217. die($postObj->err_code);
  218. }
  219. $arr = (array)$postObj;
  220. unset($arr['sign']);
  221. if (self::getSign($arr, $config['key']) == $postObj->sign) {
  222. echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
  223. return $arr;
  224. }
  225. }
  226. /**
  227. * 通过跳转获取用户的openid,跳转流程如下:
  228. * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize
  229. * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code
  230. * @return 用户的openid
  231. */
  232. public function getOpenid()
  233. {
  234. //通过code获得openid
  235. if (!isset($_GET['code'])){
  236. //触发返回code码
  237. $scheme = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on' ? 'https://' : 'http://';
  238. $uri = $_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING'];
  239. if($_SERVER['REQUEST_URI']) $uri = $_SERVER['REQUEST_URI'];
  240. $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$uri);
  241. $url = $this->createOauthUrlForCode($baseUrl);
  242. Header("Location: $url");
  243. exit();
  244. } else {
  245. //获取code码,以获取openid
  246. $code = $_GET['code'];
  247. $openid = $this->getOpenidFromMp($code);
  248. return $openid;
  249. }
  250. }
  251. /**
  252. * 通过code从工作平台获取openid机器access_token
  253. * @param string $code 微信跳转回来带上的code
  254. * @return openid
  255. */
  256. public function getOpenidFromMp($code)
  257. {
  258. $url = $this->createOauthUrlForOpenid($code);
  259. $res = self::curl_get($url);
  260. //取出openid
  261. $data = json_decode($res,true);
  262. $this->data = $data;
  263. $openid = $data['openid'];
  264. return $openid;
  265. }
  266. /**
  267. * 构造获取open和access_toke的url地址
  268. * @param string $code,微信跳转带回的code
  269. * @return 请求的url
  270. */
  271. private function createOauthUrlForOpenid($code)
  272. {
  273. $urlObj["appid"] = $this->appid;
  274. $urlObj["secret"] = $this->appkey;
  275. $urlObj["code"] = $code;
  276. $urlObj["grant_type"] = "authorization_code";
  277. $bizString = $this->ToUrlParams($urlObj);
  278. return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
  279. }
  280. /**
  281. * 构造获取code的url连接
  282. * @param string $redirectUrl 微信服务器回跳的url,需要url编码
  283. * @return 返回构造好的url
  284. */
  285. private function createOauthUrlForCode($redirectUrl)
  286. {
  287. $urlObj["appid"] = $this->appid;
  288. $urlObj["redirect_uri"] = "$redirectUrl";
  289. $urlObj["response_type"] = "code";
  290. $urlObj["scope"] = "snsapi_base";
  291. $urlObj["state"] = "STATE"."#wechat_redirect";
  292. $bizString = $this->ToUrlParams($urlObj);
  293. return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
  294. }
  295. protected static function createNonceStr($length = 16)
  296. {
  297. $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  298. $str = '';
  299. for ($i = 0; $i < $length; $i++) {
  300. $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  301. }
  302. return $str;
  303. }
  304. /**
  305. * 拼接签名字符串
  306. * @param array $urlObj
  307. * @return 返回已经拼接好的字符串
  308. */
  309. private function ToUrlParams($urlObj)
  310. {
  311. $buff = "";
  312. foreach ($urlObj as $k => $v)
  313. {
  314. if($k != "sign") $buff .= $k . "=" . $v . "&";
  315. }
  316. $buff = trim($buff, "&");
  317. return $buff;
  318. }
  319. /**
  320. * 获取签名
  321. */
  322. protected static function getSign($params, $key)
  323. {
  324. ksort($params, SORT_STRING);
  325. $unSignParaString = self::formatQueryParaMap($params, false);
  326. $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
  327. return $signStr;
  328. }
  329. protected static function formatQueryParaMap($paraMap, $urlEncode = false)
  330. {
  331. $buff = "";
  332. ksort($paraMap);
  333. foreach ($paraMap as $k => $v) {
  334. if (null != $v && "null" != $v) {
  335. if ($urlEncode) {
  336. $v = urlencode($v);
  337. }
  338. $buff .= $k . "=" . $v . "&";
  339. }
  340. }
  341. $reqPar = '';
  342. if (strlen($buff) > 0) {
  343. $reqPar = substr($buff, 0, strlen($buff) - 1);
  344. }
  345. return $reqPar;
  346. }
  347. protected static function arrayToXml($arr)
  348. {
  349. $xml = "<xml>";
  350. foreach ($arr as $key => $val) {
  351. if (is_numeric($val)) {
  352. $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
  353. } else
  354. $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
  355. }
  356. $xml .= "</xml>";
  357. return $xml;
  358. }
  359. /**
  360. * file_get_contents发送post请求
  361. * @param url 请求地址
  362. * @param postData 要传递的post数据
  363. */
  364. protected function file_post($url, $post_data) {
  365. $postdata = http_build_query($post_data);
  366. $options = array('http' => array('method' => 'POST', 'header' => 'Content-type:application/x-www-form-urlencoded', 'content' => $postdata, 'timeout' => 300
  367. // 超时时间(单位:s)
  368. ));
  369. $context = stream_context_create($options);
  370. $result = file_get_contents($url, false, $context);
  371. //去空格
  372. $result = trim($result);
  373. //转换字符编码
  374. $result = mb_convert_encoding($result, 'utf-8', 'UTF-8,GBK,GB2312,BIG5');
  375. //解决返回的json字符串中返回了BOM头的不可见字符(某些编辑器默认会加上BOM头)
  376. $result = trim($result,chr(239).chr(187).chr(191));
  377. return $result;
  378. }
  379. public static function curl_get($url = '', $options = array())
  380. {
  381. $ch = curl_init($url);
  382. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  383. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  384. if (!empty($options)) {
  385. curl_setopt_array($ch, $options);
  386. }
  387. //https请求 不验证证书和host
  388. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  389. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  390. $data = curl_exec($ch);
  391. curl_close($ch);
  392. return $data;
  393. }
  394. /**
  395. * curl发送post请求
  396. * @param url 请求地址
  397. * @param postData 要传递的post数据
  398. */
  399. protected function curl_post($url = '', $postData = '', $options = array())
  400. {
  401. if (is_array($postData)) {
  402. $postData = http_build_query($postData);
  403. }
  404. $ch = curl_init();
  405. curl_setopt($ch, CURLOPT_URL, $url);
  406. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  407. curl_setopt($ch, CURLOPT_POST, 1);
  408. curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
  409. curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
  410. if (!empty($options)) {
  411. curl_setopt_array($ch, $options);
  412. }
  413. //https请求 不验证证书和host
  414. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  415. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  416. $data = curl_exec($ch);
  417. curl_close($ch);
  418. return $data;
  419. }
  420. /**
  421. * 建立请求,以表单HTML形式构造(默认)
  422. * @param $url 请求地址
  423. * @param $params 请求参数数组
  424. * @return 提交表单HTML文本
  425. */
  426. protected function buildRequestForm($url,$params) {
  427. $sHtml = "正在跳转至支付页面...<form id='alipaysubmit' name='alipaysubmit' action='".$url."?charset=".$this->charset."' method='POST'>";
  428. foreach($params as $key=>$val){
  429. if (false === $this->checkEmpty($val)) {
  430. $val = str_replace("'","&apos;",$val);
  431. $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
  432. }
  433. }
  434. //submit按钮控件请不要含有name属性
  435. $sHtml = $sHtml."<input type='submit' value='ok' style='display:none;''></form>";
  436. $sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
  437. return $sHtml;
  438. }
  439. }
  440. ?>

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/414959
推荐阅读
  

闽ICP备14008679号