PayCallback.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <?php
  2. namespace app\common\logic;
  3. use app\api\service\SpecialService;
  4. use GuzzleHttp\Client;
  5. use GuzzleHttp\Exception\GuzzleException;
  6. use think\Env;
  7. use think\Exception;
  8. use think\Log;
  9. use think\Queue;
  10. class PayCallback
  11. {
  12. public $guzzle;
  13. private $_error;
  14. public function __construct()
  15. {
  16. $this->guzzle = new Client();
  17. }
  18. /**
  19. * 订单回调处理
  20. *
  21. * @param $orderId
  22. * @return bool
  23. * @throws \think\db\exception\DataNotFoundException
  24. * @throws \think\db\exception\ModelNotFoundException
  25. * @throws \think\exception\DbException
  26. */
  27. public function callBackToCp($orderId)
  28. {
  29. $where = [];
  30. $where['orderid'] = $orderId;
  31. $where['status'] = 0;
  32. $where['payflag'] = 1;
  33. $payCpInfoModel = model('PayCpinfo');
  34. $cpInfo = $payCpInfoModel->where($where)->find();
  35. if (!$cpInfo) {
  36. $this->setError('订单不存在,或订单已通知成功');
  37. return false;
  38. }
  39. $callback_url = $cpInfo['fcallbackurl']; // 回调地址
  40. $callback_params = $cpInfo['params']; // 通知参数
  41. // php自身做回调通知
  42. $result = $this->_doCallback($callback_url, $callback_params);
  43. $msg = 'orderid:' . $orderId . ' - url:' . $callback_url . ' - params:' . json_encode($callback_params).' - result: '.$result;
  44. if (!$result) {
  45. //发起异步通知
  46. $this->_asyncNotify($callback_url, $callback_params, $orderId);
  47. $log_msg = "[CP同步回调-ERROR] " . $msg;
  48. log_message($log_msg, 'error', LOG_PATH . 'callBacklog/');
  49. $this->setError('同步回调通知失败');
  50. $template = "厂商回调失败,订单号:" . $orderId;
  51. $ddurl = Env::get('dingtalk.warning_url');
  52. curlDD($template, $ddurl, true);
  53. return false;
  54. }
  55. $log_msg = "[CP同步回调-SUCCESS] " . $msg;
  56. log_message($log_msg, 'log', LOG_PATH . 'callBacklog/');
  57. if (!$res = $payCpInfoModel->save(['status' => 1, 'update_time' => time()], ['id' => $cpInfo['id']])) {
  58. return false;
  59. }
  60. // $this->specialCallback($callback_params);
  61. return true;
  62. }
  63. private function _doCallback($callback_url, $callback_params, $retry_times = 1, $sleep = 5)
  64. {
  65. $times = 0;
  66. // 将字符串参数转为数组形式
  67. if (!is_array($callback_params)) {
  68. parse_str($callback_params, $callback_params);
  69. }
  70. while ($times < $retry_times) {
  71. $callback_result = $this->_doPost($callback_url, $callback_params);
  72. if (0 == strcasecmp($callback_result, 'success')) {
  73. return true;
  74. }
  75. $times++;
  76. //sleep($sleep);
  77. }
  78. }
  79. private function _doPost($url, $param)
  80. {
  81. try {
  82. $result = $this->guzzle->request('post', $url, ['form_params' => $param, 'timeout' => 20]);
  83. } catch (GuzzleException $exception) {
  84. //todo 错误信息记录
  85. $msg = 'GuzzleExceptiont同步回调通知CP,url地址为:' . $url . '参数:' . json_encode($param) . ' exception:' . $exception->getMessage();
  86. //Log::info($msg);
  87. log_message($msg, 'log', LOG_PATH . 'callBacklog/');
  88. return false;
  89. }
  90. return $result->getBody();
  91. }
  92. public function setError($msg)
  93. {
  94. $this->_error = $msg;
  95. }
  96. public function getError()
  97. {
  98. return $this->_error;
  99. }
  100. /**
  101. * 异步通知
  102. * @param $callback_url string
  103. * @param $post_data 要post的数据
  104. */
  105. private function _asyncNotify($callback_url, $post_data, $orderId)
  106. {
  107. $task = [];
  108. $task['url'] = $callback_url;
  109. $task['http_method'] = 'post';
  110. $task['content_type'] = 'query_string';
  111. $task['params'] = $post_data;
  112. $task['orderid'] = $orderId;
  113. $task['success_flag'] = 'success'; // 回调CP,收到什么样的结果,视为成功
  114. // 1.当前任务将由哪个类来负责处理。
  115. // 当轮到该任务时,系统将生成一个该类的实例,并调用其 fire 方法
  116. $jobHandlerClassName = 'app\crontab\controller\asyncCallback';
  117. // 2.当前任务归属的队列名称,如果为新队列,会自动创建
  118. $jobQueueName = "asyncCallback";
  119. // 3.当前任务所需的业务数据 . 不能为 resource 类型,其他类型最终将转化为json形式的字符串
  120. // ( jobData 为对象时,需要在先在此处手动序列化,否则只存储其public属性的键值对)
  121. // 4.将该任务推送到消息队列,等待对应的消费者去执行
  122. $isPushed = Queue::push($jobHandlerClassName, $task, $jobQueueName);
  123. // database 驱动时,返回值为 1|false ; redis 驱动时,返回值为 随机字符串|false
  124. if ($isPushed !== false) {
  125. //Log::info(date('Y-m-d H:i:s') . " a new _asyncNotify Job is Pushed to the MQ");
  126. log_message(date('Y-m-d H:i:s') . " a new _asyncNotify Job is Pushed to the MQ", 'log', LOG_PATH . 'callBacklog/');
  127. } else {
  128. $msg = 'orderid:' . $orderId . '回调通知CP,url地址为:' . $callback_url . '参数:' . $post_data;
  129. //Log::info('CP回调redis入队失败' . $msg);
  130. log_message(" CP回调redis入队失败", 'log', LOG_PATH . 'callBacklog/');
  131. }
  132. }
  133. /**
  134. * 特殊回调接口
  135. * @param $callback_params
  136. * @return bool
  137. */
  138. public function specialCallback($callback_params)
  139. {
  140. try {
  141. // 将字符串参数转为数组形式
  142. if (!is_array($callback_params)) {
  143. parse_str($callback_params, $callback_params);
  144. }
  145. (new SpecialService())->welfare($callback_params);
  146. if (in_array($callback_params['gameid'], [157, 158])) {
  147. $param = [
  148. 'ctype' => 'yiyou',
  149. 'applyId' => $callback_params['orderid'],
  150. 'userId' => $callback_params['username'],
  151. 'roleId' => $callback_params['roleid'],
  152. 'serverId' => $callback_params['serverid'],
  153. 'money' => $callback_params['amount'],
  154. 'time' => time(),
  155. ];
  156. $param['sign'] = $this->_setGitemSign($param, 'd377f720058237y8c69g9062bff251d6');
  157. $callback_url = 'https://api.gamedreamsky.com/gitem_channel_pay/welfare';
  158. $result = curl($callback_url,$param,true);
  159. if ($result['code']!=1) {
  160. $template = '推送异常:https://api.gamedreamsky.com/gitem_channel_pay/welfare?' . http_build_query($param) . ' ' . $result['msg'];
  161. $ddurl = Env::get('dingtalk.warning_url');
  162. curlDD($template, $ddurl, true);
  163. }else{
  164. $msg = '推送成功:https://api.gamedreamsky.com/gitem_channel_pay/welfare?' . http_build_query($param);
  165. log_message($msg, 'log', LOG_PATH . 'callBacklog/specialCallback/');
  166. }
  167. }
  168. } catch (Exception $e) {
  169. $template = '推送异常' . $e->getMessage() . $callback_params;
  170. $ddurl = Env::get('dingtalk.warning_url');
  171. curlDD($template, $ddurl, true);
  172. }
  173. return true;
  174. }
  175. private function _setGitemSign($_srcData, $_key)
  176. {
  177. // step 1.1
  178. ksort($_srcData);
  179. $signStr = "";
  180. foreach ($_srcData as $k => $v) {
  181. if ("sign" != $k && !empty($v)) {
  182. $signStr .= $k . "=" . $v . "&";
  183. }
  184. }
  185. // step 2.2
  186. $signStr .= "key=" . $_key;
  187. $sign = md5($signStr);
  188. return $sign;
  189. }
  190. }