AliPayService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeadmin快速开发前后端分离管理后台(PHP版)
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
  8. // | github下载:https://github.com/likeshop-github/likeadmin
  9. // | 访问官网:https://www.likeadmin.cn
  10. // | likeadmin团队 版权所有 拥有最终解释权
  11. // +----------------------------------------------------------------------
  12. // | author: likeadminTeam
  13. // +----------------------------------------------------------------------
  14. namespace app\common\service\pay;
  15. use Alipay\EasySDK\Kernel\Factory;
  16. use Alipay\EasySDK\Kernel\Config;
  17. use app\common\enum\PayEnum;
  18. use app\common\enum\user\UserTerminalEnum;
  19. use app\common\logic\PayNotifyLogic;
  20. use app\common\model\member\MemberOrder;
  21. use app\common\model\pay\PayConfig;
  22. use app\common\model\recharge\RechargeOrder;
  23. use think\facade\Log;
  24. /**
  25. * 支付宝支付
  26. * Class AliPlsayService
  27. * @package app\common\server
  28. */
  29. class AliPayService extends BasePayService
  30. {
  31. /**
  32. * 用户客户端
  33. * @var
  34. */
  35. protected $terminal;
  36. /**
  37. * 支付实例
  38. * @var
  39. */
  40. protected $pay;
  41. /**
  42. * 初始化设置
  43. * AliPayService constructor.
  44. * @throws \Exception
  45. */
  46. public function __construct($terminal = null)
  47. {
  48. //设置用户终端
  49. $this->terminal = $terminal;
  50. //初始化支付配置
  51. Factory::setOptions($this->getOptions());
  52. $this->pay = Factory::payment();
  53. }
  54. /**
  55. * @notes 支付设置
  56. * @return Config
  57. * @throws \think\db\exception\DataNotFoundException
  58. * @throws \think\db\exception\DbException
  59. * @throws \think\db\exception\ModelNotFoundException
  60. * @author 段誉
  61. * @date 2021/7/28 17:43
  62. */
  63. public function getOptions()
  64. {
  65. $config = (new PayConfig())->where(['pay_way' => PayEnum::ALI_PAY])->find();
  66. if (empty($config)) {
  67. throw new \Exception('请配置好支付设置');
  68. }
  69. $options = new Config();
  70. $options->protocol = 'https';
  71. $options->gatewayHost = 'openapi.alipay.com';
  72. // $options->gatewayHost = 'openapi.alipaydev.com'; //测试沙箱地址
  73. $options->signType = 'RSA2';
  74. $options->appId = $config['config']['app_id'] ?? '';
  75. // 应用私钥
  76. $options->merchantPrivateKey = $config['config']['private_key'] ?? '';
  77. //接口加签方式
  78. // 秘钥模式
  79. if ($config['config']['mode'] == 'normal_mode') {
  80. //支付宝公钥
  81. $options->alipayPublicKey = $config['config']['ali_public_key'] ?? '';
  82. }
  83. //证书模式
  84. if ($config['config']['mode'] == 'certificate') {
  85. //判断是否已经存在证书文件夹,不存在则新建
  86. if (!file_exists(app()->getRootPath() . 'runtime/certificate')) {
  87. mkdir(app()->getRootPath() . 'runtime/certificate', 0775, true);
  88. }
  89. //写入文件
  90. $publicCert = $config['config']['public_cert'] ?? '';
  91. $aliPublicCert = $config['config']['ali_public_cert'] ?? '';
  92. $aliRootCert = $config['config']['ali_root_cert'] ?? '';
  93. $publicCertPath = app()->getRootPath() . 'runtime/certificate/' . md5($publicCert) . '.crt';
  94. $aliPublicCertPath = app()->getRootPath() . 'runtime/certificate/' . md5($aliPublicCert) . '.crt';
  95. $aliRootCertPath = app()->getRootPath() . 'runtime/certificate/' . md5($aliRootCert) . '.crt';
  96. if (!file_exists($publicCertPath)) {
  97. $fopenPublicCertPath = fopen($publicCertPath, 'w');
  98. fwrite($fopenPublicCertPath, $publicCert);
  99. fclose($fopenPublicCertPath);
  100. }
  101. if (!file_exists($aliPublicCertPath)) {
  102. $fopenAliPublicCertPath = fopen($aliPublicCertPath, 'w');
  103. fwrite($fopenAliPublicCertPath, $aliPublicCert);
  104. fclose($fopenAliPublicCertPath);
  105. }
  106. if (!file_exists($aliRootCertPath)) {
  107. $fopenAliRootCertPath = fopen($aliRootCertPath, 'w');
  108. fwrite($fopenAliRootCertPath, $aliRootCert);
  109. fclose($fopenAliRootCertPath);
  110. }
  111. //应用公钥证书路径
  112. $options->merchantCertPath = $publicCertPath;
  113. //支付宝公钥证书路径
  114. $options->alipayCertPath = $aliPublicCertPath;
  115. //支付宝根证书路径
  116. $options->alipayRootCertPath = $aliRootCertPath;
  117. }
  118. //回调地址
  119. $options->notifyUrl = (string)url('pay/aliNotify', [], false, true);
  120. return $options;
  121. }
  122. /**
  123. * @notes 支付
  124. * @param $from //订单来源;order-商品订单;recharge-充值订单
  125. * @param $order //订单信息
  126. * @return false|string[]
  127. * @author 段誉
  128. * @date 2021/8/13 17:08
  129. */
  130. public function pay($from, $order): array|bool
  131. {
  132. try {
  133. $result = match ($this->terminal) {
  134. UserTerminalEnum::PC => $this->pagePay($from, $order),
  135. UserTerminalEnum::IOS, UserTerminalEnum::ANDROID => $this->appPay($from, $order),
  136. UserTerminalEnum::WECHAT_OA, UserTerminalEnum::H5 => $this->wapPay($from, $order),
  137. default => throw new \Exception('支付方式错误'),
  138. };
  139. return [
  140. 'config' => $result,
  141. 'pay_way' => PayEnum::ALI_PAY
  142. ];
  143. } catch (\Exception $e) {
  144. $this->error = $e->getMessage();
  145. return false;
  146. }
  147. }
  148. /**
  149. * Notes: 支付回调
  150. * @param $data
  151. * @return bool
  152. * @author 段誉(2021/3/22 17:22)
  153. */
  154. public function notify($data)
  155. {
  156. try {
  157. $verify = $this->pay->common()->verifyNotify($data);
  158. if (false === $verify) {
  159. throw new \Exception('异步通知验签失败');
  160. }
  161. if (!in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
  162. return true;
  163. }
  164. $extra['transaction_id'] = $data['trade_no'];
  165. //验证订单是否已支付
  166. switch ($data['passback_params']) {
  167. case 'recharge':
  168. $order = RechargeOrder::where(['sn' => $data['out_trade_no']])->findOrEmpty();
  169. if ($order->isEmpty() || $order->pay_status == PayEnum::ISPAID) {
  170. return true;
  171. }
  172. PayNotifyLogic::handle('recharge', $data['out_trade_no'], $extra);
  173. break;
  174. }
  175. return true;
  176. } catch (\Exception $e) {
  177. $record = [
  178. __CLASS__,
  179. __FUNCTION__,
  180. $e->getFile(),
  181. $e->getLine(),
  182. $e->getMessage()
  183. ];
  184. Log::write(implode('-', $record));
  185. $this->setError($e->getMessage());
  186. return false;
  187. }
  188. }
  189. /**
  190. * @notes PC支付
  191. * @param $attach //附加参数(在回调时会返回)
  192. * @param $order //订单信息
  193. * @return string
  194. * @author 段誉
  195. * @date 2021/7/28 17:34
  196. */
  197. public function pagePay($attach, $order)
  198. {
  199. $domain = request()->domain();
  200. $result = $this->pay->page()->optional('passback_params', $attach)->pay(
  201. '订单:' . $order['sn'],
  202. $order['sn'],
  203. $order['order_amount'],
  204. $domain . $order['redirect_url']
  205. );
  206. return $result->body;
  207. }
  208. /**
  209. * @notes APP支付
  210. * @param $attach //附加参数(在回调时会返回)
  211. * @param $order //订单信息
  212. * @return string
  213. * @author 段誉
  214. * @date 2021/7/28 17:34
  215. */
  216. public function appPay($attach, $order)
  217. {
  218. $result = $this->pay->app()->optional('passback_params', $attach)->pay(
  219. $order['sn'],
  220. $order['sn'],
  221. $order['order_amount']
  222. );
  223. return $result->body;
  224. }
  225. /**
  226. * @notes 手机网页支付
  227. * @param $attach //附加参数(在回调时会返回)
  228. * @param $order //订单信息
  229. * @return string
  230. * @author 段誉
  231. * @date 2021/7/28 17:34
  232. */
  233. public function wapPay($attach, $order)
  234. {
  235. $domain = request()->domain();
  236. $url = $domain . '/mobile' . $order['redirect_url'] .'?id=' . $order['id'] . '&from='. $attach . '&checkPay=true';;
  237. $result = $this->pay->wap()->optional('passback_params', $attach)->pay(
  238. '订单:' . $order['sn'],
  239. $order['sn'],
  240. $order['order_amount'],
  241. $url,
  242. $url
  243. );
  244. return $result->body;
  245. }
  246. /**
  247. * @notes 查询订单
  248. * @param $orderSn
  249. * @return \Alipay\EasySDK\Payment\Common\Models\AlipayTradeQueryResponse
  250. * @throws \Exception
  251. * @author 段誉
  252. * @date 2021/7/28 17:36
  253. */
  254. public function checkPay($orderSn)
  255. {
  256. return $this->pay->common()->query($orderSn);
  257. }
  258. /**
  259. * @notes 退款
  260. * @param $orderSn
  261. * @param $orderAmount
  262. * @param $outRequestNo
  263. * @return \Alipay\EasySDK\Payment\Common\Models\AlipayTradeRefundResponse
  264. * @throws \Exception
  265. * @author 段誉
  266. * @date 2021/7/28 17:37
  267. */
  268. public function refund($orderSn, $orderAmount, $outRequestNo)
  269. {
  270. return $this->pay->common()->optional('out_request_no', $outRequestNo)->refund($orderSn, $orderAmount);
  271. }
  272. /**
  273. * @notes 查询退款
  274. * @author Tab
  275. * @date 2021/9/13 11:38
  276. */
  277. public function queryRefund($orderSn, $refundSn)
  278. {
  279. return $this->pay->common()->queryRefund($orderSn, $refundSn);
  280. }
  281. /**
  282. * @notes 捕获错误
  283. * @param $result
  284. * @throws \Exception
  285. * @author 段誉
  286. * @date 2023/2/28 12:09
  287. */
  288. public function checkResultFail($result)
  289. {
  290. if (isset($result['alipay_trade_precreate_response']['code']) && 10000 != $result['alipay_trade_precreate_response']['code']) {
  291. throw new \Exception('支付宝:' . $result['alipay_trade_precreate_response']['msg']);
  292. }
  293. }
  294. /**
  295. * @notes 转账到支付宝账号
  296. * @param $withdraw
  297. * @return mixed
  298. * @throws \think\db\exception\DataNotFoundException
  299. * @throws \think\db\exception\DbException
  300. * @throws \think\db\exception\ModelNotFoundException
  301. * @author ljj
  302. * @date 2023/10/9 10:58 上午
  303. */
  304. public function transfer($withdraw)
  305. {
  306. //请求参数
  307. $data = [
  308. 'out_biz_no' => $withdraw['sn'],//商家侧唯一订单号,由商家自定义。对于不同转账请求,商家需保证该订单号在自身系统唯一。
  309. 'trans_amount' => $withdraw['left_money'],//订单总金额,单位为元,不支持千位分隔符,精确到小数点后两位
  310. 'product_code' => 'TRANS_ACCOUNT_NO_PWD',//销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD。
  311. 'biz_scene' => 'DIRECT_TRANSFER',//业务场景。单笔无密转账固定为 DIRECT_TRANSFER。
  312. 'order_title' => '佣金提现',//转账业务的标题
  313. 'payee_info' => [//收款方信息
  314. 'identity' => $withdraw['account'],//参与方的标识 ID。当 identity_type=ALIPAY_USER_ID 时,填写支付宝用户 UID;当 identity_type=ALIPAY_LOGON_ID 时,填写支付宝登录号。
  315. 'identity_type' => 'ALIPAY_LOGON_ID',//参与方的标识类型。ALIPAY_USER_ID:支付宝会员的用户 ID;ALIPAY_LOGON_ID:支付宝登录号;
  316. 'name' => $withdraw['real_name'],//参与方真实姓名。如果非空,将校验收款支付宝账号姓名一致性。当 identity_type=ALIPAY_LOGON_ID 时,本字段必填。
  317. ],
  318. 'remark' => '',//业务备注
  319. ];
  320. $result = Factory::util()->generic()->execute("alipay.fund.trans.uni.transfer", [], $data);
  321. $result = json_decode($result->httpBody, true);
  322. $result = $result['alipay_fund_trans_uni_transfer_response'] ?? [];
  323. if ($result['code'] != 10000) {//接口调用失败
  324. throw new \Exception($result['sub_msg'] ?? $result['msg']);
  325. }
  326. return $result;
  327. }
  328. /**
  329. * @notes 转账查询
  330. * @param $withdraw
  331. * @return mixed
  332. * @throws \Exception
  333. * @author ljj
  334. * @date 2023/10/9 11:47 上午
  335. */
  336. public function transferQuery($withdraw)
  337. {
  338. //请求参数
  339. $data = [
  340. 'out_biz_no' => $withdraw['sn'],//商户转账唯一订单号:发起转账来源方定义的转账单据 ID。
  341. 'product_code' => 'TRANS_ACCOUNT_NO_PWD',//销售产品码,如果传了 out_biz_no,则该字段必传。单笔无密转账固定为TRANS_ACCOUNT_NO_PWD。
  342. 'biz_scene' => 'DIRECT_TRANSFER',//描述特定的业务场景,如果传递了out_biz_no 则该字段为必传。单笔无密转账固定为DIRECT_TRANSFER。
  343. ];
  344. $result = Factory::util()->generic()->execute("alipay.fund.trans.common.query", [], $data);
  345. $result = json_decode($result->httpBody, true);
  346. return $result['alipay_fund_trans_common_query_response'] ?? [];
  347. }
  348. }