AliPay.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. <?php
  2. namespace common\components\payment;
  3. use common\enums\StatusEnum;
  4. use Yii;
  5. use Omnipay\Omnipay;
  6. use Omnipay\Alipay\Responses\AopTradeAppPayResponse;
  7. use Omnipay\Alipay\Responses\AopTradePreCreateResponse;
  8. use Omnipay\Alipay\Responses\AopTradeWapPayResponse;
  9. /**
  10. * Class AliPay
  11. * @package common\components\payment
  12. */
  13. class AliPay
  14. {
  15. protected $config;
  16. const PC = 'Alipay_AopPage';
  17. const APP = 'Alipay_AopApp';
  18. const F2F = 'Alipay_AopF2F';
  19. const WAP = 'Alipay_AopWap';
  20. const JS = 'Alipay_AopJs';
  21. protected $alipay_root_cert_sn = '687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6';
  22. /**
  23. * @var \Closure
  24. */
  25. protected $getData;
  26. public function __construct($config)
  27. {
  28. $this->config = $config;
  29. // 重构AopRequest所需要的数据
  30. $this->getData = function () {
  31. $this->validateParams();
  32. // $this->getCertSN();
  33. if (strtoupper($this->getSignType()) === 'RSA2') {
  34. $alipayRootCert = $this->getAlipayRootCert();
  35. $appCert = $this->getAppCert();
  36. if (is_file($alipayRootCert) && is_file($appCert)) {
  37. // $this->setParameter('alipay_root_cert_sn', getRootCertSN($alipayRootCert));
  38. $this->setParameter('alipay_root_cert_sn', '687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6');
  39. $this->setParameter('app_cert_sn', getCertSN($appCert));
  40. }
  41. }
  42. $this->setDefaults();
  43. $this->convertToString();
  44. $data = $this->getParameters();
  45. $data['method'] = $this->method;
  46. ksort($data);
  47. $data['sign'] = $this->sign($data, $this->getSignType());
  48. return $data;
  49. };
  50. }
  51. /**
  52. * 实例化类
  53. *
  54. * @param string $type
  55. * @return \Omnipay\Alipay\AopPageGateway
  56. * @throws \Omnipay\Common\Exception\InvalidRequestException
  57. */
  58. private function create($type = self::PC)
  59. {
  60. /* @var $gateway \Omnipay\Alipay\AopPageGateway */
  61. $gateway = Omnipay::create($type);
  62. $gateway->setSignType('RSA2'); // RSA/RSA2/MD5
  63. $gateway->setAppId($this->config['app_id']);
  64. $gateway->setPrivateKey($this->config['private_key']);
  65. if (!empty($this->config['is_use_cert'])) {
  66. $gateway->setAlipayPublicCert($this->config['alipay_public_cert']);
  67. $gateway->setAppCert($this->config['app_cert']);
  68. $gateway->setAlipayRootCert($this->config['alipay_root_cert']);
  69. } else {
  70. $gateway->setAlipayPublicKey($this->config['ali_public_key']);
  71. }
  72. $gateway->setNotifyUrl($this->config['notify_url']);
  73. !empty($this->config['return_url']) && $gateway->setReturnUrl($this->config['return_url']);
  74. $this->config['sandbox'] === true && $gateway->sandbox();
  75. return $gateway;
  76. }
  77. /**
  78. * 电脑网站支付
  79. *
  80. * @param $config
  81. *
  82. * 参数说明
  83. * $config = [
  84. * 'subject' => 'test',
  85. * 'out_trade_no' => date('YmdHis') . mt_rand(1000, 9999),
  86. * 'total_amount' => '0.01',
  87. * ]
  88. *
  89. * @return string
  90. * @throws \Omnipay\Common\Exception\InvalidRequestException
  91. */
  92. public function pc($order, $debug = false)
  93. {
  94. $order['product_code'] = 'FAST_INSTANT_TRADE_PAY';
  95. $gateway = $this->create(self::PC);
  96. $gateway->setParameter('return_url', $this->config['return_url']);
  97. /** @var \Omnipay\Alipay\Requests\AopTradePagePayRequest $request */
  98. $request = $gateway->purchase();
  99. $request->setBizContent($order);
  100. /** @var \Omnipay\Common\Message\AbstractResponse $response */
  101. if (!empty($this->config['is_use_cert'])) {
  102. $response = $request->sendData($this->getData->call($request));
  103. } else {
  104. $response = $request->send();
  105. }
  106. $redirectUrl = $response->getRedirectUrl();
  107. /**
  108. * 直接跳转
  109. * return $response->redirect();
  110. */
  111. return $debug == true ? $response->getData() : $redirectUrl;
  112. }
  113. /**
  114. * APP支付
  115. *
  116. * 参数说明
  117. * $config = [
  118. * 'subject' => 'test',
  119. * 'out_trade_no' => date('YmdHis') . mt_rand(1000, 9999),
  120. * 'total_amount' => '0.01',
  121. * ]
  122. *
  123. * iOS 客户端
  124. * [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
  125. * NSLog(@"reslut = %@",resultDic);
  126. * }];
  127. *
  128. * Android 客户端
  129. * PayTask alipay = new PayTask(PayDemoActivity.this);
  130. * Map<String, String> result = alipay.payV2(orderString, true);
  131. * @param $config
  132. * @param $notifyUrl
  133. * @return mixed
  134. * @throws \Omnipay\Common\Exception\InvalidRequestException
  135. */
  136. public function app($order, $debug = false)
  137. {
  138. $order['product_code'] = 'QUICK_MSECURITY_PAY';
  139. $gateway = $this->create(self::APP);
  140. /** @var \Omnipay\Alipay\Requests\AopTradePagePayRequest $request */
  141. $request = $gateway->purchase();
  142. $request->setBizContent($order);
  143. /** @var AopTradeAppPayResponse $response */
  144. if (!empty($this->config['is_use_cert'])) {
  145. $response = $request->sendData($this->getData->call($request));
  146. } else {
  147. $response = $request->send();
  148. }
  149. return $debug == true ? $response->getData() : $response->getOrderString();
  150. }
  151. /**
  152. * 面对面支付
  153. *
  154. * @param $order
  155. * @return mixed
  156. * @throws \Omnipay\Common\Exception\InvalidRequestException
  157. */
  158. public function f2f($order, $debug = false)
  159. {
  160. $gateway = $this->create(self::F2F);
  161. /** @var \Omnipay\Alipay\Requests\AopTradePagePayRequest $request */
  162. $request = $gateway->purchase();
  163. $request->setBizContent($order);
  164. /** @var AopTradePreCreateResponse $response */
  165. if (!empty($this->config['is_use_cert'])) {
  166. $response = $request->sendData($this->getData->call($request));
  167. } else {
  168. $response = $request->send();
  169. }
  170. return $debug == true ? $response->getData() : $response->getQrCode();
  171. }
  172. /**
  173. * 手机网站支付
  174. *
  175. * @param $order
  176. * @return mixed
  177. * @throws \Omnipay\Common\Exception\InvalidRequestException
  178. */
  179. public function wap($order, $debug = false)
  180. {
  181. $order['product_code'] = 'QUICK_WAP_PAY';
  182. $gateway = $this->create(self::WAP);
  183. /** @var \Omnipay\Alipay\Requests\AopTradePagePayRequest $request */
  184. $request = $gateway->purchase();
  185. $request->setBizContent($order);
  186. /** @var AopTradeWapPayResponse $response */
  187. if (!empty($this->config['is_use_cert'])) {
  188. $response = $request->sendData($this->getData->call($request));
  189. } else {
  190. $response = $request->send();
  191. }
  192. /**
  193. * 直接跳转
  194. * return $response->redirect();
  195. */
  196. return $debug == true ? $response->getData() : $response->getRedirectUrl();
  197. }
  198. /**
  199. * 统一收单交易创建接口
  200. *
  201. * @param $order
  202. * @return mixed
  203. * @throws \Omnipay\Common\Exception\InvalidRequestException
  204. * @throws \Exception
  205. */
  206. public function js($order, $debug = false)
  207. {
  208. $this->config['return_url'] = '';
  209. $gateway = $this->create(self::JS);
  210. /** @var \Omnipay\Alipay\Requests\AopTradeCreateRequest $request */
  211. $request = $gateway->purchase();
  212. $request->setBizContent($order);
  213. /** @var AopTradeWapPayResponse $response */
  214. if (!empty($this->config['is_use_cert'])) {
  215. $response = $request->sendData($this->getData->call($request));
  216. } else {
  217. $response = $request->send();
  218. }
  219. if (!empty($response->getData()['alipay_trade_create_response'])) {
  220. $data = $response->getData()['alipay_trade_create_response'];
  221. if (empty($data['code']) || $data['code'] != 10000) {
  222. // 出错
  223. throw new \Exception($data['sub_msg'] ?? '请求支付宝失败');
  224. }
  225. return $data['trade_no'] ?? $data;
  226. }
  227. return $response->getData()['alipay_trade_create_response']??$response->getData();
  228. }
  229. /**
  230. * 转账
  231. *
  232. * $info = [
  233. * 'out_biz_no' => '转账单号',
  234. * 'payee_type' => '收款人账号类型', // ALIPAY_USERID:支付宝唯一号;ALIPAY_LOGONID:支付宝登录号
  235. * 'payee_account' => '收款人账号',
  236. * 'amount' => '收款金额',
  237. * 'payee_real_name' => '收款方真实姓名', // 非必填
  238. * 'remark' => '账业务的标题,用于在支付宝用户的账单里显示', // 非必填
  239. * ]
  240. *
  241. * payee_type
  242. * 1、ALIPAY_USERID:支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。
  243. * 2、ALIPAY_LOGONID:支付宝登录号,支持邮箱和手机号格式。
  244. *
  245. * 老的接口:
  246. * https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.toaccount.transfer
  247. *
  248. * 新的接口
  249. * https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer/
  250. *
  251. * @return mixed
  252. * @throws \Omnipay\Common\Exception\InvalidRequestException
  253. */
  254. public function transfer(array $info)
  255. {
  256. !isset($info['payee_type']) && $info['payee_type'] = 'ALIPAY_LOGONID';
  257. $gateway = $this->create();
  258. $request = $gateway->transfer();
  259. $request->setBizContent($info);
  260. if (!empty($this->config['is_use_cert'])) {
  261. $response = $request->sendData($this->getData->call($request));
  262. } else {
  263. $response = $request->send();
  264. }
  265. $data = $response->getData();
  266. return $data['alipay_fund_trans_toaccount_transfer_response'] ?? '';
  267. }
  268. /**
  269. * 付款到账查询
  270. *
  271. * $info = [
  272. * 'out_biz_no' => '转账单号',
  273. * 'order_id' => '回调单号',
  274. * ]
  275. *
  276. * @return mixed
  277. * @throws \Omnipay\Common\Exception\InvalidRequestException
  278. */
  279. public function transferQuery(array $info)
  280. {
  281. $gateway = $this->create();
  282. $request = $gateway->transferQuery();
  283. $request->setBizContent($info);
  284. if (!empty($this->config['is_use_cert'])) {
  285. $response = $request->sendData($this->getData->call($request));
  286. } else {
  287. $response = $request->send();
  288. }
  289. $data = $response->getData();
  290. return $data['alipay_fund_trans_toaccount_transfer_response'] ?? '';
  291. }
  292. /**
  293. * 退款
  294. *
  295. * $info = [
  296. * 'out_trade_no' => 'The existing Order ID',
  297. * 'trade_no' => 'The Transaction ID received in the previous request',
  298. * 'refund_amount' => 18.4,
  299. * 'out_request_no' => date('YmdHis') . mt_rand(1000, 9999)
  300. * ]
  301. *
  302. * @throws \Omnipay\Common\Exception\InvalidRequestException
  303. */
  304. public function refund(array $info)
  305. {
  306. $gateway = $this->create();
  307. $request = $gateway->refund();
  308. $request->setBizContent($info);
  309. if (!empty($this->config['is_use_cert'])) {
  310. $response = $request->sendData($this->getData->call($request));
  311. } else {
  312. $response = $request->send();
  313. }
  314. return $this->check_refund_response($response->getData());
  315. //return $response->getData();
  316. }
  317. /**
  318. * 扫码收款
  319. *
  320. * @return mixed
  321. * @throws \Omnipay\Common\Exception\InvalidRequestException
  322. */
  323. public function capture()
  324. {
  325. $gateway = $this->create('Alipay_AopF2F');
  326. $request = $gateway->capture();
  327. return $request;
  328. }
  329. /**
  330. * 异步/同步通知
  331. *
  332. * @return \Omnipay\Alipay\Requests\AopCompletePurchaseRequest
  333. * @throws \Omnipay\Common\Exception\InvalidRequestException
  334. */
  335. public function notify()
  336. {
  337. $gateway = $this->create();
  338. $request = $gateway->completePurchase();
  339. $request->setParams(array_merge(Yii::$app->request->post(), Yii::$app->request->get())); // Optional
  340. return $request;
  341. }
  342. /**
  343. * 检测退款返回结果
  344. * @param $response
  345. * @return array
  346. * @throws \Exception
  347. */
  348. public function check_refund_response($response)
  349. {
  350. Yii::error('alipay_trade_refund_response :'.json_encode($response));
  351. $alipay_trade_refund_response = $response['alipay_trade_refund_response'] ?? [];
  352. if($alipay_trade_refund_response['code'] !== '10000'){
  353. throw new \Exception($alipay_trade_refund_response['sub_msg']);
  354. }
  355. $return = [];
  356. if($alipay_trade_refund_response['fund_change'] == 'Y'){
  357. //有资金变化:退款成功
  358. $return['is_success'] = true;
  359. $return['fund_change'] = true;
  360. }else{
  361. //无资金变化
  362. $return['is_success'] = true;
  363. $return['fund_change'] = false;
  364. $return['msg'] = '本次请求没有资金变动,请到支付宝平台查看交易';
  365. }
  366. return $return;
  367. }
  368. }