Service.php 15 KB

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