You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
payment/app/Service/PaymentService.php

430 lines
15 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service;
use App\Exception\BusinessException;
use App\Helper\Efps\Api;
use App\Helper\Efps\Result;
use App\Helper\Log;
use App\Helper\StringHelper;
use App\Model\App;
use App\Model\BankCard;
use App\Model\Order;
use App\Model\PrePayLog;
use App\Model\RefundOrder;
use App\Model\User;
use App\Request\BindCardConfirmRequest;
use App\Request\BindCardRequest;
use App\Request\PaymentQueryRequest;
use App\Request\PaymentRequest;
use App\Request\ProtocolPayConfirmRequest;
use App\Request\ProtocolPayPreRequest;
use App\Request\RefundQueryRequest;
use App\Request\RefundRequest;
use App\Request\UnBindCardRequest;
class PaymentService extends AbstractService
{
public function generateMemberId($appKey, $cardNo) {
return md5($appKey . '-' . $cardNo);
}
public function generateMemberIdNew($appId, $outMemberId) {
return md5($appId . '-' . $outMemberId);
}
public function createOrder(App $app, array $params, $user) {
$order = new Order();
$order->app_id = $app->app_id;
$order->order_no = $params['outTradeNo'];
$order->member_id = $user->member_id;
$order->out_member_id = $params['outMemberId'] ?? '';
$order->out_order_no = $params['outOrderNo'] ?? '';
$order->amount = $params['payAmount'] ?? 0;
$order->notify_url = $params['notifyUrl'] ?? '';
$order->order_info = json_encode($params['orderInfo'] ?? [], JSON_UNESCAPED_UNICODE);
return $order;
}
public function handlePayResult(Result $result, Order $order) {
if ($result->get('payState') === '00') {
$order->status = Order::STATUS_PAYED;
$order->fee = $result->get('procedureFee', 0);
$order->pay_order_no = $result->get('transactionNo', '');
$order->channel_order_no = $result->get('channelOrder', '');
$order->payed_at = date('Y-m-d H:i:s');
$order->save();
} elseif ($result->get('payState') === '01') {
$order->status = Order::STATUS_FAILED;
$order->error_code = '01';
$order->error_msg = '支付失败';
$order->payed_at = date('Y-m-d H:i:s');
$order->save();
}
}
public function handleRefundResult(Result $result, RefundOrder $order) {
if ($result->get('refundState') === '00') {
$order->status = RefundOrder::STATUS_REFUND_SUCCESS;
$order->fee = $result->get('procedureFee', 0);
$order->refunded_at = date('Y-m-d H:i:s');
$order->save();
} elseif ($result->get('refundState') === '01') {
$order->status = RefundOrder::STATUS_REFUND_FAILED;
$order->error_code = '01';
$order->error_msg = '处理失败';
$order->refunded_at = date('Y-m-d H:i:s');
$order->save();
}
}
public function protocolPayPreRequest($params)
{
$req = new ProtocolPayPreRequest($params);
$app = $req->getApp();
$data = $req->getData();
if ($data['payAmount'] > 300000) {
throw new BusinessException('超出限额');
}
$order = Order::where('app_id', $app->app_id)->where('out_order_no', $data['outOrderNo'])->first();
if ($order) {
throw new BusinessException('订单重复');
}
$data['outTradeNo'] = StringHelper::generateOrderNo();
$result = Api::protocolPayPre($data);
$user = User::where('app_id', $app->app_id)->where('out_member_id', $data['outMemberId'])->first();
if (!$user) {
Log::error('UserUndefiend: ', $data);
throw new BusinessException('用户不存在');
}
$order = $this->createOrder($app, $data, $user);
if (!$result->isSuccess()) {
$order->status = Order::STATUS_APPLY_FAIL;
$order->save();
throw new BusinessException($result->getMessage());
}
if (is_null($result->get('payResult'))) {
$order->token = $result->get('token', '');
$order->protocol = $result->get('protocol', '');
$order->status = Order::STATUS_WAIT_PAY;
$order->save();
} else {
$bankCard = BankCard::where('app_id', $app->app_id)
->where('out_member_id', $data['outMemberId'])
->where('sms_no', $data['smsNo'])
->where('status', BankCard::STATUS_WAIT_CONFIRM)
->first();
if ($bankCard) {
$bankCard->status = BankCard::STATUS_ACTIVE;
$bankCard->protocol = $result->get('protocol', '');
$bankCard->save();
}
$order->protocol = $result->get('protocol', '');
$order->status = Order::STATUS_WAIT_PAY;
$order->save();
}
return $result->getData();
}
public function protocolPayConfirm($params)
{
$req = new ProtocolPayConfirmRequest($params);
$app = $req->getApp();
$data = $req->getData();
$order = Order::where('app_id', $app->app_id)
->where('token', $data['token'])
->where('protocol', $data['protocol'])
->first();
if (!$order) {
throw new BusinessException('订单不存在');
}
$result = Api::protocolPayConfirm($req->getData());
if (!$result->isSuccess()) {
$order->status = Order::STATUS_FAILED;
$order->error_code = $result->getCode();
$order->error_msg = $result->getMessage();
$order->payed_at = date('Y-m-d H:i:s');
$order->save();
throw new BusinessException($result->getMessage());
}
// 00 01 03
$this->handlePayResult($result, $order);
return $result->getData();
}
public function bindCard($params) {
$req = new BindCardRequest($params);
$app = $req->getApp();
$reqData = $req->getData();
$memberId = $this->generateMemberIdNew($app->app_id, $reqData['outMemberId']);
$reqData['memberId'] = $memberId;
$bankCard = BankCard::where('app_id', $app->app_id)
->where('out_member_id', $reqData['outMemberId'])
->where('bank_card_no', $reqData['bankCardNo'])
->where('status', BankCard::STATUS_ACTIVE)
->first();
if ($bankCard) {
throw new BusinessException('该卡已绑定');
}
$result = Api::bindCard($reqData);
if (!$result->isSuccess()) {
$message = $result->getMessage();
throw new BusinessException($message == '支付渠道未配置' ? '暂不支持该银行卡' : $message);
}
$user = User::where('out_member_id', $reqData['outMemberId'])->where('app_id', $app->app_id)->first();
if (!$user) {
$user = new User();
$user->member_id = $memberId;
$user->out_member_id = $reqData['outMemberId'];
$user->app_id = $app->app_id;
$user->real_name = $reqData['userName'];
$user->card_no = $reqData['certificatesNo'];
$user->mobile = $reqData['phoneNum'];
$user->save();
}
if ($user && empty($user->out_member_id)) {
$user->out_member_id = $reqData['outMemberId'];
$user->save();
}
$bankCard = new BankCard();
$bankCard->member_id = $memberId;
$bankCard->out_member_id = $reqData['outMemberId'];
$bankCard->app_id = $app->app_id;
$bankCard->real_name = $reqData['userName'];
$bankCard->card_no = $reqData['certificatesNo'];
$bankCard->mobile = $reqData['phoneNum'];
$bankCard->sms_no = $result->get('smsNo', '');
$bankCard->bank_card_no = $reqData['bankCardNo'];
$bankCard->bank_card_type = $reqData['bankCardType'];
$bankCard->protocol = $result->get('protocol', '');
$bankCard->expired = $reqData['expired'] ?? '';
$bankCard->cvn = $reqData['cvn'] ?? '';
$bankCard->status = BankCard::STATUS_WAIT_CONFIRM;
$bankCard->save();
return array_merge($result->getData(), ['memberId' => $memberId]);
}
public function bindCardConfirm($params)
{
$req = new BindCardConfirmRequest($params);
$app = $req->getApp();
$reqData = $req->getData();
$bankCard = BankCard::where('app_id', $app->app_id)
->where('out_member_id', $reqData['outMemberId'])
->where('sms_no', $reqData['smsNo'])
->where('status', BankCard::STATUS_WAIT_CONFIRM)
->first();
if (!$bankCard) {
throw new BusinessException('绑卡申请不存在');
}
$reqData['memberId'] = $bankCard->member_id;
$result = Api::bindCardConfirm($reqData);
if (!$result->isSuccess()) {
throw new BusinessException($result->getMessage());
}
$bankCard->status = BankCard::STATUS_ACTIVE;
$bankCard->protocol = $result->get('protocol', '');
$bankCard->save();
return $result->getData();
}
public function refundQuery($params)
{
$req = new RefundQueryRequest($params);
$data = $req->getData();
$result = Api::refundQuery($data);
if (!$result->isSuccess()) {
throw new BusinessException($result->getMessage());
}
$order = RefundOrder::where('out_refund_order_no', $result->get('outRefundNo'))
->where('status', RefundOrder::STATUS_APPLY_SUCCESS)
->first();
if ($order) {
$this->handleRefundResult($result, $order);
}
return $result->getData([
'outRefundNo',
'transactionNo',
'amount',
'refundAmount',
'refundState'
]);
}
public function paymentQuery($params)
{
$req = new PaymentQueryRequest($params);
$data = $req->getData();
$result = Api::paymentQuery($data);
if (!$result->isSuccess()) {
throw new BusinessException($result->getMessage());
}
$order = Order::where('out_order_no', $result->get('outTradeNo'))
->where('status', Order::STATUS_WAIT_PAY)
->first();
if ($order) {
$this->handlePayResult($result, $order);
}
return $result->getData([
'outTradeNo',
'transactionNo',
'payState',
'procedureFee',
'amount'
]);
}
public function unbindCard($params)
{
$req = new UnBindCardRequest($params);
$app = $req->getApp();
$reqData = $req->getData();
$bankCard = BankCard::where('app_id', $app->app_id)
->where('out_member_id', $reqData['outMemberId'])
->where('protocol', $reqData['protocol'])
->where('status', BankCard::STATUS_ACTIVE)
->first();
if (!$bankCard) {
throw new BusinessException('绑卡不存在');
}
$result = Api::unBindCard($reqData);
if (!$result->isSuccess()) {
throw new BusinessException($result->getMessage());
}
$bankCard->status = BankCard::STATUS_UNBIND;
$bankCard->save();
return $result->getData();
}
public function refund($params)
{
$req = new RefundRequest($params);
$app = $req->getApp();
$data = $req->getData();
$refundOrder = new RefundOrder();
$refundOrder->app_id = $app->app_id;
$refundOrder->out_order_no = $data['outTradeNo'] ?: '';
$refundOrder->out_refund_order_no = $data['outRefundNo'] ?: '';
$refundOrder->order_amount = $data['amount'] ?: '';
$refundOrder->refund_amount = $data['refundAmount'] ?: '';
$refundOrder->remark = $data['remark'] ?: '';
$refundOrder->notify_url = $data['notifyUrl'] ?: '';
$result = Api::refund($data);
if (!$result->isSuccess()) {
$refundOrder->status = RefundOrder::STATUS_APPLY_FAILED;
$refundOrder->error_code = $result->getCode();
$refundOrder->error_msg = $result->getMessage();
$refundOrder->save();
throw new BusinessException($result->getMessage());
}
$refundOrder->status = RefundOrder::STATUS_APPLY_SUCCESS;
$refundOrder->save();
return $result->getData();
}
public function payment($params)
{
$req = new PaymentRequest($params);
$app = $req->getApp();
$reqData = $req->getData();
if (!$reqData['outOrderNo']) {
throw new BusinessException('订单号错误');
}
if (!$reqData['outMemberId']) {
throw new BusinessException('用户ID不能为空');
}
if (isset($reqData['amount'])) {
if (!is_numeric($reqData['amount'])) {
throw new BusinessException('请输入金额');
}
if ($reqData['amount'] < 100 || $reqData['amount'] > 300000) {
Log::error('reqData:', $reqData);
throw new BusinessException('充值金额必须为100~300000');
}
}
if (!$reqData['notifyUrl']) {
throw new BusinessException('通知地址不能为空');
}
$log = PrePayLog::where('app_id', $app->app_id)->where('out_order_no', $reqData['outOrderNo'])->first();
$order = Order::where('app_id', $app->app_id)->where('out_order_no', $reqData['outOrderNo'])->first();
if ($log || $order) {
throw new BusinessException('订单重复');
}
$token = md5(microtime(true) . json_encode($reqData) . $app->app_id . $app->app_key);
$log = new PrePayLog();
$log->app_id = $app->app_id;
$log->out_order_no = $reqData['outOrderNo'];
$log->amount = $reqData['amount'] ?: 0;
$log->out_member_id = $reqData['outMemberId'];
$log->notify_url = $reqData['notifyUrl'];
$log->redirect_url = $reqData['redirectUrl'] ?? '';
$log->token = $token;
$log->save();
return ['payUrl' => env('WEB_HOST') . '/payment.html?token=' . $token];
}
public function query($params)
{
$req = new PaymentQueryRequest($params);
$data = $req->getData();
$app = $req->getApp();
if (empty($data['outOrderNo'])) {
throw new BusinessException('订单号不能为空');
}
$order = Order::where('app_id', $app->app_id)->where('out_order_no', $data['outOrderNo'])->first();
if (!$order) {
throw new BusinessException('订单不存在');
}
$result = Api::paymentQuery(['outTradeNo' => $order->order_no]);
if (!$result->isSuccess()) {
throw new BusinessException($result->getMessage());
}
$this->handlePayResult($result, $order);
return [
'outOrderNo' => $data['outOrderNo'],
'orderNo' => $order->order_no,
'payState' => $result->get('payState'),
'amount' => $result->get('amount'),
];
}
}