JdPayService.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. namespace App\Services\Payment;
  3. use App\Services\BaseService;
  4. use GuzzleHttp\Client;
  5. class JdPayService extends BaseService
  6. {
  7. public const CHANNEL = 'JDpay';
  8. public const PAY_STATUS_SUCCESS = '3';
  9. public const PAY_STATUS_FAIL = '4';
  10. public const REMIT_STATUS_SUCCESS = '2';
  11. public const REMIT_STATUS_FAIL = '3';
  12. public static function getMerchantId()
  13. {
  14. return config('app.jd_pay_mch_id');
  15. }
  16. public static function getSecret()
  17. {
  18. return config('app.jd_pay_key');
  19. }
  20. public static function getNotifyUrl(): string
  21. {
  22. return rtrim(config('app.url'), '/') . '/api/pay/harvest';
  23. }
  24. public static function getRemitNotifyUrl(): string
  25. {
  26. return rtrim(config('app.url'), '/') . '/api/pay/notify';
  27. }
  28. public static function getClient(): Client
  29. {
  30. return new Client([
  31. 'timeout' => 10.0,
  32. ]);
  33. }
  34. public static function isChannel(?string $channel): bool
  35. {
  36. return strtolower((string)$channel) === strtolower(self::CHANNEL)
  37. || strtolower((string)$channel) === 'jdpay'
  38. || (string)$channel === 'JD钱包';
  39. }
  40. public static function canUserRecharge($userId): bool
  41. {
  42. $allowedUserIds = array_values(array_filter(array_map(
  43. 'trim',
  44. explode(',', (string)config('app.jd_pay_recharge_user_ids', ''))
  45. )));
  46. if (empty($allowedUserIds)) {
  47. return true;
  48. }
  49. return in_array((string)$userId, $allowedUserIds, true);
  50. }
  51. public static function amount($amount): string
  52. {
  53. return number_format((float)$amount, 2, '.', '');
  54. }
  55. public static function signature(array $values): string
  56. {
  57. $values[] = self::getSecret();
  58. return strtoupper(md5(implode('&', $values)));
  59. }
  60. public static function pay($amount, string $orderNo): array
  61. {
  62. $amount = self::amount($amount);
  63. $data = [
  64. 'userCode' => self::getMerchantId(),
  65. 'orderCode' => $orderNo,
  66. 'amount' => $amount,
  67. 'callbackUrl' => self::getNotifyUrl(),
  68. ];
  69. $data['sign'] = self::signature([$orderNo, $amount, $data['userCode']]);
  70. return self::post(config('app.jd_pay_gateway'), $data);
  71. }
  72. public static function queryPayOrder(string $orderNo = '', string $customerOrderNo = ''): array
  73. {
  74. $data = [
  75. 'userCode' => self::getMerchantId(),
  76. 'orderCode' => $orderNo,
  77. 'customerOrderCode' => $customerOrderNo,
  78. ];
  79. $data['sign'] = self::signature([$data['orderCode'], $data['customerOrderCode'], $data['userCode']]);
  80. return self::post(config('app.jd_pay_query_gateway'), $data);
  81. }
  82. public static function remit($amount, string $orderNo, string $address): array
  83. {
  84. $amount = self::amount($amount);
  85. $data = [
  86. 'userCode' => self::getMerchantId(),
  87. 'orderCode' => $orderNo,
  88. 'amount' => $amount,
  89. 'address' => $address,
  90. 'callbackUrl' => self::getRemitNotifyUrl(),
  91. ];
  92. $data['sign'] = self::signature([$orderNo, $amount, $address, $data['userCode']]);
  93. return self::post(config('app.jd_remit_gateway'), $data);
  94. }
  95. public static function queryRemitOrder(string $orderNo = '', string $customerOrderNo = ''): array
  96. {
  97. $data = [
  98. 'userCode' => self::getMerchantId(),
  99. 'orderCode' => $orderNo,
  100. 'customerOrderCode' => $customerOrderNo,
  101. ];
  102. $data['sign'] = self::signature([$data['orderCode'], $data['customerOrderCode'], $data['userCode']]);
  103. return self::post(config('app.jd_remit_query_gateway'), $data);
  104. }
  105. public static function balance(): array
  106. {
  107. $timestamp = (string)round(microtime(true) * 1000);
  108. $userCode = self::getMerchantId();
  109. $sign = self::signature([$userCode, $timestamp]);
  110. $response = self::getClient()->get(rtrim(config('app.jd_balance_gateway'), '/') . '/' . $userCode, [
  111. 'query' => [
  112. 'timestamp' => $timestamp,
  113. 'sign' => $sign,
  114. ],
  115. ]);
  116. return json_decode($response->getBody()->getContents(), true) ?: [];
  117. }
  118. public static function verifyPayNotify(array $params): bool
  119. {
  120. if (($params['userCode'] ?? '') !== self::getMerchantId()) {
  121. return false;
  122. }
  123. $sign = self::signature([
  124. $params['orderCode'] ?? '',
  125. (string)($params['amount'] ?? ''),
  126. $params['userCode'] ?? '',
  127. (string)($params['status'] ?? ''),
  128. ]);
  129. return hash_equals($sign, strtoupper((string)($params['sign'] ?? '')));
  130. }
  131. public static function verifyRemitNotify(array $params): bool
  132. {
  133. if (($params['userCode'] ?? '') !== self::getMerchantId()) {
  134. return false;
  135. }
  136. $sign = self::signature([
  137. $params['orderCode'] ?? '',
  138. $params['customerOrderCode'] ?? '',
  139. (string)($params['amount'] ?? ''),
  140. $params['userCode'] ?? '',
  141. (string)($params['status'] ?? ''),
  142. ]);
  143. return hash_equals($sign, strtoupper((string)($params['sign'] ?? '')));
  144. }
  145. private static function post(string $url, array $data): array
  146. {
  147. $response = self::getClient()->post($url, [
  148. 'form_params' => $data,
  149. 'headers' => [
  150. 'Content-Type' => 'application/x-www-form-urlencoded',
  151. ],
  152. ]);
  153. return json_decode($response->getBody()->getContents(), true) ?: [];
  154. }
  155. public static function getWhere(array $search = []): array
  156. {
  157. return [];
  158. }
  159. }