فهرست منبع

红包,余额,提现功能

jun 4 ماه پیش
والد
کامیت
de1426f338

+ 51 - 30
app/Http/Controllers/Admin/Custom.php

@@ -300,36 +300,57 @@ class Custom extends Auth{
 	/**
 	 * 去下载
 	 */
-	private function  toDown($data){
-		// 创建新的电子表格对象
-		$spreadsheet			= new Spreadsheet();
-		// 设置合并单元格的行和列,例如合并A1到B2的单元格
-		$sheet					= $this->setStyle($spreadsheet);
-		// 从第二行写入
-		$row					= 2;
-		// 循环写入
-		foreach ($data as  $value) {
-			// 单元格内容写入
-			$sheet->setCellValue('A'.$row, $value['custom_code']);
-			//避免 = + - 识别成公式
-            $sheet->setCellValueExplicit('B'.$row, $value['username'],DataType::TYPE_STRING);
-            $sheet->setCellValue('C'.$row, $value['phone']);
-			$sheet->setCellValue('D'.$row, $value['weiban_extid']);
-			$sheet->setCellValue('E'.$row, $value['city_name']);
-			$sheet->setCellValue('F'.$row, $value['status']?'禁用':'正常');
-			$sheet->setCellValue('G'.$row, date('Y-m-d H:i:s',$value['insert_time']));
-			$sheet->setCellValue('H'.$row, date('Y-m-d H:i:s',$value['update_time']));
-			// 行数+1
-			$row++;
-		}
-		// 创建内容
-		$writer 				= IOFactory::createWriter($spreadsheet, 'Xlsx');
-		header('Pragma: public');
-		header('Content-type:application/vnd.ms-excel');
-		header('Content-Disposition: inline;filename=客户列表.xlsx');
-		// 输出数据流
-		return $writer->save('php://output');
-	}
+    private function  toDown($data){
+        $list       =   [];
+        foreach ($data as  $key=>$value) {
+            // 单元格内容写入
+            $list[$key]['custom_code']      =   $value['custom_code'];
+            $list[$key]['username']         =   $value['username'];
+            $list[$key]['phone']            =   $value['phone'];
+            $list[$key]['weiban_extid']     =   $value['weiban_extid'];
+            $list[$key]['city_name']        =   $value['city_name'];
+            $list[$key]['status']           =   $value['status']?'禁用':'正常';
+            $list[$key]['insert_time']      =   date('Y-m-d H:i:s',$value['insert_time']);
+            $list[$key]['update_time']      =   date('Y-m-d H:i:s',$value['update_time']);
+        }
+        try {
+            $config     = [
+                'path' =>public_path().'/uploads/' // xlsx文件保存路径
+            ];
+            $excel      = new \Vtiful\Kernel\Excel($config);
+            $header     =   [
+                '客户编码',
+                '客户昵称',
+                '联系方式',
+                '微伴ID',
+                '客户城市',
+                '客户状态',
+                '创建时间',
+                '更新时间',
+            ];
+            $filePath = $excel->fileName('user01.xlsx', 'sheet1')
+                ->header($header)
+                ->data($list)
+                ->output();
+            $filename   =   '客户列表.xlsx';
+            header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            header('Content-Disposition: attachment;filename="' . $filename . '"');
+            header('Content-Length: ' . filesize($filePath));
+            header('Content-Transfer-Encoding: binary');
+            header('Cache-Control: must-revalidate');
+            header('Cache-Control: max-age=0');
+            header('Pragma: public');
+            ob_clean();
+            flush();
+            if (copy($filePath, 'php://output') === false) {
+                return			json_send(['code'=>'error','msg'=>'下载失败']);
+            }
+            @unlink($filePath);
+            return			json_send(['code'=>'success','msg'=>'下载成功','path'=>'']);
+        }catch (\Exception  $exception) {
+            return			json_send(['code'=>'error','msg'=>'下载失败']);
+        }
+    }
 
 	/**
 	 * 设置表格样式

+ 3 - 3
app/Http/Controllers/Admin/Orders.php

@@ -542,12 +542,12 @@ class Orders extends Auth{
             ob_clean();
             flush();
             if (copy($filePath, 'php://output') === false) {
-                dd('下载出错');
+                return			json_send(['code'=>'error','msg'=>'下载失败']);
             }
             @unlink($filePath);
-            exit();
+            return			json_send(['code'=>'success','msg'=>'下载成功','path'=>'']);
         }catch (\Exception  $exception) {
-            return $exception->getMessage();
+            return			json_send(['code'=>'error','msg'=>'下载失败']);
         }
 	}
 

+ 12 - 1
app/Http/Controllers/Api/Custom.php

@@ -8,6 +8,7 @@ use Vinkla\Hashids\Facades\Hashids;
 use App\Http\Requests\Api\Custom as Request;
 use App\Models\City;
 use App\Models\WeiBan\Follow as WeiBanFollow;
+use App\Models\CustomAmount;
 
 /**
  * 客户接口
@@ -24,7 +25,7 @@ class Custom extends Api{
 	 * @param	string		$code		授权码
 	 * 
 	 * */
-	public function get_info(Model $Model,WeiBanQrcode $WeiBanQrcode,City $City,WeiBanFollow $WeiBanFollow){
+	public function get_info(Model $Model,WeiBanQrcode $WeiBanQrcode,City $City,WeiBanFollow $WeiBanFollow,CustomAmount $CustomAmount){
 		// 接口验签
 		// $this->verify_sign();
 		// 检查登录
@@ -62,6 +63,14 @@ class Custom extends Api{
 		$followQrcode					= $custom['weiban_extid'] ?  ['thumb'=>'','link_url'=>''] : $WeiBanQrcode->getFollowQrcode($custom['city_id']);
 		// 城市ID换城市名
 		$cityName						= (string) $City->getOne($custom['city_id'],'name');
+        //获取用户余额
+        $amountInfo                     = $CustomAmount::query()->where([['custom_id','=',$custom['uid']]])->first(['amount','transfer_amount']);
+        if (!$amountInfo){
+            $amountInfo                 = [
+                'amount'=>0,
+                'transfer_amount'=>0,
+            ];
+        }
 		// 所需数组组合
 		$custom							= [
 											'uid'=>$custom['uid'],
@@ -74,6 +83,8 @@ class Custom extends Api{
 											'city_id'=>$cityName,
 											'follow_qrcode'=>$followQrcode['thumb'],
 											'follow_linkurl'=>$followQrcode['link_url'],
+											'amount'=>$amountInfo['amount'],
+											'transfer_amount'=>$amountInfo['transfer_amount'],
 										];
 		// 返回结果
 		return							json_send(['code'=>'success','msg'=>'获取成功','data'=>$custom]);

+ 60 - 0
app/Http/Controllers/Api/CustomAmount.php

@@ -0,0 +1,60 @@
+<?php namespace App\Http\Controllers\Api;
+
+use App\Models\CustomAmountRecord as CustomAmountRecord;
+use App\Models\CustomAmount as Model;
+
+/**
+ * 用户余额接口
+ * 
+ * @author jun
+ * 
+ * */
+class CustomAmount extends Api{
+	
+	
+	/**
+	 * 获取余额记录			/api/custom_amount/get_record_list
+	 * 
+	 * @param	string		$code		授权码
+	 * 
+	 * */
+	public function get_record_list(Model $Model,CustomAmountRecord $CustomAmountRecord){
+		// 接口验签
+		// $this->verify_sign();
+		// 检查登录
+		$uid							= $this->checkLogin();
+        // 接收参数
+        $limit						= request('limit',15);
+        // 查询条件
+        $map						= [['custom_uid','=',$uid],['status','=',1]];
+        // 查询数据
+        $Paginator					= $CustomAmountRecord->query()->where($map)->orderByDesc('id')
+            ->paginate($limit,['id','buy_type','pay_type','prefix','description','insert_time','pay_time','status','transfer_bill_no']);
+        // 重置数据
+        $list						= [];
+        // 获取数据
+        $list['total']				= $Paginator->total();
+        $list['current_page']		= $Paginator->currentPage();
+        $list['per_page']			= $Paginator->perPage();
+        $list['last_page']			= $Paginator->lastPage();
+        $list['data']				= $Paginator->items();
+        // 循环数据
+        foreach ($list['data'] as $key => $value) {
+            // 处理时间
+            $value['pay_time']		= date('Y-m-d H:i:s',$value['pay_time']);
+            $value['insert_time']	= date('Y-m-d H:i:s',$value['insert_time']);
+            // 获取子列表
+            $value['type_state']	= (string) $CustomAmountRecord->getPayType($value['buy_type'],$value['pay_type'],'name');
+            if ($value['buy_type'] == 2 &&  $value['status'] == 1){
+                $value['state'] 	= '提现中';
+            }
+            // 重组
+            $list['data'][$key] 	= $value;
+        }
+
+		// 返回结果
+		return							json_send(['code'=>'success','msg'=>'获取成功','data'=>$list]);
+	}
+
+
+}

+ 131 - 0
app/Http/Controllers/Api/Redpacket.php

@@ -0,0 +1,131 @@
+<?php namespace App\Http\Controllers\Api;
+
+use App\Http\Requests\Api\Redpacket as Request;
+use App\Models\CustomAmount;
+use App\Models\CustomAmountRecord;
+use App\Models\CustomRedpacket;
+use App\Models\RedpacketActive as Model;
+use Illuminate\Support\Facades\DB;
+
+/**
+ * 客户红包接口
+ * 
+ * @author jun
+ * 
+ * */
+class Redpacket extends Api{
+
+    /**
+     * 获取红包活动			/api/redpacket/get_list
+     *
+     *
+     * */
+    public function get_list(CustomRedpacket $CustomRedpacket){
+        // 检查登录
+        $uid						= $this->checkLogin();
+        // 接收参数
+        $limit						= request('limit',10);
+        $time                       = time();
+        $where                      = [
+            ['custom_uid','=',$uid],
+            ['start_time','<=',$time],
+            ['end_time','>=',$time],
+        ];
+        // 查询活动
+        $Paginator					= $CustomRedpacket::query()->where($where)->paginate($limit);
+        // 获取数据
+        $data['total']				= $Paginator->total();
+        $data['current_page']		= $Paginator->currentPage();
+        $data['per_page']			= $Paginator->perPage();
+        $data['last_page']			= $Paginator->lastPage();
+        $list				        = $Paginator->items();
+        // 判断活动
+        if( !$list )				return json_send(['code'=>'error','msg'=>'您没有红包可领取']);
+        $data                       =   [];
+        foreach ($list as $key=>$v){
+            $data['data'][$key]['money']                    = $v['money'];
+            $data['data'][$key]['custom_redpacket_id']      = $v['id'];
+            $data['data'][$key]['status']                   = $v['status'];
+            switch ($v['status']){
+                case 0:
+                    $data['data'][$key]['state']    =   '待领取';
+                    break;
+                case 1:
+                    $data['data'][$key]['state']    =   '已领取';
+                    break;
+            }
+        }
+        // 返回成功
+        return							json_send(['code'=>'success','msg'=>'获取成功','data'=>$data]);
+    }
+	/**
+	 * 领取红包			/api/redpacket/get_redpacket
+	 * 
+	 * 
+	 * */
+	public function get_redpacket(Request $request,Model $Model,CustomRedpacket $CustomRedpacket,CustomAmountRecord $CustomAmountRecord,CustomAmount  $CustomAmount){
+		// 验证参数
+		$request->scene('get_redpacket')->validate();
+		// 检查登录
+		$uid							= $this->checkLogin();
+		// 接收参数
+		$customRedpacketId				= request('custom_redpacket_id',0);
+		// 查询红包信息
+		$info						    = $CustomRedpacket::query()->where(['id'=>$customRedpacketId,'custom_uid'=>$uid])->first();
+		// 判断是否可以领取
+		if( !$info )				return json_send(['code'=>'error','msg'=>'红包不存在']);
+		// 判断是否可以领取
+		if( $info['status'] )		return json_send(['code'=>'error','msg'=>'红包已领取']);
+		// 时间
+		if( $info['start_time'] > time() ) return json_send(['code'=>'error','msg'=>'还没到发放时间哦']);
+		// 时间
+		if( $info['end_time'] <= time() )  return json_send(['code'=>'error','msg'=>'发放时间已经结束了']);
+        DB::beginTransaction();
+        try {
+            // 获取余额信息
+            $amountInfo         =   $CustomAmount::query()->where(['custom_uid'=>$uid])->first();
+            if(!$amountInfo){
+                $result         =   $CustomAmountRecord::query()->insert(['custom_uid'=>$uid,'insert_time'=>time(),'update_time'=>time()]);
+                if (!$result){
+                    return json_send(['code'=>'error','msg'=>'提现失败','data'=>['error'=>'提现失败']]);
+                }
+                $balance    =   0;
+            }else{
+                $balance    =   $amountInfo['amount'];
+            }
+            //加入用户余额
+            $result             =   $CustomAmount::query()->where(['custom_uid'=>$uid])->increment('amount',$info['money']);
+            if (!$result)       return json_send(['code'=>'error','msg'=>'领取红包失败']);
+            //写入余额记录
+            $recordInfo     =   [
+                'transfer_bill_no'   =>  $info['id'],
+                'prefix'                =>  1,
+                'amount'                =>  $info['money'],
+                'buy_type'              =>  1,
+                'pay_type'              =>  1,
+                'balance'               =>  $balance,
+                'insert_time'           =>  time(),
+                'update_time'           =>  time(),
+            ];
+            $resultId        =   $CustomAmountRecord::query()->insertGetId($recordInfo);
+            if (!$resultId){
+                DB::rollBack();
+                return json_send(['code'=>'error','msg'=>'领取红包失败']);
+            }
+            //修改用户红包记录状态
+            $result             =   $CustomRedpacket::query()->where(['id'=>$info['id']])->update(['status'=>1,'update_time'=>time(),'amount_record_id'=>$resultId]);
+            if (!$result){
+                DB::rollBack();
+                return json_send(['code'=>'error','msg'=>'领取红包失败']);
+            }
+            DB::commit();
+        }catch (\Exception $e){
+            // 回退数据
+            DB::rollBack();
+            return json_send(['code'=>'error','msg'=>'领取红包失败','data'=>$e->getMessage()]);
+        }
+		// 返回成功
+		return							json_send(['code'=>'success','msg'=>'领取成功']);
+	}
+
+}

+ 210 - 0
app/Http/Controllers/Api/WechatTransfer.php

@@ -0,0 +1,210 @@
+<?php namespace App\Http\Controllers\Api;
+
+use App\Http\Controllers\Api\Api;
+use App\Models\Custom;
+use App\Facades\Servers\Logs\Log;
+use App\Models\CustomAmount;
+use App\Models\CustomAmountRecord;
+use Illuminate\Http\Request;
+use App\Servers\WechatPay\Transfer;
+use Kra8\Snowflake\Snowflake;
+use App\Servers\WechatMini\Mini;
+use Illuminate\Support\Facades\DB;
+use WeChatPay\Formatter;
+use WeChatPay\Crypto\AesGcm;
+use WeChatPay\Crypto\Rsa;
+
+/**
+ * 微信支付接口
+ * 
+ * @author JUN
+ * 
+ * */
+class WechatTransfer extends Api{
+    /**
+     * 小程序微信支付下单				/api/wechat_pay/pay
+     *
+     * */
+    public function transfer(Custom $Custom,CustomAmount $CustomAmount,CustomAmountRecord $CustomAmountRecord)
+    {
+        // 检查登录
+        $uid				= $this->checkLogin();
+        $code				= request('code','');
+        $amount			    = request('amount','');
+        // 获取余额信息
+        $amountInfo         =   $CustomAmount::query()->where(['custom_uid'=>$uid])->first();
+        if(!$amountInfo){
+            $result         =   $CustomAmountRecord::query()->insert(['custom_uid'=>$uid,'insert_time'=>time(),'update_time'=>time()]);
+            if (!$result){
+                return json_send(['code'=>'error','msg'=>'提现失败','data'=>['error'=>'提现失败']]);
+            }
+            $balance    =   0;
+        }else{
+            $balance    =   $amountInfo['amount'];
+        }
+        // 如果剩余余额不足
+        if( $balance < $amount ) return json_send(['code'=>'error','msg'=>'余额不足','data'=>['error'=>'余额不足']]);
+        //获取openid
+        $resultOpenid			= Mini::jscode2session($code);
+        if (!$resultOpenid['openid']) return json_send(['code'=>'error','msg'=>'获取openid失败','data'=>$resultOpenid['error']]);
+        $Snowflake                  =   new Snowflake();
+        $SnowflakeId                =   $Snowflake->next();
+        $params['amount']           =   $amount;
+        $params['out_bill_no']      =   $SnowflakeId;
+        $params['openid']           =   $resultOpenid['openid'];
+        DB::beginTransaction();
+        try {
+            //扣除用户余额
+            $amountUpdate				= [
+                'amount'=>DB::raw('amount-'.$amount),
+                'use_amount'=>DB::raw('use_amount+'.$amount),
+                'transfer_amount'=>DB::raw('transfer_amount+'.$amount),
+                'update_time'=>time()
+            ];
+            $result             =   $CustomAmount::query()->where(['custom_uid'=>$uid])->update($amountUpdate);
+            if (!$result) {
+                return json_send(['code'=>'error','msg'=>'提现申请失败','data'=>['error'=>'扣除用户余额失败']]);
+            }
+            $Transfer           =   new Transfer();
+            $transferResult     =   $Transfer->pay($params);
+            if (!$transferResult || $transferResult['state'] !== 'WAIT_USER_CONFIRM') return json_send(['code'=>'error','msg'=>'提现申请失败','data'=>'提现申请失败']);
+            $data       =   [
+                'custom_uid'		=>	$uid,
+                'out_bill_no'		=>	$transferResult['out_bill_no'],
+                'transfer_bill_no'	=>	$transferResult['transfer_bill_no'],
+                'prefix'	        =>	2,
+                'amount'	        =>	$amount,
+                'buy_type'	        =>	2,
+                'pay_type'	        =>	1,
+                'balance'	        =>	$balance,
+                'description'	    =>	'余额提现',
+                'status'	        =>	1,
+            ];
+            //写入余额记录
+            $recordResult     =   $CustomAmountRecord->add($data);
+            if (!$recordResult) {
+                DB::rollBack();
+                return json_send(['code'=>'error','msg'=>'提现申请失败','data'=>'写入余额记录失败']);
+            }
+            // 提交数据
+            DB::commit();
+        }catch (\Exception $e){
+            DB::rollBack();
+            return json_send(['code'=>'error','msg'=>'提现申请失败','data'=>'提现申请失败']);
+        }
+        $list       =   [
+            'package_info'      =>  $transferResult['package_info'],
+            'out_bill_no'       =>  $transferResult['out_bill_no'],
+        ];
+        return json_send(['code'=>'success','msg'=>'成功','data'=>$list]);
+    }
+	
+	/**
+	 * 小程序微信支付回调				/api/wechat_pay/notify
+	 * 
+	 * */
+    public function notify(Custom $Custom,CustomScore $CustomScore)
+    {
+        $content = file_get_contents("php://input");
+        if (!empty($content)) {
+            //直接json字符串
+            $params = $content;
+        } elseif (!empty($_POST)) {
+            //直接POST数据
+            $params = $_POST;
+        } else {
+            $params = [];
+        }
+        $post_data = $params;
+        Log::log('notify_wechat_transfer', 'post_data:' . $post_data);
+        //获取headers参数
+        $headers = request()->header();
+        Log::log('notify_wechat_transfer', '微信支付回调返回headers参数:' . json_encode($headers));
+
+        $inWechatpaySignature   = $headers['wechatpay-signature'][0];
+        $inWechatpayTimestamp   = $headers['wechatpay-timestamp'][0];
+        $inWechatpaySerial      = $headers['wechatpay-serial'][0];
+        $inWechatpayNonce       = $headers['wechatpay-nonce'][0];
+        $inBody = $post_data;
+        Log::log('notify_wechat_transfer', 'wechatpay-timestamp:' . $inWechatpayTimestamp);
+        $apiv3Key = Config('wechatpay.APIV3');// 在商户平台上设置的APIv3密钥
+        // 根据通知的平台证书序列号,查询本地平台证书文件,
+        $platformCertificateFilePath = Config('wechatpay.platformCertificate');
+        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
+        try {
+            // 检查通知时间偏移量,允许5分钟之内的偏移
+            $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
+            Log::log('notify_wechat_transfer', '时间偏移量:' . $timeOffsetStatus);
+            $verifiedStatus = Rsa::verify(
+            // 构造验签名串
+                Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
+                $inWechatpaySignature,
+                $platformPublicKeyInstance
+            );
+            Log::log('notify_wechat_transfer', '验签:' . $verifiedStatus.'验签inWechatpayTimestamp:' . $inWechatpayTimestamp.'验签verifiedStatus:' . $inWechatpayNonce);
+            if ($timeOffsetStatus && $verifiedStatus) {
+                // 转换通知的JSON文本消息为PHP Array数组
+                $inBodyArray = (array)json_decode($inBody, true);
+                // 使用PHP7的数据解构语法,从Array中解构并赋值变量
+                ['resource' => [
+                    'ciphertext' => $ciphertext,
+                    'nonce' => $nonce,
+                    'associated_data' => $aad
+                ]] = $inBodyArray;
+                // 加密文本消息解密
+                $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
+                // 把解密后的文本转换为PHP Array数组
+                $inBodyResourceArray = (array)json_decode($inBodyResource, true);
+                Log::log('notify_wechat_transfer', '打印解密后的结果:' . json_encode($inBodyResourceArray));
+                Log::log('notify_wechat_transfer', '参数:' . $inBodyResourceArray['trade_state'] . '订单号' . $inBodyResourceArray['out_trade_no'] . '微信支付号' . $inBodyResourceArray['transaction_id']);
+                if ($inBodyResourceArray['trade_state'] == "SUCCESS") {
+                    $status      =  2;
+                    //更新余额订单状态
+                    $data = [
+                        'pay_time' => time(),
+                        'status'=>$status,
+                    ];
+                    $res = CustomAmountRecord::query()->where('out_bill_no','=',$inBodyResourceArray['out_bill_no'])->update($data);
+                    Log::log('notify_wechat_transfer', '更新余额记录,snowflake_id:'.$inBodyResourceArray['out_bill_no'].';'. json_encode($res));
+                    if (!$res) {
+                        Log::log('notify_wechat_transfer', '更新余额记录失败' . json_encode($res));
+                        return json_send(['code'=>'FAIL']);
+                    }
+                    Log::log('notify_wechat_pay', '支付回调完成 通知返回' . json_encode($data));
+                }elseif ($inBodyResourceArray['trade_state'] == "FAIL"){
+                    $status      =  3;
+                    //更新余额订单状态
+                    $data = [
+                        'status'=>$status
+                    ];
+                    $res    = CustomAmountRecord::query()->where('out_bill_no','=',$inBodyResourceArray['out_bill_no'])->update($data);
+                    Log::log('notify_wechat_transfer', '更新余额记录,snowflake_id:'.$inBodyResourceArray['out_bill_no'].';'. json_encode($res));
+                    if (!$res) {
+                        Log::log('notify_wechat_transfer', '更新余额记录失败' . json_encode($res));
+                        return json_send(['code'=>'FAIL']);
+                    }
+                    $amountInfo = CustomAmountRecord::query()->where('out_bill_no','=',$inBodyResourceArray['out_bill_no'])->first();
+                    if ($amountInfo){
+                        Log::log('notify_wechat_transfer', '获取余额记录信息失败' . json_encode($res));
+                        return json_send(['code'=>'FAIL']);
+                    }
+                    //退回余额
+                    $res    =   CustomAmount::query()->where('uid','=',$amountInfo['custom_uid'])->increment('amount',$amountInfo['amount']);
+                    if (!$res){
+                        Log::log('notify_wechat_pay', '退回余额失败' . json_encode($amountInfo));
+                        return json_send(['code'=>'FAIL']);
+                    }
+                    Log::log('notify_wechat_pay', '回调完成 通知返回' . json_encode($amountInfo));
+                }
+                return json_send(['code'=>'SUCCESS']);
+            }else{
+                return json_send(['code'=>'FAIL']);
+            }
+        }catch (\Exception $e){
+            Log::log('notify_wechat_transfer', '回调失败getMessage:' . $e->getMessage());
+        }
+        return json_send(['code'=>'SUCCESS']);
+    }
+
+
+}

+ 45 - 0
app/Http/Requests/Api/Redpacket.php

@@ -0,0 +1,45 @@
+<?php namespace App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 客户优惠券
+ * 
+ */
+class Redpacket extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            'custom_redpacket_id'   => 'required|integer|gt:0',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_redpacket'          => ['custom_redpacket_id'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'custom_redpacket_id.required'   	        => '请选择要领取的红包',
+            'custom_redpacket_id.integer'   	        => '红包ID有误',
+            'custom_redpacket_id.gt'   	                => '红包ID有误',
+        ];
+    }
+
+}

+ 80 - 0
app/Models/CustomAmount.php

@@ -0,0 +1,80 @@
+<?php namespace App\Models;
+
+use App\Models\CustomAmountRecord as Record;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * 客户余额
+ * 
+ */
+class CustomAmount extends Model
+{
+    use HasFactory;
+
+    // 与模型关联的表名
+    protected $table = 'custom_amount';
+    // 是否主动维护时间戳
+    public $timestamps = false;
+    // 定义时间戳字段名
+    // const CREATED_AT = 'insert_time';
+    // const UPDATED_AT = 'update_time';
+
+
+    /**
+     * 获取用户余额信息
+     *
+     * @param   int      $uid       用户ID
+     *
+     */
+    public function getBalanceByUid($uid){
+        // 查询数据库获取数据
+        $balance	    = $this->getCustomAmount($uid);
+        // 返回配置数据
+        return          isset($balance['amount']) ? $balance['amount'] : 0;
+    }
+
+    /**
+     * 获取用户账户余额信息
+     * 如果没有余额账户,将创建对应的账户
+     *
+     * @param   int      $uid           用户ID
+     *
+     * @return  array    余额信息
+     *
+     */
+    public function getCustomAmount($uid){
+
+        // 查询数据
+		$cash							= $this->query()->where([['custom_uid','=',$uid]])->first(['amount','use_amount']);
+        // 如果没有数据,创建账号
+        $cash                           = $cash ? $cash->toArray() : $this->createCustomScore($uid);
+        // 返回查询结果
+        return                          $cash;
+    }
+
+    /**
+     * 创建账号
+     *
+     * @param   int      $uid           用户ID
+     * @param   array    $values        需要修改的数据
+     *
+     */
+    public function createCustomAmount($uid){
+        // 如果没有余额信息的话,需要新增账户,默认数据
+        $cash                           = ['amount'=>0,'use_amount'=>0];
+        // 尝试执行
+        try {
+            $result                     = $this->query()->insert(['custom_uid'=>$uid,'insert_time'=>time(),'update_time'=>time()]);
+            // 如果成功
+            if( $result )               return $cash;
+        } catch (\Throwable $th) {
+            // 如果失败的话,返回默认数据
+            return                      $cash;
+        }
+        // 返回用户余额数据,默认都是账户余额为0
+        return                          $cash;
+    }
+
+}

+ 55 - 0
app/Models/CustomAmountRecord.php

@@ -0,0 +1,55 @@
+<?php namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use App\Models\Traits\Amount\BuyType;
+
+/**
+ * 客户余额记录
+ * 
+ */
+class CustomAmountRecord extends Model
+{
+    use HasFactory,BuyType;
+
+    // 与模型关联的表名
+    protected $table = 'amount_record';
+    // 是否主动维护时间戳
+    public $timestamps = false;
+    // 定义时间戳字段名
+    // const CREATED_AT = 'insert_time';
+    // const UPDATED_AT = 'update_time';
+
+
+    /**
+     * 添加数据
+     * 
+     */
+    public function add($data)
+    {
+        // 时间
+        $data['insert_time']				= time();
+        $data['update_time']				= time();
+        // 写入数据表
+        $id						            = $this->query()->insertGetId($data);
+        // 返回结果
+        return                              $id;
+    }
+
+    /**
+     * 添加数据
+     * 
+     */
+    public function edit($id,$data)
+    {
+        // 更新时间
+        $data['update_time']                = time();
+        // 写入数据表
+        $result						        = $this->query()->where(['id'=>$id])->update($data);
+        // 返回结果
+        return                              $result;
+    }
+
+
+
+}

+ 84 - 0
app/Models/CustomRedpacket.php

@@ -0,0 +1,84 @@
+<?php namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * 客户红包
+ * 
+ */
+class CustomRedpacket extends Model
+{
+    use HasFactory;
+
+    // 与模型关联的表名
+    protected $table = 'custom_redpacket';
+    // 是否主动维护时间戳
+    public $timestamps = false;
+    // 定义时间戳字段名
+    // const CREATED_AT = 'insert_time';
+    // const UPDATED_AT = 'update_time';
+
+
+    /**
+     * 编码转id
+     * 
+     * @param  string $code 编码
+     * 
+     */
+    public function codeToId($code){
+        return intval(str_ireplace('klhb','',$code));
+    }
+ 
+    /**
+     * id转编码
+     * 
+     * @param  int  $id 编码
+     * 
+     */
+    public function idToCode($id){
+         return 'klhb'. str_pad($id, 9, '0', STR_PAD_LEFT);
+    }
+
+    /**
+     * 添加数据
+     * 
+     */
+    public function add($data)
+    {
+        // 时间
+        $data['insert_time']				= time();
+        $data['update_time']				= time();
+        // 写入数据表
+        $id						            = $this->query()->insertGetId($data);
+        // 返回结果
+        return                              $id;
+    }
+
+    /**
+     * 添加数据
+     * 
+     */
+    public function edit($id,$data)
+    {
+        // 更新时间
+        $data['update_time']                = time();
+        // 写入数据表
+        $result						        = $this->query()->where(['id'=>$id])->update($data);
+        // 返回结果
+        return                              $result;
+    }
+
+
+    /**
+     * 查询某条数据
+     * 
+     */
+    public function getOne($uid){
+        // 返回结果
+        $result     = $this->query()->where([['custom_uid','=',$uid]])->first();
+        // 返回结果
+        return      $result;
+    }
+
+}

+ 105 - 0
app/Models/RedpacketActive.php

@@ -0,0 +1,105 @@
+<?php namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * 红包活动
+ * 
+ */
+class RedpacketActive extends Model
+{
+    use HasFactory;
+
+    // 与模型关联的表名
+    protected $table = 'redpacket';
+    // 是否主动维护时间戳
+    public $timestamps = false;
+    // 定义时间戳字段名
+    // const CREATED_AT = 'insert_time';
+    // const UPDATED_AT = 'update_time';
+
+    /**
+     * 添加数据
+     * 
+     */
+    public function add($data)
+    {
+        // 时间
+        $data['insert_time']				= time();
+        $data['update_time']				= time();
+        // 写入数据表
+        $id						            = $this->query()->insertGetId($data);
+        // 如果操作失败
+        if( !$id )                          return $id;
+        // 更新缓存
+        $this->getList(true);
+        // 返回结果
+        return                              $id;
+    }
+
+    /**
+     * 添加数据
+     * 
+     */
+    public function edit($id,$data)
+    {
+        // 更新时间
+        $data['update_time']                = time();
+        // 写入数据表
+        $result						        = $this->query()->where(['id'=>$id])->update($data);
+        // 如果操作失败
+        if( !$result )                      return $result;
+        // 更新缓存
+        $this->getList(true);
+        // 返回结果
+        return                              $result;
+    }
+
+    /**
+     * 获取列表
+     * @param   Bool    $force  是否强制更新
+     * 
+     */
+    public function getList($force = false)
+    {
+        // 结果数据
+        $list                  = $force ? [] : cache('admin:redpacket:active:list');
+        // 不存在数据
+        if ( !$list ) {
+            // 从数据库获取数据
+            $data              = $this->query()->where(['status'=>0])->get();
+            // 是否有数据
+            $data              = $data ? $data->toArray() : [];
+            // 循环处理数据
+            $list              = [];
+            // 进行更新
+            foreach ($data as $value) {
+                // 重组数据
+                $list[$value['id']] = $value;
+            }
+            // 存起来
+            cache(['admin:redpacket:active:list'=>$list]);
+        }
+        // 返回结果
+        return                  $list;
+    }
+
+    /**
+     * 获取配置平台对应的应用数据
+     * 
+     * @param   Array      用户ID
+     * @param   String     指定字段
+     * 
+     */
+    public function getOne($id,$field='')
+    {
+        // 获取列表数据
+        $list                   = $this->getList();
+        // 获取数据
+        $one                    = isset($list[$id]) ? $list[$id] : [];
+        // 返回值
+        return                  empty($field) ? $one : ( isset($one[$field]) ? $one[$field] : null);
+    }
+
+}

+ 76 - 0
app/Models/Traits/Amount/BuyType.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace App\Models\Traits\Amount;
+
+
+/**
+ * 交易类型
+ * 交易类型 与 支付方式  用于积分余额记录 (需与订单的buy_type购买类型严格区分,即与订单信息中的的buy_type购买类型无关)
+ * 
+ */
+trait BuyType
+{
+
+    // 交易类型  与 支付方式
+    private     $buyType        =   ['1'=>[
+                                        'id'            =>1,
+                                        // 类型名称
+                                        'name'          =>'领取红包',
+                                        // 支付方式  方式名称
+                                        'pay_type'      =>['1'=>['id'=>1,'name'=>'领取红包']],
+                                    ],'2'=>[
+                                        'id'            =>2,
+                                        // 类型名称
+                                        'name'          =>'提现',
+                                        // 支付方式  方式名称
+                                        'pay_type'  =>['1'=>['id'=>1,'name'=>'提现']],
+                                    ]];
+
+    /**
+     * 交易类型列表
+     * 
+     */
+    public function getBuyTypeList(){
+        // 返回数据
+        return                  $this->buyType;
+    }
+
+    /**
+     * 获取支付方式
+     * 
+     * @param Int       $buyTid     交易类型 ID
+     * @param Int       $payTid     支付方式 ID
+     * @param String    $field      字段
+     * 
+     */
+    public function getPayType($buyTid,$payTid,$field=''){
+        // 获取数据
+        $payList            = (array) $this->getBuyType($buyTid,'pay_type');
+        // 循环数据
+        $payType            = isset($payList[$payTid]) ? $payList[$payTid] : [];
+        // 如果支付类型未传
+        if( $field )        return isset($payType[$field]) ? $payType[$field] : null;
+        // 名称
+        return              $payType;
+    }
+
+    /**
+     * 获取交易类型
+     * 
+     * @param Int       $buyTid     交易类型 ID
+     * @param String    $field      字段
+     * 
+     */
+    public function getBuyType($buyTid,$field=''){
+        // 获取数据
+        $buyType            = [];
+        // 获取交易类型
+        $buyType            = isset($this->buyType[$buyTid]) ? $this->buyType[$buyTid] : [];
+        // 如果存在需要的字段
+        if( $field )        return isset($buyType[$field]) ? $buyType[$field] : null;
+        // 返回结果
+        return              $buyType;
+    }
+
+
+}

+ 13 - 1
app/Servers/WechatMini/Mini.php

@@ -102,5 +102,17 @@ class Mini
 		// 错误
 		return                                  ['error'=>$result['errcode'].'=>'.$result['errmsg']];
     }
-    
+    /**
+     * 获取用户openid
+     * @param  string   $code           授权码
+     *
+     */
+    public function jscode2session($code){
+        // 获取手机号
+        $result							    = $this->mp->auth->session($code);
+        // 判断结果
+        if( !empty($result['errcode']) )    return ['error'=>$result['errcode'].'=>'.$result['errmsg']];
+        // 获取不包含区号的手机号(因为绑定手机号字段会有国际区号)
+        return                              $result;
+    }
 }

+ 163 - 0
app/Servers/WechatPay/Payment.php

@@ -0,0 +1,163 @@
+<?php   namespace App\Servers\WechatPay;
+
+use DateTime;
+use DateTimeZone;
+use App\Facades\Servers\Logs\Log;
+use WeChatPay\Builder;
+use WeChatPay\Crypto\Rsa;
+use WeChatPay\Util\PemUtil;
+use WeChatPay\Formatter;
+
+class Payment
+{
+    private $appid;
+    private $mchid;
+    private $instance;
+    private $merchantPrivateKeyInstance;
+    private $merchantCertificateSerial;
+
+    function __construct()
+    {
+        // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
+        $merchantPrivateKeyFilePath = Config('wechatpay.private_key');
+        //$merchantPrivateKeyFilePath = 'file://D:\phpstudy_pro\WWW\mall\kailin\resources\1612111355_20241118_cert\apiclient_key.pem';
+        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
+
+        // 「商户API证书」的「证书序列号」
+        $merchantCertificateSerial = '4A5F7CFCC3280CF8C09735D5FC2DEFBC9CCD6F46';
+
+        // 从本地文件中加载「微信支付平台证书」或者「微信支付平台公钥」,用来验证微信支付应答的签名
+        $platformCertificateOrPublicKeyFilePath = Config('wechatpay.platformCertificate');
+        $platformPublicKeyInstance =    Rsa::from($platformCertificateOrPublicKeyFilePath, Rsa::KEY_TYPE_PUBLIC);
+
+        // 「微信支付平台证书」的「证书序列号」或者是「微信支付平台公钥ID」
+        // 「平台证书序列号」及/或「平台公钥ID」可以从 商户平台 -> 账户中心 -> API安全 直接查询到
+        $platformCertificateSerialOrPublicKeyId = 'PUB_KEY_ID_0116121113552024111800218500000334';
+
+        // 构造一个 APIv3 客户端实例
+        $instance = Builder::factory([
+            'mchid'      => Config('wechatpay.mchid'),
+            'serial'     => $merchantCertificateSerial,
+            'privateKey' => $merchantPrivateKeyInstance,
+            'certs'      => [
+                $platformCertificateSerialOrPublicKeyId => $platformPublicKeyInstance,
+            ],
+        ]);
+        $this->instance   = $instance;
+        $this->appid   = Config('wechatpay.appid');
+        $this->mchid   = Config('wechatpay.mchid');
+        $this->merchantPrivateKeyInstance   = $merchantPrivateKeyInstance;
+        $this->merchantCertificateSerial   = Config('wechat.certificate');
+        return;
+    }
+    /**
+     * 拉起小程序支付
+     */
+    public function pay($params)
+    {
+        $notify_url = Config('wechatpay.notify_url');
+        //调用微信JSAPI下单获取预支付交易会话标识
+        try {
+            $resp = $this->instance
+                ->chain('v3/pay/transactions/jsapi')
+                ->post(['json' => [
+                    'mchid'        => $this->mchid,
+                    'out_trade_no' => (string)$params['out_trade_no'],
+                    'appid'        => $this->appid,
+                    'description'  => $params['description'],
+                    /*'time_expire'  => $this->timestampToRfc3339(time() + env('ORDER_OUT_TIME')*60),*/
+                    'notify_url'   => env('APP_URL').'/api/wechat_pay/notify',
+                    'amount'       => [
+                        'total'    => 1,
+                        //'total'    => $params['total_price']*100,
+                        'currency' => 'CNY'
+                    ],
+                    'payer'        => [
+                        'openid'   => $params['openid']
+                    ]
+                ]]);
+            $result = json_decode($resp->getBody(),true);
+            $prepay_id = $result['prepay_id'];
+        } catch (\Exception $e) {
+            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
+                $r = $e->getResponse();
+                // 记录错误信息
+                Log::error('wechat/Payment','拉起小程序支付,出现错误'.$r->getBody().'参数'.json_encode($params));
+            }
+            return response()->json(['error' => '拉起支付失败'], 500);
+        }
+        //根据返回的预支付交易会话标识,组装调起小程序支付参数
+        $data = [
+            'appId'     => $this->appid,
+            'timeStamp' => (string)Formatter::timestamp(),
+            'nonceStr'  => Formatter::nonce(),
+            'package'   => 'prepay_id='.$prepay_id,
+        ];
+        $data += ['paySign' => Rsa::sign(
+            Formatter::joinedByLineFeed(...array_values($data)),
+            $this->merchantPrivateKeyInstance
+        ), 'signType' => 'RSA'];
+        return json_send(['code'=>'success','msg'=>'成功','data'=>$data]);
+    }
+
+    //查询订单
+    public function query($params)
+    {
+        try {
+            $promise = $this->instance
+                ->chain('v3/pay/transactions/id/'.$params[''])
+                ->get(['json' => [
+                    'mchid'        => $this->mchid,
+                ]]);
+        } catch (\Exception $e) {
+            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
+                $r = $e->getResponse();
+                // 记录错误信息
+                Log::error('wechat/Payment','查询订单,出现错误'.$r->getBody().'参数'.json_encode($params));
+            }
+            return response()->json(['error' => '查询订单支付失败'], 500);
+        }
+        return response()->json($promise);
+    }
+
+    /**
+     * 订单微信退款
+     */
+    public function refund($params)
+    {
+        $notify_url = Config('wechat.course_refund_notify_url');
+        try {
+            $resp = $this->instance
+                ->chain('v3/refund/domestic/refunds')
+                ->post(['json' => [
+                    'transaction_id'  => $params['transaction_id'],
+                    'out_refund_no'   => $params['out_refund_no'],
+                    'reason'          => $params['reason'] ?? '',
+                    'notify_url'      => $notify_url,
+                    'amount'          => [
+                        'refund'      => $params['refund'],
+                        'total'       => $params['total'],
+                        'currency'    => 'CNY'
+                    ],
+                ]]);
+        } catch (\Exception $e) {
+            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
+                $r = $e->getResponse();
+                Log::error('wechat/Payment','订单微信退款,出现错误'.$r->getBody());
+            }
+            return response()->json(['error' => '订单微信退款失败'], 500);
+        }
+        return response()->json($resp);
+    }
+    //Rfc3339时间日期格式
+    public function timestampToRfc3339($timestamp)
+    {
+        // 转换为DateTime对象
+        $dateTime = new DateTime();
+        $dateTime->setTimestamp($timestamp);
+        // 设置时区,默认为UTC
+        $dateTime->setTimezone(new DateTimeZone('UTC'));
+        // 格式化为RFC3339
+        return $dateTime->format(DateTime::RFC3339);
+    }
+}

+ 155 - 0
app/Servers/WechatPay/Transfer.php

@@ -0,0 +1,155 @@
+<?php   namespace App\Servers\WechatPay;
+
+use DateTime;
+use DateTimeZone;
+use App\Facades\Servers\Logs\Log;
+use WeChatPay\Builder;
+use WeChatPay\Crypto\Rsa;
+use WeChatPay\Util\PemUtil;
+use WeChatPay\Formatter;
+
+class Transfer
+{
+    private $appid;
+    private $mchid;
+    private $instance;
+    private $merchantPrivateKeyInstance;
+    private $merchantCertificateSerial;
+
+    function __construct()
+    {
+        // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
+        $merchantPrivateKeyFilePath = Config('wechatpay.private_key');
+        //$merchantPrivateKeyFilePath = 'file://D:\phpstudy_pro\WWW\mall\kailin\resources\1612111355_20241118_cert\apiclient_key.pem';
+        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
+
+        // 「商户API证书」的「证书序列号」
+        $merchantCertificateSerial = '4A5F7CFCC3280CF8C09735D5FC2DEFBC9CCD6F46';
+
+        // 从本地文件中加载「微信支付平台证书」或者「微信支付平台公钥」,用来验证微信支付应答的签名
+        $platformCertificateOrPublicKeyFilePath = Config('wechatpay.platformCertificate');
+        $platformPublicKeyInstance =    Rsa::from($platformCertificateOrPublicKeyFilePath, Rsa::KEY_TYPE_PUBLIC);
+
+        // 「微信支付平台证书」的「证书序列号」或者是「微信支付平台公钥ID」
+        // 「平台证书序列号」及/或「平台公钥ID」可以从 商户平台 -> 账户中心 -> API安全 直接查询到
+        $platformCertificateSerialOrPublicKeyId = 'PUB_KEY_ID_0116121113552024111800218500000334';
+
+        // 构造一个 APIv3 客户端实例
+        $instance = Builder::factory([
+            'mchid'      => Config('wechatpay.mchid'),
+            'serial'     => $merchantCertificateSerial,
+            'privateKey' => $merchantPrivateKeyInstance,
+            'certs'      => [
+                $platformCertificateSerialOrPublicKeyId => $platformPublicKeyInstance,
+            ],
+        ]);
+        $this->instance   = $instance;
+        $this->appid   = Config('wechatpay.appid');
+        $this->mchid   = Config('wechatpay.mchid');
+        $this->merchantPrivateKeyInstance   = $merchantPrivateKeyInstance;
+        $this->merchantCertificateSerial   = Config('wechat.certificate');
+        return;
+    }
+    /**
+     * 拉起小程序支付
+     */
+    public function pay($params)
+    {
+        //调用微信JSAPI下单获取预支付交易会话标识
+        try {
+            $resp = $this->instance
+                ->chain('/v3/fund-app/mch-transfer/transfer-bills')
+                ->post(['json' => [
+                    'appid'         => $this->appid,
+                    'out_bill_no'   => (string)$params['out_bill_no'],
+                    'transfer_scene_id'  => '1000',
+                    'openid'        => $params['openid'],
+                    'transfer_amount'   => 10,
+                    'transfer_remark'   => '余额提现',
+                    'user_recv_perception'   => '现金奖励',
+                    'transfer_scene_report_infos'        => [
+                        [
+                            'info_type'         => '活动名称',
+                            'info_content'      => '下单红包',
+                        ],
+                        [
+                            'info_type'         => '奖励说明',
+                            'info_content'      => '预约下单得现金红包',
+                        ],
+                    ],
+                    'notify_url'   => env('APP_URL').'/api/wechat_transfer/notify'
+                    /*'time_expire'  => $this->timestampToRfc3339(time() + env('ORDER_OUT_TIME')*60),*/
+                ]]);
+            $result = json_decode($resp->getBody(),true);
+        } catch (\Exception $e) {
+            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
+                $r = $e->getResponse();
+                // 记录错误信息
+                Log::error('wechat/transfer','微信商户转账失败,出现错误'.$r->getBody().'参数'.json_encode($params));
+            }
+            return false;
+        }
+        return $result;
+    }
+
+    //查询订单
+    public function query($params)
+    {
+        try {
+            $promise = $this->instance
+                ->chain('v3/pay/transactions/id/'.$params[''])
+                ->get(['json' => [
+                    'mchid'        => $this->mchid,
+                ]]);
+        } catch (\Exception $e) {
+            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
+                $r = $e->getResponse();
+                // 记录错误信息
+                Log::error('wechat/Payment','查询订单,出现错误'.$r->getBody().'参数'.json_encode($params));
+            }
+            return response()->json(['error' => '查询订单支付失败'], 500);
+        }
+        return response()->json($promise);
+    }
+
+    /**
+     * 订单微信退款
+     */
+    public function refund($params)
+    {
+        $notify_url = Config('wechat.course_refund_notify_url');
+        try {
+            $resp = $this->instance
+                ->chain('v3/refund/domestic/refunds')
+                ->post(['json' => [
+                    'transaction_id'  => $params['transaction_id'],
+                    'out_refund_no'   => $params['out_refund_no'],
+                    'reason'          => $params['reason'] ?? '',
+                    'notify_url'      => $notify_url,
+                    'amount'          => [
+                        'refund'      => $params['refund'],
+                        'total'       => $params['total'],
+                        'currency'    => 'CNY'
+                    ],
+                ]]);
+        } catch (\Exception $e) {
+            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
+                $r = $e->getResponse();
+                Log::error('wechat/Payment','订单微信退款,出现错误'.$r->getBody());
+            }
+            return response()->json(['error' => '订单微信退款失败'], 500);
+        }
+        return response()->json($resp);
+    }
+    //Rfc3339时间日期格式
+    public function timestampToRfc3339($timestamp)
+    {
+        // 转换为DateTime对象
+        $dateTime = new DateTime();
+        $dateTime->setTimestamp($timestamp);
+        // 设置时区,默认为UTC
+        $dateTime->setTimezone(new DateTimeZone('UTC'));
+        // 格式化为RFC3339
+        return $dateTime->format(DateTime::RFC3339);
+    }
+}

+ 3 - 1
composer.json

@@ -13,6 +13,7 @@
         "guzzlehttp/guzzle": "^7.0.1",
         "intervention/image": "^2.5",
         "ixudra/curl": "^6.21",
+        "kra8/laravel-snowflake": "^2.1",
         "laravel/framework": "^8.12",
         "laravel/tinker": "^2.5",
         "overtrue/qcloud-cos-client": "^1.0",
@@ -21,7 +22,8 @@
         "tencentcloud/sms": "^3.0",
         "vinkla/hashids": "^9.1",
         "w7corp/easywechat": "^5.35",
-        "wantp/snowflake": "^1.2"
+        "wantp/snowflake": "^1.2",
+        "wechatpay/wechatpay": "^1.4"
     },
     "require-dev": {
         "facade/ignition": "^2.5",

+ 231 - 1
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "9c2736fcb6ae3efb111fdc710bf0e9af",
+    "content-hash": "5b9b61d91311f39b4ffa1e237fa52a1e",
     "packages": [
         {
             "name": "adbario/php-dot-notation",
@@ -1911,6 +1911,98 @@
             ],
             "time": "2023-12-03T20:05:35+00:00"
         },
+        {
+            "name": "guzzlehttp/uri-template",
+            "version": "v1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/uri-template.git",
+                "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c",
+                "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "symfony/polyfill-php80": "^1.24"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.36 || ^9.6.15",
+                "uri-template/tests": "1.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\UriTemplate\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                }
+            ],
+            "description": "A polyfill class for uri_template of PHP",
+            "keywords": [
+                "guzzlehttp",
+                "uri-template"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/uri-template/issues",
+                "source": "https://github.com/guzzle/uri-template/tree/v1.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2023-12-03T19:50:20+00:00"
+        },
         {
             "name": "hashids/hashids",
             "version": "4.1.0",
@@ -2141,6 +2233,71 @@
             },
             "time": "2022-07-31T09:58:52+00:00"
         },
+        {
+            "name": "kra8/laravel-snowflake",
+            "version": "v2.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/kra8/laravel-snowflake.git",
+                "reference": "c56250c9e5b75eb26864f298039a74720b2ab1ed"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/kra8/laravel-snowflake/zipball/c56250c9e5b75eb26864f298039a74720b2ab1ed",
+                "reference": "c56250c9e5b75eb26864f298039a74720b2ab1ed",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "illuminate/support": "^6.3|^7.0|^8.0|^9.0",
+                "php": "^7.2.5|^8.0"
+            },
+            "require-dev": {
+                "orchestra/testbench": "^5.0|^6.0|^7.0",
+                "phpunit/phpunit": "^8.5.8|^9.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Kra8\\Snowflake\\Providers\\LaravelServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Kra8\\Snowflake\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Koki Asai",
+                    "email": "me@kra.dev"
+                }
+            ],
+            "description": "Snowflake for Laravel and Lumen.",
+            "homepage": "https://github.com/kra8/laravel-snowflake",
+            "keywords": [
+                "laravel",
+                "lumen",
+                "php",
+                "snowflake"
+            ],
+            "support": {
+                "issues": "https://github.com/kra8/laravel-snowflake/issues",
+                "source": "https://github.com/kra8/laravel-snowflake/tree/v2.1.0"
+            },
+            "time": "2022-02-28T04:04:37+00:00"
+        },
         {
             "name": "laravel/framework",
             "version": "v8.83.27",
@@ -8289,6 +8446,79 @@
                 "source": "https://github.com/webmozarts/assert/tree/1.11.0"
             },
             "time": "2022-06-03T18:03:27+00:00"
+        },
+        {
+            "name": "wechatpay/wechatpay",
+            "version": "1.4.11",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/wechatpay-apiv3/wechatpay-php.git",
+                "reference": "6f2994f04412c909d3561585ba437b50b894306f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/wechatpay-apiv3/wechatpay-php/zipball/6f2994f04412c909d3561585ba437b50b894306f",
+                "reference": "6f2994f04412c909d3561585ba437b50b894306f",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-curl": "*",
+                "ext-libxml": "*",
+                "ext-openssl": "*",
+                "ext-simplexml": "*",
+                "guzzlehttp/guzzle": "^6.5 || ^7.0",
+                "guzzlehttp/uri-template": "^0.2 || ^1.0",
+                "php": ">=7.1.2"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^0.12.89 || ^1.0",
+                "phpunit/phpunit": "^7.5 || ^8.5.16 || ^9.3.5"
+            },
+            "bin": [
+                "bin/CertificateDownloader.php"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "WeChatPay\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "James ZHANG",
+                    "homepage": "https://github.com/TheNorthMemory"
+                },
+                {
+                    "name": "WeChatPay Community",
+                    "homepage": "https://developers.weixin.qq.com/community/pay"
+                }
+            ],
+            "description": "[A]Sync Chainable WeChatPay v2&v3's OpenAPI SDK for PHP",
+            "homepage": "https://pay.weixin.qq.com/",
+            "keywords": [
+                "AES-GCM",
+                "aes-ecb",
+                "openapi-chainable",
+                "rsa-oaep",
+                "wechatpay",
+                "xml-builder",
+                "xml-parser"
+            ],
+            "support": {
+                "issues": "https://github.com/wechatpay-apiv3/wechatpay-php/issues",
+                "source": "https://github.com/wechatpay-apiv3/wechatpay-php/tree/v1.4.11"
+            },
+            "time": "2024-12-27T15:22:15+00:00"
         }
     ],
     "packages-dev": [

+ 45 - 0
config/wechatpay.php

@@ -0,0 +1,45 @@
+<?php
+return array(
+    'appid'                 => "wxe270937ea4d7f7d5",//appid
+    'mchid'                 => "1612111355",//微信直连商户号mchid
+    'certificate'           => '68E43B250D73977F1DC0E0D0827AF25A8BFB1777', //证书序列号
+    'APIV3'                 => 'iD1xB1mH6zO7pP2wD1rU4aD2nH7oL2lN',
+    'notify_url'            => env('APP_URL').'/api/wechat_pay/notify',
+    'private_key'           => '-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBcmSf2j2gN8Ie
++vx0GxqFDzxetgJX+sCju9wyxSiPiFGqNRp1dXyzmb0WMDZ4EHSuwhgMp9xdY6BE
+g7WxMyT0UorDZgpSTogIXxMGsykSrkOVkLDTTK4yegObvp/XwOthhbz3vrC8QEaK
+JwiQZvS3MvwVKht3kcu3OvKYDvJDMrJPubGz+eaJPUN617K9rBqxFsVhcMv2LINZ
+8pjcPlYJtUmHn+4kAKqhLV2EG6bTlKzORDAg5OhJYx2eiE6XOJZLYUDciDR1/rSy
+n9sJUk162BBpRPdyoVVobloSswyldMosKKEnKFptPQhmHT8zluEKgyQ1J+aX04v5
+NiWeRMNhAgMBAAECggEBAJUphdmUyMwP8ok88wj8FTjghKZrJed9HKmkI6iqShpS
+/NWH2P0VOLCsLRc82eM+nRpb43tuvAbu2Re7j5AxZZ9XPlRpDDBnCZGa7erOny8e
+XFKpnQ9xIykc2Edxd4M/6L/sBcfqrBCcZ0gDzSC7oprYn3QaYc+TBkglFgaWD8wr
+pdNjXPFziXXQ2nWbvnE/70MERrSTUTnaQMuBRLW9Vyo/wg4HQ5Bfk1CnvQZvmJ3b
+zhLj7VBDk2x2WQPX9Fb53VxBeRc4c2cSncgZlom3Qdj+VO8wqZ7xsLIvN/f7qqy8
+qCCH0TU0FzNICbWVJz38UZWDv2T7mOg3DBky55JNOZkCgYEA5tLeUb1dTZqECoq5
+8z9F+4nxU7GGg/xM4GAFCkqmL4zAgAu2noLMWlXnE9s0uWJ2f+zErqEqJgKPWrc5
+5I+4GdJV+rrMM299DdKtomAvgsRVNW33sU5Q0aUDK3aQeDhlvDkmuRq03CZxlaD0
+D366ftX+/q8F5zsB9vCmkffaoDcCgYEA1ovgWQ84KpxXIABTd25wfnpmy6nOOiuv
+IeiaLmm0iPihXD5+8xZ2xQgI3tRLGvd+YxzhaIiCYIZilOyr1qhdA1tVWc3gONVw
+JmE4jZGSBVgnIfUQDbhK9tx0a+ayYII3pfC8+y6zPY7OI2t9eotVN7FncEhid3c+
+HMH3soFg/ScCgYAgVfgZIVsZzQSkaWzFW97U7y0kmWELymOGp0ztdSm0zZYGGh36
+NL6mBRS7HmFTeyy4nRWslR1Pd9pXTgXJNC0JSQMMffCvX/X34sS6U5k0S4bYVlB1
+wU/nHqPECfcRMG1oIeV/1vLxSF1vJb4rEYjUzevMB/7ettB+pJXhKwSwnwKBgQDR
+tua0h2DyneG4ITl1S9ngd8sVl0VDrUHClnhnl+Hl5Yrs/RXgjfvIuwLYtZE4qeW8
+AhX/tTgkHBgu1G1gJTDOQC/c2ZYsV84JZgz+rOWcHcAavE5V6ug0B+gxNM718TNu
+HDTOHqcMLBQpoT+FUr8Y8mtW4RqHBmMLfzqdHURuDwKBgHqGTnJ0N9G1OZKIa3Pd
+IT+VHwi+LKimXMbWL8unh3n43aIPeBVD+vvEhZsx0dHlAEbJ7Fw7zTHtVuIhe73O
+342TuO4BZaOE0pl9+iOdXNxBZx3e1p2mU8fAOoPZFDaqJNRQVQHLH3P8FMChby9B
+2s3YrNc5pJxE0mtBzbZYja9B
+-----END PRIVATE KEY-----',//API 私钥
+    'platformCertificate'       => '-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8bSm61/CgGJSIm+KPJd
+jxAvK3cgZaUS2YR0UrzlNeL8+4n6YqbUBoCTd2M9ZpxD/b0MHGEwJRmawkDJXj/r
+ygKVBJSqMYtjkJvuTAQuBUGTpz+8l+alaNPn5yYuEuNo78LyImlPkU1tIxTfP41v
+0wwgEzEJT/jTFbtBxGs+odU6kz3lzQ1yPOasRuSZMdsEVrQfaxDGYugtZxf5XHgM
+8d3K5G1gGSh0JUfnORc1b39xUpGn3SVzm3Zdm28Ghjs494RCD52ymfYYUaBFejSr
+0MeVJnZ7GmOH/m3qjtBMhKlmE8dDHxmuKIKVw4/dJZWhGxSxQp+aRIhZp8TCmdEX
+pwIDAQAB
+-----END PUBLIC KEY-----' //平台支付证书
+);

+ 26 - 1
routes/api.php

@@ -209,4 +209,29 @@ Route::any('share_message/get_list',[\App\Http\Controllers\Api\ShareMessage::cla
  *
  * */
 Route::any('recruitment/get_info',[\App\Http\Controllers\Api\Recruitment::class,'get_info']);
-Route::any('recruitment/get_record',[\App\Http\Controllers\Api\Recruitment::class,'get_record']);
+Route::any('recruitment/get_record',[\App\Http\Controllers\Api\Recruitment::class,'get_record']);
+
+/**
+ * 余额
+ *
+ * */
+//提现(微信商户转账到零钱)
+Route::any('wechat_transfer/transfer',[\App\Http\Controllers\Api\WechatTransfer::class,'transfer']);
+//提现回调接口
+Route::any('wechat_transfer/notify',[\App\Http\Controllers\Api\WechatTransfer::class,'notify']);
+
+/**
+ * 红包
+ *
+ * */
+//获取红包列表
+Route::any('redpacket/get_list',[\App\Http\Controllers\Api\Redpacket::class,'get_list']);
+//用户领取红包
+Route::any('redpacket/get_redpacket',[\App\Http\Controllers\Api\Redpacket::class,'get_redpacket']);
+
+/**
+ * 用户余额
+ *
+ * */
+//余额记录
+Route::any('custom_amount/get_record_list',[\App\Http\Controllers\Api\CustomAmount::class,'get_record_list']);