AliPay.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. /**
  3. * 支付宝
  4. */
  5. namespace utils;
  6. use alipay\AopCertClient;
  7. use alipay\AopClient;
  8. use Alipay\EasySDK\Kernel\Factory;
  9. use Alipay\EasySDK\Kernel\Util\ResponseChecker;
  10. use Alipay\EasySDK\Kernel\Config;
  11. use alipay\request\AlipayFundTransToaccountTransferRequest;
  12. use alipay\request\AlipayTradeAppPayRequest;
  13. use app\common\service\PayConfigService;
  14. use think\Exception;
  15. use think\facade\Db;
  16. class AliPay
  17. {
  18. protected $conf;
  19. public $pay_v = 1; // 1 通用 2 Easy
  20. private static $appCertPath = ''; //应用公钥证书
  21. private static $alipayCertPath = '';//支付宝公钥证书
  22. private static $rootCertPath = '';//支付宝根证书
  23. private static $appId = 0; //APPID
  24. private static $rsaPrivateKey = ''; //支付宝应用私钥
  25. private static $gatewayUrl = 'https://openapi.alipay.com/gateway.do';//网关地址
  26. private static $apiVersion = '1.0';
  27. private static $signType = 'RSA2';
  28. private static $postCharset = 'utf-8';
  29. private static $format = 'json';
  30. private static $notifyUrl = 'https://api.meikangjw.com/api/aliResult';
  31. private $class;
  32. public function __construct ($type = 'pay')
  33. {
  34. if ($type == 'pay') {
  35. self::payConfig();
  36. if ($this->pay_v == 1) {
  37. // self::payConfig();
  38. } else {
  39. Factory::setOptions(self::getOptions());
  40. }
  41. }
  42. }
  43. /**
  44. * 支付宝统一下单(通用版本sdk)
  45. * @param array $param
  46. * @return array
  47. */
  48. public function unifiedOrder (array $param)
  49. {
  50. try {
  51. $this->class = new AopCertClient();
  52. $this->class->gatewayUrl = self::$gatewayUrl;
  53. $this->class->appId = self::$appId;
  54. $this->class->rsaPrivateKey = self::$rsaPrivateKey;
  55. $this->class->format = self::$format;
  56. $this->class->postCharset = self::$postCharset;
  57. $this->class->signType = self::$signType;
  58. //调用getPublicKey从支付宝公钥证书中提取公钥
  59. $this->class->alipayrsaPublicKey = $this->class->getPublicKey(self::$alipayCertPath);
  60. //是否校验自动下载的支付宝公钥证书,如果开启校验要保证支付宝根证书在有效期内
  61. $this->class->isCheckAlipayPublicCert = true;
  62. //调用getCertSN获取证书序列号
  63. $this->class->appCertSN = $this->class->getCertSN(self::$appCertPath);
  64. //调用getRootCertSN获取支付宝根证书序列号
  65. $this->class->alipayRootCertSN = $this->class->getRootCertSN(self::$rootCertPath);
  66. //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.open.public.template.message.industry.modify
  67. $request = new AlipayTradeAppPayRequest();
  68. //SDK已经封装掉了公共参数,这里只需要传入业务参数
  69. //此次只是参数展示,未进行字符串转义,实际情况下请转义
  70. $arrData = [
  71. 'total_amount' => $param['total_amount'],
  72. 'subject' => $param['body'],
  73. 'out_trade_no' => $param['out_trade_no'],
  74. ];
  75. $notifyUrl = getWebUrl().'/api/aliResult';
  76. $cacheKey = "caches:payment:alipay:otn_{$param['out_trade_no']}:";
  77. RedisCache::set($cacheKey."unifiedOrder", ['url'=>self::$gatewayUrl,'notifyUrl'=> $notifyUrl,'params'=>$arrData,'date'=>date('Y-m-d H:i:s')], 7200);
  78. $request->setNotifyUrl($notifyUrl);
  79. $request->setBizContent(json_encode($arrData, JSON_UNESCAPED_UNICODE));
  80. $result = $this->class->sdkExecute($request);
  81. RedisCache::set($cacheKey."uni_result", ['url'=>self::$gatewayUrl,'notifyUrl'=> $notifyUrl,'params'=>$arrData,'date'=>date('Y-m-d H:i:s')], 7200);
  82. if (!empty($result))
  83. return ['flag' => true, 'msg' => '调用成功', 'data' => ['result' => $result]];
  84. else
  85. return ['flag' => false, 'msg' => "统一下单失败"];
  86. } catch (\Exception $e) {
  87. RedisCache::set("caches:payment:alipay:otn_{$param['out_trade_no']}:uni_error", ['error'=>'调用错误:'.$e->getMessage(),'url'=>self::$gatewayUrl,'params'=>$param,'date'=>date('Y-m-d H:i:s')], 7200);
  88. return ['flag' => false, 'msg' => $e->getMessage()];
  89. }
  90. }
  91. /**
  92. * 支付宝统一下单(Easy版本sdk)
  93. * @param array $param
  94. * @return array
  95. */
  96. public function easyUnifiedOrder (array $param)
  97. {
  98. try {
  99. $cacheKey = "caches:payment:alipay:otn_{$param['out_trade_no']}:";
  100. RedisCache::set($cacheKey."easyUnifiedOrder", ['params'=>$param,'date'=>date('Y-m-d H:i:s')], 7200);
  101. $result = Factory::payment()->common()->create($param['body'], $param['out_trade_no'], $param['total_amount'], null);
  102. $responseChecker = new ResponseChecker();
  103. //3. 处理响应或异常
  104. RedisCache::set($cacheKey."easy_result", ['params'=>$param,'result'=>$result,'date'=>date('Y-m-d H:i:s')], 7200);
  105. if ($responseChecker->success($result)) {
  106. return ['flag' => true, 'msg' => '调用成功', 'data' => $result];
  107. } else {
  108. return ['flag' => false, 'msg' => "调用失败,原因:" . $result->msg . "," . $result->subMsg . PHP_EOL];
  109. }
  110. } catch (\Exception $e) {
  111. RedisCache::set("caches:payment:alipay:otn_{$param['out_trade_no']}:easy_error", ['msg'=>'调用失败:'.$e->getMessage(). PHP_EOL,'params'=>$param,'date'=>date('Y-m-d H:i:s')], 7200);
  112. return ['flag' => false, 'msg' => "调用失败," . $e->getMessage() . PHP_EOL];
  113. }
  114. }
  115. /**
  116. * 提现
  117. * @param array $param
  118. * @return array
  119. * @throws \think\Exception
  120. */
  121. public function withdrawal (array $param)
  122. {
  123. $aop = new AopClient();
  124. $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
  125. $conf = PayConfigService::make()->getInfoByChannel(1);
  126. $aop->appId = $conf['app_id'];
  127. $aop->rsaPrivateKey = $conf['private_key']; // 应用私钥
  128. $aop->alipayrsaPublicKey = $conf['public_key']; // 支付宝公钥
  129. $aop->apiVersion = '1.0';
  130. $aop->signType = 'RSA2';
  131. $aop->postCharset = 'utf-8';
  132. $aop->format = 'json';
  133. $outBizNo = time() . rand(10000, 99999) . $param['uid'];
  134. $bizContent = [
  135. 'out_biz_no' => $outBizNo,
  136. 'trans_amount' => $param['practical_money'],
  137. 'product_code' => 'TRANS_ACCOUNT_NO_PWD',
  138. 'biz_scene' => 'DIRECT_TRANSFER',
  139. 'order_title' => '用户提现',
  140. 'payee_info' => [
  141. 'identity_type' => 'ALIPAY_LOGON_ID',
  142. 'identity' => $param['zfb_number'],
  143. 'name' => $param['zfb_name'],
  144. ],
  145. 'remark' => '用户提现'
  146. ];
  147. // 缓存日志
  148. $cacheKey = "caches:withdraw:catchOrder:no_{$outBizNo}:";
  149. RedisCache::set($cacheKey.'params', ['url'=>$aop->gatewayUrl,'appid'=>$aop->appId,'params'=>$bizContent], 7200);
  150. $request = new AlipayFundTransToaccountTransferRequest();
  151. $request->setBizContent(json_encode($bizContent, JSON_UNESCAPED_UNICODE));
  152. $result = $aop->execute($request);
  153. RedisCache::set($cacheKey.'result', ['url'=>$aop->gatewayUrl,'appid'=>$aop->appId,'params'=>$bizContent,'result'=>$result], 7200);
  154. if (!$result) {
  155. return ['flag' => false, 'msg' => '系统出错'];
  156. }
  157. $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
  158. $resultCode = $result->{$responseNode}->code;
  159. if (!empty($resultCode) && $resultCode == 10000) {
  160. return ['flag' => true, 'data' => ['out_biz_no' => $result->{$responseNode}->out_biz_no]];
  161. } else {
  162. return ['flag' => false, 'msg' => $request->{$responseNode}->sub_msg];
  163. }
  164. }
  165. /**
  166. * 验签
  167. * @param array $param
  168. * @return bool
  169. */
  170. public function checkSign (array $param)
  171. {
  172. try {
  173. if ($this->pay_v == 1) {
  174. $obj = new AopCertClient();
  175. $param['fund_bill_list'] = html_entity_decode($param['fund_bill_list']); // 处理数据中存在&quot;
  176. return $obj->rsaCheckV1($param, self::$alipayCertPath, 'RSA2');
  177. } else {
  178. return Factory::payment()->common()->verifyNotify($param);
  179. }
  180. } catch (\Exception $e) {
  181. RedisCache::set('caches:payment:alipay:signError', ['msg'=>'签名错误','date'=>date('Y-m-d H:i:s')], 7200);
  182. return false;
  183. }
  184. }
  185. /**
  186. * 公共参数
  187. * @return Config
  188. */
  189. static protected function getOptions ()
  190. {
  191. $options = new Config();
  192. $options->protocol = 'https';
  193. $options->gatewayHost = self::$gatewayUrl;
  194. $options->signType = self::$signType;
  195. $options->appId = self::$appId;
  196. $options->merchantPrivateKey = self::$rsaPrivateKey; // 应用私钥
  197. $options->alipayCertPath = self::$alipayCertPath; // 支付宝公钥证书文件路径
  198. $options->alipayRootCertPath = self::$rootCertPath; // 支付宝根证书文件路径
  199. $options->merchantCertPath = self::$appCertPath; // 应用公钥证书文件路径
  200. return $options;
  201. }
  202. /**
  203. * 支付宝配置
  204. * @throws \think\db\exception\DataNotFoundException
  205. * @throws \think\db\exception\DbException
  206. * @throws \think\db\exception\ModelNotFoundException
  207. */
  208. static protected function payConfig ()
  209. {
  210. $conf = PayConfigService::make()->getInfoByChannel(1);
  211. if (empty($conf)){
  212. RedisCache::set('caches:payment:alipay:error', ['msg'=>'获取支付配置错误','date'=>date('Y-m-d H:i:s')], 7200);
  213. throw new Exception('支付宝配置错误');
  214. }
  215. self::$appCertPath = $conf['app_cert_path']; //应用公钥证书
  216. self::$alipayCertPath = $conf['pay_cert_path'];//支付宝公钥证书
  217. self::$rootCertPath = $conf['pay_root_cert'];//支付宝根证书
  218. self::$appId = $conf['app_id']; //APPID
  219. self::$rsaPrivateKey = $conf['private_key']; //支付宝应用私钥
  220. }
  221. }