2
0

StarPayService.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. namespace App\Services;
  3. use App\Constant\Payment;
  4. use App\Util;
  5. use GuzzleHttp\Client;
  6. use Illuminate\Support\Facades\Log;
  7. class StarPayService
  8. {
  9. protected $code = 'StarPay';
  10. protected $config;
  11. const STATUS_MAP = [
  12. 0 => Payment::STATUS_IN_PROGRESS,
  13. 1 => Payment::STATUS_IN_PROGRESS,
  14. 2 => Payment::STATUS_SUCCESS,
  15. 3 => Payment::STATUS_SUCCESS,
  16. -1 => Payment::STATUS_FAIL,
  17. -2 => Payment::STATUS_FAIL,
  18. -3 => Payment::STATUS_REFUND,
  19. -4 => Payment::STATUS_IN_PROGRESS,
  20. ];
  21. public function __construct($config = [])
  22. {
  23. if (!$config) {
  24. $config = config('pay.StarPay');
  25. }
  26. $this->config = $config;
  27. }
  28. public function create($orderSn, $amount, $options = [])
  29. {
  30. $data = [];
  31. $data['appId'] = $this->config['appID'];
  32. $data['merOrderNo'] = $orderSn;
  33. $data['currency'] = 'MXN';
  34. $data['amount'] = strval($amount);
  35. $data['extra'] = [
  36. 'single' => false,
  37. 'minAmount' => $this->config['minOrderAmount'] ?? 20,
  38. 'maxAmount' => $this->config['maxOrderAmount'] ?? 50000,
  39. ];
  40. $data['returnUrl'] = $this->config['syncNotify'];
  41. $data['notifyUrl'] = $this->config['notify'];
  42. $data['attach'] = strval($options['userId'] ?? '0');
  43. $data['sign'] = $this->sign($this->config['secretKey'], $data);
  44. $client = new Client(['verify' => false, 'timeout' => 10]);
  45. $url = $this->config['baseUrl'] . '/api/v1/payment/order/create';
  46. $request_extra = \GuzzleHttp\json_encode($data);
  47. CreateLog::pay_request('', $request_extra, $orderSn, '', '', '');
  48. Util::WriteLog('StarPay', 'payin request: ' . $url . ' | ' . $request_extra);
  49. $resp = $client->post($url, [
  50. 'json' => $data,
  51. ]);
  52. if ($resp->getStatusCode() !== 200) {
  53. return $resp->getReasonPhrase();
  54. }
  55. $content = $resp->getBody()->getContents();
  56. Util::WriteLog('StarPay', 'payin response: ' . $url . ' | ' . $request_extra . '|' . $content);
  57. $json = json_decode($content, true);
  58. if (json_last_error() !== JSON_ERROR_NONE) {
  59. Util::WriteLog('StarPay_error', [
  60. 'result' => $content,
  61. 'url' => $url,
  62. ]);
  63. }
  64. return $json;
  65. }
  66. public function cash($OrderId, $amount, $bankCode, $bankName, $accountNo, $accountName)
  67. {
  68. $data = [
  69. 'appId' => $this->config['appID'],
  70. 'merOrderNo' => $OrderId,
  71. 'currency' => 'MXN',
  72. 'amount' => strval(round($amount, 2)),
  73. 'notifyUrl' => $this->config['notify'],
  74. 'extra' => [
  75. 'bankCode' => $bankCode,
  76. 'bankName' => $bankName,
  77. 'accountNo' => $accountNo,
  78. 'accountName' => $accountName,
  79. 'accountType' => strlen($accountNo) == 16 ? 3 : 40,
  80. ],
  81. ];
  82. $data['sign'] = $this->sign($this->config['secretKey'], $data);
  83. $url = $this->config['baseUrl'] . '/api/v1/payout/order/create';
  84. Util::WriteLog('StarPay', 'payout request: ' . $url . ' | ' . json_encode($data, JSON_UNESCAPED_UNICODE));
  85. $client = new Client(['verify' => false, 'timeout' => 10]);
  86. $resp = $client->post($url, [
  87. 'json' => $data,
  88. ]);
  89. $content = $resp->getBody()->getContents();
  90. Util::WriteLog('StarPay', 'payout response: ' . $content);
  91. $json = json_decode($content, true);
  92. if (json_last_error() !== JSON_ERROR_NONE) {
  93. Util::WriteLog('StarPay_error', ['decode fail', json_last_error_msg()]);
  94. return false;
  95. }
  96. return $json;
  97. }
  98. public function verifySign($data, $headers = []): bool
  99. {
  100. $sign = $this->sign($this->config['secretKey'], $data);
  101. return $sign == $data['sign'];
  102. }
  103. public function notifyFail()
  104. {
  105. return 'fail';
  106. }
  107. public function notifySuccess()
  108. {
  109. return 'success';
  110. }
  111. public function notify($data)
  112. {
  113. if (!isset($data['orderStatus'])) {
  114. throw new \RuntimeException('invalid notify data');
  115. }
  116. $r = new \stdClass();
  117. $r->status = self::STATUS_MAP[$data['orderStatus']] ?? Payment::STATUS_UNKNOWN;
  118. $r->orderNo = $data['orderNo'];
  119. $r->orderSn = $data['merOrderNo'];
  120. $r->amount = $data['paidAmount'];
  121. $r->buyerId = $data['attach'];
  122. return $r;
  123. }
  124. public function cashNotify($data)
  125. {
  126. $r = new \stdClass();
  127. $r->status = self::STATUS_MAP[$data['orderStatus']] ?? Payment::STATUS_UNKNOWN;
  128. $r->orderNo = $data['orderNo'];
  129. $r->orderSn = $data['merOrderNo'];
  130. $r->amount = $data['amount'];
  131. return $r;
  132. }
  133. public function search($orderNo, $orderSn)
  134. {
  135. $data = [];
  136. $data['appId'] = $this->config['appID'];
  137. $data['orderNo'] = $orderNo;
  138. $data['sign'] = $this->sign($this->config['secretKey'], $data);
  139. $client = new Client(['verify' => false, 'timeout' => 10]);
  140. $url = $this->config['baseUrl'] . '/api/v1/payment/order/query';
  141. Log::info('StarPay查询参数', [
  142. 'data' => $data,
  143. ]);
  144. $resp = $client->get($url . '?' . http_build_query($data));
  145. $r = new \stdClass();
  146. $r->status = false;
  147. if ($resp->getStatusCode() !== 200) {
  148. $this->error = $resp->getReasonPhrase();
  149. return $r;
  150. }
  151. $content = $resp->getBody()->getContents();
  152. Log::info('StarPay查询返回结果.', ['res' => $content, 'url' => $url]);
  153. $json = json_decode($content, true);
  154. if (json_last_error() !== JSON_ERROR_NONE) {
  155. Log::error('StarPay json_decode error', [
  156. 'result' => $content,
  157. 'url' => $url,
  158. ]);
  159. return $r;
  160. }
  161. $r->status = self::STATUS_MAP[$json['orderStatus']] ?? Payment::STATUS_UNKNOWN;
  162. return $r;
  163. }
  164. public function sign($secretKey, $data)
  165. {
  166. $copy = $data;
  167. unset($copy['sign']);
  168. $str = self::joinMap($copy);
  169. $str .= "&key=$secretKey";
  170. return hash('sha256', $str);
  171. }
  172. protected static function joinMap($arr)
  173. {
  174. ksort($arr);
  175. $pair = [];
  176. foreach ($arr as $k => $v) {
  177. if (is_array($v)) {
  178. $pair[] = "$k=" . self::joinMap($v);
  179. } else {
  180. $pair[] = "$k=" . (is_bool($v) ? json_encode($v) : $v);
  181. }
  182. }
  183. return implode('&', $pair);
  184. }
  185. }