614 lines
24 KiB
PHP
614 lines
24 KiB
PHP
<?php
|
||
|
||
namespace app\support;
|
||
|
||
use support\think\Db;
|
||
|
||
class ShouqianbaPaymentService
|
||
{
|
||
private ?ShouqianbaConfigService $configService;
|
||
private ?ShouqianbaClient $client;
|
||
private ?MiniProgramAuthService $miniProgramAuthService;
|
||
|
||
public function __construct(
|
||
?ShouqianbaConfigService $configService = null,
|
||
?ShouqianbaClient $client = null,
|
||
?MiniProgramAuthService $miniProgramAuthService = null
|
||
) {
|
||
$this->configService = $configService ?: new ShouqianbaConfigService();
|
||
$this->client = $client ?: new ShouqianbaClient($this->configService);
|
||
$this->miniProgramAuthService = $miniProgramAuthService ?: new MiniProgramAuthService();
|
||
}
|
||
|
||
public function assertReadyForSource(string $sourceChannel, int $userId): void
|
||
{
|
||
$this->configService->assertReady(true);
|
||
if ($sourceChannel === 'h5' && $this->configService->h5OrderDetailUrl(1) === '') {
|
||
throw new \RuntimeException('H5 页面根地址未配置,无法生成支付回跳地址');
|
||
}
|
||
if ($sourceChannel === 'mini_program' && $this->miniProgramAuthService->openidForUser($userId) === '') {
|
||
throw new \RuntimeException('小程序 openid 未绑定,请先完成小程序登录授权');
|
||
}
|
||
}
|
||
|
||
public function createOrReusePayment(int $orderId): array
|
||
{
|
||
$order = $this->findOrder($orderId);
|
||
if ((string)$order['payment_status'] === 'paid') {
|
||
$payment = $this->latestPayment($orderId);
|
||
return [
|
||
'status' => 'paid',
|
||
'channel' => (string)$order['source_channel'],
|
||
'check_sn' => (string)($payment['check_sn'] ?? ''),
|
||
'order_token' => (string)($payment['order_token'] ?? ''),
|
||
'cashier_url' => (string)($payment['cashier_url'] ?? ''),
|
||
'order_sn' => (string)($payment['order_sn'] ?? ''),
|
||
'order_status' => (string)$order['order_status'],
|
||
];
|
||
}
|
||
if ((string)$order['order_status'] !== 'pending_payment') {
|
||
throw new \RuntimeException('当前订单状态不可发起支付');
|
||
}
|
||
|
||
$this->assertReadyForSource((string)$order['source_channel'], (int)$order['user_id']);
|
||
|
||
$latest = $this->latestPayment($orderId);
|
||
if ($latest && in_array((string)$latest['status'], ['pending', 'created'], true) && (string)$latest['order_token'] !== '') {
|
||
return $this->buildPaymentLaunchPayload($latest, $order);
|
||
}
|
||
|
||
return $this->createPayment($order);
|
||
}
|
||
|
||
public function syncOrderPaymentStatus(int $orderId, ?int $userId = null): array
|
||
{
|
||
$order = $this->findOrder($orderId, $userId);
|
||
$payment = $this->latestPayment($orderId);
|
||
if (!$payment) {
|
||
return $this->orderPaymentPayload($order, null);
|
||
}
|
||
|
||
if ((string)$order['payment_status'] === 'paid') {
|
||
return $this->orderPaymentPayload($order, $payment);
|
||
}
|
||
|
||
if (!in_array((string)$payment['status'], ['pending', 'created', 'failed'], true)) {
|
||
return $this->orderPaymentPayload($order, $payment);
|
||
}
|
||
|
||
$data = $this->queryRemotePayment($payment);
|
||
$this->applyRemotePaymentData($payment, $data, 'query');
|
||
|
||
return $this->orderPaymentPayload($this->findOrder($orderId, $userId), $this->latestPayment($orderId));
|
||
}
|
||
|
||
public function cancelPendingOrder(int $orderId, int $userId): array
|
||
{
|
||
$order = $this->findOrder($orderId, $userId);
|
||
if ((string)$order['payment_status'] === 'paid') {
|
||
throw new \RuntimeException('订单已支付,不能取消');
|
||
}
|
||
if ((string)$order['order_status'] !== 'pending_payment') {
|
||
throw new \RuntimeException('当前订单状态不可取消');
|
||
}
|
||
|
||
$payment = $this->latestPayment($orderId);
|
||
if ($payment && in_array((string)$payment['status'], ['pending', 'created'], true)) {
|
||
$data = $this->queryRemotePayment($payment);
|
||
if ($this->isRemotePaid($data)) {
|
||
$this->applyRemotePaymentData($payment, $data, 'query');
|
||
throw new \RuntimeException('订单已支付,不能取消');
|
||
}
|
||
|
||
if (!in_array((string)($data['order_status'] ?? ''), ['0', '6', '7'], true)) {
|
||
$this->voidRemotePayment($payment);
|
||
}
|
||
}
|
||
|
||
$now = date('Y-m-d H:i:s');
|
||
Db::startTrans();
|
||
try {
|
||
Db::name('orders')->where('id', $orderId)->update([
|
||
'order_status' => 'cancelled',
|
||
'display_status' => '已取消',
|
||
'cancelled_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
if ($payment) {
|
||
Db::name('shouqianba_payments')->where('id', (int)$payment['id'])->update([
|
||
'status' => 'cancelled',
|
||
'cancelled_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
}
|
||
if (!$this->timelineExists($orderId, 'cancelled')) {
|
||
Db::name('order_timelines')->insert([
|
||
'order_id' => $orderId,
|
||
'node_code' => 'cancelled',
|
||
'node_text' => '订单已取消',
|
||
'node_desc' => '用户已取消待支付订单。',
|
||
'operator_type' => 'user',
|
||
'operator_id' => $userId,
|
||
'occurred_at' => $now,
|
||
'created_at' => $now,
|
||
]);
|
||
}
|
||
Db::commit();
|
||
} catch (\Throwable $e) {
|
||
Db::rollback();
|
||
throw $e;
|
||
}
|
||
|
||
return $this->orderPaymentPayload($this->findOrder($orderId, $userId), $this->latestPayment($orderId));
|
||
}
|
||
|
||
public function handleNotification(string $rawBody): array
|
||
{
|
||
$data = $this->client->decodeNotification($rawBody);
|
||
$payment = $this->findPaymentByRemoteData($data);
|
||
if (!$payment) {
|
||
throw new \RuntimeException('收钱吧通知对应的支付流水不存在');
|
||
}
|
||
|
||
Db::name('shouqianba_payments')->where('id', (int)$payment['id'])->update([
|
||
'notify_json' => $this->encodeJson($data),
|
||
'updated_at' => date('Y-m-d H:i:s'),
|
||
]);
|
||
$payment = $this->latestPayment((int)$payment['order_id']) ?: $payment;
|
||
|
||
$this->applyRemotePaymentData($payment, $data, 'notify');
|
||
|
||
return [
|
||
'order_id' => (int)$payment['order_id'],
|
||
'check_sn' => (string)$payment['check_sn'],
|
||
'status' => (string)($this->latestPayment((int)$payment['order_id'])['status'] ?? ''),
|
||
];
|
||
}
|
||
|
||
public function notificationResponse(bool $ok = true): array
|
||
{
|
||
return $this->client->signedResponse($ok ? '200' : '500', $ok ? '200' : '500');
|
||
}
|
||
|
||
private function createPayment(array $order): array
|
||
{
|
||
$sourceChannel = (string)$order['source_channel'];
|
||
$config = $this->configService->assertReady(true);
|
||
$amount = $this->amountCents((float)$order['pay_amount']);
|
||
if ($amount <= 0) {
|
||
throw new \RuntimeException('订单支付金额必须大于 0');
|
||
}
|
||
|
||
$checkSn = $this->generateCheckSn((string)$order['order_no']);
|
||
$product = Db::name('order_products')->where('order_id', (int)$order['id'])->find() ?: [];
|
||
$subject = $this->truncateText('安心验鉴定', 64);
|
||
$description = $this->truncateText((string)($product['product_name'] ?? $order['appraisal_no']), 255);
|
||
$scene = $sourceChannel === 'h5' ? '2' : '5';
|
||
|
||
$body = [
|
||
'request_id' => $this->generateRequestId('P'),
|
||
'brand_code' => $config['brand_code'],
|
||
'store_sn' => $config['store_sn'],
|
||
'workstation_sn' => $config['workstation_sn'],
|
||
'check_sn' => $checkSn,
|
||
'sales_sn' => (string)$order['order_no'],
|
||
'scene' => $scene,
|
||
'sales_time' => date('c'),
|
||
'expire_time' => (string)$config['order_expire_minutes'],
|
||
'amount' => (string)$amount,
|
||
'currency' => '156',
|
||
'subject' => $subject,
|
||
'description' => $description,
|
||
'operator' => 'system',
|
||
'industry_code' => $config['industry_code'],
|
||
'pos_info' => 'anxinyan',
|
||
'notify_url' => $config['notify_url'],
|
||
'reflect' => (string)$order['order_no'],
|
||
];
|
||
if ($config['store_name'] !== '') {
|
||
$body['store_name'] = $config['store_name'];
|
||
}
|
||
if ($sourceChannel === 'h5') {
|
||
$returnUrl = $this->configService->h5OrderDetailUrl((int)$order['id']);
|
||
if ($returnUrl !== '') {
|
||
$body['return_url'] = $returnUrl;
|
||
$body['back_url'] = $returnUrl;
|
||
}
|
||
}
|
||
|
||
$remote = $this->client->purchase($body);
|
||
$data = $remote['data'];
|
||
$now = date('Y-m-d H:i:s');
|
||
$paymentId = (int)Db::name('shouqianba_payments')->insertGetId([
|
||
'order_id' => (int)$order['id'],
|
||
'order_no' => (string)$order['order_no'],
|
||
'check_sn' => $checkSn,
|
||
'order_sn' => (string)($data['order_sn'] ?? ''),
|
||
'order_token' => (string)($data['order_token'] ?? ''),
|
||
'cashier_url' => (string)($data['cashier_url'] ?? ''),
|
||
'order_image_url' => (string)($data['order_image_url'] ?? ''),
|
||
'order_landing_url' => (string)($data['order_landing_url'] ?? ''),
|
||
'scene' => $scene,
|
||
'source_channel' => $sourceChannel,
|
||
'status' => 'pending',
|
||
'amount' => $amount,
|
||
'currency' => '156',
|
||
'request_json' => $this->encodeJson($remote['request']),
|
||
'response_json' => $this->encodeJson($remote['response']),
|
||
'notify_json' => null,
|
||
'paid_at' => null,
|
||
'cancelled_at' => null,
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
$payment = Db::name('shouqianba_payments')->where('id', $paymentId)->find();
|
||
if (!$payment) {
|
||
throw new \RuntimeException('收钱吧支付流水创建失败');
|
||
}
|
||
|
||
return $this->buildPaymentLaunchPayload($payment, $order);
|
||
}
|
||
|
||
private function buildPaymentLaunchPayload(array $payment, array $order): array
|
||
{
|
||
$sourceChannel = (string)$order['source_channel'];
|
||
$payload = [
|
||
'status' => (string)$payment['status'],
|
||
'channel' => $sourceChannel,
|
||
'check_sn' => (string)$payment['check_sn'],
|
||
'order_token' => (string)$payment['order_token'],
|
||
'cashier_url' => (string)$payment['cashier_url'],
|
||
'order_sn' => (string)$payment['order_sn'],
|
||
];
|
||
|
||
if ($sourceChannel === 'mini_program') {
|
||
$appId = $this->systemConfig('mini_program', 'app_id');
|
||
$openid = $this->miniProgramAuthService->openidForUser((int)$order['user_id']);
|
||
if ($appId === '' || $openid === '') {
|
||
throw new \RuntimeException('小程序支付参数未准备完成');
|
||
}
|
||
$query = http_build_query([
|
||
'token' => (string)$payment['order_token'],
|
||
'appid' => $appId,
|
||
'openid' => $openid,
|
||
'callback_url' => $this->configService->miniProgramCallbackPath((int)$order['id']),
|
||
], '', '&', PHP_QUERY_RFC3986);
|
||
$payload['plugin_url'] = 'plugin://lite-pos-plugin/cashierV2?' . $query;
|
||
$payload['plugin_provider'] = ShouqianbaConfigService::MINI_PROGRAM_PLUGIN_PROVIDER;
|
||
}
|
||
|
||
return $payload;
|
||
}
|
||
|
||
private function queryRemotePayment(array $payment): array
|
||
{
|
||
$config = $this->configService->assertReady(true);
|
||
$body = [
|
||
'brand_code' => $config['brand_code'],
|
||
'store_sn' => $config['store_sn'],
|
||
'workstation_sn' => $config['workstation_sn'],
|
||
'check_sn' => (string)$payment['check_sn'],
|
||
];
|
||
if ((string)$payment['order_sn'] !== '') {
|
||
$body['order_sn'] = (string)$payment['order_sn'];
|
||
}
|
||
|
||
$remote = $this->client->query($body);
|
||
Db::name('shouqianba_payments')->where('id', (int)$payment['id'])->update([
|
||
'response_json' => $this->encodeJson($remote['response']),
|
||
'updated_at' => date('Y-m-d H:i:s'),
|
||
]);
|
||
|
||
return $remote['data'];
|
||
}
|
||
|
||
private function voidRemotePayment(array $payment): void
|
||
{
|
||
$config = $this->configService->assertReady(true);
|
||
$body = [
|
||
'request_id' => $this->generateRequestId('V'),
|
||
'brand_code' => $config['brand_code'],
|
||
'original_store_sn' => $config['store_sn'],
|
||
'original_workstation_sn' => $config['workstation_sn'],
|
||
'original_check_sn' => (string)$payment['check_sn'],
|
||
'reflect' => (string)$payment['order_no'],
|
||
];
|
||
if ((string)$payment['order_sn'] !== '') {
|
||
$body['original_order_sn'] = (string)$payment['order_sn'];
|
||
}
|
||
|
||
$remote = $this->client->void($body);
|
||
Db::name('shouqianba_payments')->where('id', (int)$payment['id'])->update([
|
||
'response_json' => $this->encodeJson($remote['response']),
|
||
'updated_at' => date('Y-m-d H:i:s'),
|
||
]);
|
||
}
|
||
|
||
private function applyRemotePaymentData(array $payment, array $data, string $source): void
|
||
{
|
||
if (isset($data['amount']) && (int)$data['amount'] !== (int)$payment['amount']) {
|
||
throw new \RuntimeException('收钱吧支付金额与本地订单金额不一致');
|
||
}
|
||
|
||
if ($this->isRemotePaid($data)) {
|
||
$this->markOrderPaid($payment, $data, $source);
|
||
return;
|
||
}
|
||
|
||
$remoteStatus = (string)($data['order_status'] ?? '');
|
||
$statusMap = [
|
||
'0' => 'cancelled',
|
||
'6' => 'failed',
|
||
'7' => 'terminated',
|
||
];
|
||
$status = $statusMap[$remoteStatus] ?? 'pending';
|
||
Db::name('shouqianba_payments')->where('id', (int)$payment['id'])->update([
|
||
'status' => $status,
|
||
'order_sn' => (string)($data['order_sn'] ?? $payment['order_sn']),
|
||
'updated_at' => date('Y-m-d H:i:s'),
|
||
]);
|
||
|
||
if ($status === 'cancelled') {
|
||
$this->markPendingOrderCancelled($payment, '收钱吧已取消该支付订单。');
|
||
}
|
||
}
|
||
|
||
private function markOrderPaid(array $payment, array $data, string $source): void
|
||
{
|
||
$orderId = (int)$payment['order_id'];
|
||
$now = date('Y-m-d H:i:s');
|
||
|
||
Db::startTrans();
|
||
try {
|
||
$order = Db::name('orders')->where('id', $orderId)->lock(true)->find();
|
||
if (!$order) {
|
||
throw new \RuntimeException('支付对应订单不存在');
|
||
}
|
||
|
||
Db::name('shouqianba_payments')->where('id', (int)$payment['id'])->update([
|
||
'status' => 'paid',
|
||
'order_sn' => (string)($data['order_sn'] ?? $payment['order_sn']),
|
||
'paid_at' => $payment['paid_at'] ?: $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
if ((string)$order['payment_status'] !== 'paid') {
|
||
Db::name('orders')->where('id', $orderId)->update([
|
||
'payment_status' => 'paid',
|
||
'order_status' => 'pending_shipping',
|
||
'display_status' => '待寄送商品',
|
||
'paid_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
$product = Db::name('order_products')->where('order_id', $orderId)->find() ?: [];
|
||
$defaultAddress = Db::name('user_addresses')
|
||
->where('user_id', (int)$order['user_id'])
|
||
->where('is_default', 1)
|
||
->find();
|
||
$shippingTarget = (new WarehouseService())->bindOrderTarget(
|
||
$orderId,
|
||
(string)$order['service_provider'],
|
||
!empty($product['category_id']) ? (int)$product['category_id'] : null,
|
||
$defaultAddress ?: null
|
||
);
|
||
|
||
if (!Db::name('appraisal_tasks')->where('order_id', $orderId)->find()) {
|
||
Db::name('appraisal_tasks')->insert([
|
||
'order_id' => $orderId,
|
||
'task_stage' => 'first_review',
|
||
'service_provider' => (string)$order['service_provider'],
|
||
'status' => 'pending',
|
||
'assignee_id' => null,
|
||
'assignee_name' => '未分配',
|
||
'started_at' => null,
|
||
'submitted_at' => null,
|
||
'sla_deadline' => $order['estimated_finish_time'],
|
||
'is_overtime' => 0,
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
}
|
||
|
||
if (!$this->timelineExists($orderId, 'payment_paid')) {
|
||
Db::name('order_timelines')->insert([
|
||
'order_id' => $orderId,
|
||
'node_code' => 'payment_paid',
|
||
'node_text' => '支付成功',
|
||
'node_desc' => $source === 'notify' ? '已收到收钱吧支付成功通知。' : '已同步确认收钱吧支付成功。',
|
||
'operator_type' => 'system',
|
||
'operator_id' => null,
|
||
'occurred_at' => $now,
|
||
'created_at' => $now,
|
||
]);
|
||
}
|
||
if (!$this->timelineExists($orderId, 'pending_shipping')) {
|
||
Db::name('order_timelines')->insert([
|
||
'order_id' => $orderId,
|
||
'node_code' => 'pending_shipping',
|
||
'node_text' => '待寄送商品',
|
||
'node_desc' => sprintf('请尽快将商品寄送至%s,以免影响处理时效', $shippingTarget['warehouse_name'] ?: '鉴定中心'),
|
||
'operator_type' => 'system',
|
||
'operator_id' => null,
|
||
'occurred_at' => $now,
|
||
'created_at' => $now,
|
||
]);
|
||
}
|
||
|
||
(new MessageDispatcher())->sendInboxEvent('order_created', [
|
||
'user_id' => (int)$order['user_id'],
|
||
'biz_type' => 'order',
|
||
'biz_id' => $orderId,
|
||
'order_no' => (string)$order['order_no'],
|
||
'appraisal_no' => (string)$order['appraisal_no'],
|
||
'product_name' => (string)($product['product_name'] ?? ''),
|
||
'pay_amount' => (string)$order['pay_amount'],
|
||
'fallback_title' => '订单支付成功',
|
||
'fallback_content' => '您的鉴定订单已支付成功,可前往订单中心查看进度。',
|
||
]);
|
||
}
|
||
|
||
Db::commit();
|
||
} catch (\Throwable $e) {
|
||
Db::rollback();
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
private function orderPaymentPayload(array $order, ?array $payment): array
|
||
{
|
||
return [
|
||
'order_id' => (int)$order['id'],
|
||
'order_no' => (string)$order['order_no'],
|
||
'payment_status' => (string)$order['payment_status'],
|
||
'order_status' => (string)$order['order_status'],
|
||
'display_status' => (string)$order['display_status'],
|
||
'payment' => $payment ? [
|
||
'status' => (string)$payment['status'],
|
||
'channel' => (string)$payment['source_channel'],
|
||
'check_sn' => (string)$payment['check_sn'],
|
||
'order_sn' => (string)$payment['order_sn'],
|
||
'order_token' => (string)$payment['order_token'],
|
||
'cashier_url' => (string)$payment['cashier_url'],
|
||
] : null,
|
||
];
|
||
}
|
||
|
||
private function markPendingOrderCancelled(array $payment, string $nodeDesc): void
|
||
{
|
||
$orderId = (int)$payment['order_id'];
|
||
$now = date('Y-m-d H:i:s');
|
||
|
||
Db::startTrans();
|
||
try {
|
||
$order = Db::name('orders')->where('id', $orderId)->lock(true)->find();
|
||
if (!$order || (string)$order['payment_status'] === 'paid' || (string)$order['order_status'] !== 'pending_payment') {
|
||
Db::commit();
|
||
return;
|
||
}
|
||
|
||
Db::name('orders')->where('id', $orderId)->update([
|
||
'order_status' => 'cancelled',
|
||
'display_status' => '已取消',
|
||
'cancelled_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
if (!$this->timelineExists($orderId, 'cancelled')) {
|
||
Db::name('order_timelines')->insert([
|
||
'order_id' => $orderId,
|
||
'node_code' => 'cancelled',
|
||
'node_text' => '订单已取消',
|
||
'node_desc' => $nodeDesc,
|
||
'operator_type' => 'system',
|
||
'operator_id' => null,
|
||
'occurred_at' => $now,
|
||
'created_at' => $now,
|
||
]);
|
||
}
|
||
|
||
Db::commit();
|
||
} catch (\Throwable $e) {
|
||
Db::rollback();
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
private function findOrder(int $orderId, ?int $userId = null): array
|
||
{
|
||
$query = Db::name('orders')->where('id', $orderId);
|
||
if ($userId !== null) {
|
||
$query->where('user_id', $userId);
|
||
}
|
||
$order = $query->find();
|
||
if (!$order) {
|
||
throw new \RuntimeException('订单不存在');
|
||
}
|
||
|
||
return $order;
|
||
}
|
||
|
||
private function latestPayment(int $orderId): ?array
|
||
{
|
||
$payment = Db::name('shouqianba_payments')->where('order_id', $orderId)->order('id', 'desc')->find();
|
||
return $payment ?: null;
|
||
}
|
||
|
||
private function findPaymentByRemoteData(array $data): ?array
|
||
{
|
||
$checkSn = trim((string)($data['check_sn'] ?? ''));
|
||
if ($checkSn !== '') {
|
||
$payment = Db::name('shouqianba_payments')->where('check_sn', $checkSn)->find();
|
||
if ($payment) {
|
||
return $payment;
|
||
}
|
||
}
|
||
|
||
$orderSn = trim((string)($data['order_sn'] ?? ''));
|
||
if ($orderSn !== '') {
|
||
$payment = Db::name('shouqianba_payments')->where('order_sn', $orderSn)->order('id', 'desc')->find();
|
||
if ($payment) {
|
||
return $payment;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private function isRemotePaid(array $data): bool
|
||
{
|
||
return in_array((string)($data['order_status'] ?? ''), ['4', '5'], true);
|
||
}
|
||
|
||
private function timelineExists(int $orderId, string $nodeCode): bool
|
||
{
|
||
return (bool)Db::name('order_timelines')
|
||
->where('order_id', $orderId)
|
||
->where('node_code', $nodeCode)
|
||
->find();
|
||
}
|
||
|
||
private function amountCents(float $amount): int
|
||
{
|
||
return (int)round($amount * 100);
|
||
}
|
||
|
||
private function generateCheckSn(string $orderNo): string
|
||
{
|
||
return substr($orderNo . 'P' . date('His') . random_int(10, 99), 0, 32);
|
||
}
|
||
|
||
private function generateRequestId(string $prefix): string
|
||
{
|
||
return $prefix . date('YmdHis') . bin2hex(random_bytes(6));
|
||
}
|
||
|
||
private function truncateText(string $value, int $maxLength): string
|
||
{
|
||
if (mb_strlen($value, 'UTF-8') <= $maxLength) {
|
||
return $value;
|
||
}
|
||
|
||
return mb_substr($value, 0, $maxLength, 'UTF-8');
|
||
}
|
||
|
||
private function encodeJson(array $payload): string
|
||
{
|
||
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
if (!is_string($json)) {
|
||
throw new \RuntimeException('收钱吧支付数据编码失败');
|
||
}
|
||
|
||
return $json;
|
||
}
|
||
|
||
private function systemConfig(string $group, string $key): string
|
||
{
|
||
return trim((string)Db::name('system_configs')
|
||
->where('config_group', $group)
|
||
->where('config_key', $key)
|
||
->value('config_value'));
|
||
}
|
||
}
|