All checks were successful
Deploy api / deploy (push) Successful in 22s
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
291 lines
9.7 KiB
PHP
291 lines
9.7 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Api;
|
||
|
||
use App\Exceptions\WellnetSoapException;
|
||
use App\Http\Controllers\Controller;
|
||
use App\Http\Requests\Api\PaymentUpdateRequest;
|
||
use App\Models\PaymentTransaction;
|
||
use App\Services\Wellnet\WellnetSoapService;
|
||
use Illuminate\Http\JsonResponse;
|
||
use RuntimeException;
|
||
|
||
/**
|
||
* 決済情報更新コントローラ(API 5)
|
||
*/
|
||
class PaymentUpdateController extends Controller
|
||
{
|
||
/**
|
||
* 決済情報更新/取消
|
||
*
|
||
* PUT /api/newwipe/update
|
||
*/
|
||
public function update(
|
||
PaymentUpdateRequest $request,
|
||
WellnetSoapService $soapService
|
||
): JsonResponse {
|
||
$validated = $request->validated();
|
||
|
||
// トランザクション検索
|
||
$transaction = PaymentTransaction::where('syuno_recv_num', $validated['SyunoRecvNum'])->first();
|
||
|
||
if (!$transaction) {
|
||
return response()->json([
|
||
'error' => [
|
||
// api.pdf の更新APIでは「未存在/見つからない」専用コードが無いため、実質的に削除済み扱いとする
|
||
'code' => 'E24',
|
||
'message' => '該当の支払データは既に削除済みです。',
|
||
]
|
||
], 400);
|
||
}
|
||
|
||
// ステータスチェック
|
||
if ($transaction->status === '入金済み') {
|
||
return response()->json([
|
||
'error' => [
|
||
'code' => 'E17',
|
||
'message' => '入金処理完了済みのため更新できません。',
|
||
]
|
||
], 400);
|
||
}
|
||
|
||
if ($transaction->status === '取消済み') {
|
||
return response()->json([
|
||
'error' => [
|
||
'code' => 'E24',
|
||
'message' => '既に取消済みです。',
|
||
]
|
||
], 400);
|
||
}
|
||
|
||
// 期限切れ(DB未更新)を補正:更新は不可(取消は可)
|
||
if ($transaction->status === '入金待ち' && $transaction->pay_limit && $transaction->pay_limit->lessThan(now())) {
|
||
$transaction->update(['status' => '支払期限切れ']);
|
||
}
|
||
|
||
// 取消処理
|
||
$cancelFlag = $validated['cancelFlag'] ?? null;
|
||
if ($cancelFlag === true || $cancelFlag === 'true') {
|
||
return $this->handleCancel($transaction, $soapService);
|
||
}
|
||
|
||
// 変更項目が一つも無い場合はエラー
|
||
$updatableKeys = [
|
||
'SyunoPayLimit', 'SyunoPayAmount', 'SyunoNameKanji', 'SyunoNameKana', 'SyunoTel',
|
||
'SyunoFree1', 'SyunoFree2', 'SyunoFree3', 'SyunoFree4', 'SyunoFree5', 'SyunoFree6',
|
||
'SyunoFree7', 'SyunoFree8', 'SyunoFree9', 'SyunoFree10', 'SyunoFree11', 'SyunoFree12',
|
||
'SyunoFree13', 'SyunoFree14', 'SyunoFree15', 'SyunoFree16', 'SyunoFree17', 'SyunoFree18',
|
||
'SyunoFree19',
|
||
'SyunoFree20', 'SyunoFree21',
|
||
];
|
||
$hasUpdate = false;
|
||
foreach ($updatableKeys as $key) {
|
||
if (array_key_exists($key, $validated)) {
|
||
$hasUpdate = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!$hasUpdate) {
|
||
return response()->json([
|
||
'error' => [
|
||
'code' => 'INVALID_REQUEST',
|
||
'message' => '更新項目が指定されていません。',
|
||
]
|
||
], 400);
|
||
}
|
||
|
||
// 期限切れチェック(更新の場合のみ、取消は可能)
|
||
if ($transaction->status === '支払期限切れ') {
|
||
return response()->json([
|
||
'error' => [
|
||
'code' => 'E23',
|
||
'message' => '支払期限切れのため更新できません。',
|
||
]
|
||
], 400);
|
||
}
|
||
|
||
// 更新処理
|
||
return $this->handleUpdate($transaction, $validated, $soapService);
|
||
}
|
||
|
||
/**
|
||
* 取消処理
|
||
*/
|
||
private function handleCancel(
|
||
PaymentTransaction $transaction,
|
||
WellnetSoapService $soapService
|
||
): JsonResponse {
|
||
try {
|
||
$soapService->cancel($transaction->syuno_recv_num);
|
||
} catch (WellnetSoapException $e) {
|
||
$code = $e->getWellnetCode();
|
||
$errorCode = $this->normalizeWellnetErrorCode($code);
|
||
return response()->json([
|
||
'error' => [
|
||
'code' => $errorCode,
|
||
'message' => 'Wellnet取消処理に失敗しました: ' . $e->getMessage(),
|
||
]
|
||
], 400);
|
||
} catch (RuntimeException $e) {
|
||
return response()->json([
|
||
'error' => [
|
||
'code' => 'E99',
|
||
'message' => 'Wellnet通信エラー: ' . $e->getMessage(),
|
||
]
|
||
], 500);
|
||
}
|
||
|
||
$transaction->update(['status' => '取消済み']);
|
||
|
||
return response()->json([
|
||
'updateStatus' => 'CANCELLED',
|
||
'SyunoRecvNum' => $transaction->syuno_recv_num,
|
||
'status' => '取消済み',
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 更新処理
|
||
*/
|
||
private function handleUpdate(
|
||
PaymentTransaction $transaction,
|
||
array $validated,
|
||
WellnetSoapService $soapService
|
||
): JsonResponse {
|
||
// 変更対象フィールドの抽出
|
||
$inData = [
|
||
'SyunoRecvNum' => $validated['SyunoRecvNum'],
|
||
];
|
||
|
||
// 基本フィールド
|
||
$updateFields = ['SyunoPayLimit', 'SyunoPayAmount', 'SyunoNameKanji', 'SyunoNameKana', 'SyunoTel'];
|
||
foreach ($updateFields as $field) {
|
||
if (isset($validated[$field])) {
|
||
$inData[$field] = $field === 'SyunoPayAmount'
|
||
? (string) $validated[$field]
|
||
: $validated[$field];
|
||
}
|
||
}
|
||
|
||
// Free Area構築(未指定項目は'*'で送信)
|
||
$freeArray = [];
|
||
for ($i = 1; $i <= 21; $i++) {
|
||
$key = 'SyunoFree' . $i;
|
||
if (isset($validated[$key])) {
|
||
$freeArray[$i] = $validated[$key];
|
||
} else {
|
||
$freeArray[$i] = '*';
|
||
}
|
||
}
|
||
$inData['SyunoFreeArray'] = $freeArray;
|
||
|
||
try {
|
||
$soapService->update($inData);
|
||
} catch (WellnetSoapException $e) {
|
||
$code = $e->getWellnetCode();
|
||
$errorCode = $this->normalizeWellnetErrorCode($code);
|
||
return response()->json([
|
||
'error' => [
|
||
'code' => $errorCode,
|
||
'message' => 'Wellnet更新処理に失敗しました: ' . $e->getMessage(),
|
||
]
|
||
], 400);
|
||
} catch (RuntimeException $e) {
|
||
return response()->json([
|
||
'error' => [
|
||
'code' => 'E99',
|
||
'message' => 'Wellnet通信エラー: ' . $e->getMessage(),
|
||
]
|
||
], 500);
|
||
}
|
||
|
||
// ローカルDB更新
|
||
$dbUpdate = [];
|
||
if (isset($validated['SyunoPayLimit'])) {
|
||
$dbUpdate['pay_limit'] = $this->parsePayLimit($validated['SyunoPayLimit']);
|
||
}
|
||
if (isset($validated['SyunoPayAmount'])) {
|
||
$dbUpdate['amount'] = $validated['SyunoPayAmount'];
|
||
}
|
||
if (isset($validated['SyunoNameKanji'])) {
|
||
$dbUpdate['name_kanji'] = $validated['SyunoNameKanji'];
|
||
}
|
||
if (isset($validated['SyunoNameKana'])) {
|
||
$dbUpdate['name_kana'] = $validated['SyunoNameKana'];
|
||
}
|
||
if (isset($validated['SyunoTel'])) {
|
||
$dbUpdate['tel'] = $validated['SyunoTel'];
|
||
}
|
||
|
||
// Free Area JSONも更新
|
||
$freeAreaJson = $transaction->free_area ?? [];
|
||
for ($i = 1; $i <= 21; $i++) {
|
||
$key = 'SyunoFree' . $i;
|
||
if (isset($validated[$key])) {
|
||
$freeAreaJson[$key] = $validated[$key];
|
||
}
|
||
}
|
||
$dbUpdate['free_area'] = $freeAreaJson;
|
||
|
||
if (!empty($dbUpdate)) {
|
||
$transaction->update($dbUpdate);
|
||
}
|
||
|
||
// 最新の状態を取得
|
||
$transaction->refresh();
|
||
|
||
$response = [
|
||
'updateStatus' => 'UPDATED',
|
||
'SyunoRecvNum' => $transaction->syuno_recv_num,
|
||
'amount' => $transaction->amount,
|
||
'status' => $transaction->status,
|
||
];
|
||
|
||
if (isset($validated['SyunoPayLimit'])) {
|
||
$response['newPayLimit'] = $this->parsePayLimitIso8601($validated['SyunoPayLimit']);
|
||
}
|
||
if (isset($validated['SyunoPayAmount'])) {
|
||
$response['newPayAmount'] = $validated['SyunoPayAmount'];
|
||
}
|
||
|
||
return response()->json($response);
|
||
}
|
||
|
||
/**
|
||
* 支払期限文字列(YYYYMMDDhhmm)をdatetime形式に変換(DB保存用)
|
||
*/
|
||
private function parsePayLimit(string $payLimit): string
|
||
{
|
||
return substr($payLimit, 0, 4) . '-'
|
||
. substr($payLimit, 4, 2) . '-'
|
||
. substr($payLimit, 6, 2) . ' '
|
||
. substr($payLimit, 8, 2) . ':'
|
||
. substr($payLimit, 10, 2) . ':00';
|
||
}
|
||
|
||
/**
|
||
* 支払期限文字列(YYYYMMDDhhmm)をISO8601形式に変換(レスポンス用)
|
||
*/
|
||
private function parsePayLimitIso8601(string $payLimit): string
|
||
{
|
||
$datetime = substr($payLimit, 0, 4) . '-'
|
||
. substr($payLimit, 4, 2) . '-'
|
||
. substr($payLimit, 6, 2) . 'T'
|
||
. substr($payLimit, 8, 2) . ':'
|
||
. substr($payLimit, 10, 2) . ':00+09:00';
|
||
return $datetime;
|
||
}
|
||
|
||
/**
|
||
* WellnetエラーコードをAPIエラーコード形式へ正規化
|
||
*/
|
||
private function normalizeWellnetErrorCode(string $code): string
|
||
{
|
||
if (!ctype_digit($code)) {
|
||
return $code;
|
||
}
|
||
$trimmed = ltrim($code, '0');
|
||
return 'R' . ($trimmed === '' ? '0' : $trimmed);
|
||
}
|
||
}
|