# 確認も取り消しもできずに支払いが確定するチェックアウト

Source: https://easeweb.dev/learn/error-prevention
Topics: フォーム > 支払いや法的な手続きを確認・取り消しできる (WCAG 3.3.4)
The fix: 「確定して支払う」ボタンの前に、選んだ内容をそれぞれの「変更」リンク付きで読み返す段階を加えます。または、全額返金で予約を取り消せる期限を明記し、予約完了のページに「予約を取り消す」ボタンを置きます。要約のそばに必須の「確認しました」チェックボックスを置く方法も基準を満たしますが、習慣でチェックする人は間違いに気づかないまま支払います。
Test with: keyboard, screen reader, zoom, touch
References: https://www.w3.org/WAI/WCAG22/Understanding/error-prevention-legal-financial-data.html https://www.w3.org/WAI/WCAG22/Techniques/general/G98 https://www.w3.org/WAI/WCAG22/Techniques/general/G164 https://www.w3.org/WAI/WCAG22/Techniques/general/G155 https://design-system.service.gov.uk/patterns/check-answers/

よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。

## 場面

ある劇場の予約ページは、二つのことを尋ねます。公演が11月13日（金）か11月14日（土）か、そしてチケットの枚数です。1枚24ドルです。利用者はログインしているので、カードが保存されています。欄の下には、チケットは返金も交換もできないという一文と、「今すぐ支払う」ボタンがあります。

金曜日のチケットを1枚買うつもりでしたが、途中で手が滑って、枚数が2になりました。「今すぐ支払う」を押すと、その場で48ドルが請求され、次のページには「予約が完了しました」と表示されます。変更する方法も、取り消す方法もありません。間違いは画面上にありましたが、確定する前にそれを示すものは何もありませんでした。

## 確認できる問題

達成基準3.3.4「エラー回避（法的、金融、データ）」はレベルAAです。法的な義務や金融取引が発生するページ、利用者が管理するデータを変更・削除するページ、テストの回答を送信するページでは、次の三つのうち少なくとも一つが必要です。送信を取り消せること。入力エラーがチェックされ、修正できること。確定する前に、情報を確認し、確定し、修正する手段があること。このフォームは、どれも満たしていません。

購入について、W3Cが十分な達成方法として挙げるのは、送信の前に回答を確認して修正できる段階（G98）、注文後に変更や取り消しができる期限の明記（G164）、送信ボタンに加えた確認のチェックボックス（G155）です。入力のチェックは基準の二つ目の方法ですが、対象は入力エラー、つまり値が欠けている場合や、フォームが受け付ける範囲外の場合です。2枚のチケットも違う公演日も正しい選択で、どんなチェックでも見つけられません。W3Cは、この基準ではクライアント側の検証を推奨事項としてしか挙げていません。検証は3.3.1と3.3.3のためには大切ですが、意図しない正しい選択への対策にはなりません。

返金についての一文も役に立ちません。間違いが取り返せないことは伝えても、間違いを見つける機会は与えていないからです。

## 利用者への影響

その負担は、いちばん負担に耐えにくい人にかかります。家計に余裕のない人にとって、間違った予約は何かを我慢することを意味し、例外を頼むには電話かメールが必要で、それ自体がまた別の障壁になります。

## 自動チェックとAIの限界

各欄にはラベルがあり、ボタンには名前があり、ページは自動チェックをすべて通ります。注文を確認できるか、取り消せるかは、複数のページとその裏のサーバーにまたがっており、スキャナーが実際のサイトで「支払う」を押すことはありません。EaseWebの診断は、支払いや注文を行うフォームを人の確認に回しますが、送信はしないため、サイトが三つの方法のどれを使っているか、そもそも使っているかは分かりません。コーディングアシスタントにこのフォームの「エラーを防いで」と頼むと、数値の欄に`min`と`max`を加えます。0枚や99枚は防げても、2枚は防げません。

## 修正前：「今すぐ支払う」で即座に請求される

```html book.html
<form method="post" action="/book">
  <label for="night">公演</label>
  <select id="night" name="night">
    <option>11月13日（金）19:30</option>
    <option>11月14日（土）19:30</option>
  </select>
  <label for="tickets">枚数（1枚24ドル）</label>
  <input id="tickets" name="tickets" type="number" value="1">
  <p>保存済みのカード（末尾4242）で支払います。チケットの返金・交換はできません。</p>
  <button type="submit">今すぐ支払う</button>
</form>
```

フォームが届くとすぐに、サーバーが保存済みのカードに請求し、チケットを発行します。選んだ内容を読み返すことも、確認を求めることも、取り消すこともないため、一度押せば確定です。

## 修正後：支払う前に予約内容を確認する段階

```html book.html
<!-- 手順1：同じフォームだが、「次へ」は選択内容を保存するだけ -->
<form method="post" action="/book/review">
  <label for="night">公演</label>
  <select id="night" name="night">
    <option>11月13日（金）19:30</option>
    <option>11月14日（土）19:30</option>
  </select>
  <label for="tickets">枚数（1枚24ドル）</label>
  <input id="tickets" name="tickets" type="number" value="1">
  <p>保存済みのカード（末尾4242）で支払います。チケットの返金・交換はできません。</p>
  <button type="submit">次へ</button>
</form>
```

```html review.html
<!-- 手順2：予約内容の確認 -->
<h1>予約内容の確認</h1>
<dl>
  <dt>公演</dt>
  <dd>
    11月13日（金）19:30
    <a href="/book#night">変更<span class="visually-hidden">（公演）</span></a>
  </dd>
  <dt>枚数</dt>
  <dd>
    2
    <a href="/book#tickets">変更<span class="visually-hidden">（枚数）</span></a>
  </dd>
  <dt>合計</dt>
  <dd>48ドル、保存済みのカード（末尾4242）で支払い</dd>
</dl>
<p>支払い後は、チケットの返金・交換はできません。</p>
<form method="post" action="/book/confirm">
  <button type="submit">確定して48ドルを支払う</button>
</form>
```

「次へ」は何も請求せずに選択内容を保存し、次のページで合計とともに言葉で読み返します。それぞれの「変更」リンクは、すべての回答を保ったまま該当する欄へ戻るため、間違いは一か所を直すだけで済み、予約をやり直す必要はありません。カードに請求するのは「確定して48ドルを支払う」だけで、ボタンの名前が金額を伝えます。見出しとページのタイトルが今の段階を示すため、スクリーンリーダーの利用者にも、予約完了ではなく確認の段階に来たことが分かります。

## 修正後：取り消せる期限の明記と「予約を取り消す」ボタン

```html book.html
<!-- 予約フォーム：支払いの前に取り消しの期限を明記 -->
<form method="post" action="/book">
  <label for="night">公演</label>
  <select id="night" name="night">
    <option>11月13日（金）19:30</option>
    <option>11月14日（土）19:30</option>
  </select>
  <label for="tickets">枚数（1枚24ドル）</label>
  <input id="tickets" name="tickets" type="number" value="1">
  <p>保存済みのカード（末尾4242）で支払います。公演前日の18時まで、全額返金で取り消せます。</p>
  <button type="submit">今すぐ支払う</button>
</form>
```

```html confirmation.html
<!-- 予約完了のページと、確認メールに -->
<h1>予約が完了しました</h1>
<p>11月13日（金）19:30の公演、2枚。保存済みのカード（末尾4242）で48ドルを支払いました。</p>
<p>11月12日（木）18時まで、全額返金でこの予約を取り消せます。</p>
<form method="post" action="/bookings/B7Q2/cancel">
  <button type="submit">予約を取り消す</button>
</form>
```

注文はその場で確定しますが、取り消せます。期限は支払いの前と後に、実際の日付と時刻で明記し、取り消しは今いるページのボタン一つで済みます。ただし、これが機能するのは、間違いに気づけるだけの長さが期限にあり、取り消しが予約より難しくない場合に限ります。電話も、入力するフォームも、アカウントのページでリンクを探すことも不要でなければなりません。それでもお金は一度口座から出て、何日か後に戻ってきます。期限を過ぎてから気づいた間違いは、支払ったままです。

## 修正後：注文の要約のそばに置くチェックボックス

```html book.html
<form method="post" action="/book">
  <label for="night">公演</label>
  <select id="night" name="night">
    <option>11月13日（金）19:30</option>
    <option>11月14日（土）19:30</option>
  </select>
  <label for="tickets">枚数（1枚24ドル）</label>
  <input id="tickets" name="tickets" type="number" value="1">
  <p>保存済みのカード（末尾4242）で支払います。チケットの返金・交換はできません。</p>
  <p id="summary">11月13日（金）19:30の公演、1枚：24ドル</p>
  <label>
    <input type="checkbox" id="order-checked" name="order-checked" required
      aria-describedby="summary">
    公演と枚数を確認しました
  </label>
  <button type="submit">24ドルを支払う</button>
</form>
```

```js
const form = document.querySelector('form');
const box = document.getElementById('order-checked');
form.addEventListener('input', (event) => {
  if (event.target === box) return;
  // 注文が変わった。今の内容を示し、もう一度確認を求める。
  box.checked = false;
  const tickets = Number(form.tickets.value);
  const total = `${tickets * 24}ドル`;
  document.getElementById('summary').textContent =
    `${form.night.value}の公演、${tickets}枚：${total}`;
  form.querySelector('button[type=submit]').textContent = `${total}を支払う`;
});
```

最小限の変更で、フォームは1ページのままです。要約はチェックボックスの隣にあって一緒に読み上げられ、ボタンは合計を示し、チェックボックスは必須なので、チェックがなければブラウザーが支払いを止めて理由を伝えます。チェックした後に注文を変えるとチェックが外れるため、確認した内容と支払う内容が一致します。ただし、これだけでは十分ではありません。チェックボックスは確かめることを求めるだけで、新しく何かを示すわけではなく、多くの人は習慣でチェックします。拡大して使う人やスクリーンリーダーを使う人は、要約と欄を一緒に把握できません。

## どの方法を選ぶか

三つとも3.3.4を満たします。確認の段階を使ってください。どんな注文でも、お金が動く前に間違いを止められ、裏で返金の手続きを用意する必要もありません。事業として予約を取り消せる場合は、取り消しの期限も加えてください。ほかの予定と重なっていたなど、後から気づく間違いにも対応できます。チェックボックスは、まだ段階を増やせない1ページのチェックアウトにだけ使い、できるようになったら確認の段階に置き換えてください。

## 実装の判断

段階の間の選択内容は、ページの中だけでなく、サーバーの予約セッションに保存します。そうすれば、戻るボタン、「変更」リンク、再読み込みのどれでも、すべての回答がそのまま戻ります。「[同じ住所を二度入力させるチェックアウトフォーム](/ja/learn/redundant-entry)」も参照してください。

確認の段階では、それぞれの選択内容を省略せず、人が話すとおりに書きます。「13/11 19:30」や座席コードではなく、「11月13日（金）19:30」です。合計、支払い方法、返金できないといった大事な条件は、ボタンより前に示します。

決済事業者のページは、そのページのボタンを押した時点で請求することがよくあります。確認の段階は事業者のページへ移る前に置き、事業者のページを最後の段階にして、何を請求するかが分かるようにします。

お金が絡まなくても、同じ配慮が必要な操作があります。アカウントやプロジェクトの削除、署名したフォームの送信、試験の提出です。削除なら、失われるものを伝える確認か、直後の「元に戻す」で対応できます。「[質問が読み上げられない確認ダイアログ](/ja/learn/confirmation-dialogs)」と「[読む前や元に戻す前に消えるトースト](/ja/learn/toast-timing)」も参照してください。

二度押しを防ぐために、最後のボタンを無効にしないでください。フォームにトークンを入れ、サーバーが一つの注文を一度だけ受け付けるようにすれば、二度押しても予約は一つで、一度目が失敗したときもボタンは使えます。

## 修正の確認

1. チケットを1枚多くする、違う公演を選ぶなど、わざと間違えてテスト予約を行い、お金が引き落とされる直前まで進む。確定する前に間違いが言葉で示されること、または後から取り消せることを確認する。
2. 確認の段階から、それぞれの「変更」リンクを使う。正しい欄へすべての回答を保ったまま移動し、確認の段階に修正後の注文が表示されることを確認する。
3. 取り消しの期限がある場合、その終わりが支払いの前と後に日付と時刻で明記されていることを確認し、予約完了のページからキーボードだけでテスト予約を取り消す。
4. スクリーンリーダーで、確定するボタンより前に、すべての選択内容と合計が読み上げられ、ボタンの名前が何をするかを示していることを確認する。
5. 400%の拡大表示やスマートフォンで、要約と最後のボタンを一緒に、または続けて読めることを確認する。

OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。チェックアウトの段階や決済事業者を変更した際は、再度確認します。

## 制限

このサンプルは、どのプロセスでも同じ結果になることを保証するものではありません。例はチケットの予約ですが、ショップ、定期購入、ローン、申し込み、試験も、確定する段階で同じ確認が必要です。3.3.4は、自分のデータを保存・編集するたびに確認を求めるものではなく、間違いの代償が大きいか、取り消しにくい場合に限ります。送られるメールも含めてプロセス全体を、利用者が使う支援技術や道具で確認してください。

---

このガイドをコーディングエージェントで使う: MCP https://easeweb.dev/mcp（設定は https://easeweb.dev/ja/services/developers）
