travel/service/app/api/controller/DyNotifyController.php

453 lines
20 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 app\api\controller;
use app\model\Admins;
use app\model\DyOrderProductAppointments;
use app\model\DyOrderProducts;
use app\model\DyOrders;
use app\model\OrderAfterSales;
use app\model\Orders;
use app\model\Products;
use app\server\Douyin;
use support\Log;
use support\Redis;
use support\Request;
use think\facade\Db;
/**
* 抖音回调处理
*/
class DyNotifyController extends base {
/**
* 创建预售订单
* @param Request $request
* @return string
* @throws \Exception
*/
public function orderCreate(Request $request) {
try {
$params = $request->all();
Log::info('orderCreate params:' . json_encode($params));
if (!empty($order = DyOrders::query()->where(['dy_order_id' => $params['order_id']])->find())) {
return $this->success(['order_out_id' => $order->order_id, 'order_id' => $order->dy_order_id]);
}
$orderId = getNewOrderId('Dy');
$orderData = [
'order_id' => $orderId,
'dy_order_id' => $params['order_id'],
'biz_type' => $params['biz_type'] ?? '',
'total_amount' => $params['total_amount'] ?? 0,
'total_coupon_count' => $params['total_coupon_count'] ?? 0,
'each_coupon_amount' => $params['each_coupon_amount'] ?? 0,
'pay_amount' => $params['pay_amount'] ?? 0,
'actual_amount' => $params['actual_amount'] ?? 0,
'merchant_discount_amount' => $params['merchant_discount_amount'] ?? 0,
'pay_time_unix' => $params['pay_info']['pay_time_unix'] ?? 0,
'status' => 0,
'phone' => $this->decrypt($params['buyer_info']['phone'], env('DY_APPSECRET')),
];
// 已支付
if ($orderData['pay_time_unix']) {
$orderData['status'] = 10;
}
$res = DyOrders::create($orderData);
Log::info('res:' . json_encode($res));
if (!empty($res) && isset($params['product_snap_shot']) && !empty($params['product_snap_shot'])) {
$orderProducts = [];
foreach ($params['product_snap_shot'] as $product) {
array_push($orderProducts, [
'dy_order_id' => $orderData['dy_order_id'],
'sku_id' => $product['sku_id'] ?? '',
'product_id' => $product['product_id'] ?? '',
'out_product_id' => $product['out_product_id'] ?? '',
'name' => $product['name'] ?? '',
'category_id' => $product['category_id'] ?? '',
'category_full_name' => $product['category_full_name'] ?? '',
'travel_details' => json_encode($product['travel_details'] ?? []),
'commodity' => json_encode($product['commodity'] ?? []),
'is_superimposed_discounts' => $product['is_superimposed_discounts'] ? 1: 0,
'date_add_price_rule' => isset($product['date_add_price_rule']) ? json_encode($product['date_add_price_rule']) : '',
'user_add_price_rule' => isset($product['user_add_price_rule']) ? json_encode($product['user_add_price_rule']) : '',
'room_add_price_rule' => isset($product['room_add_price_rule']) ? json_encode($product['room_add_price_rule']) : '',
'room_upgrade_rule' => isset($product['room_upgrade_rule']) ? json_encode($product['room_upgrade_rule']) : '',
'add_price_policy' => isset($product['add_price_policy']) ? json_encode($product['add_price_policy']) : '',
'description_rich_text' => isset($product['description_rich_text']) ? json_encode($product['description_rich_text']) : '',
'fee_include' => isset($product['fee_include']) ? json_encode($product['fee_include']) : '',
'fee_not_include' => isset($product['fee_not_include']) ? json_encode($product['fee_not_include']) : '',
'self_pay_expense_rule' => isset($product['self_pay_expense_rule']) ? json_encode($product['self_pay_expense_rule']) : '',
'earliest_appointment' => isset($product['earliest_appointment']) ? json_encode($product['earliest_appointment']) : '',
'appointment' => isset($product['appointment']) ? json_encode($product['appointment']) : '',
'refund_rule' => isset($product['refund_rule']) ? json_encode($product['refund_rule']) : '',
'use_date' => isset($product['use_date']) ? json_encode($product['use_date']) : '',
'refund_description' => isset($product['refund_description']) ? json_encode($product['refund_description']) : '',
'merchant_break_rule' => isset($product['merchant_break_rule']) ? json_encode($product['merchant_break_rule']) : '',
'merchant_break_description' => isset($product['merchant_break_description']) ? json_encode($product['merchant_break_description']) : '',
'presale_appointment_cancel_policy' => isset($product['presale_appointment_cancel_policy']) ? json_encode($product['presale_appointment_cancel_policy']) : '',
]);
}
Log::info('orderProducts:' . json_encode($orderProducts));
// 批量插入
/** @var DyOrderProducts $dyOrderProducts */
$dyOrderProducts = (new DyOrderProducts());
$dyOrderProducts->insertAll($orderProducts);
if ($orderData['status'] == 10) {
$this->createPlatformOrder($orderData, $orderProducts[0]);
}
}
// order_out_id - 第三方订单ID, order_id - 抖音订单号
return $this->success(['order_out_id' => $orderId, 'order_id' => $params['order_id']]);
} catch (\Exception $exception) {
return $this->error('-1', $exception->getMessage());
}
}
// 通知支付结果
public function pay(Request $request) {
try {
$params = $request->all();
// Validator::input($params, [
// 'order_id' => Validator::notEmpty()->setName('订单号'),
// 'biz_type' => Validator::notEmpty()->setName('旅行社预售券'),
// 'pay_amount' => Validator::notEmpty()->setName('支付金额'),
// 'pay_time_unix' => Validator::notEmpty()->setName('支付时间'),
// ]);
Log::info('pay params:' . json_encode($params));
// » biz_type - 旅行社预售券3011旅行社预定订单3012
// » pay_amount 用户实付金额(分)
// » pay_time_unix 支付时间戳
if (isset($params['biz_type']) && $params['biz_type'] == 3012) {
if (empty($order = DyOrderProductAppointments::query()->where(['dy_order_id' => $params['source_order_id']])->find())) {
return $this->error('-2', '订单信息未找到');
}
$res = DyOrderProductAppointments::query()->where(['order_id' => $params['order_id']])->update(['pay_amount' => $params['pay_amount'] ?? 0]);
} else {
if (empty($order = DyOrders::query()->where(['dy_order_id' => $params['source_order_id']])->find())) {
return $this->error('-2', '订单信息未找到');
}
$res = DyOrders::query()->where(['dy_order_id' => $params['order_id']])->update(['status' => 10, 'pay_amount' => $params['pay_amount'], 'pay_time_unix' => $params['pay_time_unix']]);
$orderProduct = DyOrderProducts::query()->where(['dy_order_id' => $params['order_id']])->find();
if (!$res) {
$this->createPlatformOrder($order, $orderProduct);
}
}
if ($res) {
return $this->success([]);
} else {
return $this->error('-2', '操作失败');
}
} catch (\Exception $exception) {
return $this->error('-1', $exception->getMessage());
}
}
/**
* @param $order
* @param $orderProduct
* @return Orders
*/
private function createPlatformOrder($order, $orderProduct) {
$item = Orders::query()->where(['sn' => $order['dy_order_id']])->find();
if (!empty($item)) {
return $item;
}
$adminId = 0;
// 查找用户归属
try {
// 直播间uid
$dyService = new Douyin(5);
$dyRes = $dyService->_certificateList(1, null, null, $order['dy_order_id']);
if (!empty($dyRes) && $dyRes->data) {
$dyRes = json_decode(json_encode($dyRes->data), true);
$saleUserNickName = trim($dyRes['list'][0]['extra']['attribute']['sale_user_nickname']);
Log::info('$saleUserNickName:' . $saleUserNickName);
if (!empty($saleUserNickName)) {
$adminId = Admins::where('status', 1)->whereLike('dy_nickname', "%{$saleUserNickName}%")->value('id');
}
}
} catch (\Exception $e) {
Log::info('get admin_info error:' . $e->getMessage());
}
if (!$adminId) {
$product = Products::query()->where('third_product_id', $orderProduct['product_id'])->find();
if (!empty($product)) {
$adminId = Admins::where('status', 1)->whereFindInSet('product_ids', $product->id)->value('id');
}
}
if ($adminId) {
// 新获得一个用户的,提示管理员有新的订单
Redis::incrBy('CRM:USER:ONLINE:NEW:' . $adminId, 1);
}
// 订单支付成功,写入平台订单表
$item = new Orders();
$item->os = 5;
$item->sn = $order['dy_order_id'];
$item->product_id = $orderProduct['product_id'];
$item->product_name = $orderProduct['name'];
$item->category_id = $orderProduct['category_id'];
$item->create_at = $order['pay_time_unix'] * 1000;
$item->mobile = $order['phone'] ?? null;
$item->unit_price = $order['pay_amount'];
$item->total_price = $order['pay_amount'];
$item->actual_price = $order['pay_amount'];
$item->quantity = $order['total_coupon_count'];
$item->order_status = 1;
$item->asset_status = 0;
$item->category_desc = $orderProduct['category_full_name'];
$item->asset_price = 0;
$item->is_refunded = 0; // 是否已退款1-已退款0-未退款)
$item->admin_id = $adminId;
$item->is_direct_mode = 1; // 是否直连模式0-否1-是)
$item->save();
return $item;
}
/**
* 处理预约信息
* @param $params
* @return array
*/
private function handleBookInfo($params) {
$bookInfo = [];
if (isset($params['book_info']) && !empty($params['book_info'])) {
$bookInfo = $params['book_info'];
if (isset($bookInfo['occupancies']) && is_array($bookInfo['occupancies'])) {
foreach ($bookInfo['occupancies'] as &$occupancy) {
if (isset($occupancy['name']) && !empty($occupancy['name'])) {
$occupancy['name'] = $this->decrypt($occupancy['name'], env('DY_APPSECRET'));
}
if (isset($occupancy['license_id']) && !empty($occupancy['license_id'])) {
$occupancy['license_id'] = $this->decrypt($occupancy['license_id'], env('DY_APPSECRET'));
}
}
}
}
return $bookInfo;
}
// 用户创建预约订单
public function userMakeAppointment(Request $request) {
$params = $request->all();
Log::info('userMakeAppointment params:' . json_encode($params));
$bookInfo = $this->handleBookInfo($params);
$orderData = [
'dy_order_id' => $params['order_id'] ?? '',
'source_order_id' => $params['source_order_id'] ?? '',
'presale_coupon_id' => $params['presale_coupon_id'] ?? '',
'out_presale_coupon_id' => $params['out_presale_coupon_id'] ?? '',
'biz_type' => $params['biz_type'] ?? 0,
'pay_amount' => $params['pay_amount'] ?? 0,
'actual_amount' => $params['actual_amount'] ?? 0,
'merchant_discount_amount' => $params['merchant_discount_amount'] ?? 0,
'discount_amount' => $params['discount_amount'] ?? 0,
'original_amount' => $params['original_amount'] ?? 0,
'pay_info' => isset($params['pay_info']) ? json_encode($params['pay_info']) : '',
'book_info' => $bookInfo,
'buyer_info' => isset($params['buyer_info']) ? json_encode($params['buyer_info']) : '',
'remark_from_guest' => $params['remark_from_guest'],
];
if (empty($order = DyOrders::query()->where(['dy_order_id' => $orderData['source_order_id']])->find())) {
return $this->error('-2', '订单信息未找到');
}
$orderData['appoint_order_id'] = getNewOrderId('Yy');
if (!empty($appoint = DyOrderProductAppointments::query()->where(['dy_order_id' => $orderData['dy_order_id']])->find())) {
return $this->success(['order_id' => $appoint->dy_order_id, 'order_out_id' => $appoint->appoint_order_id]);
}
// biz_type - 旅行社预售券3011旅行社预定订单3012
$res = DyOrderProductAppointments::create($orderData);
if ($res) {
// 更新平台订单表
Orders::query()->where('sn', $params['source_order_id'])->update(['appointment_status' => 1, 'travel_date' => $bookInfo['book_start_date']]);
return $this->success(['order_id' => $orderData['dy_order_id'], 'order_out_id' => $orderData['appoint_order_id']]);
} else {
return $this->error('-1');
}
}
// 用户取消订单
public function userCancelOrder(Request $request) {
$params = $request->all();
Log::info('userCancelOrder params:' . json_encode($params));
if (!isset($params['order_id']) || empty($params['order_id'])) {
throw new \Exception('订单号缺失');
}
if (isset($params['biz_type']) && $params['biz_type'] == 3012) {
// 取消预约
$appoint = DyOrderProductAppointments::query()->where('dy_order_id', $params['order_id'])->find();
Orders::query()->where('sn', $appoint['source_order_id'])->update(['appointment_status' => 0, 'travel_date' => null]);
OrderAfterSales::query()->where('order_id', $appoint['source_order_id'])->update(['status' => 1]);
return $this->success([]);
}
if (empty($order = Orders::query()->where(['sn' => $params['order_id']])->find())) {
return $this->error('-2', '订单信息未找到');
}
// » refund_type 枚举值1、订单退款2、补差价退款
$res = Db::transaction(function () use ($order, $params) {
DyOrders::query()->where('dy_order_id', $params['order_id'])->update(['status' => 30]);
// 更新平台订单表
if ($order->order_status < 3) {
Orders::query()->where('sn', $params['order_id'])->update(['order_status' => 3]);
}
return true;
});
if ($res) {
return $this->success([]);
}
return $this->error('-1', '取消失败');
}
// 订单退款结果通知
public function orderRefundNotify(Request $request) {
$params = $request->all();
Log::info('orderRefundNotify params:' . json_encode($params));
if (!isset($params['order_id']) || empty($params['order_id'])) {
throw new \Exception('订单号缺失');
}
if (isset($params['biz_type']) && $params['biz_type'] == 3012) {
// 取消预约退款, 暂不处理
return $this->success([]);
}
if (empty($order = DyOrders::query()->where(['dy_order_id' => $params['order_id']])->find())) {
return $this->error('-2', '订单信息未找到');
}
$res = Db::transaction(function () use ($order, $params) {
DyOrders::query()->where(['order_id' => $params['order_id']])->update(['status' => 40]);
// 更新平台订单表
Orders::query()->where(['sn' => $params['order_id']])->update(['order_status' => 4, 'is_refunded' => 1, 'refund_status' => 3]);
return true;
});
if ($res) {
return $this->success([]);
}
return $this->error('-1', '取消失败');
}
public function success($data,$mess = null, $ext = null) {
$res = [
'error_code' => 0,
'description' => 'success',
];
if (isset($data['order_id'])) {
$res['order_id'] = $data['order_id'];
}
if (isset($data['order_out_id'])) {
$res['order_out_id'] = $data['order_out_id'];
}
return json([
'data' => $res
]);
}
public function error($code, $mess = null) {
$res = [
'error_code' => $code,
'description' => $mess,
];
return json([
'data' => $res
]);
}
/**
1.根据ClientKev找到ClientSecret将ClientSecret向左右使用字符补齐32位/裁剪至32位补齐:补位字符:#先补左侧再补右侧再补左侧………直到补满32位。
裁剪:先裁剪左侧再裁右侧再裁左侧………直到剩余32位。(正常不需要补齐secret默认为32位此举是为了
以防万一)
2.将ClientSecret作为Key,右侧16位为向量IV
3.将密文进行base64解码。
4.使用AES-256-CBC模式解密解码后的密文对齐使用PKCS5Padding方式
* @param $cipherText
* @param $clientSecret
* @return false|string
*/
private function decrypt($cipherText, $clientSecret) {
Log::info('$cipherText:' . $cipherText);
// 补齐或裁剪 ClientSecret 为 32 位
$clientSecret = $this->padOrTruncate($clientSecret);
Log::info('$cipherText step 2:' . $clientSecret);
// 使用 ClientSecret 作为密钥
$key = $clientSecret;
// 右侧16位为初始化向量IV
$iv = substr($key, 16, 16);
// 将密文 base64 解码
$cipherTextDecoded = base64_decode($cipherText);
// 使用 AES-256-CBC 解密
return openssl_decrypt($cipherTextDecoded, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
}
/**
* @param $clientSecret
* @return false|string
*/
private function padOrTruncate($clientSecret) {
// 1. 如果 clientSecret 长度为32直接返回
if (strlen($clientSecret) == 32) {
return $clientSecret;
}
// 2. 如果 length < 32补齐至32位
if (strlen($clientSecret) < 32) {
// 补齐过程先补左侧再补右侧再补左侧直到长度为32
$paddingChar = '#';
$padded = $clientSecret;
// 左侧补齐
$left = true;
while (strlen($padded) < 32) {
if ($left) {
$padded = $paddingChar . $padded;
$left = false;
} else {
$padded = $padded . $paddingChar;
$left = true;
}
}
return substr($padded, 0, 32); // 确保返回32位
}
// 3. 如果 length > 32裁剪至32位
if (strlen($clientSecret) > 32) {
// 裁剪过程先裁左侧再裁右侧再裁左侧直到长度为32
$cropped = $clientSecret;
// 左侧裁剪
while (strlen($cropped) > 32) {
$cropped = substr($cropped, 1);
}
// 右侧裁剪
while (strlen($cropped) > 32) {
$cropped = substr($cropped, 0, -1);
}
// 左侧裁剪(再一次)
while (strlen($cropped) > 32) {
$cropped = substr($cropped, 1);
}
return substr($cropped, 0, 32); // 确保返回32位
}
return $clientSecret;
}
}