Square EコマースAPIをNode.jsで実装する

Squareでは従来POSレジを提供してきましたが、今年から各種Web APIも提供しています。このWeb APIを使うことでオンライン決済なども可能になります。そのEコマースAPIは、サービス提供事業者はサービス利用者のカード番号を知らないままに決済が提供できるのが利点です。しかもよくある決済ASPのように別画面にいったりすることもなく、デザインの自由度も非常に高いものになっています。

今回はそんなEコマースAPIをNode.jsで実装する方法を紹介します。

## フローについて

最初に処理フローについて紹介します。

1. SquareのJavaScriptライブラリがフォームを生成する
2. 入力されたカード番号などをSquareのサーバに送信する
3. Squareのサーバから一時的に利用できるトークン（nonce）が返される
4. 入力された顧客情報（名前、住所など）とトークンを自社サーバに送信する
5. nonceと決済額をSquareに送信し、決済処理を行う

このようにカード番号などの情報をnonceに置き換えて決済を行うことで、サービス事業者はカード番号を知らずに決済処理が実現できます。

## Webフォームを生成する

まずHTMLに次のように記述します。

```
<label>Card Number</label>
<div id="sq-card-number"></div>
<label>CVV</label>
<div id="sq-cvv"></div>
<label>Expiration Date</label>
<div id="sq-expiration-date"></div>
<label>Postal Code</label>
<div id="sq-postal-code"></div>
```

さらにJavaScriptタグとして下記を指定します。

```
<script type="text/javascript" src="https://js.squareup.com/v2/paymentform"></script>
```

さらにJavaScriptでidとカード情報の紐付けを行います。

```
var paymentForm = new SqPaymentForm({
  applicationId: applicationId,
  inputClass: 'sq-input',
  inputStyles: [
    {
      fontSize: '15px'
    }
  ],
  cardNumber: {
    elementId: 'sq-card-number',
    placeholder: '•••• •••• •••• ••••'
  },
  cvv: {
    elementId: 'sq-cvv',
    placeholder: 'CVV'
  },
  expirationDate: {
    elementId: 'sq-expiration-date',
    placeholder: 'MM/YY'
  },
  postalCode: {
    elementId: 'sq-postal-code'
  },
  callbacks: {
    // 後ほど記述
  }
});
```

そうするとページが表示された際に下記のようにフォームが生成されます。

```
<label>Card Number</label>
<iframe id="sq-card-number" name="sq-card-number-iframe" class="sq-input" frameborder="0" width="100%" scrolling="no" height="20" src="https://connect.squareup.com/v2/iframe?type=cardNumber"></iframe>
<label>CVV</label>
<iframe id="sq-cvv" name="sq-cvv-iframe" class="sq-input" frameborder="0" width="100%" scrolling="no" height="20" src="https://connect.squareup.com/v2/iframe?type=cvv"></iframe>
<label>Expiration Date</label>
<iframe id="sq-expiration-date" name="sq-expiration-date-iframe" class="sq-input" frameborder="0" width="100%" scrolling="no" height="20" src="https://connect.squareup.com/v2/iframe?type=expirationDate"></iframe>
<label>Postal Code</label>
<iframe id="sq-postal-code" name="sq-postal-code-iframe" class="sq-input" frameborder="0" width="100%" scrolling="no" height="20" src="https://connect.squareup.com/v2/iframe?type=postalCode"></iframe>
```

これで準備は完了です。

## フォーム送信処理の変更

決済処理を開始するフォーム送信処理を変更します。例えばボタンを次のようにします。

```
<button type="submit" class="btn btn-primary"
  onclick="requestCardNonce(event)">注文する</button>
```

そしてJavaScriptを次のように書きます。

```
function requestCardNonce(event) {
  event.preventDefault();
  paymentForm.requestCardNonce();
}
```

`paymentForm.requestCardNonce()` にて前述のnonceを取得する処理が実行されます。その結果は `cardNonceResponseReceived` に呼び出されます。

```
cardNonceResponseReceived: function(errors, nonce, cardData) {
  if (errors) {
    // エラーの場合
    console.log("Encountered errors:");
    errors.forEach(function(error) {
      console.log('  ' + error.message);
    });
  } else {
    // 処理成功の場合
    document.getElementsByName('nonce')[0].value = nonce;
    for (var key in cardData) {
      document.getElementsByName(key)[0].value = cardData[key];
    }
    document.getElementById('commerceForm').submit();
  }
},
```

上記のように記述した場合、HTMLのフォーム側にあらかじめhiddenを使って情報を書き込めるようにしておきます。

```
<!-- カードのnonceが入ります -->
<input type="hidden" name="nonce">
<!-- カードのブランドが入ります -->
<input type="hidden" name="card_brand">
<!-- カードの最後の4桁が入ります -->
<input type="hidden" name="last_4">
<!-- カードの有効期限（月）が入ります -->
<input type="hidden" name="exp_month">
<!-- カードの有効期限（年）が入ります -->
<input type="hidden" name="exp_year">
<!-- 郵便番号が入ります -->
<input type="hidden" name="billing_postal_code">
```

そして実際のフォームをサーバに送信します。

```
document.getElementById('commerceForm').submit();
```

## サーバでの処理について

ではここからNode.jsの処理になります。Squareでは2017年09月現在、Node.js向けにフレームワークを提供していません。そこで unirest という汎用的なRESTクライアントライブラリを使います。npm を使ってインストール可能です。

```
$ npm install unirest -S
```

まずアクセストークンを用意しておきます。

```
let access_token = 'REPLACE_ME';
```

次に先のJavaScriptの処理で手に入れたnonce（テンポラリトークン）を得ます。ない場合はエラーです。

```
// データを取得します
const params = req.body;
const nonce = params.nonce;
if (typeof nonce === 'undefined') {
  res.status(422).render();
  return;
}
```

決済を行う店舗についてはあらかじめ分かっていることとします。

```
let location_id = 'AAA...BBB';
```

ではリクエスト情報を生成します。 idempotency_key はユニークなIDです。今回は `uuid/v1` というライブラリを使ってUUIDを生成します。sendメソッドで決済額を指定し、決済処理を実行します。

```
// REST APIをコールします
unirest
  // POSTメソッドを指定します
  .post(url)
  // ヘッダー情報です。アクセストークンを指定します
  .headers({
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`,
  })
  // sendの中でパラメータを指定します
  .send({
    // Squareから取得したnonceを設定します
    'card_nonce': nonce,
    // 金額を指定します
    'amount_money': {
      'amount': 5000,
      'currency': 'JPY'
    },
    // ユニークなIDを設定します
    'idempotency_key': uuidv1()
  })
  .end(function (response) {
    // 処理がうまくいけばこちらに結果がきます
    // 再読込で処理が重複実行されないよう、リダイレクトします
    res.redirect(`/success?data=${JSON.stringify(response.body.transaction)}`);
  });
```

これで完了になります。カード情報は一度しか使えないnonceとなっていますので、番号などを知ることはありません。カード番号を知らずに決済できる仕組みは幾つかありますが、SquareのEコマースAPIはフォームも自前で持てるので自由度が高い仕組みになっています。JavaScriptの知識も必要ですが、ぜひ導入してみてください！

[Embedding the Square Payment Form - Square Connect API Documentation](https://docs.connect.squareup.com/articles/adding-payment-form)