385 lines
14 KiB
PHP
385 lines
14 KiB
PHP
<?php
|
|
|
|
namespace app\service;
|
|
|
|
use app\config\BusinessCacheKey;
|
|
use app\exception\BusinessException;
|
|
use app\exception\LogicException;
|
|
use app\model\Order;
|
|
use app\model\Product;
|
|
use app\model\Sign;
|
|
use app\model\User;
|
|
use app\service\util\CmbLifeUtils;
|
|
use app\service\util\SmsUtil;
|
|
use app\sms\AliSms;
|
|
use app\util\CmbHttpUtils;
|
|
use app\util\StringUtil;
|
|
use think\facade\Db;
|
|
use think\facade\Log;
|
|
|
|
class AgreementService extends BaseService
|
|
{
|
|
/**
|
|
* 生成签约协议
|
|
* @param int $userId
|
|
* @return mixed
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\DbException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
public static function agreeApproval(int $userId, string $callback, string $account): string
|
|
{
|
|
$cmbConfig = config('cmb');
|
|
$user = User::getUserById($userId);
|
|
if ($user->isEmpty()) {
|
|
throw new LogicException('用户不存在!');
|
|
}
|
|
// 查询当前用户是否可再签约
|
|
$isCanAgree = self::checkCanAgree($userId, $account);
|
|
if (!$isCanAgree['success']) {
|
|
throw new \LogicException($isCanAgree['msg']);
|
|
}
|
|
$params = [
|
|
'mAgreementId' => StringUtil::makeAgreementId(),
|
|
'merchantUserId' => StringUtil::maskPhone($account),
|
|
'requestSerial' => uniqid(), // 请求流水号(商户侧唯一)
|
|
'notifyUrl' => $cmbConfig['agree_notify_url'],
|
|
'callback' => $callback,
|
|
];
|
|
$sign = Sign::getWaitSignByUserId($userId);
|
|
if (!$sign->isEmpty()) {
|
|
$params['mAgreementId'] = $sign->m_agreement_id;
|
|
$params['requestSerial'] = $sign->request_serial;
|
|
}
|
|
$funcName = 'authorize';
|
|
// 生成新签约记录
|
|
Db::startTrans();
|
|
try {
|
|
if (!$sign->isEmpty()) { // 更新
|
|
$sign->mobile = $account;
|
|
$sign->create_time = time();
|
|
$sign->save();
|
|
$order = Order::getByAgreementId($sign->m_agreement_id);
|
|
$order->account = $account;
|
|
$order->create_time = time();
|
|
$order->save();
|
|
} else {
|
|
Sign::create(
|
|
['user_id' => $userId,
|
|
'open_id' => $user->open_id,
|
|
'mobile' => $account,
|
|
'request_serial' => $params['requestSerial'],
|
|
'm_agreement_id' => $params['mAgreementId']]
|
|
);
|
|
// 创建订单
|
|
$data = [
|
|
'user_id' => $user->id,
|
|
'product_id' => config('cmb.product_id'),
|
|
'account' => $account,
|
|
'agreement_id' => $params['mAgreementId']
|
|
];
|
|
CmbService::createOrder($data);
|
|
}
|
|
Db::commit();
|
|
} catch (\Throwable $e) {
|
|
Db::rollback();
|
|
throw new \LogicException($e->getMessage());
|
|
}
|
|
return CmbLifeUtils::genProtocol($funcName, $params);
|
|
}
|
|
|
|
/**
|
|
* 签约通知
|
|
* @param $data
|
|
* @return array
|
|
*/
|
|
public static function agreeNotify($data)
|
|
{
|
|
Log::info('签约回调:' . json_encode($data));
|
|
$verifyRes = CmbLifeUtils::verifyForResponse($data);
|
|
if (!$verifyRes) {
|
|
return ['respCode' => 1001, 'msg' => '验签不通过'];
|
|
}
|
|
$requestSerial = $data['requestSerial'];
|
|
$sign = Sign::getByRequestSerial($requestSerial);
|
|
if ($sign->isEmpty()) {
|
|
return ['respCode' => 1000, 'msg' => '处理成功'];
|
|
}
|
|
$sign->sign_status = Sign::SIGN_STATUS_SUCCESS;
|
|
$sign->agreement_id = $data['agreementId'];
|
|
$sign->save();
|
|
// 签约成功开始扣款
|
|
$order = Order::getWaitSignByAgreementId($data['mAgreementId']);
|
|
if ($order->isEmpty() || $order->order_status == Order::STATUS_SIGNED) {
|
|
return ['respCode' => 1000, 'msg' => '处理成功'];
|
|
}
|
|
$order->order_status = Order::STATUS_SIGNED;
|
|
$order->save();
|
|
// 签约第一次扣款
|
|
self::firstPay($sign, $order);
|
|
// if ($agreePayRes['respCode'] != '1000') {
|
|
// return ['respCode' => 1001, 'msg' => '扣款失败' . $agreePayRes['respMsg']];
|
|
// }
|
|
return ['respCode' => 1000, 'msg' => '处理成功'];
|
|
}
|
|
|
|
/**
|
|
* 签约状态查询处理
|
|
* @return mixed
|
|
*/
|
|
public static function queryAgreementStatus(Sign $sign)
|
|
{
|
|
$funcName = 'queryAgreementStatus';
|
|
$requestParams = [
|
|
'mAgreementId' => $sign->m_agreement_id // 商户端协议号
|
|
];
|
|
$res = CmbHttpUtils::doPost($funcName, $requestParams);
|
|
if ($res['respCode'] !== '1000') {
|
|
return true;
|
|
}
|
|
$sign->sign_status = $res['status'];
|
|
$sign->agreement_id = $res['agreementId'];
|
|
$sign->save();
|
|
$agreementId = $sign->m_agreement_id;
|
|
// 如果待签约未扣款订单开始执行扣款操作
|
|
$order = Order::getWaitSignByAgreementId($agreementId);
|
|
if ($res['status'] != 1 || $order->isEmpty()) {
|
|
return true;
|
|
}
|
|
$order->order_status = Order::STATUS_SIGNED;
|
|
$order->save();
|
|
// 签约完成开始处理第一次签约扣款
|
|
self::firstPay($sign, $order);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 第一次扣款
|
|
* @param Sign $collection
|
|
* @param Order $orderCollection
|
|
* @return mixed|string
|
|
*/
|
|
public static function firstPay(Sign $collection, Order $orderCollection)
|
|
{
|
|
$productId = $orderCollection->product_id;
|
|
$product = Product::getBySupplierProductId($productId);
|
|
// 扣款参数
|
|
$requestParams = [
|
|
'agreementId' => $collection->agreement_id,
|
|
'billNo' => $orderCollection->order_number,
|
|
'amount' => $orderCollection->price * 100,
|
|
'bonus' => $orderCollection->bonus,
|
|
'notifyUrl' => config('cmb.pay_notify_url'),
|
|
'productName' => $product['name']
|
|
];
|
|
$funcName = 'agreementPay';
|
|
return CmbHttpUtils::doPost($funcName, $requestParams);
|
|
}
|
|
|
|
/**
|
|
* 处理协议扣款
|
|
* @param Sign $collection
|
|
* @param Order $orderCollection
|
|
* @return mixed
|
|
* @throws \think\db\exception\DbException
|
|
*/
|
|
public static function agreementPay(Sign $collection, Order $orderCollection)
|
|
{
|
|
$productId = $orderCollection->product_id;
|
|
$product = Product::getBySupplierProductId($productId);
|
|
$order = [
|
|
'user_id' => $orderCollection->user_id,
|
|
'order_number' => StringUtil::makeOrderNumber(),
|
|
'account' => $orderCollection->account,
|
|
'type' => $orderCollection->type,
|
|
'product_id' => $orderCollection->product_id,
|
|
'price' => $product['price'],
|
|
'agreement_id' => $orderCollection->agreement_id,
|
|
'order_status' => Order::STATUS_SIGNED
|
|
];
|
|
// 查询第几次扣款
|
|
$count = Order::getCountByAgreementId($collection->m_agreement_id);
|
|
if ($count < 2) {
|
|
$order['price'] = config('cmb.continue_price');
|
|
}
|
|
// 生成连续包月订单
|
|
Order::create($order);
|
|
// 扣款参数
|
|
$requestParams = [
|
|
'agreementId' => $collection->agreement_id,
|
|
'billNo' => $order['order_number'],
|
|
'amount' => $order['price'] * 100,
|
|
'notifyUrl' => config('cmb.pay_notify_url'),
|
|
'productName' => $product['name']
|
|
];
|
|
$funcName = 'agreementPay';
|
|
return CmbHttpUtils::doPost($funcName, $requestParams);
|
|
}
|
|
|
|
/**
|
|
* 协议扣款回调处理
|
|
* @param array $data
|
|
* @return int[]
|
|
*/
|
|
public static function payNotify(array $data)
|
|
{
|
|
Log::info('付款回调:' . json_encode($data));
|
|
$verifyRes = CmbLifeUtils::verifyForResponse($data);
|
|
if (!$verifyRes) {
|
|
return ['respCode' => 1001, 'respMsg' => '签名有误'];
|
|
}
|
|
$encryptBody = $data['encryptBody'] ?? '';
|
|
if (empty($encryptBody)) {
|
|
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
|
}
|
|
$encryptData = CmbLifeUtils::decrypt($encryptBody, config('cmb.merchant_sm2_pri_key'));
|
|
$orderNumber = $encryptData['billNo'];
|
|
$order = Order::getByOrderNumber($orderNumber);
|
|
if ($order->isEmpty() || $order->pay_status == Order::PAY_STATUS_PAID) {
|
|
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
|
}
|
|
$update = [
|
|
'pay_time' => isset($encryptData['payDate']) ? strtotime($encryptData['payDate']) : time(),
|
|
'pay_status' => $encryptData['result'],
|
|
'ref_num' => $encryptData['refNum'] ?? '',
|
|
'pay_type' => $encryptData['payType'] ?? '',
|
|
'pay_card_no' => $encryptData['shieldCardNo'] ?? '',
|
|
];
|
|
if ($encryptData['result'] == 2) {
|
|
$update['order_status'] = Order::STATUS_WAIT_RECHARGE;
|
|
}
|
|
$res = Order::update($update, ['order_number' => $orderNumber]);
|
|
if ($encryptData['result'] == 2) {
|
|
RechargeService::rechargeOrder($orderNumber); // 支付成功,到直连天下充值
|
|
}
|
|
if ($res) {
|
|
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
|
} else {
|
|
return ['respCode' => 1001, 'respMsg' => '处理失败'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 解约处理
|
|
* @param array $data
|
|
* @return int[]
|
|
*/
|
|
public static function releaseNotify(array $data)
|
|
{
|
|
Log::info('解约回调:' . json_encode($data));
|
|
$verifyRes = CmbLifeUtils::verifyForResponse($data);
|
|
if (!$verifyRes) {
|
|
return ['respCode' => 1001, 'msg' => '验签不通过'];
|
|
}
|
|
$requestSerial = $data['requestSerial'];
|
|
$sign = Sign::getByRequestSerial($requestSerial);
|
|
if ($sign->isEmpty() || $sign->sign_status == Sign::SIGN_STATUS_RELEASE) {
|
|
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
|
}
|
|
$sign->sign_status = Sign::SIGN_STATUS_RELEASE;
|
|
$res = $sign->save();
|
|
if ($res) {
|
|
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
|
} else {
|
|
return ['respCode' => 1000, 'respMsg' => '处理失败'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 取消订阅
|
|
* @param int $userId
|
|
* @return mixed
|
|
*/
|
|
public static function unsubscribe($params)
|
|
{
|
|
$userId = $params['user_id'];
|
|
// 校验验证码
|
|
if (!in_array($params['phone'], ['13205115489', '13305093595', '15107950285'])) {
|
|
// 校验验证码
|
|
$codeCacheKey = SmsUtil::getSmsKey($params['phone']);
|
|
SmsUtil::compareSmsCode($codeCacheKey, $params['code']);
|
|
}
|
|
// 用户解约
|
|
$signInfo = Sign::getSubscribeByUserId($userId);
|
|
if ($signInfo->isEmpty()) {
|
|
throw new LogicException('该用户无可退订商品');
|
|
}
|
|
// 解约
|
|
$funcName = 'releaseForMerchant';
|
|
$requestParams = [
|
|
'mAgreementId' => $signInfo->m_agreement_id,
|
|
'merchantUserId' => StringUtil::maskPhone($signInfo->mobile)
|
|
];
|
|
$res = CmbHttpUtils::doPost($funcName, $requestParams);
|
|
if ($res['respCode'] == '1000') {
|
|
$signInfo->sign_status = Sign::SIGN_STATUS_RELEASE;
|
|
$signInfo->save();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 校验是否能签约
|
|
* @param int $userId
|
|
* @param string $account
|
|
* @return array
|
|
* @throws \think\db\exception\DbException
|
|
*/
|
|
public static function checkCanAgree(int $userId, string $account)
|
|
{
|
|
// 1. 该用户已经签约
|
|
$sign = Sign::getSignLastYear($userId);
|
|
if (!$sign->isEmpty()) {
|
|
return ['success' => false, 'msg' => '该用户已签约'];
|
|
}
|
|
if (!empty($account)) {
|
|
$mobileSign = Sign::getSignLastYearByMobile($account);
|
|
if (!$mobileSign->isEmpty()) {
|
|
return ['success' => false, 'msg' => '该手机号已签约!'];
|
|
}
|
|
}
|
|
// 3. 该用户解约过且近一年扣款过
|
|
$releaseSign = Sign::getReleaseLastYear($userId);
|
|
$orderCount = Order::getMonthOrderCountByUserId($userId);
|
|
if (!$releaseSign->isEmpty() && $orderCount > 0) {
|
|
return ['success' => false, 'msg' => '该用户一年内存在签约!'];
|
|
}
|
|
return ['success' => true];
|
|
}
|
|
/**
|
|
* 解约发送验证码
|
|
* @throws BusinessException
|
|
*/
|
|
public static function releaseSendSms($params)
|
|
{
|
|
$key = SmsUtil::getSmsKey($params['phone']);
|
|
SmsUtil::requestLimit($key, BusinessCacheKey::RELEASE_SMS_CODE['ttl']);
|
|
$code = SmsUtil::getSmsCode($key, BusinessCacheKey::RELEASE_SMS_CODE['ttl']);
|
|
AliSms::sendSms(['phone_numbers' => $params['phone'], 'code' => $code]);
|
|
}
|
|
|
|
/**
|
|
* 获取绑定手机号
|
|
* @param int $userId
|
|
* @return array
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
public static function getBindMobile(int $userId)
|
|
{
|
|
$user = User::getUserById($userId);
|
|
return ['mobile' => $user->mobile];
|
|
}
|
|
/**
|
|
* 判断是否能兑换
|
|
* @param array $params
|
|
* @return array
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
public static function getExchangeStatus(array $params): array
|
|
{
|
|
$isCanAgree = self::checkCanAgree($params['user_id'], '');
|
|
return ['is_exchange' => $isCanAgree['success']];
|
|
}
|
|
} |