Alipay.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. namespace Yansongda\Pay\Gateways\Alipay;
  3. use Yansongda\Pay\Contracts\GatewayInterface;
  4. use Yansongda\Pay\Exceptions\GatewayException;
  5. use Yansongda\Pay\Exceptions\InvalidArgumentException;
  6. use Yansongda\Pay\Support\Config;
  7. use Yansongda\Pay\Traits\HasHttpRequest;
  8. abstract class Alipay implements GatewayInterface
  9. {
  10. use HasHttpRequest;
  11. /**
  12. * @var string
  13. */
  14. protected $gateway = 'https://openapi.alipay.com/gateway.do?charset=UTF-8';
  15. /**
  16. * alipay global config params.
  17. *
  18. * @var array
  19. */
  20. protected $config;
  21. /**
  22. * user's config params.
  23. *
  24. * @var \Yansongda\Pay\Support\Config
  25. */
  26. protected $user_config;
  27. /**
  28. * [__construct description].
  29. *
  30. * @author yansongda <me@yansongda.cn>
  31. *
  32. * @param array $config [description]
  33. */
  34. public function __construct(array $config)
  35. {
  36. $this->user_config = new Config($config);
  37. if (is_null($this->user_config->get('app_id'))) {
  38. throw new InvalidArgumentException('Missing Config -- [app_id]');
  39. }
  40. $this->config = [
  41. 'app_id' => $this->user_config->get('app_id'),
  42. 'method' => '',
  43. 'format' => 'JSON',
  44. 'charset' => 'UTF-8',
  45. 'sign_type' => 'RSA2',
  46. 'version' => '1.0',
  47. 'return_url' => $this->user_config->get('return_url', ''),
  48. 'notify_url' => $this->user_config->get('notify_url', ''),
  49. 'timestamp' => date('Y-m-d H:i:s'),
  50. 'sign' => '',
  51. 'biz_content' => '',
  52. ];
  53. }
  54. /**
  55. * pay a order.
  56. *
  57. * @author yansongda <me@yansongda.cn>
  58. *
  59. * @param array $config_biz
  60. *
  61. * @return mixed
  62. */
  63. public function pay(array $config_biz)
  64. {
  65. $config_biz['product_code'] = $this->getProductCode();
  66. $this->config['method'] = $this->getMethod();
  67. $this->config['biz_content'] = json_encode($config_biz);
  68. $this->config['sign'] = $this->getSign();
  69. }
  70. /**
  71. * refund a order.
  72. *
  73. * @author yansongda <me@yansongda.cn>
  74. *
  75. * @param mixed $config_biz
  76. *
  77. * @return array|bool
  78. */
  79. public function refund($config_biz, $refund_amount = null)
  80. {
  81. if (!is_array($config_biz)) {
  82. $config_biz = [
  83. 'out_trade_no' => $config_biz,
  84. 'refund_amount' => $refund_amount,
  85. ];
  86. }
  87. return $this->getResult($config_biz, 'alipay.trade.refund');
  88. }
  89. /**
  90. * close a order.
  91. *
  92. * @author yansongda <me@yansongda.cn>
  93. *
  94. * @param array|string $config_biz
  95. *
  96. * @return array|bool
  97. */
  98. public function close($config_biz)
  99. {
  100. if (!is_array($config_biz)) {
  101. $config_biz = [
  102. 'out_trade_no' => $config_biz,
  103. ];
  104. }
  105. return $this->getResult($config_biz, 'alipay.trade.close');
  106. }
  107. /**
  108. * find a order.
  109. *
  110. * @author yansongda <me@yansongda.cn>
  111. *
  112. * @param string $out_trade_no
  113. *
  114. * @return array|bool
  115. */
  116. public function find($out_trade_no = '')
  117. {
  118. $config_biz = [
  119. 'out_trade_no' => $out_trade_no,
  120. ];
  121. return $this->getResult($config_biz, 'alipay.trade.query');
  122. }
  123. /**
  124. * verify the notify.
  125. *
  126. * @author yansongda <me@yansongda.cn>
  127. *
  128. * @param array $data
  129. * @param string $sign
  130. * @param bool $sync
  131. *
  132. * @return array|bool
  133. */
  134. public function verify($data, $sign = null, $sync = false)
  135. {
  136. if (is_null($this->user_config->get('ali_public_key'))) {
  137. throw new InvalidArgumentException('Missing Config -- [ali_public_key]');
  138. }
  139. $sign = is_null($sign) ? $data['sign'] : $sign;
  140. $res = "-----BEGIN PUBLIC KEY-----\n".
  141. wordwrap($this->user_config->get('ali_public_key'), 64, "\n", true).
  142. "\n-----END PUBLIC KEY-----";
  143. $toVerify = $sync ? json_encode($data) : $this->getSignContent($data, true);
  144. return openssl_verify($toVerify, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1 ? $data : false;
  145. }
  146. /**
  147. * get method config.
  148. *
  149. * @author yansongda <me@yansongda.cn>
  150. *
  151. * @return string
  152. */
  153. abstract protected function getMethod();
  154. /**
  155. * get productCode config.
  156. *
  157. * @author yansongda <me@yansongda.cn>
  158. *
  159. * @return string
  160. */
  161. abstract protected function getProductCode();
  162. /**
  163. * build pay html.
  164. *
  165. * @author yansongda <me@yansongda.cn>
  166. *
  167. * @return string
  168. */
  169. protected function buildPayHtml()
  170. {
  171. $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->gateway."' method='POST'>";
  172. foreach ($this->config as $key => $val) {
  173. $val = str_replace("'", '&apos;', $val);
  174. $sHtml .= "<input type='hidden' name='".$key."' value='".$val."'/>";
  175. }
  176. $sHtml .= "<input type='submit' value='ok' style='display:none;'></form>";
  177. $sHtml .= "<script>document.forms['alipaysubmit'].submit();</script>";
  178. return $sHtml;
  179. }
  180. /**
  181. * get alipay api result.
  182. *
  183. * @author yansongda <me@yansongda.cn>
  184. *
  185. * @param array $config_biz
  186. * @param string $method
  187. *
  188. * @return array|bool
  189. */
  190. protected function getResult($config_biz, $method)
  191. {
  192. $this->config['biz_content'] = json_encode($config_biz);
  193. $this->config['method'] = $method;
  194. $this->config['sign'] = $this->getSign();
  195. $this->config = array_filter($this->config, function ($value) {
  196. return $value !== '' && !is_null($value);
  197. });
  198. $method = str_replace('.', '_', $method).'_response';
  199. $data = json_decode($this->post($this->gateway, $this->config), true);
  200. if (!isset($data[$method]['code']) || $data[$method]['code'] !== '10000') {
  201. throw new GatewayException(
  202. 'get result error:'.$data[$method]['msg'].' - '.$data[$method]['sub_code'],
  203. $data[$method]['code'],
  204. $data);
  205. }
  206. return $this->verify($data[$method], $data['sign'], true);
  207. }
  208. /**
  209. * get sign.
  210. *
  211. * @author yansongda <me@yansongda.cn>
  212. *
  213. * @return string
  214. */
  215. protected function getSign()
  216. {
  217. if (is_null($this->user_config->get('private_key'))) {
  218. throw new InvalidArgumentException('Missing Config -- [private_key]');
  219. }
  220. $res = "-----BEGIN RSA PRIVATE KEY-----\n".
  221. wordwrap($this->user_config->get('private_key'), 64, "\n", true).
  222. "\n-----END RSA PRIVATE KEY-----";
  223. openssl_sign($this->getSignContent($this->config), $sign, $res, OPENSSL_ALGO_SHA256);
  224. return base64_encode($sign);
  225. }
  226. /**
  227. * get signContent that is to be signed.
  228. *
  229. * @author yansongda <me@yansongda.cn>
  230. *
  231. * @param array $toBeSigned
  232. * @param bool $verify
  233. *
  234. * @return string
  235. */
  236. protected function getSignContent(array $toBeSigned, $verify = false)
  237. {
  238. ksort($toBeSigned);
  239. $stringToBeSigned = '';
  240. foreach ($toBeSigned as $k => $v) {
  241. if ($verify && $k != 'sign' && $k != 'sign_type') {
  242. $stringToBeSigned .= $k.'='.$v.'&';
  243. }
  244. if (!$verify && $v !== '' && !is_null($v) && $k != 'sign' && '@' != substr($v, 0, 1)) {
  245. $stringToBeSigned .= $k.'='.$v.'&';
  246. }
  247. }
  248. $stringToBeSigned = substr($stringToBeSigned, 0, -1);
  249. unset($k, $v);
  250. return $stringToBeSigned;
  251. }
  252. }