問い合わせフォームに届いた内容を、担当者が管理画面を開いて読み、CRMの新規リード画面に手で打ち直す。1件あたりは2分ほどでも、届く件数が増えるほど転記だけで時間が溶けますし、打ち間違いと入れ忘れが必ず混ざります。これを止める仕組み自体は難しくありません。難しいのは、同じ問い合わせが2件登録されないようにすることと、送信に失敗したときに黙って消えないようにすることです。この記事はその2つの持ち方の話です。
まず、直接APIを叩くのかどうかを決める
ZohoのようなクラウドサービスにWordPressから登録する方法は、大きく2つに分かれます。1つはREST APIを直接叩く方法。もう1つは、Zoho Flowのように「決まったURLにJSONをPOSTすれば、あとは向こう側の画面で組んだ流れに沿って処理される」タイプのWebhookに投げる方法です。
直接APIを叩く場合、認証はOAuthになります。アクセストークンの寿命は短く、リフレッシュトークンを保管して定期的に取り直す処理を自前で持つことになります。ここが止まると、エラー画面も出ないまま登録だけが行われなくなります。加えて、登録先の項目名(API名)を1つずつコード側に書くことになるため、相手側で項目を1つ増やしただけでコードの修正が必要になります。
Webhookに投げる方法は、この2つの面倒がなくなります。認証はURLに含まれるキーで完結し、トークンの更新処理を持ちません。項目の対応付けは相手側の管理画面で設定するので、後から項目が増えても自社のコードは触らずに済みます。会員制サイトを構築した案件では、この形を採っています。運用に入ったあとで直す場所が自社側にないのは、費用の面でも大きい違いです。
| 比較する点 | REST APIを直接叩く | WebhookへPOSTする |
|---|---|---|
| 認証 | OAuth。トークンの更新処理を自作する | URLに含まれるキーのみ |
| 項目が増えたとき | コードの修正が必要 | 相手側の画面で設定するだけ |
| できること | 検索・更新・削除まで自由 | 基本は登録・通知。複雑な条件は相手側の機能に依存 |
| 止まったときの原因 | トークン切れ、項目名の不一致 | URLの失効、相手側フローの停止 |
| 初期の実装量 | 多い | 少ない |
更新や照会まで必要なら前者、フォームからの一方通行なら後者。判断の分かれ目はここだけです。最初から前者を選ぶ理由は、思っているより少ないはずです。認証まわりでつまずく箇所は共通しているので、直接APIを選ぶ場合はOAuth連携でつまずくのは、たいてい同じ3か所に絞られるを先に読んでおくと時間が節約できます。
発火点は、メール送信の成否と切り離す
WordPressのフォームプラグインには、送信のタイミングで処理を差し込むためのフックが用意されています。よく使われるContact Form 7なら、メール送信後に走る wpcf7_mail_sent と、送信直前の wpcf7_before_send_mail です。ここで wpcf7_mail_sent を選ぶと、メール送信が失敗したときに外部への登録も行われません。サーバーのメール設定が原因で通知メールだけ届かない、という状況は珍しくないので、登録を確実にしたいなら送信直前のフックを使ってください。
もう一つ重要なのが、外部への送信を利用者のリクエストの中で同期実行しないことです。相手側の応答が遅れたり落ちていたりすると、その待ち時間がそのままフォームの応答時間になります。数十秒待たされた利用者は送信ボタンをもう一度押しますし、その結果として二重に登録されます。フォーム側の処理はいったん完了させ、外部への送信は wp_schedule_single_event などで切り離してください。
ただしWordPress標準の擬似cronは、サイトへのアクセスがないと動きません。問い合わせが夜間に届いて翌朝まで送信されない、という事態を避けるには、wp-cron.php をサーバー側のcronから定期的に叩く形にしておく必要があります。
重複登録は、3か所から発生する
同じ問い合わせが2件登録される原因は、利用者の二重送信、こちら側の再送、相手側の受信処理のリトライの3つです。どれか1つを塞いでも残りから入ってくるので、対策は1か所に集約します。
やり方は単純で、送信ログのテーブルを1枚持ちます。フォーム送信ごとに一意のキーを作って記録し、そのキーで送信済みかどうかを判定する。キーはフォームの送信IDでもよいですし、無ければメールアドレスと本文と送信時刻を連結してハッシュ化した値で足ります。相手側にも同じキーを一緒に渡しておくと、向こうの画面で重複を弾いてもらえることがあります。テーブルの列は、キー、送信内容、状態、試行回数、次回の試行時刻、最後のエラー内容の6つで足ります。
WordPressの中に独自のテーブルを足すことをためらう必要はありません。会員制サイトを作った案件では、WordPress標準のユーザーとは完全に独立した会員管理を、独自テーブル5種で組んでいます。会員、グループ、会員とグループの中間テーブル、グループと閲覧可能なコンテンツの中間テーブル、そしてメールのテンプレート。会員とグループ、グループとコンテンツをそれぞれ中間テーブルで持つ構造は、多対多の閲覧制限を実現するときの標準形で、そのまま他の案件にも流用できています。
失敗したときに、どう気づいて、どう戻すか
状態の列には pending、sent、failed の3つを持たせます。送信に失敗したら試行回数を1つ増やし、次回の試行時刻を少し先に設定して pending のまま残す。cronで pending の行を拾って再送し、間隔は1分、5分、30分、2時間と広げていく。5回失敗したら failed にして、管理者宛にメールを1通投げる。この程度で十分に実用的です。
判定で気をつけるのは、HTTPステータスが200でも中身がエラーであることがある点です。レスポンスの本文まで見て、期待した識別子が返っているかで成否を判定してください。そして、送った内容そのものをログに残しておくことです。相手側のフォーマットが変わって数日分が落ちていた、という事態になっても、本文が残っていれば後からまとめて投入し直せます。
連携が止まったことに気づく仕組みも、必ず入れてください。外部サービスとの連携は、コードが壊れなくても止まります。契約の変更、支払い方法の期限切れ、キーの失効。実際に、地図が表示されなくなったという相談の原因が、コードでもサーバーでもなく請求先アカウントの失効だった、ということがあります(Google Maps APIのキー取得と課金設定の落とし穴に詳しく書いています)。連携も同じで、こちらが何もしていないのに突然止まります。1日に1度、直近24時間の送信件数と失敗件数を管理者宛に送る。それだけで、気づくまでの日数が変わります。
実際にあった案件:会員制サイトと外部サービスの連携
会員だけが閲覧できるコンテンツを持つサイトの構築案件で、外部のクラウドサービスとの連携を含めて対応しました。WordPress標準の会員機能では、会社単位のグループと閲覧範囲を細かく分ける要件に対応できなかったため、独自プラグインとして会員管理を作り、前述の5テーブルを切っています。
この案件で運用面に効いたのが、メール文面をデータベースに持たせたことです。案内メールのテンプレートをテーブルに置き、管理画面から編集できるようにしたうえで、%company_name% のような差し込み用のプレースホルダを用意しました。文面を1文字変えるたびに発注が必要な作りにしてしまうと、運用が始まった瞬間に負担になります。同じ考え方で、以前は自動返信メールの本文を mail.txt という独立したテキストファイルに切り出し、担当者がFTPでその1ファイルだけを直せる形にしたこともあります。
一方で、この案件は要件が膨らみました。承認フロー、メール文面、一覧画面でのクイック編集、ログイン管理と、着手後に必要だと分かった機能が積み上がり、要件定義の資料は5版まで版を重ねています。フルスクラッチで会員機能を作ると、必ずこうなります。発注する側の予防策としては、要件定義と実装を分けて発注してください。要件を詰める作業を小さな有償の案件として先に切り出せば、実装フェーズの見積り精度が上がり、途中での追加交渉も減ります。この案件も、数千円の相談から始まって本開発につながっています。
個人情報を外へ出す前に
フォームで受け取った氏名やメールアドレスを外部サービスへ送るのであれば、プライバシーポリシーに送信先と目的を書いてください。「取得した個人情報を外部に提供することはありません」と書いたままCRMへ自動登録している、という状態は避けたいところです。
技術面では3点あります。Webhookの受け口URLは推測できない文字列にして、公開しないこと。送る項目は必要なものだけに絞り、フォームの全項目をそのまま丸ごと投げないこと。そしてキーの置き場所を決めておくことです。なお、既にプラグインで外部サービスとつないでいる現場では、APIキーがそのプラグインの設定画面に保存されていることがよくあります。担当者に聞き直す前に管理画面を確認すると、やり取りが1往復減ります。鍵の管理そのものについてはAPIキーはどこに置き、誰が管理するのかにまとめています。
よくある質問
プラグインを入れるだけでは足りませんか
送るだけなら足ります。足りなくなるのは、送信に失敗した分を後から追える形にしたいとき、送る前に内容を加工したいとき、そして複数のフォームで送り先を出し分けたいときです。この3つが要らないなら、プラグインで十分です。
連携が止まったことに気づけますか
作り方次第です。何もしなければ、担当者が「最近フォームから登録が来ていない」と気づくまで分かりません。送信ログを残し、失敗件数の日次通知を入れておけば、翌日には気づけます。この通知は実装量が小さいわりに効果が大きいので、最初から入れておくことを勧めます。
過去の問い合わせもまとめて登録できますか
データが残っていれば可能です。フォームプラグインが送信内容をデータベースに保存している設定なら、そこから読み出して同じ送信処理に流すだけです。保存していない設定で運用していた場合、残っているのは通知メールだけになるため、メールから拾い直す作業になります。
グループウェアなど社内システムとの連携も同じ考え方ですか
重複と再送の持ち方は同じです。違うのは認証とアクセス制限で、社内システムはIPアドレス制限などで外部からの接続を絞っていることが多く、そちらの調整に時間がかかります。サイボウズOffice・GaroonをWebフォームとつなぐ前に確認することに、確認すべき項目を整理しています。
問い合わせの転記をなくす仕組みは、送るところより、失敗したときにどう戻すかで出来が決まります。いまお使いのフォームと登録先を確認したうえで、どこまで自動化するのが妥当かをお伝えします。
