361 lines
12 KiB
PHP
361 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Dozn;
|
|
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
final class DoznAccountAuthService
|
|
{
|
|
private string $mode;
|
|
private string $orgCode;
|
|
private string $apiKey;
|
|
private string $urlReal;
|
|
private string $urlTest;
|
|
private int $timeout;
|
|
|
|
public function __construct()
|
|
{
|
|
$cfg = config('services.dozn', []);
|
|
|
|
$this->mode = (string)($cfg['mode'] ?? 'real');
|
|
$this->orgCode = (string)($cfg['org_code'] ?? '');
|
|
$this->apiKey = (string)($cfg['api_key'] ?? '');
|
|
$this->urlReal = (string)($cfg['url_real'] ?? '');
|
|
$this->urlTest = (string)($cfg['url_test'] ?? '');
|
|
$this->timeout = (int)($cfg['timeout'] ?? 10);
|
|
}
|
|
|
|
/**
|
|
* 출금(out) 계좌 성명 인증 + 저장까지 한 번에 처리
|
|
*
|
|
* @param int $memNo 회원번호
|
|
* @param string $memberRealName 회원 실명(서버에서 꺼낸 값)
|
|
* @param string $inputDepositor 입력한 예금주(화면 입력)
|
|
* @param string $bankCode 은행코드(001~)
|
|
* @param string $account 계좌번호(숫자만 권장)
|
|
* @param bool $requireSameName 입력 예금주 == 회원실명 강제 여부
|
|
*
|
|
* @return array { ok, code, message, status, depositor, telegram_no }
|
|
*/
|
|
public function verifyAndSaveOutAccount(
|
|
int $memNo,
|
|
string $memberRealName,
|
|
string $inputDepositor,
|
|
string $bankCode,
|
|
string $account,
|
|
bool $requireSameName = true
|
|
): array {
|
|
$memNo = (int)$memNo;
|
|
$memberRealName = trim($memberRealName);
|
|
$inputDepositor = trim($inputDepositor);
|
|
$bankCode = trim($bankCode);
|
|
$account = preg_replace('/[^0-9]/', '', (string)$account);
|
|
|
|
// 은행코드 검증 (config/bank_code.php)
|
|
$bankMap = (array) config('bank_code.flat', []);
|
|
if ($bankCode === '' || !isset($bankMap[$bankCode])) {
|
|
return $this->fail('INVALID_BANK', '은행코드가 올바르지 않습니다.');
|
|
}
|
|
if ($account === '') {
|
|
return $this->fail('INVALID_ACCOUNT', '계좌번호를 입력해 주세요.');
|
|
}
|
|
if ($inputDepositor === '') {
|
|
return $this->fail('INVALID_DEPOSITOR', '예금주를 입력해 주세요.');
|
|
}
|
|
|
|
// 입력 예금주와 회원 실명 일치 강제 (네 CI3 로직과 동일)
|
|
if ($requireSameName && $memberRealName !== '' && $memberRealName !== $inputDepositor) {
|
|
// act_state=4(정보불일치) 기록(선택)
|
|
$this->upsertOutAccountState($memNo, $bankCode, $account, $inputDepositor, '4', [
|
|
'reason' => 'member_name_mismatch',
|
|
'member_real_name' => $memberRealName,
|
|
'input_depositor' => $inputDepositor,
|
|
]);
|
|
|
|
return $this->fail('NAME_MISMATCH', '가입자 성명과 예금주가 일치하지 않습니다!');
|
|
}
|
|
|
|
// telegram_no 발급 (일별 6자리)
|
|
$telegramNo = $this->issueTelegramNo();
|
|
|
|
// 요청 페이로드 (Dozn 스펙)
|
|
$payload = [
|
|
'org_code' => $this->orgCode,
|
|
'api_key' => $this->apiKey,
|
|
'telegram_no' => $telegramNo,
|
|
'bank_code' => $bankCode,
|
|
'account' => $account,
|
|
'check_depositor' => 'N', // 기존 코드 유지 (실명체크 identify_no는 추후)
|
|
// 'identify_no' => '761127',
|
|
];
|
|
|
|
// 요청 로그 insert
|
|
$logSeq = $this->logRequest($memNo, $payload, [
|
|
'input_depositor' => $inputDepositor,
|
|
'mode' => $this->mode,
|
|
]);
|
|
|
|
// 진행상태 기록 (2: 인증진행중)
|
|
$this->upsertOutAccountState($memNo, $bankCode, $account, $inputDepositor, '2', [
|
|
'log_seq' => $logSeq,
|
|
'telegram_no' => $telegramNo,
|
|
]);
|
|
|
|
// Dozn 호출
|
|
$resp = $this->callDozn($payload);
|
|
|
|
// 결과 로그 update
|
|
$this->logResult($logSeq, $resp['raw'] ?? []);
|
|
|
|
$status = (string)($resp['status'] ?? '');
|
|
$depositor = (string)($resp['depositor'] ?? '');
|
|
|
|
// 성공(200) 처리
|
|
if ($status === '200') {
|
|
if ($depositor === '' || $depositor !== $inputDepositor) {
|
|
// 정보 불일치 (4)
|
|
$this->upsertOutAccountState($memNo, $bankCode, $account, $inputDepositor, '4', [
|
|
'status' => $status,
|
|
'returned_depositor' => $depositor,
|
|
'input_depositor' => $inputDepositor,
|
|
'telegram_no' => $telegramNo,
|
|
]);
|
|
|
|
return [
|
|
'ok' => false,
|
|
'code' => 'DEPOSITOR_MISMATCH',
|
|
'message' => '예금주가 일치하지 않습니다!',
|
|
'status' => $status,
|
|
'depositor' => $depositor,
|
|
'telegram_no' => $telegramNo,
|
|
];
|
|
}
|
|
|
|
// 성공(3): 계좌 저장
|
|
$this->saveOutAccountSuccess($memNo, $bankCode, $account, $depositor, [
|
|
'status' => $status,
|
|
'telegram_no' => $telegramNo,
|
|
'result' => $resp['raw'] ?? [],
|
|
]);
|
|
|
|
return [
|
|
'ok' => true,
|
|
'code' => 'OK',
|
|
'message' => '인증완료 및 계좌번호가 등록되었습니다.',
|
|
'status' => $status,
|
|
'depositor' => $depositor,
|
|
'telegram_no' => $telegramNo,
|
|
];
|
|
}
|
|
|
|
// 잘못된 계좌(520)
|
|
if ($status === '520') {
|
|
$this->upsertOutAccountState($memNo, $bankCode, $account, $inputDepositor, '5', [
|
|
'status' => $status,
|
|
'telegram_no' => $telegramNo,
|
|
'result' => $resp['raw'] ?? [],
|
|
]);
|
|
|
|
return [
|
|
'ok' => false,
|
|
'code' => 'INVALID_ACCOUNT',
|
|
'message' => '잘 못된 계좌번호 입니다.',
|
|
'status' => $status,
|
|
'depositor' => $depositor,
|
|
'telegram_no' => $telegramNo,
|
|
];
|
|
}
|
|
|
|
// 기타 오류
|
|
$this->upsertOutAccountState($memNo, $bankCode, $account, $inputDepositor, '5', [
|
|
'status' => $status,
|
|
'telegram_no' => $telegramNo,
|
|
'result' => $resp['raw'] ?? [],
|
|
]);
|
|
|
|
return [
|
|
'ok' => false,
|
|
'code' => 'DOZN_ERROR',
|
|
'message' => ($status ?: 'ERR') . '|인증에 문제가 발생했습니다. 잠시후 다시 시도해주세요.',
|
|
'status' => $status,
|
|
'depositor' => $depositor,
|
|
'telegram_no' => $telegramNo,
|
|
];
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Dozn HTTP call
|
|
// ------------------------------------------------------------------
|
|
private function callDozn(array $payload): array
|
|
{
|
|
$url = $this->mode === 'test' ? $this->urlTest : $this->urlReal;
|
|
if ($url === '' || $this->orgCode === '' || $this->apiKey === '') {
|
|
return [
|
|
'status' => 'CONFIG',
|
|
'depositor' => '',
|
|
'raw' => ['error' => 'dozn_config_missing'],
|
|
];
|
|
}
|
|
|
|
try {
|
|
$res = Http::timeout($this->timeout)
|
|
->acceptJson()
|
|
->asJson()
|
|
->post($url, $payload);
|
|
|
|
$json = $res->json();
|
|
if (!is_array($json)) $json = [];
|
|
|
|
return [
|
|
'status' => (string)($json['status'] ?? ''),
|
|
'depositor' => (string)($json['depositor'] ?? ''),
|
|
'raw' => $json,
|
|
];
|
|
} catch (\Throwable $e) {
|
|
Log::warning('[dozn] request failed', [
|
|
'err' => $e->getMessage(),
|
|
]);
|
|
|
|
return [
|
|
'status' => 'HTTP',
|
|
'depositor' => '',
|
|
'raw' => ['error' => 'http_exception', 'message' => $e->getMessage()],
|
|
];
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// telegram_no: mem_account_telegram_no 사용 (일별 6자리)
|
|
// ------------------------------------------------------------------
|
|
private function issueTelegramNo(): int
|
|
{
|
|
$today = Carbon::today()->toDateString();
|
|
|
|
return (int) DB::transaction(function () use ($today) {
|
|
// 오늘 발급된 마지막 telegram_no를 잠그고 다음 번호 생성
|
|
$last = DB::table('mem_account_telegram_no')
|
|
->where('date', $today)
|
|
->lockForUpdate()
|
|
->max('telegram_no');
|
|
|
|
$next = $last ? ((int)$last + 1) : 1;
|
|
if ($next > 999999) $next = 1;
|
|
|
|
DB::table('mem_account_telegram_no')->insert([
|
|
'telegram_no' => $next,
|
|
'date' => $today,
|
|
]);
|
|
|
|
return $next;
|
|
});
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Logs: mem_account_log
|
|
// ------------------------------------------------------------------
|
|
private function logRequest(int $memNo, array $payload, array $meta = []): int
|
|
{
|
|
$now = Carbon::now()->format('Y-m-d H:i:s');
|
|
|
|
$req = array_merge($payload, $meta);
|
|
|
|
$reqJson = json_encode($req, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
if ($reqJson === false) $reqJson = '{}';
|
|
|
|
return (int) DB::table('mem_account_log')->insertGetId([
|
|
'mem_no' => $memNo,
|
|
'request_data' => $reqJson,
|
|
'request_time' => $now,
|
|
'result_data' => '{}',
|
|
'result_time' => $now,
|
|
]);
|
|
}
|
|
|
|
private function logResult(int $seq, array $result): void
|
|
{
|
|
if ($seq <= 0) return;
|
|
|
|
$now = Carbon::now()->format('Y-m-d H:i:s');
|
|
|
|
$resJson = json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
if ($resJson === false) $resJson = '{}';
|
|
|
|
DB::table('mem_account_log')
|
|
->where('seq', $seq)
|
|
->update([
|
|
'result_data' => $resJson,
|
|
'result_time' => $now,
|
|
]);
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// mem_account: 회원당 out 1개 유지 (스키마 유지, 코드로 강제)
|
|
// ------------------------------------------------------------------
|
|
private function upsertOutAccountState(
|
|
int $memNo,
|
|
string $bankCode,
|
|
string $account,
|
|
string $depositor,
|
|
string $actState,
|
|
array $confirmLog = []
|
|
): void {
|
|
$bankMap = (array) config('bank_code.flat', []);
|
|
$bankName = (string)($bankMap[$bankCode] ?? '');
|
|
|
|
$now = Carbon::now()->format('Y-m-d H:i:s');
|
|
|
|
DB::transaction(function () use ($memNo, $bankCode, $bankName, $account, $depositor, $actState, $confirmLog, $now) {
|
|
// mem_no + act_type='out' 기준으로 row 잠금
|
|
$row = DB::table('mem_account')
|
|
->where('mem_no', $memNo)
|
|
->where('act_type', 'out')
|
|
->lockForUpdate()
|
|
->first();
|
|
|
|
$data = [
|
|
'mem_no' => $memNo,
|
|
'act_type' => 'out',
|
|
'act_state' => $actState,
|
|
'bank_code' => $bankCode,
|
|
'bank_name' => $bankName,
|
|
'bank_act_name' => $depositor,
|
|
'bank_act_num' => $account,
|
|
'in_date' => $now,
|
|
'act_date' => $now,
|
|
'confirm_log' => json_encode($confirmLog ?: new \stdClass(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
|
];
|
|
|
|
if ($row) {
|
|
DB::table('mem_account')->where('seq', $row->seq)->update($data);
|
|
} else {
|
|
DB::table('mem_account')->insert($data);
|
|
}
|
|
});
|
|
}
|
|
|
|
private function saveOutAccountSuccess(
|
|
int $memNo,
|
|
string $bankCode,
|
|
string $account,
|
|
string $depositor,
|
|
array $confirmLog = []
|
|
): void {
|
|
// 성공은 act_state=3
|
|
$this->upsertOutAccountState($memNo, $bankCode, $account, $depositor, '3', $confirmLog);
|
|
}
|
|
|
|
private function fail(string $code, string $message): array
|
|
{
|
|
return [
|
|
'ok' => false,
|
|
'code' => $code,
|
|
'message' => $message,
|
|
];
|
|
}
|
|
}
|