2025-08-01 11:39:06 +08:00

275 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace addons\xilufitness\services\pay;
use addons\xilufitness\services\BaseService;
use addons\xilufitness\services\user\UserService;
use think\Db;
use think\Exception;
use function EasyWeChat\Kernel\Support\get_client_ip;
class PayService extends BaseService
{
/**
* 支付网关
* @param int $order_id 订单id
* @param int $order_type 订单类型 0 充值订单 1 团课 2 私教课 3 活动
* @param int $pay_type 支付类型 0 免支付 1 会员卡支付 2 微信支付
*/
public function gateWay(int $order_id, int $order_type,int $pay_type = 2){
$orderInfo = $this->getOrderInfo($order_id,$order_type);
if(empty($orderInfo)){
$this->resultError('订单不存在');
} else if($orderInfo['pay_status'] == 1){
$this->resultError('订单已支付');
} else {
$cashResult = $this->createCash($orderInfo,$order_type);
if(false !== $cashResult){
if($pay_type == 0){
$result = $this->freePay($orderInfo);
} elseif ($pay_type == 1){
$result = $this->getMemberCardPay($orderInfo);
} else {
$result = $this->getWechatPay($orderInfo,$order_type);
}
return $result;
} else {
$this->resultError('支付发起失败');
}
}
}
/**
* 创建支付流水记录
* @param object $orderInfo 订单详情
* @param int $order_type 订单类型
*/
private function createCash(object $orderInfo, int $order_type){
$cashModel = new \addons\xilufitness\model\CashFlow;
$cashInfo = $cashModel
->where(['user_id' => $orderInfo->user_id, 'order_no' => $orderInfo->order_no, 'order_type' => $order_type])
->field(['id','user_id','order_no','updatetime','createtime'])
->find();
if(!empty($cashInfo)){
$result = $cashInfo->save(['updatetime' => time(), 'createtime' => time()]);
} else {
$result = $cashModel->save([
'brand_id' => $orderInfo->brand_id,
'user_id' => $orderInfo->user_id,
'order_type' => $order_type,
'order_no' => $orderInfo->order_no,
'pay_amount' => $orderInfo->pay_amount,
'pay_type' => $orderInfo->pay_type,
]);
}
return $result;
}
/**
* 获取订单详情
* @param int $order_id 订单id
* @param int $order_type 订单类型 0 充值订单 1 团课 2 私教课 3 活动
* @param string $order_no 订单号
*/
public function getOrderInfo(int $order_id, int $order_type,string $order_no='') {
$orderModel = new \addons\xilufitness\model\Order;
if(!empty($this->getUserId())){
$where['user_id'] = $this->getUserId();
}
if(!empty($order_id)){
$where['id'] = $order_id;
}
if(!empty($order_no)){
$where['order_no'] = $order_no;
}
$info = $orderModel
->where($where)
->find();
return $info;
}
/**
* 获取用户小程序openid
*/
public function getOpenid(){
$model = new \addons\xilufitness\model\UserConnect;
$openid = $model
->where(['brand_id' => $this->brand_id, 'account_user_id' => $this->getUserId()])
->value('openid');
return $openid;
}
/**
* 微信支付发起
* @param object $orderInfo 订单详情
* @param int $order_type 订单类型
*/
public function getWechatPay(object $orderInfo,int $order_type){
try {
$test_pay_uids = array_filter(explode(',',config('site.wechar_pay_user_ids')));
$wechat = \WeChat\Pay::instance($this->data['mini_config']);
// 4. 组装参数,可以参考官方商户文档
$options = [
'body' => $orderInfo['pay_body'] ?? '健身小程序商品支付',
'out_trade_no' => $orderInfo['order_no'],
'total_fee' => !empty($test_pay_uids) && in_array($orderInfo['user_id'],$test_pay_uids) ? 1 : $orderInfo['pay_amount']*100,
'openid' => $this->getOpenid(),
'trade_type' => 'JSAPI',
'notify_url' => url('/addons/xilufitness/pay/notify',['order_type' => $order_type, 'brand_id' => $orderInfo['brand_id'] ?? 0],true,true),
'spbill_create_ip' => get_client_ip(),
];
// 生成预支付码
$result = $wechat->createOrder($options);
// 创建JSAPI参数签名
$options = $wechat->createParamsForJsApi($result['prepay_id']);
return $options;
} catch (\Exception $e){
$this->resultError($e->getMessage());
}
}
/**
* 会员卡支付
* @param object $orderInfo 订单详情
*/
public function getMemberCardPay(object $orderInfo){
return $orderInfo->payAfter($orderInfo,time(),1);
}
/**
* 免支付
* @param object $orderInfo 订单详情
*/
public function freePay(object $orderInfo){
return $orderInfo->payAfter($orderInfo,time(),0);
}
/**
* 支付异步通知
*/
public function notify($order_type){
try {
$wechat = \WeChat\Pay::instance($this->data['mini_config']);
// 4. 获取通知参数
$data = $wechat->getNotify();
// Db::name('xilufitness_logs')->insertGetId(['content' => json_encode($data)]);
// $data = json_decode(Db::name('xilufitness_logs')->where(['id' => 37])->value('content'),true);
$orderInfo = $this->getOrderInfo(0,$order_type,$data['out_trade_no']);
if ($data['return_code'] === 'SUCCESS' && $data['result_code'] === 'SUCCESS') {
if(!empty($orderInfo) && $orderInfo['pay_status'] == 0){
$orderInfo->payAfter($orderInfo,$data['transaction_id'] ?? '',$orderInfo['pay_type']);
}
// 返回接收成功的回复
ob_clean();
echo $wechat->getNotifySuccessReply();
}
} catch (\Exception $e){
echo $e->getMessage() . PHP_EOL;
}
}
/**
* 退款申请
* @param int $order_id 订单id
* @param int $brand_id 品牌商id
* @return bool
*/
public function refundOrder(int $order_id,int $brand_id = 0){
$model = new \addons\xilufitness\model\Order;
$where['id'] = $order_id;
if(!empty($this->getUserId())){
$where['user_id'] = $this->getUserId();
}
$where['brand_id'] = !empty($brand_id) ? $brand_id : $this->brand_id;
$orderInfo = $model->where($where)->find();
$cancel_able_time = $orderInfo['starttime'] - 300;
if(empty($orderInfo)){
throw new Exception('订单不存在');
} else if($cancel_able_time < time()){
throw new Exception('离开课不到5分钟暂时不能取消');
} else if($orderInfo['pay_type'] == 0){
throw new Exception('免支付订单,暂时不能取消');
} else {
if($orderInfo['pay_type'] == 1){
//会员卡
return $this->refundCard($orderInfo);
} elseif($orderInfo['pay_type'] == 2) {
//微信支付
return $this->refundWechat($orderInfo);
}
}
}
/**
* 会员卡原路退回
*/
private function refundCard($orderInfo){
$title = $this->getRefundTitle($orderInfo);
return UserService::getInstance()->userAccountChange($title,$orderInfo->user_id,xilufitness_get_id_value($orderInfo->id),$orderInfo['pay_amount'],1,$orderInfo['order_type']);
}
/**
* 微信原路退回
*/
private function refundWechat($orderInfo){
$wechat = \WeChat\Pay::instance($this->data['mini_config']);
$return_no = 'R'.xilufitness_build_order_no();
$test_pay_uids = array_filter(explode(',',config('site.wechar_pay_user_ids')));
// 4. 组装参数,可以参考官方商户文档
$options = [
'transaction_id' => $orderInfo['trade_no'],
'out_refund_no' => $return_no,
'total_fee' => !empty($test_pay_uids) && in_array($orderInfo['user_id'],$test_pay_uids) ? 1 : $orderInfo['pay_amount'] * 100,
'refund_fee' => !empty($test_pay_uids) && in_array($orderInfo['user_id'],$test_pay_uids) ? 1 : $orderInfo['pay_amount'] * 100,
];
$result = $wechat->createRefund($options);
if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
$orderInfo->allowField(true)->save(['re_no' => $return_no ?? '']);
return true;
} else {
throw new Exception($result['err_code_des'] ?? '微信退款申请失败');
}
}
/**
* 获取退款标题
*/
private function getRefundTitle($orderInfo){
if($orderInfo['order_type'] == 1){
$title = '团课取消退款:'.$orderInfo['order_no'];
} elseif ($orderInfo['order_type'] == 2){
$title = '私教课取消退款:'.$orderInfo['order_no'];
} else {
$title = '活动取消退款:'.$orderInfo['order_no'];
}
return $title;
}
/**
* 排队释放
* @param int $num 释放人数
* @param int $data_id 排课id
* @param int $course_type 课程类型 1 团课 2 私教课 3 活动
* @return bool
*/
public function orderQueue(int $num = 0,int $data_id,int $course_type,int $brand_id=0){
if($num == 0) return false;
$model = new \addons\xilufitness\model\Order;
$queueList = $model
->where(['brand_id' => $brand_id, 'data_id' => $data_id, 'order_type' => $course_type, 'order_status' => 10])
->order("pay_time asc")
->limit($num)
->select();
if(empty($queueList)) return false;
foreach ($queueList as $key => $val){
$val->save(['order_status' => 1]);
}
return true;
}
}