api.so-manager-dev.com/app/Http/Controllers/Api/PaymentUpdateController.php
Your Name f139a3f608
All checks were successful
Deploy api / deploy (push) Successful in 22s
支払いAPI実装
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 20:02:25 +09:00

291 lines
9.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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