Callback.php 9.1 KB

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