You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
4.0 KiB
117 lines
4.0 KiB
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Http;
|
|
use RuntimeException;
|
|
|
|
class WeChatMiniProgramService
|
|
{
|
|
public function getAccessToken(): string
|
|
{
|
|
$appId = config('services.wechat.mini_program.app_id');
|
|
$secret = config('services.wechat.mini_program.secret');
|
|
|
|
if (! $appId || ! $secret) {
|
|
throw new RuntimeException('微信小程序 AppID/Secret 未配置');
|
|
}
|
|
|
|
return Cache::remember('wechat_miniapp_access_token', 7000, function () use ($appId, $secret) {
|
|
$response = Http::timeout(10)->get('https://api.weixin.qq.com/cgi-bin/token', [
|
|
'grant_type' => 'client_credential',
|
|
'appid' => $appId,
|
|
'secret' => $secret,
|
|
]);
|
|
|
|
$payload = $response->json();
|
|
if (! is_array($payload) || ! empty($payload['errcode'])) {
|
|
throw new RuntimeException((string) ($payload['errmsg'] ?? '获取微信 access_token 失败'));
|
|
}
|
|
|
|
$token = $payload['access_token'] ?? null;
|
|
if (! is_string($token) || $token === '') {
|
|
throw new RuntimeException('微信 access_token 响应异常');
|
|
}
|
|
|
|
return $token;
|
|
});
|
|
}
|
|
|
|
public function getUnlimitedWxaCode(string $page, string $scene): string
|
|
{
|
|
$scene = trim($scene);
|
|
if ($scene === '') {
|
|
throw new RuntimeException('签到码不能为空');
|
|
}
|
|
if (strlen($scene) > 32) {
|
|
throw new RuntimeException('签到码不能超过 32 个字符');
|
|
}
|
|
|
|
$token = $this->getAccessToken();
|
|
$response = Http::timeout(20)
|
|
->withHeaders(['Content-Type' => 'application/json'])
|
|
->post("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={$token}", [
|
|
'scene' => $scene,
|
|
'page' => ltrim($page, '/'),
|
|
'check_path' => false,
|
|
'env_version' => config('services.wechat.mini_program.env_version', 'develop'),
|
|
'width' => 430,
|
|
]);
|
|
|
|
$contentType = (string) $response->header('Content-Type');
|
|
if (str_contains($contentType, 'json') || str_starts_with(trim($response->body()), '{')) {
|
|
$payload = $response->json();
|
|
if (is_array($payload) && ! empty($payload['errcode'])) {
|
|
throw new RuntimeException((string) ($payload['errmsg'] ?? '生成小程序码失败'));
|
|
}
|
|
}
|
|
|
|
$body = $response->body();
|
|
if ($body === '') {
|
|
throw new RuntimeException('生成小程序码失败:空响应');
|
|
}
|
|
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* @return array{openid: string, unionid?: string, session_key: string}
|
|
*/
|
|
public function codeToSession(string $code): array
|
|
{
|
|
$appId = config('services.wechat.mini_program.app_id');
|
|
$secret = config('services.wechat.mini_program.secret');
|
|
|
|
if (! $appId || ! $secret) {
|
|
throw new RuntimeException('微信小程序 AppID/Secret 未配置');
|
|
}
|
|
|
|
$response = Http::timeout(10)->get('https://api.weixin.qq.com/sns/jscode2session', [
|
|
'appid' => $appId,
|
|
'secret' => $secret,
|
|
'js_code' => $code,
|
|
'grant_type' => 'authorization_code',
|
|
]);
|
|
|
|
$payload = $response->json();
|
|
if (! is_array($payload)) {
|
|
throw new RuntimeException('微信登录响应异常');
|
|
}
|
|
|
|
if (! empty($payload['errcode'])) {
|
|
throw new RuntimeException((string) ($payload['errmsg'] ?? '微信登录失败'));
|
|
}
|
|
|
|
if (empty($payload['openid'])) {
|
|
throw new RuntimeException('微信登录未返回 openid');
|
|
}
|
|
|
|
return [
|
|
'openid' => (string) $payload['openid'],
|
|
'unionid' => isset($payload['unionid']) ? (string) $payload['unionid'] : null,
|
|
'session_key' => (string) ($payload['session_key'] ?? ''),
|
|
];
|
|
}
|
|
}
|