Callback.php 7.9 KB

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