fengketrade/addons/shopro/library/ccblife/CcbPaymentService.php

408 lines
12 KiB
PHP
Raw Normal View History

2025-10-17 16:32:16 +08:00
<?php
namespace addons\shopro\library\ccblife;
2025-10-17 17:18:15 +08:00
use app\admin\model\shopro\order\Order;
use think\Db;
use think\Log;
2025-10-17 16:32:16 +08:00
/**
2025-10-17 17:18:15 +08:00
* 建行生活支付服务类
* 处理支付串生成、支付回调、支付验证等业务
2025-10-17 16:32:16 +08:00
*/
class CcbPaymentService
{
/**
* 配置信息
*/
private $config;
/**
2025-10-17 17:18:15 +08:00
* 订单服务实例
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
private $orderService;
2025-10-17 16:32:16 +08:00
/**
* 构造函数
*/
2025-10-17 17:18:15 +08:00
public function __construct()
2025-10-17 16:32:16 +08:00
{
2025-10-17 17:18:15 +08:00
$this->config = config('ccblife');
$this->orderService = new CcbOrderService();
2025-10-17 16:32:16 +08:00
}
/**
2025-10-17 17:18:15 +08:00
* 生成建行支付串
* 用于前端JSBridge调用建行收银台
2025-10-17 16:32:16 +08:00
*
2025-10-17 17:18:15 +08:00
* @param int $orderId Shopro订单ID
* @return array ['status' => bool, 'message' => string, 'data' => array]
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
public function generatePaymentString($orderId)
2025-10-17 16:32:16 +08:00
{
try {
2025-10-17 17:18:15 +08:00
// 获取订单信息
$order = Order::find($orderId);
if (!$order) {
throw new \Exception('订单不存在');
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 检查订单状态
if ($order['status'] != 'unpaid') {
throw new \Exception('订单状态不正确');
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 获取用户建行ID
$user = Db::name('user')->where('id', $order['user_id'])->field('ccb_user_id')->find();
if (empty($user['ccb_user_id'])) {
throw new \Exception('用户未绑定建行账号');
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 生成支付串签名Shopro 使用 total_fee 作为支付金额字段)
$result = CcbMD5::generatePaymentSignature(
$this->config['merchant_id'],
$this->config['pos_id'],
$this->config['branch_id'],
$order['order_sn'],
number_format($order['total_fee'], 2, '.', ''), // 使用 total_fee
$this->config['private_key'],
'01', // 币种:人民币
'530550' // 交易码
);
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 构建完整的支付URL
$paymentUrl = $this->buildPaymentUrl($result['params'], $result['mac']);
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 记录支付请求
$this->recordPaymentRequest($orderId, $result);
2025-10-17 16:32:16 +08:00
return [
2025-10-17 17:18:15 +08:00
'status' => true,
'message' => '支付串生成成功',
'data' => [
'payment_string' => $result['payment_string'],
'mac' => $result['mac'],
'payment_url' => $paymentUrl,
'order_sn' => $order['order_sn'],
'amount' => number_format($order['total_fee'], 2, '.', '')
]
2025-10-17 16:32:16 +08:00
];
2025-10-17 17:18:15 +08:00
} catch (\Exception $e) {
Log::error('建行支付串生成失败: ' . $e->getMessage());
2025-10-17 16:32:16 +08:00
return [
2025-10-17 17:18:15 +08:00
'status' => false,
'message' => $e->getMessage(),
'data' => null
2025-10-17 16:32:16 +08:00
];
}
}
/**
* 处理支付回调
2025-10-17 17:18:15 +08:00
* 建行支付完成后的同步回调
*
* @param array $params URL参数
* @return array
*/
public function handleCallback($params)
{
try {
// 解密ccbParamSJ参数
if (isset($params['ccbParamSJ'])) {
$decryptedParams = CcbUrlDecrypt::decrypt($params['ccbParamSJ'], $this->config['service_id']);
if ($decryptedParams) {
$params = array_merge($params, $decryptedParams);
}
}
// 获取关键参数
$orderSn = $params['ORDERID'] ?? '';
$posId = $params['POSID'] ?? '';
$success = $params['SUCCESS'] ?? 'N';
// 验证参数
if (empty($orderSn)) {
throw new \Exception('订单号不能为空');
}
// 验证POS号
if ($posId != $this->config['pos_id']) {
throw new \Exception('POS号验证失败');
}
// 查询订单
$order = Order::where('order_sn', $orderSn)->find();
if (!$order) {
throw new \Exception('订单不存在');
}
// 处理支付结果
if ($success == 'Y') {
// 支付成功,更新订单状态
$this->updateOrderPaymentStatus($order, $params);
// 同步订单到建行
$this->orderService->pushOrder($order['id']);
return [
'status' => true,
'message' => '支付成功',
'data' => [
'order_id' => $order['id'],
'order_sn' => $orderSn,
'amount' => $params['PAYMENT'] ?? ''
]
];
} else {
// 支付失败
return [
'status' => false,
'message' => '支付失败',
'data' => [
'order_id' => $order['id'],
'order_sn' => $orderSn,
'error_code' => $params['ERRCODE'] ?? '',
'error_msg' => $params['ERRMSG'] ?? ''
]
];
}
} catch (\Exception $e) {
Log::error('建行支付回调处理失败: ' . $e->getMessage());
return [
'status' => false,
'message' => $e->getMessage(),
'data' => null
];
}
}
/**
* 处理异步通知
* 建行支付异步通知处理
2025-10-17 16:32:16 +08:00
*
2025-10-17 17:18:15 +08:00
* @param array $params 通知参数
* @return string 'success' 'fail'
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
public function handleNotify($params)
2025-10-17 16:32:16 +08:00
{
2025-10-17 17:18:15 +08:00
try {
// 验证签名
if (!$this->verifyNotifySignature($params)) {
throw new \Exception('签名验证失败');
}
// 获取订单信息
$orderSn = $params['ORDERID'] ?? '';
$order = Order::where('order_sn', $orderSn)->find();
if (!$order) {
throw new \Exception('订单不存在');
}
// 如果订单已支付,直接返回成功
if ($order['status'] == 'paid') {
return 'success';
}
// 更新订单状态
$this->updateOrderPaymentStatus($order, $params);
// 同步到建行
$this->orderService->pushOrder($order['id']);
return 'success';
} catch (\Exception $e) {
Log::error('建行支付异步通知处理失败: ' . $e->getMessage());
return 'fail';
}
2025-10-17 16:32:16 +08:00
}
/**
* 验证支付结果
2025-10-17 17:18:15 +08:00
* 主动查询订单支付状态
2025-10-17 16:32:16 +08:00
*
2025-10-17 17:18:15 +08:00
* @param string $orderSn 订单号
* @return bool
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
public function verifyPayment($orderSn)
2025-10-17 16:32:16 +08:00
{
2025-10-17 17:18:15 +08:00
try {
// 查询建行订单状态
$result = $this->orderService->queryOrder($orderSn);
if ($result['status']) {
$data = $result['data']['CLD_BODY'] ?? [];
$txnStatus = $data['TXN_STATUS'] ?? '';
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 00=交易成功
return $txnStatus == '00';
}
return false;
} catch (\Exception $e) {
Log::error('建行支付验证失败: ' . $e->getMessage());
return false;
2025-10-17 16:32:16 +08:00
}
2025-10-17 17:18:15 +08:00
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
/**
* 构建支付URL
*
* @param array $params 支付参数
* @param string $mac 签名
* @return string
*/
private function buildPaymentUrl($params, $mac)
{
// 添加必要参数
$params['MAC'] = $mac;
$params['REMARK2'] = $this->config['service_id']; // 服务方编号
// 生成查询字符串
$queryString = http_build_query($params);
// 返回完整URL实际使用时通过JSBridge调用不直接访问
return $this->config['cashier_url'] . '?' . $queryString;
2025-10-17 16:32:16 +08:00
}
/**
2025-10-17 17:18:15 +08:00
* 更新订单支付状态
2025-10-17 16:32:16 +08:00
*
2025-10-17 17:18:15 +08:00
* @param object $order 订单对象
* @param array $params 支付参数
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
private function updateOrderPaymentStatus($order, $params)
2025-10-17 16:32:16 +08:00
{
2025-10-17 17:18:15 +08:00
// 更新订单状态为已支付
Order::where('id', $order['id'])->update([
'status' => 'paid',
'pay_type' => 'ccb',
'paytime' => time(),
'transaction_id' => $params['ORDERID'] ?? '',
'updatetime' => time()
]);
// 记录支付日志
$this->recordPaymentLog($order['id'], 'payment_success', $params);
2025-10-17 16:32:16 +08:00
}
/**
2025-10-17 17:18:15 +08:00
* 验证异步通知签名
2025-10-17 16:32:16 +08:00
*
2025-10-17 17:18:15 +08:00
* @param array $params 通知参数
* @return bool
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
private function verifyNotifySignature($params)
2025-10-17 16:32:16 +08:00
{
2025-10-17 17:18:15 +08:00
// 获取签名
$signature = $params['SIGN'] ?? '';
if (empty($signature)) {
return false;
2025-10-17 16:32:16 +08:00
}
2025-10-17 17:18:15 +08:00
// 移除签名字段
unset($params['SIGN']);
// 按照建行要求的方式构建签名字符串
ksort($params);
$signStr = '';
foreach ($params as $key => $value) {
if ($value !== '') {
$signStr .= $key . '=' . $value . '&';
}
2025-10-17 16:32:16 +08:00
}
2025-10-17 17:18:15 +08:00
$signStr = rtrim($signStr, '&');
// 使用私钥计算签名
$expectedSign = md5($signStr . $this->config['private_key']);
return strtolower($signature) === strtolower($expectedSign);
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
/**
* 记录支付请求
*
* @param int $orderId 订单ID
* @param array $paymentData 支付数据
*/
private function recordPaymentRequest($orderId, $paymentData)
{
// 获取订单信息
$order = Order::find($orderId);
$user = Db::name('user')->where('id', $order['user_id'])->field('ccb_user_id')->find();
// 记录到建行支付日志表
Db::name('ccb_payment_log')->insert([
'order_id' => $orderId,
'order_sn' => $order['order_sn'],
'pay_flow_id' => $order['order_sn'], // 使用订单号作为流水号
'payment_string' => $paymentData['payment_string'] ?? '',
'user_id' => $order['user_id'],
'ccb_user_id' => $user['ccb_user_id'] ?? '',
'amount' => $order['total_fee'],
'status' => 0, // 待支付
'create_time' => time()
]);
}
/**
* 记录支付日志
*
* @param int $orderId 订单ID
* @param string $type 日志类型
* @param array $data 数据
*/
private function recordPaymentLog($orderId, $type, $data)
{
// 更新建行支付日志
if ($type == 'payment_success') {
Db::name('ccb_payment_log')
->where('order_id', $orderId)
->update([
'status' => 1, // 支付成功
'pay_time' => time(),
'trans_id' => $data['ORDERID'] ?? '',
'callback_data' => json_encode($data, JSON_UNESCAPED_UNICODE)
]);
}
}
/**
* 生成退款申请
*
* @param int $orderId 订单ID
* @param float $refundAmount 退款金额
* @param string $refundReason 退款原因
* @return array
*/
public function refund($orderId, $refundAmount, $refundReason = '')
{
try {
// 调用订单服务处理退款
$result = $this->orderService->refundOrder($orderId, $refundAmount, $refundReason);
if ($result['status']) {
// 记录退款日志
$this->recordPaymentLog($orderId, 'refund_request', [
'amount' => $refundAmount,
'reason' => $refundReason
]);
}
return $result;
} catch (\Exception $e) {
Log::error('建行退款申请失败: ' . $e->getMessage());
return [
'status' => false,
'message' => $e->getMessage(),
'data' => null
];
}
2025-10-17 16:32:16 +08:00
}
}