chore: prepare production release package

This commit is contained in:
wushumin
2026-06-05 16:12:56 +08:00
parent ed87ea1541
commit 995eae3969
15 changed files with 740 additions and 37 deletions

View File

@@ -593,39 +593,92 @@ class SystemConfigsController
throw new \RuntimeException('收钱吧订单有效分钟数需填写 1-43200 之间的整数');
}
if (!$this->isPemContentOrReadablePath((string)$configValueMap['payment.merchant_private_key'])) {
throw new \RuntimeException('商户 RSA 私钥需填写 PEM 内容,或填写服务器可读取的 PEM 文件路径');
if (!$this->isPrivateKeyContentOrReadablePath((string)$configValueMap['payment.merchant_private_key'])) {
throw new \RuntimeException('商户 RSA 私钥需填写可被 OpenSSL 解析的 PEM 内容,或填写服务器可读取的 PEM 文件路径');
}
if (!$this->isPublicKeyContentOrReadablePath((string)$configValueMap['payment.shouqianba_public_key'])) {
throw new \RuntimeException('收钱吧 RSA 公钥需填写 PEM 内容、纯公钥文本,或填写服务器可读取的 PEM 文件路径');
throw new \RuntimeException('收钱吧 RSA 公钥需填写可被 OpenSSL 解析的 PEM 内容、纯公钥文本,或填写服务器可读取的 PEM 文件路径');
}
}
private function isPrivateKeyContentOrReadablePath(string $value): bool
{
$content = $this->pemContentOrReadablePath($value);
if ($content === '') {
return false;
}
$key = openssl_pkey_get_private($content);
$ok = $key !== false;
$this->clearOpenSslErrors();
return $ok;
}
private function isPublicKeyContentOrReadablePath(string $value): bool
{
$value = trim($value);
if ($this->isPemContentOrReadablePath($value)) {
$content = $this->pemContentOrReadablePath($value);
if ($content !== '' && $this->canOpenPublicKey($content)) {
return true;
}
return $this->looksLikeBase64KeyBody($value);
if (!$this->looksLikeBase64KeyBody($value)) {
return false;
}
return $this->canOpenPublicKey($this->wrapPemKey($value, 'PUBLIC KEY'));
}
private function isPemContentOrReadablePath(string $value): bool
private function pemContentOrReadablePath(string $value): string
{
$value = trim($value);
if ($value === '') {
return false;
return '';
}
if (str_contains($value, '-----BEGIN')) {
return true;
return $this->normalizePemNewlines($value);
}
if (!is_file($value) || !is_readable($value)) {
return false;
return '';
}
$content = file_get_contents($value);
return is_string($content) && str_contains($content, '-----BEGIN');
if (!is_string($content) || !str_contains($content, '-----BEGIN')) {
return '';
}
return $this->normalizePemNewlines($content);
}
private function canOpenPublicKey(string $content): bool
{
$key = openssl_pkey_get_public($content);
$ok = $key !== false;
$this->clearOpenSslErrors();
return $ok;
}
private function wrapPemKey(string $value, string $pemLabel): string
{
$body = preg_replace('/\s+/', '', trim($value)) ?: '';
return sprintf(
"-----BEGIN %s-----\n%s\n-----END %s-----",
$pemLabel,
rtrim(chunk_split($body, 64, "\n")),
$pemLabel
);
}
private function normalizePemNewlines(string $value): string
{
return str_replace(["\\r\\n", "\\n", "\\r"], ["\n", "\n", "\r"], $value);
}
private function clearOpenSslErrors(): void
{
while (openssl_error_string() !== false) {
}
}
private function looksLikeBase64KeyBody(string $value): bool

View File

@@ -140,6 +140,46 @@ class AuthController
}
}
public function miniProgramExchange(Request $request)
{
$code = trim((string)$request->input('code', ''));
if ($code === '') {
return api_error('小程序登录 code 不能为空', 422);
}
try {
$payload = (new MiniProgramAuthService())->exchangeCode($code, $request);
return api_success($payload, ($payload['status'] ?? '') === 'need_bind' ? '请绑定手机号' : '登录成功');
} catch (\RuntimeException $e) {
return api_error($e->getMessage(), 422);
} catch (\Throwable $e) {
return api_error('小程序授权登录失败', 500, [
'detail' => $e->getMessage(),
]);
}
}
public function miniProgramBindMobile(Request $request)
{
$bindTicket = trim((string)$request->input('bind_ticket', ''));
$mobile = trim((string)$request->input('mobile', ''));
$code = trim((string)$request->input('code', ''));
if ($bindTicket === '' || $mobile === '' || $code === '') {
return api_error('小程序绑定凭证、手机号和验证码不能为空', 422);
}
try {
$payload = (new MiniProgramAuthService())->bindMobile($bindTicket, $mobile, $code, $request);
return api_success($payload, '绑定成功');
} catch (\RuntimeException $e) {
return api_error($e->getMessage(), 422);
} catch (\Throwable $e) {
return api_error('小程序绑定手机号失败', 500, [
'detail' => $e->getMessage(),
]);
}
}
public function me(Request $request)
{
$userInfo = (new AppAuthService())->current($request);