|
|
|
<?php
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
// | Author: 萤火科技 <admin@yiovo.com>
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
declare (strict_types=1);
|
|
|
|
|
|
|
|
namespace app\api\service\cashier;
|
|
|
|
|
|
|
|
use app\api\model\Order as OrderModel;
|
|
|
|
use app\api\model\Payment as PaymentModel;
|
|
|
|
use app\api\model\PaymentTrade as PaymentTradeModel;
|
|
|
|
use app\api\service\User as UserService;
|
|
|
|
use app\api\service\Order as OrderService;
|
|
|
|
use app\api\service\order\PaySuccess as OrderPaySuccesService;
|
|
|
|
use app\api\service\order\source\Factory as OrderSourceFactory;
|
|
|
|
use app\common\service\BaseService;
|
|
|
|
use app\common\enum\Client as ClientEnum;
|
|
|
|
use app\common\enum\OrderType as OrderTypeEnum;
|
|
|
|
use app\common\enum\payment\Method as PaymentMethodEnum;
|
|
|
|
use app\common\library\payment\Facade as PaymentFacade;
|
|
|
|
use cores\exception\BaseException;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 订单付款服务类
|
|
|
|
* Class Payment
|
|
|
|
* @package app\api\controller
|
|
|
|
*/
|
|
|
|
class Payment extends BaseService
|
|
|
|
{
|
|
|
|
// 提示信息
|
|
|
|
private string $message = '';
|
|
|
|
|
|
|
|
// 订单ID
|
|
|
|
private int $orderId;
|
|
|
|
|
|
|
|
// 订单信息
|
|
|
|
private ?OrderModel $orderInfo;
|
|
|
|
|
|
|
|
// 支付方式 (微信支付、支付宝)
|
|
|
|
private string $method;
|
|
|
|
|
|
|
|
// 下单的客户端
|
|
|
|
private string $client;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 设置支付的订单ID
|
|
|
|
* @param int $orderId 订单ID
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setOrderId(int $orderId): Payment
|
|
|
|
{
|
|
|
|
$this->orderId = $orderId;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 设置当前支付方式
|
|
|
|
* @param string $method 支付方式
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setMethod(string $method): Payment
|
|
|
|
{
|
|
|
|
$this->method = $method;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 设置下单的客户端
|
|
|
|
* @param string $client 客户端
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setClient(string $client): Payment
|
|
|
|
{
|
|
|
|
$this->client = $client;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取支付订单的信息
|
|
|
|
* @return array
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
|
|
* @throws \think\db\exception\DbException
|
|
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
|
|
*/
|
|
|
|
public function orderInfo(): array
|
|
|
|
{
|
|
|
|
// 当期用户信息
|
|
|
|
$userInfo = UserService::getCurrentLoginUser(true);
|
|
|
|
// 根据指定客户端获取可用的支付方式
|
|
|
|
$PaymentModel = new PaymentModel;
|
|
|
|
|
|
|
|
// 获取结算订单信息
|
|
|
|
$OrderModel = new OrderModel;
|
|
|
|
$orderInfo = $OrderModel->getUnpaidOrderDetail($this->orderId);
|
|
|
|
//当前商户是系统自动创建的商户,自动读取商城主的支付配置
|
|
|
|
$model = \app\store\model\Merchant::detail($orderInfo['merchant_id']);
|
|
|
|
$merchantId = ($model && $model->channel_id > 0) ? 0 : $orderInfo['merchant_id'];
|
|
|
|
|
|
|
|
$methods = $PaymentModel->getMethodsByClient($this->client, true, $orderInfo['store_id'], $merchantId);
|
|
|
|
return [
|
|
|
|
'order' => $orderInfo,
|
|
|
|
'personal' => $userInfo,
|
|
|
|
'paymentMethods' => $methods
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 确认订单支付事件
|
|
|
|
* @param array $extra 附加数据
|
|
|
|
* @return array[]
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
|
|
* @throws \think\db\exception\DbException
|
|
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
|
|
*/
|
|
|
|
public function orderPay(array $extra = []): array
|
|
|
|
{
|
|
|
|
// 获取订单信息
|
|
|
|
$this->orderInfo = OrderModel::getDetail($this->orderId);
|
|
|
|
// 订单支付事件
|
|
|
|
$this->orderPayEvent();
|
|
|
|
//判断当前订单是否需要设置为分账订单
|
|
|
|
if ($this->orderInfo->commission_ratio > 0) {
|
|
|
|
$precent = $this->orderInfo->commission_ratio / 1000;
|
|
|
|
$precentPrice = round($precent * $this->orderInfo->pay_price, 2);
|
|
|
|
if ($precentPrice > 0) {
|
|
|
|
$extra['profit_sharing'] = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 构建第三方支付请求的参数
|
|
|
|
$payment = $this->unifiedorder($extra,$this->orderInfo->merchant_id);
|
|
|
|
// 记录第三方交易信息
|
|
|
|
$this->recordPaymentTrade($payment);
|
|
|
|
// 返回结果
|
|
|
|
return compact('payment');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 查询订单是否支付成功 (仅限第三方支付订单)
|
|
|
|
* @param string $outTradeNo 商户订单号
|
|
|
|
* @return bool
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
|
|
* @throws \think\db\exception\DbException
|
|
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
|
|
*/
|
|
|
|
public function tradeQuery(string $outTradeNo): bool
|
|
|
|
{
|
|
|
|
// 判断支付方式是否合法
|
|
|
|
if (!in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//新增获取商户的支付配置问题
|
|
|
|
$trade = PaymentTradeModel::where('out_trade_no', $outTradeNo)->find();
|
|
|
|
$orderInfo = OrderModel::where('trade_id', $trade['trade_id'] ?? 0)->field('merchant_id')->find();
|
|
|
|
// 获取支付方式的配置信息
|
|
|
|
$options = $this->getPaymentConfig($orderInfo['merchant_id'] ?? 0);
|
|
|
|
// 构建支付模块
|
|
|
|
$Payment = PaymentFacade::store($this->method)->setOptions($options, $this->client);
|
|
|
|
// 执行第三方支付查询API
|
|
|
|
$result = $Payment->tradeQuery($outTradeNo);
|
|
|
|
// 订单支付成功事件
|
|
|
|
if (!empty($result) && $result['paySuccess']) {
|
|
|
|
// 获取第三方交易记录信息
|
|
|
|
$tradeInfo = PaymentTradeModel::detailByOutTradeNo($outTradeNo);
|
|
|
|
// 订单支付成功事件
|
|
|
|
$this->orderPaySucces($tradeInfo['order_no'], $tradeInfo['trade_id'], $result);
|
|
|
|
}
|
|
|
|
// 返回订单状态
|
|
|
|
return $result ? $result['paySuccess'] : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 记录第三方交易信息
|
|
|
|
* @param array $payment 第三方支付数据
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
|
|
* @throws \think\db\exception\DbException
|
|
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
|
|
*/
|
|
|
|
private function recordPaymentTrade(array $payment): void
|
|
|
|
{
|
|
|
|
if ($this->method != PaymentMethodEnum::BALANCE) {
|
|
|
|
PaymentTradeModel::record($this->orderInfo, $this->method, $this->client, OrderTypeEnum::ORDER, $payment);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 返回消息提示
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function getMessage()
|
|
|
|
{
|
|
|
|
return $this->message;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 订单支付事件
|
|
|
|
* @return void
|
|
|
|
* @throws BaseException
|
|
|
|
*/
|
|
|
|
private function orderPayEvent(): void
|
|
|
|
{
|
|
|
|
// 验证当前订单是否允许支付
|
|
|
|
$this->checkOrderStatusOnPay();
|
|
|
|
// 余额支付
|
|
|
|
if ($this->method == PaymentMethodEnum::BALANCE) {
|
|
|
|
$this->orderPaySucces($this->orderInfo['order_no']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 验证当前订单是否允许支付
|
|
|
|
* @throws BaseException
|
|
|
|
*/
|
|
|
|
private function checkOrderStatusOnPay()
|
|
|
|
{
|
|
|
|
$orderSource = OrderSourceFactory::getFactory($this->orderInfo['order_source']);
|
|
|
|
if (!$orderSource->checkOrderStatusOnPay($this->orderInfo)) {
|
|
|
|
throwError($orderSource->getError() ?: '当前订单状态不允许支付');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 构建第三方支付请求的参数
|
|
|
|
* @param array $extra 附加数据
|
|
|
|
* @return array
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
|
|
* @throws \think\db\exception\DbException
|
|
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
|
|
*/
|
|
|
|
private function unifiedorder(array $extra = [], int $merchantId = null): array
|
|
|
|
{
|
|
|
|
// 判断支付方式是否合法
|
|
|
|
if (!in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY, PaymentMethodEnum::HUIFU])) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
// 生成第三方交易订单号 (并非主订单号)
|
|
|
|
$outTradeNo = OrderService::createOrderNo();
|
|
|
|
// 获取支付方式的配置信息
|
|
|
|
$options = $this->getPaymentConfig($merchantId);
|
|
|
|
// 整理下单接口所需的附加数据
|
|
|
|
$extra = $this->extraAsUnify($extra);
|
|
|
|
// 构建支付模块
|
|
|
|
$Payment = PaymentFacade::store($this->method)->setOptions($options, $this->client);
|
|
|
|
// 执行第三方支付下单API
|
|
|
|
if (!$Payment->unify($outTradeNo, (string)$this->orderInfo['pay_price'], $extra)) {
|
|
|
|
throwError('第三方支付下单API调用失败');
|
|
|
|
}
|
|
|
|
// 返回客户端需要的支付参数
|
|
|
|
return $Payment->getUnifyResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取支付方式的配置信息
|
|
|
|
* @return mixed
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
|
|
* @throws \think\db\exception\DbException
|
|
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
|
|
*/
|
|
|
|
private function getPaymentConfig(int $merchantId = null)
|
|
|
|
{
|
|
|
|
$PaymentModel = new PaymentModel;
|
|
|
|
//当前商户是系统自动创建的商户,自动读取商城主的支付配置
|
|
|
|
$model = \app\store\model\Merchant::detail($merchantId);
|
|
|
|
$merchantId = ($model && $model->channel_id > 0) ? 0 : $merchantId;
|
|
|
|
$templateInfo = $PaymentModel->getPaymentInfo($this->method, $this->client, $this->getStoreId(), $merchantId);
|
|
|
|
return $templateInfo['template']['config'][$this->method];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 整理下单接口所需的附加数据
|
|
|
|
* @param array $extra
|
|
|
|
* @return array
|
|
|
|
* @throws BaseException
|
|
|
|
*/
|
|
|
|
private function extraAsUnify(array $extra = []): array
|
|
|
|
{
|
|
|
|
// 微信支付时需要的附加数据
|
|
|
|
if ($this->method === PaymentMethodEnum::WECHAT) {
|
|
|
|
// 微信小程序端和微信公众号端需要openid
|
|
|
|
if (in_array($this->client, [ClientEnum::WXOFFICIAL, ClientEnum::MP_WEIXIN])) {
|
|
|
|
$extra['openid'] = $this->getWechatOpenid();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 支付宝支付时需要的附加数据
|
|
|
|
if ($this->method === PaymentMethodEnum::ALIPAY) {
|
|
|
|
// 支付宝小程序端需要buyerId
|
|
|
|
if ($this->client == ClientEnum::MP_ALIPAY) {
|
|
|
|
$extra['buyerId'] = $this->getAlipayBuyerId();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($this->method === PaymentMethodEnum::HUIFU) {
|
|
|
|
// 微信小程序端和微信公众号端需要openid
|
|
|
|
if (in_array($this->client, [ClientEnum::WXOFFICIAL, ClientEnum::MP_WEIXIN])) {
|
|
|
|
$extra['openid'] = $this->getWechatOpenid();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $extra;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取微信端的用户openid(仅微信小程序和微信公众号)
|
|
|
|
* @return null
|
|
|
|
* @throws BaseException
|
|
|
|
*/
|
|
|
|
private function getWechatOpenid()
|
|
|
|
{
|
|
|
|
if (in_array($this->client, [ClientEnum::MP_WEIXIN, ClientEnum::WXOFFICIAL])) {
|
|
|
|
// 当前登录用户信息
|
|
|
|
$useInfo = UserService::getCurrentLoginUser(true);
|
|
|
|
if (!$useInfo['currentOauth'] || empty($useInfo['currentOauth']['oauth_id'])) {
|
|
|
|
throwError('很抱歉,您当前不存在openid 无法发起微信支付');
|
|
|
|
}
|
|
|
|
// 当前第三方用户标识
|
|
|
|
return $useInfo['currentOauth']['oauth_id'];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取支付宝端的用户buyerId(仅支付宝小程序端)
|
|
|
|
* @return null
|
|
|
|
* @throws BaseException
|
|
|
|
*/
|
|
|
|
private function getAlipayBuyerId()
|
|
|
|
{
|
|
|
|
if ($this->client == ClientEnum::MP_ALIPAY) {
|
|
|
|
// 当前登录用户信息
|
|
|
|
$useInfo = UserService::getCurrentLoginUser(true);
|
|
|
|
if (!$useInfo['currentOauth'] || empty($useInfo['currentOauth']['oauth_id'])) {
|
|
|
|
throwError('很抱歉,您当前不存在buyerId 无法发起支付宝支付');
|
|
|
|
}
|
|
|
|
// 当前第三方用户标识
|
|
|
|
return $useInfo['currentOauth']['oauth_id'];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 订单支付成功事件
|
|
|
|
* @param string $orderNo 当前订单号
|
|
|
|
* @param int|null $tradeId 第三方交易记录ID
|
|
|
|
* @param array $paymentData 第三方支付成功返回的数据
|
|
|
|
* @return void
|
|
|
|
* @throws BaseException
|
|
|
|
*/
|
|
|
|
private function orderPaySucces(string $orderNo, ?int $tradeId = null, array $paymentData = []): void
|
|
|
|
{
|
|
|
|
// 获取订单详情
|
|
|
|
$service = new OrderPaySuccesService;
|
|
|
|
// 订单支付成功业务处理
|
|
|
|
$service->setOrderNo($orderNo)->setMethod($this->method)->setTradeId($tradeId)->setPaymentData($paymentData);
|
|
|
|
if (!$service->handle()) {
|
|
|
|
throwError($service->getError() ?: '订单支付失败');
|
|
|
|
}
|
|
|
|
$this->message = '恭喜您,订单已付款成功';
|
|
|
|
}
|
|
|
|
}
|