AlipayF2F.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <?php
  2. namespace Library;
  3. use Illuminate\Support\Facades\Http;
  4. class AlipayF2F {
  5. private $appId;
  6. private $privateKey;
  7. private $alipayPublicKey;
  8. private $signType = 'RSA2';
  9. public $bizContent;
  10. public $method;
  11. public $notifyUrl;
  12. public $response;
  13. public function __construct()
  14. {
  15. }
  16. public function verify($data): bool
  17. {
  18. if (is_string($data)) {
  19. parse_str($data, $data);
  20. }
  21. $sign = $data['sign'];
  22. unset($data['sign']);
  23. unset($data['sign_type']);
  24. ksort($data);
  25. $data = $this->buildQuery($data);
  26. $res = "-----BEGIN PUBLIC KEY-----\n" .
  27. wordwrap($this->alipayPublicKey, 64, "\n", true) .
  28. "\n-----END PUBLIC KEY-----";
  29. if ("RSA2" == $this->signType) {
  30. $result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1);
  31. } else {
  32. $result = (openssl_verify($data, base64_decode($sign), $res) === 1);
  33. }
  34. openssl_free_key(openssl_get_publickey($res));
  35. return $result;
  36. }
  37. public function setBizContent($bizContent = [])
  38. {
  39. $this->bizContent = json_encode($bizContent);
  40. }
  41. public function setMethod($method)
  42. {
  43. $this->method = $method;
  44. }
  45. public function setAppId($appId)
  46. {
  47. $this->appId = $appId;
  48. }
  49. public function setPrivateKey($privateKey)
  50. {
  51. $this->privateKey = $privateKey;
  52. }
  53. public function setAlipayPublicKey($alipayPublicKey)
  54. {
  55. $this->alipayPublicKey = $alipayPublicKey;
  56. }
  57. public function setNotifyUrl($url)
  58. {
  59. $this->notifyUrl = $url;
  60. }
  61. public function send()
  62. {
  63. $response = Http::get('https://openapi.alipay.com/gateway.do', $this->buildParam())->json();
  64. $resKey = str_replace('.', '_', $this->method) . '_response';
  65. if (!isset($response[$resKey])) throw new \Exception('从支付宝请求失败');
  66. $response = $response[$resKey];
  67. if ($response['msg'] !== 'Success') throw new \Exception($response['sub_msg']);
  68. $this->response = $response;
  69. }
  70. public function getQrCodeUrl()
  71. {
  72. $response = $this->response;
  73. if (!isset($response['qr_code'])) throw new \Exception('获取付款二维码失败');
  74. return $response['qr_code'];
  75. }
  76. public function getResponse()
  77. {
  78. return $this->response;
  79. }
  80. public function buildParam(): array
  81. {
  82. $params = [
  83. 'app_id' => $this->appId,
  84. 'method' => $this->method,
  85. 'charset' => 'UTF-8',
  86. 'sign_type' => $this->signType,
  87. 'timestamp' => date('Y-m-d H:m:s'),
  88. 'biz_content' => $this->bizContent,
  89. 'version' => '1.0',
  90. '_input_charset' => 'UTF-8'
  91. ];
  92. if ($this->notifyUrl) $params['notify_url'] = $this->notifyUrl;
  93. ksort($params);
  94. $params['sign'] = $this->buildSign($this->buildQuery($params));
  95. return $params;
  96. }
  97. public function buildQuery($query)
  98. {
  99. if (!$query) {
  100. throw new \Exception('参数构造错误');
  101. }
  102. //将要 参数 排序
  103. ksort($query);
  104. //重新组装参数
  105. $params = array();
  106. foreach ($query as $key => $value) {
  107. $params[] = $key . '=' . $value;
  108. }
  109. $data = implode('&', $params);
  110. return $data;
  111. }
  112. private function buildSign(string $signData): string
  113. {
  114. $privateKey = $this->privateKey;
  115. $p_key = array();
  116. //如果私钥是 1行
  117. if (!stripos($privateKey, "\n")) {
  118. $i = 0;
  119. while ($key_str = substr($privateKey, $i * 64, 64)) {
  120. $p_key[] = $key_str;
  121. $i++;
  122. }
  123. }
  124. $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . implode("\n", $p_key);
  125. $privateKey = $privateKey . "\n-----END RSA PRIVATE KEY-----";
  126. //私钥
  127. $privateId = openssl_pkey_get_private($privateKey, '');
  128. // 签名
  129. $signature = '';
  130. if ("RSA2" == $this->signType) {
  131. openssl_sign($signData, $signature, $privateId, OPENSSL_ALGO_SHA256);
  132. } else {
  133. openssl_sign($signData, $signature, $privateId, OPENSSL_ALGO_SHA1);
  134. }
  135. openssl_free_key($privateId);
  136. //加密后的内容通常含有特殊字符,需要编码转换下
  137. $signature = base64_encode($signature);
  138. return $signature;
  139. }
  140. }