Service.php 13 KB

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