feat: 增强玩家 API,新增 locale 和时间字段,更新钱包 API 以支持可用余额计算,添加错误码与多语言支持

This commit is contained in:
2026-05-09 15:05:46 +08:00
parent f1b38ef421
commit a0f86a4e36
36 changed files with 2523 additions and 34 deletions

View File

@@ -0,0 +1,150 @@
<?php
namespace App\Services\Wallet;
use App\Models\Player;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Support\Facades\Http;
/**
* 通过 HTTP 调用主站钱包 API路径见 config lottery.main_site.wallet_*_path
*/
final class HttpMainSiteWalletGateway implements MainSiteWalletGateway
{
public function debitMainForLotteryDeposit(
Player $player,
string $currencyCode,
int $amountMinor,
string $idempotentKey,
): MainSiteWalletResult {
return $this->post(
(string) config('lottery.main_site.wallet_debit_path'),
$player,
$currencyCode,
$amountMinor,
$idempotentKey,
);
}
public function creditMainForLotteryWithdraw(
Player $player,
string $currencyCode,
int $amountMinor,
string $idempotentKey,
): MainSiteWalletResult {
return $this->post(
(string) config('lottery.main_site.wallet_credit_path'),
$player,
$currencyCode,
$amountMinor,
$idempotentKey,
);
}
private function post(
string $path,
Player $player,
string $currencyCode,
int $amountMinor,
string $idempotentKey,
): MainSiteWalletResult {
$base = rtrim((string) config('lottery.main_site.wallet_api_url'), '/');
$url = $base.'/'.ltrim($path, '/');
$timeout = (int) config('lottery.main_site.wallet_timeout', 10);
$apiKey = config('lottery.main_site.wallet_api_key');
$requestBody = [
'site_code' => $player->site_code,
'site_player_id' => $player->site_player_id,
'player_id' => $player->id,
'currency_code' => $currencyCode,
'amount_minor' => $amountMinor,
'idempotent_key' => $idempotentKey,
];
$requestSnapshot = array_merge($requestBody, [
'_meta' => [
'method' => 'POST',
'path' => '/'.ltrim($path, '/'),
],
]);
$headers = [];
if (is_string($apiKey) && $apiKey !== '') {
$headers['Authorization'] = 'Bearer '.$apiKey;
}
try {
$response = Http::withHeaders($headers)
->timeout($timeout)
->acceptJson()
->asJson()
->post($url, $requestBody);
} catch (\Throwable $e) {
return MainSiteWalletResult::failure(
$e->getMessage(),
null,
self::isUncertainTransportFailure($e),
$requestSnapshot,
);
}
$payload = $response->json();
if (! is_array($payload)) {
$payload = ['raw' => $response->body()];
}
$status = $response->status();
if ($status === 408 || $status === 504) {
return MainSiteWalletResult::failure(
'HTTP '.$status,
$payload,
true,
$requestSnapshot,
);
}
if (! $response->successful()) {
return MainSiteWalletResult::failure(
is_string(data_get($payload, 'message'))
? (string) data_get($payload, 'message')
: ('HTTP '.$status),
$payload,
false,
$requestSnapshot,
);
}
$ok = (bool) data_get($payload, 'success', true);
if (! $ok) {
return MainSiteWalletResult::failure(
is_string(data_get($payload, 'message'))
? (string) data_get($payload, 'message')
: 'main_site_rejected',
$payload,
false,
$requestSnapshot,
);
}
$ref = data_get($payload, 'external_ref_no')
?? data_get($payload, 'data.external_ref_no')
?? data_get($payload, 'ref');
return MainSiteWalletResult::success(is_string($ref) ? $ref : null, $payload, $requestSnapshot);
}
private static function isUncertainTransportFailure(\Throwable $e): bool
{
if ($e instanceof ConnectException) {
return true;
}
$msg = strtolower($e->getMessage());
return str_contains($msg, 'curl error 28')
|| str_contains($msg, 'connection timed out')
|| str_contains($msg, 'timed out')
|| str_contains($msg, 'operation timed out');
}
}