NotifyController.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <?php
  2. namespace frontend\controllers;
  3. use common\enums\AliPayPayTypeEnum;
  4. use common\models\common\PaymentTradeLog;
  5. use yii\web\Controller;
  6. use Yii;
  7. use Exception;
  8. use common\helpers\WechatHelper;
  9. use common\helpers\FileHelper;
  10. use common\helpers\ArrayHelper;
  11. use common\enums\PayTypeEnum;
  12. /**
  13. * 支付回调
  14. *
  15. * Class NotifyController
  16. * @package frontend\controllers
  17. */
  18. class NotifyController extends Controller
  19. {
  20. //use PayNotify;
  21. /**
  22. * 关闭csrf
  23. *
  24. * @var bool
  25. */
  26. public $enableCsrfValidation = false;
  27. /**
  28. * 公用支付回调 - 微信
  29. *
  30. * @return bool|string
  31. */
  32. public function actionWechat()
  33. {
  34. try{
  35. $xml = file_get_contents('php://input');
  36. $wechat_post = WechatHelper::xmlToArray($xml);
  37. if(!$wechat_post || !$wechat_post['out_trade_no']){
  38. throw new Exception('参数不合法');
  39. }
  40. $trade = Yii::$app->services->pay->getByTradeNo(trim($wechat_post['out_trade_no']));
  41. if(empty($trade)){
  42. throw new Exception('交易流水号不存在:'.trim($wechat_post['out_trade_no']));
  43. }
  44. $config = Yii::$app->services->pay->getConfig($trade->mall_id,'wechat');
  45. //微信支付配置获取
  46. $wechat = Yii::$app->payment->wechat($config);
  47. $response = $wechat->notify();
  48. if ($response->isPaid()) {
  49. $message = $response->getRequestData();
  50. //日志记录回调文件
  51. FileHelper::writeLog($this->getLogPath('wechat'), json_encode(ArrayHelper::toArray($message)));
  52. ///////////// 建议在这里调用微信的【订单查询】接口查一下该笔订单的情况,确认是已经支付 /////////////
  53. $message['pay_fee'] = bcdiv($message['total_fee'],100,2);
  54. //pay success 注意微信会发二次消息过来 需要判断是通知还是回调
  55. if ($this->pay($message,PayTypeEnum::WECHAT)) {
  56. return WechatHelper::success();
  57. }
  58. throw new Exception('pay error:'.trim($wechat_post['out_trade_no']));
  59. } else {
  60. throw new Exception('response is not Paid:'.trim($wechat_post['out_trade_no']));
  61. }
  62. }catch(Exception $e){
  63. FileHelper::writeLog($this->getLogPath('error'), $e->getMessage());
  64. return WechatHelper::fail();
  65. }
  66. }
  67. /**
  68. * 公用支付回调 - 微信小程序支付回调
  69. * @return bool|string|\Symfony\Component\HttpFoundation\Response
  70. * @throws \EasyWeChat\Kernel\Exceptions\Exception
  71. */
  72. public function actionWechatMp()
  73. {
  74. $xml = file_get_contents('php://input');
  75. $wechat_post = WechatHelper::xmlToArray($xml);
  76. if(!$wechat_post || !$wechat_post['out_trade_no']){
  77. return WechatHelper::fail();
  78. }
  79. $trade = Yii::$app->services->pay->getByTradeNo(trim($wechat_post['out_trade_no']));
  80. if(empty($trade)){
  81. return WechatHelper::fail();
  82. }
  83. // 微信支付参数配置
  84. // 设置appid
  85. $config = Yii::$app->services->pay->getConfig($trade->mall_id,'wechat');
  86. $config['app_id'] = $config['miniprogram_appid'];
  87. Yii::$app->params['wechatPaymentConfig'] = $config;
  88. $response = Yii::$app->wechat->payment->handlePaidNotify(function ($message, $fail) {
  89. $logPath = $this->getLogPath('wechat-mp');
  90. FileHelper::writeLog($logPath, Json::encode(ArrayHelper::toArray($message)));
  91. if ($message['return_code'] === 'SUCCESS') {
  92. $message['pay_fee'] = bcdiv($message['total_fee'],100,2);
  93. if ($this->pay($message,PayTypeEnum::WECHAT)) {
  94. return true;
  95. }
  96. }
  97. return $fail('处理失败,请稍后再通知我');
  98. });
  99. return $response;
  100. }
  101. /**
  102. * 公用支付回调 - 支付宝
  103. *
  104. * @throws Exception
  105. */
  106. public function actionAlipay()
  107. {
  108. $out_trade_no = $_POST['out_trade_no'];
  109. if (!$out_trade_no) {
  110. die('操作失败');
  111. }
  112. $trade = Yii::$app->services->pay->getByTradeNo(trim($out_trade_no));
  113. if(empty($trade)){
  114. die('操作失败');
  115. }
  116. try {
  117. $config = Yii::$app->services->pay->getConfig($trade->mall_id, 'alipay');
  118. $trade_log = PaymentTradeLog::find()->where(['trade_id' => $trade->id,'pay_type' => PayTypeEnum::ALI])->orderBy('id desc')->one();
  119. if($trade_log && $trade_log->trade_type == AliPayPayTypeEnum::JS){
  120. $config = Yii::$app->services->setting->mall->get($trade->mall_id, "alipay_mini");
  121. $config['alipay_app_id'] = $config['app_id'];
  122. $base_path = dirname(Yii::$app->basePath);
  123. $config['alipay_public_key'] = file_get_contents("$base_path/{$config['alipayCertPath']}");
  124. $config['alipay_private_key'] = $config['merchantPrivateKey'];
  125. }
  126. //$config = Yii::$app->services->pay->getConfig($trade->mall_id, 'alipay');
  127. $alipay = Yii::$app->payment->alipay($config);
  128. $request = $alipay->notify();
  129. /** @var \Omnipay\Alipay\Responses\AopCompletePurchaseResponse $response */
  130. $response = $request->send();
  131. if ($response->isPaid()) {
  132. $message = Yii::$app->request->post();
  133. $message['pay_fee'] = $message['total_amount'];
  134. $message['mch_id'] = $message['auth_app_id'];
  135. //支付宝回调内有 out_trade_no 商户订单号
  136. // 日志记录
  137. $logPath = $this->getLogPath('alipay');
  138. FileHelper::writeLog($logPath, json_encode(ArrayHelper::toArray($message)));
  139. if ($this->pay($message, PayTypeEnum::ALI)) {
  140. die('操作成功');
  141. }else{
  142. throw new Exception('alipay pay error:'.$out_trade_no);
  143. }
  144. }
  145. throw new Exception('alipay response isPaid error:'.$out_trade_no);
  146. } catch (\Exception $e) {
  147. // 记录报错日志
  148. FileHelper::writeLog($this->getLogPath('error'), $e->getMessage());
  149. die('操作失败'); // 通知响应
  150. }
  151. }
  152. /**
  153. * 公用支付回调 - 汇聚支付
  154. * @return string
  155. */
  156. public function actionJoinpay()
  157. {
  158. $params = \Yii::$app->request->get();
  159. $params = array_map('urldecode', $params);
  160. try{
  161. $my_param = !empty($params['r5_Mp']) ? json_decode($params['r5_Mp'],true) : []; //json格式
  162. //获取对应支付配置
  163. $mall_id = $my_param['mall_id'] ?? 0;
  164. $config = Yii::$app->services->pay->getConfig($mall_id,'joinpay');
  165. //调用支付类:回调方法
  166. $pay = Yii::$app->payment->joinpay($config);
  167. $message = $pay->notify();
  168. if ($this->pay($message,PayTypeEnum::JOINPAY)) {
  169. die('操作成功');
  170. }
  171. die('操作失败');
  172. }catch(\Exception $e){
  173. // 记录报错日志
  174. $logPath = $this->getLogPath('error');
  175. FileHelper::writeLog($logPath, $e->getMessage());
  176. die('操作失败'); // 通知响应
  177. }
  178. }
  179. /**
  180. * 公用支付回调 - 汇聚快捷支付
  181. * @return string
  182. */
  183. public function actionJoinpayFastPay()
  184. {
  185. try{
  186. $post = \Yii::$app->request->post();
  187. if(empty($post) || empty($post['data'])){
  188. die('操作失败'); // 通知响应
  189. }
  190. $data = json_decode($post['data'],true);
  191. $callback_param = json_decode($data['callback_param'],true);
  192. $mall_id = $callback_param['mall_id'] ?? 0;
  193. //获取对应支付配置
  194. $config = Yii::$app->services->pay->getConfig($mall_id,'joinpay_fast');
  195. $pay = Yii::$app->payment->joinpayFastPay($config);
  196. $message = $pay->notify($post);
  197. if ($this->pay($message,PayTypeEnum::JOINPAY_FAST_PAY)) {
  198. die('操作成功');
  199. }
  200. die('操作失败');
  201. }catch(\Exception $e){
  202. // 记录报错日志
  203. $logPath = $this->getLogPath('error');
  204. FileHelper::writeLog($logPath, $e->getMessage());
  205. die('操作失败'); // 通知响应
  206. }
  207. }
  208. /**
  209. * 支付公用方法
  210. * @param $message
  211. * @param int $pay_type 支付类型
  212. * @return bool
  213. */
  214. protected function pay($message,$pay_type)
  215. {
  216. $transaction = Yii::$app->db->beginTransaction();
  217. try {
  218. if (!($payLog = Yii::$app->services->pay->getByTradeNo($message['out_trade_no']))) {
  219. throw new Exception('找不到支付信息');
  220. }
  221. // 支付完成
  222. if ($payLog->is_pay == 1) {
  223. return true;
  224. }
  225. //支付金额验证
  226. if(bccomp($message['pay_fee'],$payLog->pay_fee) !== 0){
  227. throw new Exception('支付金额不一致');
  228. }
  229. $payLog->pay_type = $pay_type;
  230. $payLog->is_pay = 1;
  231. $payLog->pay_time = time();
  232. $payLog->third_trade_no = $message['trade_no'] ?? '';
  233. if (!$payLog->save()) {
  234. throw new Exception('日志修改失败');
  235. }
  236. // 业务回调
  237. $orders = Yii::$app->services->pay->changePaymentTradeOrder($payLog,$pay_type);
  238. $transaction->commit();
  239. Yii::$app->services->pay->notify($orders);
  240. return true;
  241. } catch (\Exception $e) {
  242. $transaction->rollBack();
  243. //FileHelper::writeLog($this->getLogPath('error'), $e->getMessage());
  244. // 记录报错日志
  245. FileHelper::writeLog($this->getLogPath('error'), json_encode(ArrayHelper::toArray([
  246. 'message' => $e->getMessage(),
  247. 'line' => $e->getLine(),
  248. 'file' => method_exists($e, 'getFile') ? $e->getFile() : '',
  249. 'trace' => $e->getTrace(),
  250. 'code' => $e->getCode(),
  251. ])));
  252. return false;
  253. }
  254. }
  255. /**
  256. * @param $type
  257. * @return string
  258. */
  259. protected function getLogPath($type)
  260. {
  261. return Yii::getAlias('@runtime') . "/pay-logs/" . date('Y_m_d') . '/' . $type . '.txt';
  262. }
  263. }