validated(); // 受付番号の重複チェック $existing = PaymentTransaction::where('syuno_recv_num', $validated['SyunoRecvNum'])->first(); if ($existing) { // 期限切れ(DB未更新)を補正 if ($existing->status === '入金待ち' && $existing->pay_limit && $existing->pay_limit->lessThan(now())) { $existing->update(['status' => '支払期限切れ']); } if ($existing->status === '入金済み') { return response()->json([ 'error' => ['code' => 'E17', 'message' => '入金処理完了済みの受付番号です。'] ], 400); } if ($existing->status === '支払期限切れ') { return response()->json([ 'error' => ['code' => 'E23', 'message' => '支払期限切れの受付番号です。'] ], 400); } if ($existing->status === '取消済み') { return response()->json([ 'error' => ['code' => 'E24', 'message' => '取消済みの受付番号です。'] ], 400); } // 冪等対応:入金待ちの既存データがある場合は同一リンクを返す if (!empty($existing->kessai_no) && $existing->status === '入金待ち') { $paymentGuideUrl = $linkBuilder->buildRemUrl($existing->kessai_no); return response()->json([ 'result' => 'OK', 'paymentGuideUrl' => $paymentGuideUrl, 'SyunoRecvNum' => $existing->syuno_recv_num, ]); } return response()->json([ 'error' => ['code' => 'INVALID_REQUEST', 'message' => '既に登録済みの受付番号です。'] ], 400); } // SyunoFreeArray構築 $freeArray = $this->buildFreeArray($validated); // SOAP送信データ構築 $inData = [ 'SyunoRecvNum' => $validated['SyunoRecvNum'], 'SyunoTel' => $validated['SyunoTel'], 'SyunoNameKanji' => $validated['SyunoNameKanji'], 'SyunoPayLimit' => $validated['SyunoPayLimit'], 'SyunoPayAmount' => (string) $validated['SyunoPayAmount'], 'SyunoFreeArray' => $freeArray, ]; if (!empty($validated['SyunoNameKana'])) { $inData['SyunoNameKana'] = $validated['SyunoNameKana']; } if (!empty($validated['SyunoReserveNum'])) { $inData['SyunoReserveNum'] = $validated['SyunoReserveNum']; } if (!empty($validated['SyunoMemberNum'])) { $inData['SyunoMemberNum'] = $validated['SyunoMemberNum']; } try { // Wellnet受付登録 $result = $soapService->register($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); } $kessaiNo = $result['KKessaiNo']; // 支払案内リンク生成 $paymentGuideUrl = $linkBuilder->buildRemUrl($kessaiNo); // トランザクション保存 PaymentTransaction::create([ 'syuno_recv_num' => $validated['SyunoRecvNum'], 'payment_type' => 'rem', 'status' => '入金待ち', 'amount' => $validated['SyunoPayAmount'], 'pay_limit' => $this->parsePayLimit($validated['SyunoPayLimit']), 'kessai_no' => $kessaiNo, 'name_kanji' => $validated['SyunoNameKanji'], 'name_kana' => $validated['SyunoNameKana'] ?? null, 'tel' => $validated['SyunoTel'], 'subscription_flg' => 0, 'free_area' => $this->buildFreeAreaJson($validated), 'wellnet_response' => json_encode($result, JSON_UNESCAPED_UNICODE), ]); return response()->json([ 'result' => 'OK', 'paymentGuideUrl' => $paymentGuideUrl, 'SyunoRecvNum' => $validated['SyunoRecvNum'], ]); } /** * SyunoFreeArray構築(Index付き配列) */ private function buildFreeArray(array $validated): array { $freeArray = []; for ($i = 1; $i <= 23; $i++) { $key = 'SyunoFree' . $i; if (isset($validated[$key]) && $validated[$key] !== '') { $freeArray[$i] = $validated[$key]; } } return $freeArray; } /** * Free AreaをJSON保存用に構築 */ private function buildFreeAreaJson(array $validated): ?array { $freeArea = []; for ($i = 1; $i <= 23; $i++) { $key = 'SyunoFree' . $i; if (isset($validated[$key]) && $validated[$key] !== '') { $freeArea[$key] = $validated[$key]; } } return !empty($freeArea) ? $freeArea : null; } /** * 支払期限文字列(YYYYMMDDhhmm)をdatetime形式に変換 */ 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'; } /** * WellnetエラーコードをAPIエラーコード形式へ正規化 */ private function normalizeWellnetErrorCode(string $code): string { if (!ctype_digit($code)) { return $code; } $trimmed = ltrim($code, '0'); return 'R' . ($trimmed === '' ? '0' : $trimmed); } }