giftcon_dev/app/Repositories/Payments/GcPaymentAttemptRepository.php

146 lines
4.6 KiB
PHP

<?php
namespace App\Repositories\Payments;
use App\Models\Payments\GcPaymentAttempt;
final class GcPaymentAttemptRepository
{
public function upsertForUpdate(array $data): GcPaymentAttempt
{
// uq_provider_oid_method 때문에 1개만 존재(동일 method 재시도는 정책적으로 “새 주문 생성” 권장)
$q = GcPaymentAttempt::query()
->where('provider', $data['provider'])
->where('oid', $data['oid'])
->where('pay_method', $data['pay_method'])
->lockForUpdate();
$row = $q->first();
if (!$row) {
$row = new GcPaymentAttempt();
$row->provider = $data['provider'];
$row->oid = $data['oid'];
$row->mem_no = $data['mem_no'];
$row->order_id = $data['order_id'] ?? null;
$row->pay_method = $data['pay_method'];
$row->amount = $data['amount'];
$row->currency = $data['currency'] ?? 'KRW';
$row->ready_at = now();
}
$row->status = 'ready';
$row->token_hash = $data['token_hash'];
$row->card_kind = $data['card_kind'] ?? null;
$row->vact_kind = $data['vact_kind'] ?? null;
$row->user_agent = $data['user_agent'] ?? null;
$row->user_ip = $data['user_ip'] ?? null;
$row->save();
return $row;
}
public function findByTokenForUpdate(string $method, string $token): ?GcPaymentAttempt
{
$hash = hash('sha256', $token);
return GcPaymentAttempt::query()
->where('provider', 'danal')
->where('pay_method', $method)
->where('token_hash', $hash)
->lockForUpdate()
->first();
}
public function markRedirected(GcPaymentAttempt $a, array $req, array $res): void
{
$a->status = 'redirected';
$a->redirected_at = now();
$a->request_payload = $this->jsonSafe($req);
$a->response_payload = $this->jsonSafe($res);
$a->save();
}
public function markReturned(GcPaymentAttempt $a, array $payload, ?string $tid, string $code, string $msg, string $status): void
{
// status: auth_ok/issued/paid/failed/cancelled
$a->status = $status;
$a->returned_at = now();
$a->pg_tid = $tid ?: $a->pg_tid;
$a->return_code = $code ?: $a->return_code;
$a->return_msg = $msg ?: $a->return_msg;
$a->return_payload = $this->jsonSafe($payload);
$a->save();
}
public function markNotiPaid(GcPaymentAttempt $a, array $payload, string $tid, int $amount): void
{
if ($a->status === 'paid') return;
$a->status = 'paid';
$a->pg_tid = $tid ?: $a->pg_tid;
$a->amount = $amount ?: $a->amount;
$a->noti_payload = $this->jsonSafe($payload);
$a->noti_at = now();
$a->save();
}
public function markCancelled(GcPaymentAttempt $a, array $payload = []): void
{
if ($a->status === 'paid') return;
$a->status = 'cancelled';
$a->returned_at = now();
if ($payload) $a->return_payload = $this->jsonSafe($payload);
$a->save();
}
public function markFailed(GcPaymentAttempt $a, string $code, string $msg, array $payload = []): void
{
if ($a->status === 'paid') return;
$a->status = 'failed';
$a->returned_at = now();
$a->return_code = $code;
$a->return_msg = $msg;
if ($payload) $a->return_payload = $this->jsonSafe($payload);
$a->save();
}
private function jsonSafe(mixed $v): mixed
{
if (is_array($v)) {
foreach ($v as $k => $vv) $v[$k] = $this->jsonSafe($vv);
return $v;
}
if (is_object($v)) {
return $this->jsonSafe((array)$v);
}
if (is_string($v)) {
// 이미 UTF-8이면 그대로
if (function_exists('mb_check_encoding') && mb_check_encoding($v, 'UTF-8')) return $v;
// EUC-KR로 가정하고 UTF-8로 변환(실패 시 깨진 바이트 제거)
$out = @iconv('EUC-KR', 'UTF-8//IGNORE', $v);
if ($out === false) $out = '';
if (function_exists('mb_check_encoding') && !mb_check_encoding($out, 'UTF-8')) {
$out = @iconv('UTF-8', 'UTF-8//IGNORE', $out) ?: '';
}
return $out;
}
return $v;
}
public function findAnyByTokenForUpdate(string $token): ?GcPaymentAttempt
{
$hash = hash('sha256', $token);
return GcPaymentAttempt::query()
->where('provider', 'danal')
->where('token_hash', $hash)
->lockForUpdate()
->first();
}
}