176 lines
5.9 KiB
PHP
176 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace app\support;
|
|
|
|
use support\think\Db;
|
|
|
|
class ShouqianbaConfigService
|
|
{
|
|
private const GROUP = 'payment';
|
|
private const SHOUQIANBA_NOTIFY_PATH = '/api/open/shouqianba/payment/notify';
|
|
public const MINI_PROGRAM_PLUGIN_PROVIDER = 'wx7903bb295ac26ac7';
|
|
|
|
public function getConfig(): array
|
|
{
|
|
$rows = Db::name('system_configs')
|
|
->where('config_group', self::GROUP)
|
|
->column('config_value', 'config_key');
|
|
|
|
$expireMinutes = trim((string)($rows['order_expire_minutes'] ?? '1440'));
|
|
if ($expireMinutes === '' || !ctype_digit($expireMinutes)) {
|
|
$expireMinutes = '1440';
|
|
}
|
|
|
|
$workstationSn = trim((string)($rows['workstation_sn'] ?? '0'));
|
|
if ($workstationSn === '') {
|
|
$workstationSn = '0';
|
|
}
|
|
$industryCode = trim((string)($rows['industry_code'] ?? '0'));
|
|
if ($industryCode === '') {
|
|
$industryCode = '0';
|
|
}
|
|
$notifyUrl = trim((string)($rows['notify_url'] ?? ''));
|
|
if ($notifyUrl === '') {
|
|
$notifyUrl = $this->defaultNotifyUrl();
|
|
}
|
|
|
|
return [
|
|
'enabled' => (string)($rows['enabled'] ?? 'disabled') === 'enabled',
|
|
'api_domain' => rtrim(trim((string)($rows['api_domain'] ?? '')), '/'),
|
|
'appid' => trim((string)($rows['appid'] ?? '')),
|
|
'brand_code' => trim((string)($rows['brand_code'] ?? '')),
|
|
'store_sn' => trim((string)($rows['store_sn'] ?? '')),
|
|
'store_name' => trim((string)($rows['store_name'] ?? '')),
|
|
'workstation_sn' => $workstationSn,
|
|
'industry_code' => $industryCode,
|
|
'order_expire_minutes' => max(1, min(43200, (int)$expireMinutes)),
|
|
'merchant_private_key' => $this->normalizeKey((string)($rows['merchant_private_key'] ?? '')),
|
|
'shouqianba_public_key' => $this->normalizeKey((string)($rows['shouqianba_public_key'] ?? ''), 'PUBLIC KEY'),
|
|
'notify_url' => $notifyUrl,
|
|
'mini_program_plugin_version' => trim((string)($rows['mini_program_plugin_version'] ?? '')),
|
|
];
|
|
}
|
|
|
|
public function assertReady(bool $requirePublicKey = false): array
|
|
{
|
|
$config = $this->getConfig();
|
|
if (!$config['enabled']) {
|
|
throw new \RuntimeException('收钱吧支付未启用,请先在后台系统配置中启用。');
|
|
}
|
|
|
|
$required = [
|
|
'api_domain' => '收钱吧 API 域名',
|
|
'appid' => '收钱吧 AppID',
|
|
'brand_code' => '收钱吧品牌编号',
|
|
'store_sn' => '门店编号',
|
|
'workstation_sn' => '收银机编号',
|
|
'industry_code' => '行业代码',
|
|
'merchant_private_key' => '商户 RSA 私钥',
|
|
'notify_url' => '支付通知地址',
|
|
];
|
|
if ($requirePublicKey) {
|
|
$required['shouqianba_public_key'] = '收钱吧 RSA 公钥';
|
|
}
|
|
|
|
foreach ($required as $key => $label) {
|
|
if (trim((string)($config[$key] ?? '')) === '') {
|
|
throw new \RuntimeException(sprintf('收钱吧支付配置未完成,请先填写%s。', $label));
|
|
}
|
|
}
|
|
|
|
return $config;
|
|
}
|
|
|
|
public function h5OrderDetailUrl(int $orderId): string
|
|
{
|
|
$baseUrl = $this->h5PageBaseUrl();
|
|
if ($baseUrl === '') {
|
|
return '';
|
|
}
|
|
|
|
return $baseUrl . '/#/pages/order/detail?id=' . $orderId;
|
|
}
|
|
|
|
public function miniProgramCallbackPath(int $orderId): string
|
|
{
|
|
return '/pages/order/detail?id=' . $orderId;
|
|
}
|
|
|
|
private function h5PageBaseUrl(): string
|
|
{
|
|
$value = (string)Db::name('system_configs')
|
|
->where('config_group', 'h5')
|
|
->where('config_key', 'page_base_url')
|
|
->value('config_value');
|
|
$baseUrl = trim($value);
|
|
if ($baseUrl === '') {
|
|
return '';
|
|
}
|
|
|
|
$hashPos = strpos($baseUrl, '#');
|
|
if ($hashPos !== false) {
|
|
$baseUrl = substr($baseUrl, 0, $hashPos);
|
|
}
|
|
if (!preg_match('/^https?:\/\//i', $baseUrl)) {
|
|
$baseUrl = 'https://' . ltrim($baseUrl, '/');
|
|
}
|
|
|
|
return rtrim($baseUrl, '/');
|
|
}
|
|
|
|
private function defaultNotifyUrl(): string
|
|
{
|
|
$baseUrl = trim((string)($_ENV['PUBLIC_FILE_BASE_URL'] ?? ''));
|
|
if ($baseUrl === '') {
|
|
$baseUrl = trim((string)Db::name('system_configs')
|
|
->where('config_group', 'file_storage')
|
|
->where('config_key', 'public_base_url')
|
|
->value('config_value'));
|
|
}
|
|
if ($baseUrl === '') {
|
|
return '';
|
|
}
|
|
if (!preg_match('/^https?:\/\//i', $baseUrl)) {
|
|
$baseUrl = 'https://' . ltrim($baseUrl, '/');
|
|
}
|
|
|
|
return rtrim($baseUrl, '/') . self::SHOUQIANBA_NOTIFY_PATH;
|
|
}
|
|
|
|
private function normalizeKey(string $value, string $pemLabel = ''): string
|
|
{
|
|
$value = trim($value);
|
|
if ($value === '') {
|
|
return '';
|
|
}
|
|
if (str_contains($value, '-----BEGIN')) {
|
|
return $value;
|
|
}
|
|
if (is_file($value)) {
|
|
$content = file_get_contents($value);
|
|
return is_string($content) ? $this->normalizeKey($content, $pemLabel) : '';
|
|
}
|
|
if ($pemLabel !== '' && $this->looksLikeBase64KeyBody($value)) {
|
|
$body = preg_replace('/\s+/', '', $value) ?: '';
|
|
return sprintf(
|
|
"-----BEGIN %s-----\n%s\n-----END %s-----",
|
|
$pemLabel,
|
|
rtrim(chunk_split($body, 64, "\n")),
|
|
$pemLabel
|
|
);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
private function looksLikeBase64KeyBody(string $value): bool
|
|
{
|
|
$body = preg_replace('/\s+/', '', trim($value));
|
|
if (!is_string($body) || strlen($body) < 64) {
|
|
return false;
|
|
}
|
|
|
|
return preg_match('/^[A-Za-z0-9+\/=]+$/', $body) === 1 && base64_decode($body, true) !== false;
|
|
}
|
|
}
|