開発情報・ナレッジ

投稿者: ShiningStar株式会社 2024年4月8日 (月)

フォームに電子署名を追加する

この記事ではフォームに電子署名(マウスやスマートフォンのタップで描けるサイン)を、
追加する方法をご紹介いたします。

この様な形でマウスで文字を書く事ができます。


DB設定

DBを新規作成する場合はDBタイトルをsignDBとして作成してください。
既存のDBを利用する場合は後述するサンキューページ内のPHPにて利用したいDBタイトルを記載してください。

電子署名を格納したいDBにファイル型フィールドを追加してください。
本サンプルにつきましてはファイル型フィールドの差し替えキーワードをsignatureImgとして設定しています。

入力画面

HTML
<dl class="cf">
      <dt class="title">
       ファイル
      </dt>
  <dd class="data file">
   <canvas id="signature-pad" width="400" height="200" style="border:1px solid #000;"></canvas>
   <button type="button" id="clear">クリア</button>
   <input type="hidden" name="signature" id="signature-base64">
  </dd>
 </dl> 
            
JavaScirpt
<script>
document.addEventListener("DOMContentLoaded", function() {
    var canvas = document.getElementById('signature-pad');
    var ctx = canvas.getContext('2d');
    var drawing = false;

    function getTouchPos(canvasDom, touchEvent) {
        var rect = canvasDom.getBoundingClientRect();
        return {
            x: touchEvent.touches[0].clientX - rect.left,
            y: touchEvent.touches[0].clientY - rect.top
        };
    }

    function startDrawing(e) {
        drawing = true;
        // タッチイベントの場合は座標を調整
        if (e.touches) {
            e.preventDefault(); // スクロールを防ぎます
            var touch = getTouchPos(canvas, e);
            draw(touch.x, touch.y);
        } else {
            draw(e.offsetX, e.offsetY);
        }
    }

    function draw(x, y) {
        if (!drawing) return;
        ctx.lineWidth = 2;
        ctx.lineCap = 'round';
        ctx.lineTo(x, y);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(x, y);
    }

    function stopDrawing() {
        drawing = false;
        ctx.beginPath();
        document.getElementById('signature-base64').value = canvas.toDataURL();
    }

    function clearCanvas() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    }

    canvas.addEventListener('mousedown', startDrawing);
    canvas.addEventListener('mousemove', (e) => {
        if (drawing) {
            draw(e.offsetX, e.offsetY);
        }
    });
    canvas.addEventListener('mouseup', stopDrawing);
    canvas.addEventListener('mouseout', stopDrawing);

    // タッチイベントリスナーを追加
    canvas.addEventListener('touchstart', startDrawing);
    canvas.addEventListener('touchmove', (e) => {
        var touch = getTouchPos(canvas, e);
        draw(touch.x, touch.y);
    });
    canvas.addEventListener('touchend', stopDrawing);

    document.getElementById('clear').addEventListener('click', clearCanvas);
});
</script> 
            
HTMLは任意の入力欄を出力したい箇所へ、
JavaScriptは/bodyの直前へ貼り付けてください。

確認画面

PHP
<? //<!-- SMP_DYNAMIC_PAGE DISPLAY_ERRORS=OFF NAME=XXX --> ?>
<?php
// POSTリクエストからサインデータを取得
$signatureData = $SPIRAL->getParam("signature");
?>
 
            
ページの最上部に貼り付けてください。
※下記HTMLより上の位置に貼り付けないと動作いたしません。
HTML
     <dl class="cf">
      <dt class="title">
       電子サイン
      </dt><dd class="data file">
       <img src="<?echo $signatureData;?>" alt="サイン画像" />      </dd>
     </dl> 
            
任意の表示したいフォーム上の箇所に貼り付けてください。

サンキューページ

PHP
<? //<!-- SMP_DYNAMIC_PAGE DISPLAY_ERRORS=OFF NAME=XXX --> ?>
<?php
// APIの設定値
define("API_LOCATOR_URL", "https://www.pi-pe.co.jp/api/locator"); //原則変更不要
define("API_TOKEN", ""); //SPIRALAPIトークンを指定
define("API_SECRET", ""); //SPIRALAPIトークンシークレットを指定
define("FILE_FIELD", "signatureImg"); //電子署名のファイルを格納するファイル型フィールド名を指定
define("DB_NAME", ""); //格納されるDB名を指定
define("FILE_NAME", "signature"); //格納されるファイル名を指定
// POSTからBase64のサインデータを取得
$signatureData = $SPIRAL->getParam("signature");

// APIリクエストを送信する関数
function sendApiRequest($url, $method, $data, $headers) {
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

    $response = curl_exec($curl);
    if (curl_errno($curl)) {
        $error_msg = 'Curl error: ' . curl_error($curl);
        curl_close($curl);
        throw new Exception($error_msg);
    }
    curl_close($curl);
    return $response;
}

// ロケータAPIを呼び出して、サービスURLを取得
$locatorResponse = sendApiRequest(API_LOCATOR_URL, "POST", json_encode(["spiral_api_token" => API_TOKEN]), [
    "X-SPIRAL-API: locator/apiserver/request",
    "Content-Type: application/json; charset=UTF-8",
]);
$locatorData = json_decode($locatorResponse, true);
$APIURL = $locatorData['location'];

// データ更新APIの呼び出し
$id = $SPIRAL->getContextByFieldTitle("id");
$time = time();

// 署名データの準備
$signatureData = explode(',', $signatureData, 2)[1];
$decodedData = base64_decode($signatureData);
// POSTデータの構築部分を修正
$multipartBoundary = "SPIRAL_API_MULTIPART_BOUNDARY";

// JSONデータパート
$jsonData = json_encode([
    "spiral_api_token" => API_TOKEN,
    "db_title" => DB_NAME,
    "id" => $id,
    "passkey" => $time,
    "data" => [["name" => FILE_FIELD, "value" => $signatureData]],
    "signature" => hash_hmac('sha1', API_TOKEN . "&" . $time, API_SECRET, false)
]);

$postData = "--" . $multipartBoundary . "\r\n";
$postData .= "Content-Disposition: form-data; name=\"json\"\r\n";
$postData .= "Content-Type: application/json\r\n\r\n"; // JSONデータのコンテントタイプを指定
$postData .= $jsonData . "\r\n";

// ファイルデータパート
$postData .= "--" . $multipartBoundary . "\r\n";
$postData .= "Content-Disposition: form-data; name=\"" . FILE_FIELD . "\"; filename=\"" . FILE_NAME . ".png\"\r\n";
$postData .= "Content-Type: application/octet-stream\r\n\r\n";
$postData .= $decodedData . "\r\n";
$postData .= "--" . $multipartBoundary . "--\r\n"; // 終了バウンダリー

// curlリクエスト部分
$curl = curl_init($APIURL);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
    "X-SPIRAL-API: database/update/request",
    "Content-Type: multipart/form-data; boundary=" . $multipartBoundary,
]);
// APIリクエスト送信
$updateResponse = sendApiRequest($APIURL, "POST", $postData, [
    "X-SPIRAL-API: database/update/request",
    "Content-Type: multipart/form-data; boundary=" . $multipartBoundary,
]);
?> 
            
ページの最上部に貼り付けてください。
その後APIの設定値の値をお使いの環境のものに指定をしてください。

解説

入力ページにてJavaScriptを用いて電子署名を書いてその画像をbase64形式に変換しPOSTを行います。
POSTされたbase64の画像データをデコードしバイナリに変換を行います。
フォーム上にて登録されたレコードに対して、バイナリ化した画像ファイルを SPIRAL API で更新する処理になっています。

解決しない場合はこちら コンテンツに関しての
要望はこちら