Service.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. namespace addons\epay\library;
  3. use addons\third\model\Third;
  4. use app\common\library\Auth;
  5. use Exception;
  6. use think\Env;
  7. use think\Session;
  8. use Yansongda\Pay\Pay;
  9. /**
  10. * 订单服务类
  11. *
  12. * @package addons\epay\library
  13. */
  14. class Service
  15. {
  16. /**
  17. * 提交订单
  18. * @param array|float $amount 订单金额
  19. * @param string $orderid 订单号
  20. * @param string $type 支付类型,可选alipay或wechat
  21. * @param string $title 订单标题
  22. * @param string $notifyurl 通知回调URL
  23. * @param string $returnurl 跳转返回URL
  24. * @param string $method 支付方法
  25. * @return Response|RedirectResponse|Collection
  26. * @throws Exception
  27. */
  28. public static function submitOrder($amount, $orderid = null, $type = null, $title = null, $notifyurl = null, $returnurl = null, $method = null, $openid = '')
  29. {
  30. if (!is_array($amount)) {
  31. $params = [
  32. 'amount' => 0.01,
  33. 'orderid' => $orderid,
  34. 'type' => $type,
  35. 'title' => $title,
  36. 'notifyurl' => $notifyurl,
  37. 'returnurl' => $returnurl,
  38. 'method' => $method,
  39. 'openid' => $openid,
  40. ];
  41. } else {
  42. $params = $amount;
  43. }
  44. $type = isset($params['type']) && in_array($params['type'], ['alipay', 'driver_wechat', 'user_wechat', 'mini_wechat']) ? $params['type'] : 'user_wechat';
  45. $method = isset($params['method']) ? $params['method'] : 'web';
  46. $orderid = isset($params['orderid']) ? $params['orderid'] : date("YmdHis") . mt_rand(100000, 999999);
  47. $amount = isset($params['amount']) ? $params['amount'] : 1;
  48. $title = isset($params['title']) ? $params['title'] : "支付";
  49. $auth_code = isset($params['auth_code']) ? $params['auth_code'] : '';
  50. $openid = isset($params['openid']) ? $params['openid'] : '';
  51. $request = request();
  52. $notifyurl = isset($params['notifyurl']) ? $params['notifyurl'] : $request->root(true) . '/addons/epay/index/' . $type . 'notify';
  53. $returnurl = isset($params['returnurl']) ? $params['returnurl'] : $request->root(true) . '/addons/epay/index/' . $type . 'return/out_trade_no/' . $orderid;
  54. $html = '';
  55. $config = Service::getConfig($type);
  56. $config['notify_url'] = $notifyurl;
  57. $config['return_url'] = $returnurl;
  58. $isWechat = strpos($request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
  59. $result = null;
  60. if ($type == 'alipay') {
  61. //如果是PC支付,判断当前环境,进行跳转
  62. if ($method == 'web') {
  63. //如果是微信环境或后台配置PC使用扫码支付
  64. if ($isWechat || $config['scanpay']) {
  65. Session::set("alipayorderdata", $params);
  66. $url = addon_url('epay/api/alipay', [], true, true);
  67. return RedirectResponse::create($url);
  68. } elseif ($request->isMobile()) {
  69. $method = 'wap';
  70. }
  71. }
  72. //创建支付对象
  73. $pay = Pay::alipay($config);
  74. $params = [
  75. 'out_trade_no' => $orderid,//你的订单号
  76. 'total_amount' => $amount,//单位元
  77. 'subject' => $title,
  78. ];
  79. switch ($method) {
  80. case 'web':
  81. //电脑支付
  82. $result = $pay->web($params);
  83. break;
  84. case 'wap':
  85. //手机网页支付
  86. $result = $pay->wap($params);
  87. break;
  88. case 'app':
  89. //APP支付
  90. $result = $pay->app($params);
  91. break;
  92. case 'scan':
  93. //扫码支付
  94. $result = $pay->scan($params);
  95. break;
  96. case 'pos':
  97. //刷卡支付必须要有auth_code
  98. $params['auth_code'] = $auth_code;
  99. $result = $pay->pos($params);
  100. break;
  101. default:
  102. }
  103. } else {
  104. //如果是PC支付,判断当前环境,进行跳转
  105. if ($method == 'web') {
  106. //如果是移动端,但不是微信环境
  107. if ($request->isMobile() && !$isWechat) {
  108. $method = 'wap';
  109. } else {
  110. Session::set("wechatorderdata", $params);
  111. $url = addon_url('epay/api/wechat', [], true, true);
  112. return RedirectResponse::create($url);
  113. }
  114. }
  115. //创建支付对象
  116. $pay = Pay::wechat($config);
  117. $params = [
  118. 'out_trade_no' => $orderid,//你的订单号
  119. 'body' => $title,
  120. 'total_fee' => $amount * 100, //单位分
  121. ];
  122. switch ($method) {
  123. //case 'web':
  124. // //电脑支付,跳转到自定义展示页面(FastAdmin独有)
  125. // $result = $pay->web($params);
  126. // break;
  127. case 'mp':
  128. //公众号支付
  129. //公众号支付必须有openid
  130. $params['openid'] = $openid;
  131. $result = $pay->mp($params);
  132. break;
  133. case 'wap':
  134. //手机网页支付,跳转
  135. $params['spbill_create_ip'] = $request->ip(0, false);
  136. $result = $pay->wap($params);
  137. break;
  138. case 'app':
  139. //APP支付,直接返回字符串
  140. $result = $pay->app($params);
  141. break;
  142. case 'scan':
  143. //扫码支付,直接返回字符串
  144. $result = $pay->scan($params);
  145. break;
  146. case 'pos':
  147. //刷卡支付,直接返回字符串
  148. //刷卡支付必须要有auth_code
  149. $params['auth_code'] = $auth_code;
  150. $result = $pay->pos($params);
  151. break;
  152. case 'miniapp':
  153. //小程序支付,直接返回字符串
  154. //小程序支付必须要有openid
  155. $params['openid'] = $openid;
  156. $result = $pay->miniapp($params);
  157. break;
  158. default:
  159. }
  160. }
  161. //使用重写的Response类、RedirectResponse、Collection类
  162. if ($result instanceof \Symfony\Component\HttpFoundation\RedirectResponse) {
  163. $result = RedirectResponse::create($result->getTargetUrl());
  164. } elseif ($result instanceof \Symfony\Component\HttpFoundation\Response) {
  165. $result = Response::create($result->getContent());
  166. } elseif ($result instanceof \Yansongda\Supports\Collection) {
  167. $result = Collection::make($result->all());
  168. }
  169. return $result;
  170. }
  171. /**
  172. * 验证回调是否成功
  173. * @param string $type 支付类型
  174. * @param array $config 配置信息
  175. * @return bool|\Yansongda\Pay\Gateways\Alipay|\Yansongda\Pay\Gateways\Wechat
  176. */
  177. public static function checkNotify($type, $config = [])
  178. {
  179. $type = strtolower($type);
  180. if (!in_array($type, ['alipay', 'driver_wechat', 'user_wechat', 'mini_wechat'])) {
  181. return false;
  182. }
  183. try {
  184. $config = self::getConfig($type);
  185. $pay = $type != 'alipay' ? Pay::wechat($config) : Pay::alipay($config);
  186. $data = $pay->verify();
  187. if ($type == 'alipay') {
  188. if (in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
  189. return $pay;
  190. }
  191. } else {
  192. return $pay;
  193. }
  194. } catch (Exception $e) {
  195. return false;
  196. }
  197. return false;
  198. }
  199. /**
  200. * 验证返回是否成功,请勿用于判断是否支付成功的逻辑验证
  201. * 已弃用
  202. *
  203. * @param string $type 支付类型
  204. * @param array $config 配置信息
  205. * @return bool
  206. * @deprecated 已弃用,请勿用于逻辑验证
  207. */
  208. public static function checkReturn($type, $config = [])
  209. {
  210. //由于PC及移动端无法获取请求的参数信息,取消return验证,均返回true
  211. return true;
  212. }
  213. /**
  214. * 获取配置
  215. * @param string $type 支付类型
  216. * @return array|mixed
  217. */
  218. public static function getConfig($type = 'wechat')
  219. {
  220. $ddrive = get_addon_config('ddrive');
  221. $request = request();
  222. if($type == 'driver_wechat'){
  223. $wechat = [
  224. 'appid' => $ddrive['driver_wx_appid'],
  225. //'app_secret' => '67c4edea3897b8bbdddfff45a95631de',
  226. 'mch_id' => $ddrive['mch_id'],
  227. 'key' => $ddrive['key'],
  228. 'notify_url' => $request->root(true) . '/addons/epay/api/notifyx?type=driver_wechat',
  229. 'cert_client' => Env::get('app.wechatpay_cert_client', '/addons/epay/certs/apiclient_cert.pem'),
  230. 'cert_key' => Env::get('app.wechatpay_cert_key', '/addons/epay/certs/apiclient_key.pem'),
  231. ];
  232. }elseif ($type == 'user_wechat'){
  233. $wechat = [
  234. 'appid' => $ddrive['user_wx_appid'],
  235. //'app_secret' => '67c4edea3897b8bbdddfff45a95631de',
  236. 'mch_id' => $ddrive['mch_id'],
  237. 'key' => $ddrive['key'],
  238. 'notify_url' => $request->root(true) . '/addons/ddrive/order/notifyx/paytype/user_wechat',
  239. 'cert_client' => '/addons/epay/certs/apiclient_cert.pem',
  240. 'cert_key' => '/addons/epay/certs/apiclient_key.pem',
  241. ];
  242. }elseif($type == 'mini_wechat'){
  243. $wechat = [
  244. 'miniapp_id' => $ddrive['wx_appid'],
  245. //'app_secret' => '67c4edea3897b8bbdddfff45a95631de',
  246. 'mch_id' => $ddrive['mch_id'],
  247. 'key' => $ddrive['key'],
  248. 'notify_url' => $request->root(true) . '/addons/ddrive/order/notifyx/paytype/mini_wechat',
  249. 'cert_client' => Env::get('app.wechatpay_cert_client', '/addons/epay/certs/apiclient_cert.pem'),
  250. 'cert_key' => Env::get('app.wechatpay_cert_key', '/addons/epay/certs/apiclient_key.pem'),
  251. ];
  252. }else{
  253. $alipay = [
  254. 'app_id' => $ddrive['user_alipay_app_id'],
  255. //'app_secret' => '67c4edea3897b8bbdddfff45a95631de',
  256. 'ali_public_key' => $ddrive['user_alipay_ali_public_key'],
  257. 'private_key' => $ddrive['user_alipay_private_key'],
  258. 'notify_url' =>$request->root(true) . '/addons/ddrive/order/notifyx/paytype/alipay',
  259. ];
  260. }
  261. if($type == 'alipay'){
  262. $config['alipay'] = $alipay;
  263. }else{
  264. $config['wechat'] = $wechat;
  265. }
  266. $config = isset($config[$type]) ? $config[$type] : $config['wechat'];
  267. if (isset($config['log'])) {
  268. $config['log'] = [
  269. 'file' => LOG_PATH . 'epaylogs' . DS . $type . '-' . date("Y-m-d") . '.log',
  270. 'level' => 'debug'
  271. ];
  272. }
  273. if (isset($config['cert_client']) && substr($config['cert_client'], 0, 8) == '/addons/') {
  274. $config['cert_client'] = ROOT_PATH . str_replace('/', DS, substr($config['cert_client'], 1));
  275. }
  276. if (isset($config['cert_key']) && substr($config['cert_key'], 0, 8) == '/addons/') {
  277. $config['cert_key'] = ROOT_PATH . str_replace('/', DS, substr($config['cert_key'], 1));
  278. }
  279. // 可选
  280. $config['http'] = [
  281. 'timeout' => 10,
  282. 'connect_timeout' => 10,
  283. // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
  284. ];
  285. $config['notify_url'] = empty($config['notify_url']) ? addon_url('epay/api/notifyx', [], false) . '/type/' . $type : $config['notify_url'];
  286. $config['notify_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['notify_url']) ? request()->root(true) . $config['notify_url'] : $config['notify_url'];
  287. $config['return_url'] = empty($config['return_url']) ? addon_url('epay/api/returnx', [], false) . '/type/' . $type : $config['return_url'];
  288. $config['return_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['return_url']) ? request()->root(true) . $config['return_url'] : $config['return_url'];
  289. return $config;
  290. }
  291. /**
  292. * 获取微信Openid
  293. *
  294. * @return mixed|string
  295. */
  296. public static function getOpenid()
  297. {
  298. $config = self::getConfig('wechat');
  299. $openid = '';
  300. $auth = Auth::instance();
  301. if ($auth->isLogin()) {
  302. $third = get_addon_info('third');
  303. if ($third && $third['state']) {
  304. $thirdInfo = Third::where('user_id', $auth->id)->where('platform', 'wechat')->where('apptype', 'mp')->find();
  305. $openid = $thirdInfo ? $thirdInfo['openid'] : '';
  306. }
  307. }
  308. if (!$openid) {
  309. $openid = Session::get("openid");
  310. //如果未传openid,则去读取openid
  311. if (!$openid) {
  312. $wechat = new Wechat($config['app_id'], $config['app_secret']);
  313. $openid = $wechat->getOpenid();
  314. }
  315. }
  316. return $openid;
  317. }
  318. }