AlipayTradeWapService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace service;
  12. use app\wap\model\activity\EventSignUp;
  13. use app\wap\model\store\StoreOrder;
  14. use think\Log;
  15. use think\Request;
  16. use think\Url;
  17. use behavior\wechat\PaymentBehavior;
  18. use service\HookService;
  19. use service\SystemConfigService;
  20. use app\wap\model\topic\TestPaperOrder;
  21. use app\wap\model\material\DataDownloadOrder;
  22. use app\wap\model\user\UserRecharge;
  23. class AlipayTradeWapService
  24. {
  25. /**
  26. * 异步通知地址
  27. * @var string
  28. */
  29. protected static $notifyUrl;
  30. /**
  31. * 同步跳转地址
  32. * @var mixed
  33. */
  34. protected static $returnUrl;
  35. /**
  36. * 支付宝公钥
  37. * @var mixed
  38. */
  39. protected static $alipayPublicKey;
  40. /**
  41. * 应用appid
  42. * @var mixed
  43. */
  44. protected static $alipayAppId;
  45. /**
  46. * 应用私钥
  47. * @var mixed
  48. */
  49. protected static $alipayPrivateKey;
  50. /**
  51. * 编码格式
  52. * @var mixed|string
  53. */
  54. protected static $charset = 'UTF-8';
  55. /**
  56. * 请求网管
  57. * @var string
  58. */
  59. protected static $gatewayUrl = 'https://openapi.alipay.com/gateway.do';
  60. /**
  61. * 加密方式
  62. * @var mixed|string
  63. */
  64. protected static $signType = 'RSA2';
  65. /**
  66. * 是否开启调试模式
  67. * @var bool
  68. */
  69. public static $isDeBug = true;
  70. /**
  71. * 获取不到配置信息错误次数
  72. * @var int
  73. */
  74. private static $ErrorCount = 0;
  75. /**
  76. * 获取不到配置信息错误最大次数
  77. * @var int
  78. */
  79. private static $ErrorSum = 3;
  80. /**
  81. * AlipayTradeWapService constructor.
  82. * @param array $confing
  83. * @throws \Exception
  84. */
  85. public function __construct($confing = [])
  86. {
  87. self::$ErrorCount++;
  88. if (self::$ErrorCount >= self::$ErrorSum) return exception('请配置支付宝公钥私钥APPID');
  89. if ((!self::$alipayAppId || !self::$alipayPublicKey || !self::$alipayPrivateKey) && !$confing) self::confing(true);
  90. if (isset($confing['returnUrl'])) self::$returnUrl = $confing['returnUrl'];
  91. if (isset($confing['notifyUrl'])) self::$returnUrl = $confing['notifyUrl'];
  92. if (isset($confing['signType'])) self::$signType = $confing['signType'];
  93. if (isset($confing['charset'])) self::$charset = $confing['charset'];
  94. if (isset($confing['alipay_app_id'])) self::$alipayAppId = $confing['alipay_app_id'];
  95. if (isset($confing['alipay_public_key'])) self::$alipayPublicKey = $confing['alipay_public_key'];
  96. if (isset($confing['alipay_private_key'])) self::$alipayPrivateKey = $confing['alipay_private_key'];
  97. if (!self::$alipayAppId || !self::$alipayPublicKey || !self::$alipayPrivateKey) exception('请配置支付宝公钥私钥APPID');
  98. self::$ErrorCount = 0;
  99. }
  100. /**
  101. * 设置加密方式
  102. * @param $signType
  103. * @return $this
  104. */
  105. public function setSignType($signType)
  106. {
  107. self::$signType = $signType;
  108. return $this;
  109. }
  110. /**
  111. * 设置同步回调地址
  112. * @param $returnUrl
  113. * @return $this
  114. */
  115. public function setReturnUrl($returnUrl)
  116. {
  117. self::$returnUrl = $returnUrl;
  118. return $this;
  119. }
  120. /**
  121. * 设置异步回调地址
  122. * @param $notifyUrl
  123. */
  124. public function setNotifyUrl($notifyUrl)
  125. {
  126. self::$notifyUrl = $notifyUrl;
  127. return $this;
  128. }
  129. /**
  130. * 设置业务参数
  131. * @param array $biz_content
  132. * @return string
  133. */
  134. protected static function setBizContent(array $biz_content = [])
  135. {
  136. if (isset($biz_content['passback_params'])) $biz_content['passback_params'] = urlencode($biz_content['passback_params']);
  137. if (isset($biz_content['trade_no']) && empty($biz_content['trade_no'])) unset($biz_content['trade_no']);
  138. $bizContent = json_encode($biz_content);
  139. //打印业务参数
  140. self::$isDeBug && self::WriteLog($bizContent);
  141. return $bizContent;
  142. }
  143. /**
  144. * 获取同步回调地址
  145. * @return mixed
  146. */
  147. public function getReturnUrl()
  148. {
  149. return self::$returnUrl;
  150. }
  151. /**
  152. * 获取异步回调地址
  153. * @return mixed
  154. */
  155. public function getNotifyUrl()
  156. {
  157. return self::$notifyUrl;
  158. }
  159. /**
  160. * 读取系统配置赋值给静态变量 并加载支付宝官方支付sdk
  161. * @param bool $isReturn
  162. * @return AlipayTradeWapService
  163. */
  164. public static function confing($isReturn = false)
  165. {
  166. $confing = SystemConfigService::more([
  167. 'alipay_public_key',
  168. 'alipay_app_id',
  169. 'alipay_private_key',
  170. ]);
  171. self::$alipayAppId = isset($confing['alipay_app_id']) ? trim($confing['alipay_app_id']) : '';
  172. self::$alipayPublicKey = isset($confing['alipay_public_key']) ? trim($confing['alipay_public_key']) : '';
  173. self::$alipayPrivateKey = isset($confing['alipay_private_key']) ? trim($confing['alipay_private_key']) : '';
  174. self::$returnUrl = SystemConfigService::get('site_url') . Url::build('wap/alipay/alipay_success_synchro');
  175. self::$notifyUrl = SystemConfigService::get('site_url') . Url::build('wap/alipay/alipay_success_notify');
  176. vendor('alipay.AopSdk');
  177. if ($isReturn == false) return new self;
  178. }
  179. /**
  180. * 静态调用初始化数据
  181. * @return AlipayTradeWapService
  182. */
  183. public static function init()
  184. {
  185. return self::confing();
  186. }
  187. /**
  188. * 支付宝异步回调
  189. */
  190. public static function handleNotify()
  191. {
  192. self::init()->AliPayNotify(function ($data, $result) {
  193. if ($result && isset($data->out_trade_no) && $data->passback_params) {
  194. if ($data->passback_params == 'special') {
  195. StoreOrder::where('order_id', $data->out_trade_no)->where('type', 0)->update(['trade_no' => $data->trade_no]);
  196. } elseif ($data->passback_params == 'signup') {
  197. EventSignUp::where('order_id', $data->out_trade_no)->update(['trade_no' => $data->trade_no]);
  198. } elseif ($data->passback_params == 'member') {
  199. StoreOrder::where('order_id', $data->out_trade_no)->where('type', 1)->update(['trade_no' => $data->trade_no]);
  200. } elseif ($data->passback_params == 'goods') {
  201. StoreOrder::where('order_id', $data->out_trade_no)->where('type', 2)->update(['trade_no' => $data->trade_no]);
  202. } elseif ($data->passback_params == 'testpaper') {
  203. TestPaperOrder::where('order_id', $data->out_trade_no)->update(['trade_no' => $data->trade_no]);
  204. } elseif ($data->passback_params == 'datadownload') {
  205. DataDownloadOrder::where('order_id', $data->out_trade_no)->update(['trade_no' => $data->trade_no]);
  206. } elseif ($data->passback_params == 'recharge') {
  207. UserRecharge::where('order_id', $data->out_trade_no)->update(['trade_no' => $data->trade_no]);
  208. }
  209. HookService::listen('wechat_pay_success', $data, null, true, PaymentBehavior::class);
  210. }
  211. });
  212. }
  213. /**
  214. * 支付宝异步回调
  215. * @param callable $notifyFn 闭包函数 参数1,回调返回的参数,回调结果
  216. * @return bool
  217. */
  218. protected function AliPayNotify(callable $notifyFn)
  219. {
  220. $post = Request::instance()->post();
  221. $result = self::AliPaycheck($post);
  222. if ($result) {
  223. //商户订单号
  224. $post['out_trade_no'] = isset($post['out_trade_no']) ? $post['out_trade_no'] : '';
  225. //支付宝交易号
  226. $post['trade_no'] = isset($post['trade_no']) ? $post['trade_no'] : '';
  227. //交易状态
  228. $post['trade_status'] = isset($post['trade_status']) ? $post['trade_status'] : '';
  229. //备注
  230. $post['attach'] = isset($post['passback_params']) ? urldecode($post['passback_params']) : '';
  231. //异步回调成功执行
  232. try {
  233. if (is_callable($notifyFn)) $notifyFn((object)$post, $result);
  234. } catch (\Exception $e) {
  235. self::$isDeBug && self::WriteLog('支付宝支付成功,订单号为:' . $post['out_trade_no'] . '.回调报错:' . $e->getMessage());
  236. }
  237. echo 'success';
  238. } else {
  239. echo 'fail';
  240. }
  241. self::$isDeBug && self::WriteLog($result);
  242. return true;
  243. }
  244. /**
  245. * 支付宝同步回调
  246. * @return array
  247. */
  248. public function AliPayReturn()
  249. {
  250. //获取返回参数
  251. $get = Request::instance()->get();
  252. //验签成功与否
  253. $result = self::AliPaycheck($get);
  254. //记录日志
  255. self::$isDeBug && self::WriteLog(compact('result', 'get'));
  256. return compact('result', 'get');
  257. }
  258. /**
  259. * 验签方法
  260. * @param $arr 验签支付宝返回的信息,使用支付宝公钥。
  261. * @return boolean
  262. */
  263. protected static function AliPaycheck($post)
  264. {
  265. $aop = new \AopClient();
  266. $aop->alipayrsaPublicKey = self::$alipayPublicKey;
  267. return $aop->rsaCheckV1($post, self::$alipayPrivateKey, self::$signType);
  268. }
  269. /**
  270. * 初始化参数
  271. * @param $request
  272. * @param bool $ispage
  273. * @return mixed|\SimpleXMLElement|string|\提交表单HTML文本
  274. * @throws \Exception
  275. */
  276. protected static function AopclientRequestExecute($request, $ispage = false)
  277. {
  278. $aop = new \AopClient ();
  279. //网管地址
  280. $aop->gatewayUrl = self::$gatewayUrl;
  281. //appid
  282. $aop->appId = self::$alipayAppId;
  283. //私钥
  284. $aop->rsaPrivateKey = self::$alipayPrivateKey;
  285. //公钥
  286. $aop->alipayrsaPublicKey = self::$alipayPublicKey;
  287. //版本
  288. $aop->apiVersion = "1.0";
  289. //编码格式
  290. $aop->postCharset = self::$charset;
  291. //内容格式
  292. $aop->format = 'json';
  293. //加密方式
  294. $aop->signType = self::$signType;
  295. // 开启页面信息输出
  296. $aop->debugInfo = true;
  297. if ($ispage) {
  298. $result = $aop->pageExecute($request, "post");
  299. echo $result;
  300. } else
  301. $result = $aop->Execute($request);
  302. //打开后,将报文写入log文件
  303. self::$isDeBug && self::WriteLog($result);
  304. return $result;
  305. }
  306. /**
  307. * alipay.trade.wap.pay 下单支付手机网站支付版本
  308. * @param $out_trade_no 下单号
  309. * @param $total_amount 订单金额 单位元
  310. * @param $subject 订单标题
  311. * @param $passback_params 订单备注 会原样返回通常用于回调监听函数
  312. * @param $product_code 销售产品码,商家和支付宝签约的产品码
  313. * @param $ispage 是否直接输出
  314. * @return $response 支付宝返回的信息
  315. */
  316. public function AliPayWap($out_trade_no, $total_amount, $subject, $passback_params, $product_code = 'QUICK_MSECURITY_PAY', $ispage = true)
  317. {
  318. $request = new \AlipayTradeWapPayRequest();
  319. $request->setNotifyUrl(self::$notifyUrl);
  320. $request->setReturnUrl(self::$returnUrl);
  321. $request->setBizContent(self::setBizContent(compact('out_trade_no', 'total_amount', 'subject', 'passback_params', 'product_code')));
  322. return self::AopclientRequestExecute($request, $ispage);
  323. }
  324. /**支付宝 当面付
  325. * @param $out_trade_no
  326. * @param $total_amount
  327. * @param $subject
  328. * @param $passback_params
  329. * @param string $product_code
  330. * @param bool $ispage
  331. * @return mixed|\SimpleXMLElement|string|\提交表单HTML文本
  332. * @throws \Exception
  333. */
  334. public function AliPayNative($out_trade_no, $total_amount, $subject, $passback_params, $product_code = 'FACE_TO_FACE_PAYMENT', $ispage = false)
  335. {
  336. $request = new \AlipayTradePrecreateRequest();
  337. $request->setNotifyUrl(self::$notifyUrl);
  338. $request->setReturnUrl(self::$returnUrl);
  339. $request->setBizContent(self::setBizContent(compact('out_trade_no', 'total_amount', 'subject', 'passback_params', 'product_code')));
  340. $result = self::AopclientRequestExecute($request, $ispage);
  341. $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
  342. $resultCode = $result->$responseNode->code;
  343. $qr_code_url = $result->$responseNode->qr_code;
  344. if (!empty($resultCode) && $resultCode == 10000) {
  345. return $qr_code_url;
  346. } else {
  347. return '';
  348. }
  349. }
  350. /**
  351. * alipay.trade.query (统一收单线下交易查询)
  352. * @param $out_trade_no 下单号
  353. * @param $trade_no 支付宝订单号
  354. * @param $passback_params 订单备注 会原样返回通常用于回调监听函数
  355. * @return $response 支付宝返回的信息
  356. */
  357. public function AliPayQuery($out_trade_no, $trade_no, $passback_params)
  358. {
  359. $request = new \AlipayTradeQueryRequest();
  360. $request->setBizContent(self::setBizContent(compact('out_trade_no', 'passback_params', 'trade_no')));
  361. return self::AopclientRequestExecute($request);
  362. }
  363. /**
  364. * alipay.trade.refund (统一收单交易退款接口)
  365. * @param $out_trade_no 下单订单号
  366. * @param $trade_no 支付宝订单号
  367. * @param $refund_amount 退款金额
  368. * @param $refund_reason 退款说明
  369. * @param $passback_params 备注
  370. * @return $response 支付宝返回的信息
  371. */
  372. public function AliPayRefund($out_trade_no, $trade_no, $refund_amount, $refund_reason, $passback_params)
  373. {
  374. $request = new \AlipayTradeRefundRequest();
  375. $request->setBizContent(self::setBizContent(compact('out_trade_no', 'trade_no', 'refund_amount', 'refund_reason', 'passback_params')));
  376. $result = self::AopclientRequestExecute($request);
  377. $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
  378. $resultCode = $result->$responseNode->code;
  379. return $resultCode;
  380. }
  381. /**
  382. * alipay.trade.close (统一收单交易关闭接口)
  383. * @param $out_trade_no 订单号
  384. * @param $trade_no 支付宝订单号
  385. * @return $response 支付宝返回的信息
  386. */
  387. public function AliPayClose($out_trade_no, $trade_no)
  388. {
  389. $request = new \AlipayTradeCloseRequest();
  390. $request->setBizContent(self::setBizContent(compact('out_trade_no', 'trade_no')));
  391. return self::AopclientRequestExecute($request);
  392. }
  393. /**
  394. * 写入日志
  395. * @param $content string | array | object
  396. * @return boolen
  397. * */
  398. public static function WriteLog($content)
  399. {
  400. try {
  401. Log::init([
  402. 'type' => 'File',
  403. 'path' => LOG_PATH . 'alipay/'
  404. ]);
  405. if (is_array($content)) $content = 'response: ' . var_export($content, true);
  406. if (is_object($content)) $content = 'response: ' . var_export($content, true);
  407. Log::write(date('Y-m-d H:i:s', time()) . ' ' . $content);
  408. } catch (\Exception $e) {
  409. }
  410. }
  411. }