WechatPay.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <?php
  2. namespace app\expand\controller;
  3. use app\common\service\HelperService;
  4. use app\index\service\wechat\JsApi_pub;
  5. use app\index\service\wechat\UnifiedOrder_pub;
  6. use app\index\service\wechat\Refund_pub;
  7. use think\Config;
  8. use think\Validate;
  9. use think\Log;
  10. /**
  11. * 微信支付接口
  12. * Class WeChat
  13. * @package app\expand\controller
  14. */
  15. class WechatPay extends BaseAuth
  16. {
  17. private $_Account = null;
  18. private $_APPID = null;
  19. private $_APPSECRET = null;
  20. public function __construct(){
  21. parent::__construct();
  22. $this->getFileContext();
  23. /**
  24. * 当且仅当微信异步回调地址发起请求的的时候,放他通过
  25. */
  26. if($this->_inWhiteList){
  27. return true;
  28. }
  29. $this->_Account = $this->getKey($this->_apiCode);
  30. //验证是否具有访问这个接口的权限
  31. if(!isset($this->_Account['Wechat_pay_appId'])
  32. || !isset($this->_Account['Wechat_pay_appsecret'])
  33. || !isset($this->_Account['Wechat_pay_key'])
  34. || !isset($this->_Account['Wechat_pay_mchId'])
  35. || !isset($this->_Account['apiclientCert'])
  36. || !isset($this->_Account['apiclientKey'])
  37. ){
  38. HelperService::returnJson(['code'=>400,'msg'=>'WECHAT pay interface unauthorized access','data'=>$this->_Account]);
  39. }
  40. $this->_APPID = $this->_Account['Wechat_pay_appId'];
  41. $this->_APPSECRET = $this->_Account['Wechat_pay_appsecret'];
  42. $this->_apiclientCert = $this->_Account['apiclientCert'];
  43. $this->_apiclientKey = $this->_Account['apiclientKey'];
  44. $WechatPayTip = isset($this->_Account['Wechat_pay_tip'])
  45. ?$this->_Account['Wechat_pay_tip']:"";
  46. $WechatPaySubMchId = isset($this->_Account['Wechat_pay_sub_mchId'])
  47. ?$this->_Account['Wechat_pay_sub_mchId']:"";
  48. Config::set('WECHAT_APPID',$this->_APPID);
  49. Config::set('WECHAT_APPSECRET',$this->_APPSECRET);
  50. Config::set('WECHAT_CERT',$this->_apiclientCert);
  51. Config::set('WECHAT_KEY',$this->_apiclientKey);
  52. Config::set('WECHAT_TIP',$WechatPayTip);
  53. Config::set('WECHAT_MCHID',$this->_Account['Wechat_pay_mchId']);
  54. Config::set('WECHAT_SUB_MCHID',$WechatPaySubMchId);
  55. Config::set('WECHAT_PAY_KEY',$this->_Account['Wechat_pay_key']);
  56. }
  57. /**
  58. * 平台版支付接口
  59. */
  60. public function platformWxPay(){
  61. $param = $this->_sysParams;
  62. $rule = [
  63. 'pay_notify_url|支付通知跳转'=>'max:1000',
  64. 'order_no|订单编号'=>'require',
  65. 'total_price|总金额'=>'require'
  66. ];
  67. // Log::record("platformWxPay:".$param);
  68. $validate = new Validate($rule);
  69. if(!$validate->check($param)){
  70. HelperService::returnJson(['code'=>400,'msg'=>$validate->getError(),'data'=>[]]);
  71. }
  72. session('pay_notify_url',$param['pay_notify_url']);
  73. session('order_no',$param['order_no']);
  74. session('total_price',$param['total_price']);
  75. require_once(APP_PATH.'/common/service/wechat/WxPayPubHelper.php');
  76. //使用jsapi接口
  77. $jsApi = new JsApi_pub();
  78. //通过code获得openid,通知的接口
  79. $httpFix = HelperService::getHttpHeader();
  80. $url = $jsApi->createOauthOpenidForCode("{$httpFix}{$_SERVER['HTTP_HOST']}/v1/getOpenId2Pay/apiCode/".$this->_apiCode, rand(1000,9999));
  81. HelperService::addLog([
  82. 'data'=>$param,
  83. 'url'=>$url,
  84. ]);
  85. header("Location: $url");
  86. exit;
  87. }
  88. /**
  89. * 支付获取openid之后跳转的地址
  90. */
  91. public function getOpenId2Pay(){
  92. //使用jsapi接口
  93. require_once(APP_PATH.'/common/service/wechat/WxPayPubHelper.php');
  94. $jsApi = new JsApi_pub();
  95. $code = $this->_sysParams['code'];
  96. $this->_apiCode = $this->_sysParams['apiCode'];
  97. $account = $this->getKey($this->_apiCode, false);
  98. Config::set('WECHAT_APPID',$account['Wechat_pay_appId']);
  99. Config::set('WECHAT_APPSECRET',$account['Wechat_pay_appsecret']);
  100. Config::set('WECHAT_MCHID',$account['Wechat_pay_mchId']);
  101. Config::set('WECHAT_PAY_KEY',$account['Wechat_pay_key']);
  102. $jsApi->setCode($code);
  103. $openInfo = $jsApi->getOpenidInfo();
  104. HelperService::addLog([
  105. 'openInfo'=>$openInfo,
  106. 'code'=>$code,
  107. ]);
  108. if(!isset($openInfo['openid'])){
  109. $notify_url = base64_decode(session('pay_notify_url'));
  110. header("HTTP/1.1 301 Moved Permanently");
  111. header("Location: $notify_url");
  112. exit;
  113. }
  114. $openid = $openInfo['openid'];
  115. $unifiedOrder = new UnifiedOrder_pub();
  116. $money = intval(session('total_price'));
  117. if($money<=0){
  118. header("HTTP/1.1 500 支付调用失败(支付金额为0)");
  119. exit;
  120. }
  121. //设置统一支付接口参数
  122. $unifiedOrder->setParameter("openid", "$openid"); //商品描述
  123. $unifiedOrder->setParameter("body",Config::get('WECHAT_TIP')."[".date('Y-m-d H:i:s')."]"); //商品描述
  124. //自定义订单号,此处仅作举例
  125. $out_trade_no = $this->_apiCode."_".session('order_no');
  126. $unifiedOrder->setParameter("out_trade_no", $out_trade_no); //商户订单号
  127. $unifiedOrder->setParameter("total_fee", $money); //总金额
  128. $httpFix = HelperService::getHttpHeader();
  129. $unifiedOrder->setParameter("notify_url", "{$httpFix}{$_SERVER['HTTP_HOST']}/v1/platformNotifyUrl.html"); //通知地址
  130. $unifiedOrder->setParameter("trade_type", "JSAPI"); //交易类型
  131. $prepay_id = $unifiedOrder->getPrepayId($out_trade_no."_".time());
  132. $jsApi->setPrepayId($prepay_id);
  133. $jsApiParameters = $jsApi->getParameters();
  134. $notify_url = session('pay_notify_url')?base64_decode(session('pay_notify_url')):"/v1/paySuccess";
  135. HelperService::addLog([
  136. 'data'=>$prepay_id,
  137. 'url'=>$notify_url,
  138. ]);
  139. require_once(APP_PATH.'/expand/view/wechat/wxPlatformPay.php');
  140. }
  141. public function paySuccess(){
  142. return $this->fetch('wechat/paySuccess');
  143. }
  144. /**
  145. * 支付异步通知接口
  146. */
  147. public function platformNotifyUrl(){
  148. $this->getInput();
  149. $param = $this->_oldParams;
  150. $params = HelperService::object2Arr(simplexml_load_string($param, 'SimpleXMLElement', LIBXML_NOCDATA));
  151. if(empty($params)){
  152. HelperService::returnJson(['code' => 400, 'msg' => "参数错误"]);
  153. }
  154. try {
  155. $arr = ['total_fee', 'out_trade_no'];
  156. foreach ($arr as $key=>$value) {
  157. if (!isset($params[$value])) {
  158. HelperService::returnJson(['code' => 400, 'msg' => "参数错误({$key})",'params'=>$params]);
  159. }
  160. }
  161. $tradeNoArr = explode('_', $params['out_trade_no']);
  162. if(count($tradeNoArr)<2){
  163. HelperService::returnJson(['code' => 400, 'msg' => "参数错误(trade)"],$param);
  164. }
  165. $companyCode = $tradeNoArr[0];
  166. $this->_Account = $this->getKey($companyCode,false);
  167. $orderNo = isset($tradeNoArr[1]) ? $tradeNoArr[1] : 0;
  168. //开发分发给不同的支付主体
  169. if(!isset($this->_Account['Wechat_pay_notify_url'])
  170. || empty($this->_Account['Wechat_pay_notify_url'])){
  171. HelperService::returnJson(['code' => 400, 'msg' => "回调地址不存在"],$param);
  172. }
  173. if(!is_array($this->_Account['Wechat_pay_notify_url'])){
  174. HelperService::returnJson(['code' => 400, 'msg' => "回调地址不是数组"],$param);
  175. }
  176. $notifyUrlArr = $this->_Account['Wechat_pay_notify_url'];
  177. foreach($notifyUrlArr as $url){
  178. $is_ssl = strpos($url, 'https://')!==false?true:false;
  179. $data = [
  180. 'order_no'=>$orderNo,
  181. 'total_price'=>"{$params['total_fee']}",
  182. 'out_trade_no'=>"{$params['out_trade_no']}"
  183. ];
  184. $times = 3;
  185. while($times--){
  186. $res = HelperService::httpPost($url,json_encode($data),$is_ssl);
  187. HelperService::addLog(['item'=>$url,'data'=>$data,'result'=>$res]);
  188. if($res != false){
  189. break;
  190. }
  191. }
  192. if($times ==0){
  193. Log::record("支付推送异常=>url:".$url."=>res:". json_encode($res)."=>data:". json_encode($data));
  194. }
  195. }
  196. HelperService::addLog(['ret'=>'success']);
  197. die('success');
  198. }catch (\Exception $ex){
  199. HelperService::addLog(['msg'=>$ex->getMessage(),"file"=>$ex->getFile(),"line"=>$ex->getLine()]);
  200. die('fail');
  201. }
  202. }
  203. /**
  204. * 平台版退款接口
  205. */
  206. public function platformRefund(){
  207. $params = $this->_params;
  208. $rule = [
  209. 'out_trade_no'=>'require',
  210. 'total_fee'=>'require',
  211. 'refund_fee'=>'require',
  212. 'op_user_id'=>'require'
  213. ];
  214. $validate = new Validate($rule);
  215. if(!$validate->check($params)){
  216. HelperService::returnJson(['code'=>400,'msg'=>$validate->getError()],$params);
  217. }
  218. require_once(APP_PATH.'/index/service/wechat/WxPayPubHelper.php');
  219. $Refund = new Refund_pub();
  220. $Refund->setParameter('out_trade_no',$params['out_trade_no']);
  221. $Refund->setParameter('out_refund_no',uniqid());
  222. $Refund->setParameter('total_fee',$params['total_fee']);
  223. $Refund->setParameter('refund_fee',$params['refund_fee']);
  224. $Refund->setParameter('op_user_id',$params['op_user_id']);
  225. $data = $Refund->getResult();
  226. $msg = isset($data['result_code'])?$data['result_code']:'fail';
  227. HelperService::returnJson(['code'=>200,'msg'=>"{$msg}",'data'=>$data]);
  228. }
  229. }