Callback.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <?php
  2. namespace app\api\controller;
  3. use app\admin\command\Api;
  4. use app\api\service\CallbackService;
  5. use app\api\service\WxService;
  6. use think\Response;
  7. use WeChatPay\Crypto\AesGcm;
  8. use WeChatPay\Crypto\Rsa;
  9. use WeChatPay\Formatter;
  10. class Callback extends Api
  11. {
  12. protected $noNeedLogin = ["*"];
  13. private $service;
  14. public function __construct($name = null)
  15. {
  16. $this->service = new CallbackService();
  17. parent::__construct($name);
  18. }
  19. public function index()
  20. {
  21. return 1;
  22. }
  23. //获取微信回调http头信息
  24. private function getHeaders()
  25. {
  26. $headers = [];
  27. foreach ($_SERVER as $key => $value) {
  28. if ('HTTP_' == substr($key, 0, 5)) {
  29. $headers[str_replace('_', '-', substr($key, 5))] = $value;
  30. }
  31. if (isset($_SERVER['PHP_AUTH_DIGEST'])) {
  32. $header['AUTHORIZATION'] = $_SERVER['PHP_AUTH_DIGEST'];
  33. } elseif (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
  34. $header['AUTHORIZATION'] = base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW']);
  35. }
  36. if (isset($_SERVER['CONTENT_LENGTH'])) {
  37. $header['CONTENT-LENGTH'] = $_SERVER['CONTENT_LENGTH'];
  38. }
  39. if (isset($_SERVER['CONTENT_TYPE'])) {
  40. $header['CONTENT-TYPE'] = $_SERVER['CONTENT_TYPE'];
  41. }
  42. }
  43. return $headers;
  44. }
  45. private function verify()
  46. {
  47. $header = $this->getHeaders();
  48. // 请根据实际情况获取
  49. $inWechatpaySignature = isset($header['WECHATPAY-SIGNATURE']) ? $header['WECHATPAY-SIGNATURE'] : "";
  50. // 请根据实际情况获取
  51. $inWechatpayTimestamp = isset($header['WECHATPAY-TIMESTAMP']) ? $header['WECHATPAY-TIMESTAMP'] : "";
  52. // 请根据实际情况获取
  53. // $inWechatpaySerial = $header['WECHATPAY-SERIAL'];
  54. // 请根据实际情况获取
  55. $inWechatpayNonce = isset($header['WECHATPAY-NONCE']) ? $header['WECHATPAY-NONCE'] : "";
  56. //读取微信传过来的信息,是一个json字符串
  57. $inBody = file_get_contents('php://input');
  58. $config = WxService::wxPayConfigByType("app");
  59. // 在商户平台上设置的APIv3密钥
  60. $apiv3Key = $config["mch_v3_api"];
  61. // 根据通知的平台证书序列号,查询本地平台证书文件
  62. $platformPublicKeyInstance = Rsa::from("file://" . $config["platform_certificate_file_path"], Rsa::KEY_TYPE_PUBLIC);
  63. // 检查通知时间偏移量,允许5分钟之内的偏移
  64. $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
  65. // 构造验签名串
  66. $verifiedStatus = Rsa::verify(
  67. Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
  68. $inWechatpaySignature,
  69. $platformPublicKeyInstance
  70. );
  71. return [
  72. $timeOffsetStatus,
  73. $verifiedStatus,
  74. $inBody,
  75. $apiv3Key
  76. ];
  77. }
  78. /**
  79. * @throws \Exception
  80. */
  81. public function wxpay()
  82. {
  83. list($timeOffsetStatus, $verifiedStatus, $inBody, $apiv3Key) = $this->verify();
  84. if ($timeOffsetStatus && $verifiedStatus) {
  85. // 转换通知的JSON文本消息为PHP Array数组
  86. $inBodyArray = (array)json_decode($inBody, true);
  87. // 使用PHP7的数据解构语法,从Array中解构并赋值变量
  88. ['resource' => [
  89. 'ciphertext' => $ciphertext,
  90. 'nonce' => $nonce,
  91. 'associated_data' => $aad
  92. ]] = $inBodyArray;
  93. // 加密文本消息解密
  94. $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
  95. // 把解密后的文本转换为PHP Array数组
  96. $inBodyResourceArray = (array)json_decode($inBodyResource, true);
  97. if (isset($inBodyResourceArray["out_trade_no"]) && isset($inBodyResourceArray["trade_state"])) {
  98. if ($inBodyResourceArray["trade_state"] == "SUCCESS") {
  99. if (false !== stristr($inBodyResourceArray["out_trade_no"], "RBU")) {
  100. $res = $this->service->rechargeBalanceSuccess($inBodyResourceArray["out_trade_no"]);
  101. } else if (false !== stristr($inBodyResourceArray["out_trade_no"], "RBM")) {
  102. $res = $this->service->rechargeMembershipSuccess($inBodyResourceArray["out_trade_no"]);
  103. } else {
  104. $res = $this->service->payOrderSuccess($inBodyResourceArray["out_trade_no"]);
  105. }
  106. $response_code = 200;
  107. $response_body = ["code" => "SUCCESS", "message" => "成功"];
  108. if (0 == $res->code()) {
  109. $response_code = 500;
  110. $response_body = ["code" => "FAIL", "message" => "失败"];
  111. }
  112. \think\Log::custom_log("微信支付回调 no:{$inBodyResourceArray["out_trade_no"]}!",
  113. [
  114. "inBodyResourceArray" => $inBodyResourceArray,
  115. "res" => [$res->code(), $res->msg(), $res->data()],
  116. "response_code" => $response_code,
  117. "response_body" => $response_body
  118. ]);
  119. return new Response(json_encode($response_body), $response_code);
  120. }
  121. }
  122. }
  123. \think\Log::custom_log("微信支付回调, 支付失败! inBody", $inBody);
  124. return new Response(json_encode(["code" => "FAIL", "message" => "失败"]), 500);
  125. }
  126. public function wxrefund()
  127. {
  128. list($timeOffsetStatus, $verifiedStatus, $inBody, $apiv3Key) = $this->verify();
  129. if ($timeOffsetStatus && $verifiedStatus) {
  130. // 转换通知的JSON文本消息为PHP Array数组
  131. $inBodyArray = (array)json_decode($inBody, true);
  132. // 使用PHP7的数据解构语法,从Array中解构并赋值变量
  133. ['resource' => [
  134. 'ciphertext' => $ciphertext,
  135. 'nonce' => $nonce,
  136. 'associated_data' => $aad
  137. ]] = $inBodyArray;
  138. // 加密文本消息解密
  139. $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
  140. // 把解密后的文本转换为PHP Array数组
  141. $inBodyResourceArray = (array)json_decode($inBodyResource, true);
  142. if (isset($inBodyResourceArray["out_trade_no"]) && isset($inBodyResourceArray["refund_status"])) {
  143. if ($inBodyResourceArray["refund_status"] == "SUCCESS") {
  144. $res = $this->service->refundSuccess($inBodyResourceArray["out_trade_no"]);
  145. $response_code = 200;
  146. $response_body = ["code" => "SUCCESS", "message" => "成功"];
  147. if (0 == $res->code()) {
  148. $response_code = 500;
  149. $response_body = ["code" => "FAIL", "message" => "失败"];
  150. }
  151. \think\Log::custom_log("微信退款回调 no:{$inBodyResourceArray["out_trade_no"]}!",
  152. [
  153. "inBodyResourceArray" => $inBodyResourceArray,
  154. "res" => [$res->code(), $res->msg(), $res->data()],
  155. "response_code" => $response_code,
  156. "response_body" => $response_body
  157. ]);
  158. return new Response(json_encode($response_body), $response_code);
  159. }
  160. }
  161. }
  162. \think\Log::custom_log("微信支付退款回调, 支付失败! inBody", $inBody);
  163. return new Response(json_encode(["code" => "FAIL", "message" => "失败"]), 500);
  164. }
  165. public function alipay()
  166. {
  167. echo "SUCCESS";
  168. }
  169. }