WechatTransfer.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php namespace App\Http\Controllers\Api;
  2. use App\Http\Controllers\Api\Api;
  3. use App\Models\Custom;
  4. use App\Facades\Servers\Logs\Log;
  5. use App\Models\CustomAmount;
  6. use App\Models\CustomAmountRecord;
  7. use Illuminate\Http\Request;
  8. use App\Servers\WechatPay\Transfer;
  9. use Kra8\Snowflake\Snowflake;
  10. use App\Servers\WechatMini\Mini;
  11. use Illuminate\Support\Facades\DB;
  12. use WeChatPay\Formatter;
  13. use WeChatPay\Crypto\AesGcm;
  14. use WeChatPay\Crypto\Rsa;
  15. /**
  16. * 微信支付接口
  17. *
  18. * @author JUN
  19. *
  20. * */
  21. class WechatTransfer extends Api{
  22. /**
  23. * 小程序微信支付下单 /api/wechat_pay/pay
  24. *
  25. * */
  26. public function transfer(Custom $Custom,CustomAmount $CustomAmount,CustomAmountRecord $CustomAmountRecord)
  27. {
  28. // 检查登录
  29. $uid = $this->checkLogin();
  30. $code = request('code','');
  31. $amount = request('amount','');
  32. // 获取余额信息
  33. $amountInfo = $CustomAmount::query()->where(['custom_uid'=>$uid])->first();
  34. if(!$amountInfo){
  35. $result = $CustomAmountRecord::query()->insert(['custom_uid'=>$uid,'insert_time'=>time(),'update_time'=>time()]);
  36. if (!$result){
  37. return json_send(['code'=>'error','msg'=>'提现失败','data'=>['error'=>'提现失败']]);
  38. }
  39. $balance = 0;
  40. }else{
  41. $balance = $amountInfo['amount'];
  42. }
  43. // 如果剩余余额不足
  44. if( $balance < $amount ) return json_send(['code'=>'error','msg'=>'余额不足','data'=>['error'=>'余额不足']]);
  45. //获取openid
  46. $resultOpenid = Mini::jscode2session($code);
  47. if (!$resultOpenid['openid']) return json_send(['code'=>'error','msg'=>'获取openid失败','data'=>$resultOpenid['error']]);
  48. $Snowflake = new Snowflake();
  49. $SnowflakeId = $Snowflake->next();
  50. $params['amount'] = $amount;
  51. $params['out_bill_no'] = $SnowflakeId;
  52. $params['openid'] = $resultOpenid['openid'];
  53. DB::beginTransaction();
  54. try {
  55. //扣除用户余额
  56. $amountUpdate = [
  57. 'amount'=>DB::raw('amount-'.$amount),
  58. 'use_amount'=>DB::raw('use_amount+'.$amount),
  59. 'transfer_amount'=>DB::raw('transfer_amount+'.$amount),
  60. 'update_time'=>time()
  61. ];
  62. $result = $CustomAmount::query()->where(['custom_uid'=>$uid])->update($amountUpdate);
  63. if (!$result) {
  64. return json_send(['code'=>'error','msg'=>'提现申请失败','data'=>['error'=>'扣除用户余额失败']]);
  65. }
  66. $Transfer = new Transfer();
  67. $transferResult = $Transfer->pay($params);
  68. if (!$transferResult || $transferResult['state'] !== 'WAIT_USER_CONFIRM') return json_send(['code'=>'error','msg'=>'提现申请失败','data'=>'提现申请失败']);
  69. $data = [
  70. 'custom_uid' => $uid,
  71. 'out_bill_no' => $transferResult['out_bill_no'],
  72. 'transfer_bill_no' => $transferResult['transfer_bill_no'],
  73. 'prefix' => 2,
  74. 'amount' => $amount,
  75. 'buy_type' => 2,
  76. 'pay_type' => 1,
  77. 'balance' => $balance,
  78. 'description' => '余额提现',
  79. 'status' => 1,
  80. ];
  81. //写入余额记录
  82. $recordResult = $CustomAmountRecord->add($data);
  83. if (!$recordResult) {
  84. DB::rollBack();
  85. return json_send(['code'=>'error','msg'=>'提现申请失败','data'=>'写入余额记录失败']);
  86. }
  87. // 提交数据
  88. DB::commit();
  89. }catch (\Exception $e){
  90. DB::rollBack();
  91. return json_send(['code'=>'error','msg'=>'提现申请失败','data'=>'提现申请失败']);
  92. }
  93. $list = [
  94. 'package_info' => $transferResult['package_info'],
  95. 'out_bill_no' => $transferResult['out_bill_no'],
  96. ];
  97. return json_send(['code'=>'success','msg'=>'成功','data'=>$list]);
  98. }
  99. /**
  100. * 小程序微信支付回调 /api/wechat_pay/notify
  101. *
  102. * */
  103. public function notify(Custom $Custom,CustomScore $CustomScore)
  104. {
  105. $content = file_get_contents("php://input");
  106. if (!empty($content)) {
  107. //直接json字符串
  108. $params = $content;
  109. } elseif (!empty($_POST)) {
  110. //直接POST数据
  111. $params = $_POST;
  112. } else {
  113. $params = [];
  114. }
  115. $post_data = $params;
  116. Log::log('notify_wechat_transfer', 'post_data:' . $post_data);
  117. //获取headers参数
  118. $headers = request()->header();
  119. Log::log('notify_wechat_transfer', '微信支付回调返回headers参数:' . json_encode($headers));
  120. $inWechatpaySignature = $headers['wechatpay-signature'][0];
  121. $inWechatpayTimestamp = $headers['wechatpay-timestamp'][0];
  122. $inWechatpaySerial = $headers['wechatpay-serial'][0];
  123. $inWechatpayNonce = $headers['wechatpay-nonce'][0];
  124. $inBody = $post_data;
  125. Log::log('notify_wechat_transfer', 'wechatpay-timestamp:' . $inWechatpayTimestamp);
  126. $apiv3Key = Config('wechatpay.APIV3');// 在商户平台上设置的APIv3密钥
  127. // 根据通知的平台证书序列号,查询本地平台证书文件,
  128. $platformCertificateFilePath = Config('wechatpay.platformCertificate');
  129. $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
  130. try {
  131. // 检查通知时间偏移量,允许5分钟之内的偏移
  132. $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
  133. Log::log('notify_wechat_transfer', '时间偏移量:' . $timeOffsetStatus);
  134. $verifiedStatus = Rsa::verify(
  135. // 构造验签名串
  136. Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
  137. $inWechatpaySignature,
  138. $platformPublicKeyInstance
  139. );
  140. Log::log('notify_wechat_transfer', '验签:' . $verifiedStatus.'验签inWechatpayTimestamp:' . $inWechatpayTimestamp.'验签verifiedStatus:' . $inWechatpayNonce);
  141. if ($timeOffsetStatus && $verifiedStatus) {
  142. // 转换通知的JSON文本消息为PHP Array数组
  143. $inBodyArray = (array)json_decode($inBody, true);
  144. // 使用PHP7的数据解构语法,从Array中解构并赋值变量
  145. ['resource' => [
  146. 'ciphertext' => $ciphertext,
  147. 'nonce' => $nonce,
  148. 'associated_data' => $aad
  149. ]] = $inBodyArray;
  150. // 加密文本消息解密
  151. $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
  152. // 把解密后的文本转换为PHP Array数组
  153. $inBodyResourceArray = (array)json_decode($inBodyResource, true);
  154. Log::log('notify_wechat_transfer', '打印解密后的结果:' . json_encode($inBodyResourceArray));
  155. Log::log('notify_wechat_transfer', '参数:' . $inBodyResourceArray['trade_state'] . '订单号' . $inBodyResourceArray['out_trade_no'] . '微信支付号' . $inBodyResourceArray['transaction_id']);
  156. if ($inBodyResourceArray['trade_state'] == "SUCCESS") {
  157. $status = 2;
  158. //更新余额订单状态
  159. $data = [
  160. 'pay_time' => time(),
  161. 'status'=>$status,
  162. ];
  163. $res = CustomAmountRecord::query()->where('out_bill_no','=',$inBodyResourceArray['out_bill_no'])->update($data);
  164. Log::log('notify_wechat_transfer', '更新余额记录,snowflake_id:'.$inBodyResourceArray['out_bill_no'].';'. json_encode($res));
  165. if (!$res) {
  166. Log::log('notify_wechat_transfer', '更新余额记录失败' . json_encode($res));
  167. return json_send(['code'=>'FAIL']);
  168. }
  169. Log::log('notify_wechat_pay', '支付回调完成 通知返回' . json_encode($data));
  170. }elseif ($inBodyResourceArray['trade_state'] == "FAIL"){
  171. $status = 3;
  172. //更新余额订单状态
  173. $data = [
  174. 'status'=>$status
  175. ];
  176. $res = CustomAmountRecord::query()->where('out_bill_no','=',$inBodyResourceArray['out_bill_no'])->update($data);
  177. Log::log('notify_wechat_transfer', '更新余额记录,snowflake_id:'.$inBodyResourceArray['out_bill_no'].';'. json_encode($res));
  178. if (!$res) {
  179. Log::log('notify_wechat_transfer', '更新余额记录失败' . json_encode($res));
  180. return json_send(['code'=>'FAIL']);
  181. }
  182. $amountInfo = CustomAmountRecord::query()->where('out_bill_no','=',$inBodyResourceArray['out_bill_no'])->first();
  183. if ($amountInfo){
  184. Log::log('notify_wechat_transfer', '获取余额记录信息失败' . json_encode($res));
  185. return json_send(['code'=>'FAIL']);
  186. }
  187. //退回余额
  188. $res = CustomAmount::query()->where('uid','=',$amountInfo['custom_uid'])->increment('amount',$amountInfo['amount']);
  189. if (!$res){
  190. Log::log('notify_wechat_pay', '退回余额失败' . json_encode($amountInfo));
  191. return json_send(['code'=>'FAIL']);
  192. }
  193. Log::log('notify_wechat_pay', '回调完成 通知返回' . json_encode($amountInfo));
  194. }
  195. return json_send(['code'=>'SUCCESS']);
  196. }else{
  197. return json_send(['code'=>'FAIL']);
  198. }
  199. }catch (\Exception $e){
  200. Log::log('notify_wechat_transfer', '回调失败getMessage:' . $e->getMessage());
  201. }
  202. return json_send(['code'=>'SUCCESS']);
  203. }
  204. }