WechatTransfer.php 12 KB

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