You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
10 KiB
311 lines
10 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | Niucloud-admin 企业快速开发的多应用管理平台
|
|
// +----------------------------------------------------------------------
|
|
// | 官方网址:https://www.niucloud.com
|
|
// +----------------------------------------------------------------------
|
|
// | niucloud团队 版权所有 开源版本可自由商用
|
|
// +----------------------------------------------------------------------
|
|
// | Author: Niucloud Team
|
|
// +----------------------------------------------------------------------
|
|
|
|
namespace app\service\api\pay;
|
|
|
|
use app\dict\common\ChannelDict;
|
|
use app\dict\pay\PayDict;
|
|
use app\dict\pay\PaySceneDict;
|
|
use app\model\customer_resources\CustomerResources;
|
|
use app\model\member\Member;
|
|
use app\model\order_table\OrderTable;
|
|
use app\model\pay\Pay;
|
|
use app\model\student\Student;
|
|
use app\model\sys\Poster;
|
|
use app\service\api\apiService\OrderTableService;
|
|
use app\service\core\member\CoreMemberService;
|
|
use app\service\core\pay\CorePayService;
|
|
use core\base\BaseApiService;
|
|
use core\exception\ApiException;
|
|
use think\db\exception\DataNotFoundException;
|
|
use think\db\exception\DbException;
|
|
use think\db\exception\ModelNotFoundException;
|
|
use think\facade\Db;
|
|
use think\facade\Log;
|
|
|
|
/**
|
|
* 支付业务
|
|
*/
|
|
class PayService extends BaseApiService
|
|
{
|
|
|
|
public $core_pay_service;
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
$this->core_pay_service = new CorePayService();
|
|
}
|
|
|
|
/**
|
|
* 去支付
|
|
* @param string $type
|
|
* @param string $trade_type
|
|
* @param int $trade_id
|
|
* @param string $return_url
|
|
* @param string $quit_url
|
|
* @param string $buyer_id
|
|
* @return mixed
|
|
* @throws DataNotFoundException
|
|
* @throws DbException
|
|
* @throws ModelNotFoundException
|
|
*/
|
|
public function pay(string $type, string $trade_type, int $trade_id, string $return_url = '', string $quit_url = '', string $buyer_id = '', string $voucher = '', string $openid = ''){
|
|
|
|
$member = (new CoreMemberService())->getInfoByMemberId($this->member_id);
|
|
switch ($this->channel) {
|
|
case ChannelDict::WECHAT://公众号
|
|
$openid = $openid ? $openid : $member['wx_openid'] ?? '';
|
|
break;
|
|
case ChannelDict::WEAPP://微信小程序
|
|
$openid = $openid ? $openid : $member['weapp_openid'] ?? '';
|
|
break;
|
|
}
|
|
|
|
return $this->core_pay_service->pay($trade_type, $trade_id, $type, $this->channel, $openid, $return_url, $quit_url, $buyer_id, $voucher, $this->member_id);
|
|
}
|
|
|
|
/**
|
|
* 关闭支付
|
|
* @param string $type
|
|
* @param string $out_trade_no
|
|
* @return null
|
|
*/
|
|
public function close(string $type, string $out_trade_no){
|
|
return $this->core_pay_service->close($out_trade_no);
|
|
}
|
|
|
|
/**
|
|
* 支付异步通知
|
|
* @param string $channel
|
|
* @param string $type
|
|
* @param string $action
|
|
* @return void|null
|
|
*/
|
|
public function notify(string $channel, string $type, string $action){
|
|
return $this->core_pay_service->notify($channel, $type, $action);
|
|
}
|
|
|
|
/**
|
|
* 通过交易流水号查询支付信息以及支付方式
|
|
* @param $out_trade_no
|
|
* @return array
|
|
*/
|
|
public function getInfoByOutTradeNo($out_trade_no){
|
|
return $this->core_pay_service->getInfoByOutTradeNo($out_trade_no, $this->channel);
|
|
}
|
|
|
|
public function getInfoByTrade(string $trade_type, int $trade_id, array $data){
|
|
return $this->core_pay_service->getInfoByTrade($trade_type, $trade_id, $this->channel, $data['scene']);
|
|
}
|
|
|
|
/**
|
|
* 获取找朋友帮忙付支付信息
|
|
* @param string $trade_type
|
|
* @param int $trade_id
|
|
* @return array
|
|
*/
|
|
public function getFriendspayInfoByTrade($trade_type, $trade_id){
|
|
$from_pay_info = ( new Pay() )->field('id')->where([ [ 'trade_type', '=', $trade_type ], [ 'trade_id', '=', $trade_id ] ])->findOrEmpty()->toArray();//查询发起交易所属的站点id
|
|
if (empty($from_pay_info)) throw new ApiException('TRADE_NOT_EXIST');
|
|
$pay_info = $this->core_pay_service->getInfoByTrade($trade_type, $trade_id, $this->channel, PaySceneDict::FRIENDSPAY);
|
|
if (!empty($pay_info)) {
|
|
//todo 查询订单交易信息,其它插件可实现该钩子
|
|
$trade_info = array_values(array_filter(event('PayTradeInfo',[ 'trade_type' => $trade_type, 'trade_id' => $trade_id ])))[0] ?? [];
|
|
$pay_info['trade_info'] = $trade_info;
|
|
if ($pay_info['from_main_id'] != $this->member_id) {
|
|
$pay_info['is_self'] = false;
|
|
} else {
|
|
$pay_info['is_self'] = true;
|
|
}
|
|
//海报
|
|
$poster = ( new Poster() )->field('id')->where([
|
|
[ 'type', '=', 'friendspay' ],
|
|
[ 'status', '=', 1 ],
|
|
[ 'is_default', '=', 1 ]
|
|
])->findOrEmpty()->toArray();
|
|
if (!empty($poster)) {
|
|
$pay_info['poster_id'] = $poster['id'];
|
|
}
|
|
//发起帮付会员信息
|
|
$member = ( new Member() )->field('member_id,nickname,headimg')->where([
|
|
[ 'member_id', '=', $pay_info['from_main_id'] ]
|
|
])->findOrEmpty()->toArray();
|
|
$pay_info['member'] = $member;
|
|
}
|
|
return $pay_info;
|
|
}
|
|
|
|
/**
|
|
* 获取支付方法
|
|
* @param string $trade_type
|
|
* @return array|array[]
|
|
* @throws DataNotFoundException
|
|
* @throws DbException
|
|
* @throws ModelNotFoundException
|
|
*/
|
|
public function getPayTypeByTrade(string $trade_type){
|
|
return $this->core_pay_service->getPayTypeByTrade($trade_type, $this->channel);
|
|
}
|
|
|
|
/**
|
|
* 二维码支付回调
|
|
*/
|
|
public function qrcodeNotify($data,$order_id)
|
|
{
|
|
Log::debug('qrcodeNotify',$data);
|
|
$order = new OrderTable();
|
|
$order_info = $order->where(['id' => $order_id])->find();
|
|
if($order_info['order_status'] == 'pending' and !empty($order_info['ipv3'])){
|
|
$resource = $data['resource'];
|
|
$decrypted = decryptWechatPayNotify(
|
|
$resource['ciphertext'],
|
|
$resource['nonce'],
|
|
$resource['associated_data'],
|
|
$order_info['ipv3']
|
|
);
|
|
$info = json_decode($decrypted, true);
|
|
|
|
$order->where(['payment_id' => $info['out_trade_no']])
|
|
->update(['order_status' => 'paid','payment_time' => date("Y-m-d H:i:s")]);
|
|
|
|
$payModel = new Pay();
|
|
$payModel->where(['trade_id' => $order_id])->update([
|
|
'status' => 2,
|
|
'pay_time' => time()
|
|
]);
|
|
|
|
(new OrderTableService())->handlePaymentSuccess($order_info->toArray());
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 小程序支付(绕过 CorePayService,直接使用 return_pay_config)
|
|
* @param array $data
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
public function weappPay(array $data)
|
|
{
|
|
$order_id = $data['order_id'];
|
|
$openid = $data['openid'];
|
|
|
|
// 获取订单信息
|
|
$order = new OrderTable();
|
|
$order_info = $order->where(['id' => $order_id])->find();
|
|
|
|
if (!$order_info) {
|
|
throw new \Exception('订单不存在');
|
|
}
|
|
|
|
if ($order_info['order_status'] != 'pending') {
|
|
throw new \Exception('订单状态不正确');
|
|
}
|
|
|
|
// 获取校区支付配置
|
|
$config = return_pay_config($order_info['campus_id'], $order_id);
|
|
|
|
// 生成支付流水号
|
|
$out_trade_no = 'WEAPP' . date("YmdHis") . $order_id;
|
|
|
|
// 构建支付参数
|
|
$params = [
|
|
'out_trade_no' => $out_trade_no,
|
|
'body' => '订单支付:' . $out_trade_no,
|
|
'money' => $order_info['order_amount'] * 100, // 转换为分
|
|
'openid' => $openid
|
|
];
|
|
|
|
// 使用 PayLoader 调用微信小程序支付
|
|
$pay = new \core\pay\PayLoader('Wechatpay', $config);
|
|
$result = $pay->mini($params);
|
|
|
|
// 更新订单支付流水号和密钥
|
|
$order->where(['id' => $order_id])->update([
|
|
'payment_id' => $out_trade_no,
|
|
'ipv3' => $config['mch_secret_key']
|
|
]);
|
|
|
|
// 记录支付信息到 pay 表
|
|
$payModel = new Pay();
|
|
$payModel->insert([
|
|
'main_id' => $order_info['student_id'],
|
|
'from_main_id' => $order_info['student_id'],
|
|
'out_trade_no' => $out_trade_no,
|
|
'trade_type' => 'school_order_table',
|
|
'trade_id' => $order_id,
|
|
'trade_no' => $out_trade_no,
|
|
'body' => '小程序支付',
|
|
'money' => $order_info['order_amount'],
|
|
'status' => 1, // 支付中
|
|
'create_time' => time(),
|
|
'type' => 'wechatpay',
|
|
'main_type' => 'wechatpay',
|
|
'channel' => 'weapp'
|
|
]);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* 小程序支付回调处理
|
|
* @param array $data
|
|
* @param int $order_id
|
|
* @return void
|
|
*/
|
|
public function weappNotify($data, $order_id)
|
|
{
|
|
Log::debug('weappNotify', $data);
|
|
|
|
$order = new OrderTable();
|
|
$order_info = $order->where(['id' => $order_id])->find();
|
|
|
|
if (!$order_info) {
|
|
Log::error('订单不存在: ' . $order_id);
|
|
return;
|
|
}
|
|
|
|
if ($order_info['order_status'] == 'pending' && !empty($order_info['ipv3'])) {
|
|
$resource = $data['resource'];
|
|
|
|
// 解密微信支付回调数据
|
|
$decrypted = decryptWechatPayNotify(
|
|
$resource['ciphertext'],
|
|
$resource['nonce'],
|
|
$resource['associated_data'],
|
|
$order_info['ipv3']
|
|
);
|
|
|
|
$info = json_decode($decrypted, true);
|
|
|
|
Log::info('小程序支付回调解密数据', $info);
|
|
|
|
// 更新订单状态
|
|
$order->where(['payment_id' => $info['out_trade_no']])
|
|
->update([
|
|
'order_status' => 'paid',
|
|
'payment_time' => date("Y-m-d H:i:s")
|
|
]);
|
|
|
|
// 更新支付记录状态
|
|
$payModel = new Pay();
|
|
$payModel->where(['trade_id' => $order_id])->update([
|
|
'status' => 2, // 已支付
|
|
'pay_time' => time()
|
|
]);
|
|
|
|
// 处理支付成功后的业务逻辑
|
|
(new OrderTableService())->handlePaymentSuccess($order_info->toArray());
|
|
|
|
Log::info('小程序支付成功处理完成: 订单ID ' . $order_id);
|
|
}
|
|
}
|
|
}
|
|
|