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); } }