Square Connect JavaScript SDKを使って決済処理を実装する

Squareの決済処理をAPIで行うためのJavaScriptライブラリ、[Square Connect Node SDK](https://github.com/square/connect-javascript-sdk)がリリースされました。今回はこのSDKを使って、Expressで決済処理を実装してみます。

## ベース

ベースは express-generator を使って生成します。

```sh
$ npm install express-generator -g
$ express express_square
$ cd express_square
$ npm i
```

### ライブラリのインストール

使うライブラリは `square-connect` です。

```sh
$ npm install square-connect --save
```

### 設定ファイルの作成

Square Connect APIを使うための各種設定を記載した設定ファイルを準備します。

```sh
$ cat config.json
{
  "square": {
    "applicationId": "YOUR_APPLICATION_ID",
    "accessToken": "YOUR_ACCESS_TOKEN",
    "locationId": "YOUR_LOCATION_ID"
  }
}
```

## Square Connectの初期化

まずSquare Connectの初期化を行います。作成した設定ファイルを読み込みつつ、そこに記載されているアクセストークンを適用します。

```javascript
// routes/index.js
const config = require('../config');
// Square Connect JavaScript SDKを初期化します
const SquareConnect = require('square-connect');
SquareConnect
  .ApiClient
  .instance
  .authentications
  .oauth2
  .accessToken = config.square.accessToken;
```

## 決済入力画面の表示

決済入力画面を生成する際にはSquare ConnectのアプリケーションID、ロケーションIDが必要になります。これはクレジットカード番号を一時的に使えるトークンに置き換える際に、開発者側は一切カード番号に触れることなく変換を行うためです。

![Screenshot_ 2017-12-13 18.34.53.png](https://qiita-image-store.s3.amazonaws.com/0/197026/9ee48ba6-6914-02b8-58d6-004fa478b932.png)

この時、描画するHTMLに対してアプリケーションIDとロケーションIDを送ります。

```javascript
// routes/index.js
// 決済ページです
router.get('/', (req, res, next) => {
  res.render('index', {
    applicationId: config.square.applicationId,
    locationId: config.square.locationId
  });
});
```

### HTMLの表示

デフォルトの設定なので `views/index.jade` が読み込まれます。この時、アプリケーションIDとロケーションIDをHTML側で定義してしまいます。後はBootstrapを使ってフォームを作っています。注意点として、カード番号の一時的なトークンを受け取る場所を `input#card-nonce(type="hidden",name="nonce")` として用意していることです。

```jade
extends layout

block content
  script.
    const applicationId = "#{applicationId}";
    const locationId    = "#{locationId}";
  script(src="https://js.squareup.com/v2/paymentform")
  script(src="/Javascripts/app.js")
  div.row
    div.col-md-12
      h1 JavaScript SDKデモ
      p カード番号は 5409889944179029 を使ってください。
      form#nonce-form(action="/pay",method="post")
        div.form-group
          label 住所
          input.form-control(type="text",name="address")
        div.form-group
          label カード番号
          div#sq-card-number
        div.form-group
          label 有効期限
          div#sq-expiration-date
        div.form-group
          label CVV
          div#sq-cvv
        div.form-group
          label 郵便番号
          div#sq-postal-code
        input#card-nonce(type="hidden",name="nonce")
        button.btn.btn-default(type="submit",onclick="requestCardNonce(event)") 注文する
```

### JavaScriptの作成

次にクライアント側のJavaScriptを作成します。パスは `public/javascripts/app.js` になります。ここに書いてある内容は[SqPaymentForm Overview](https://docs.connect.squareup.com/payments/sqpaymentform/sqpaymentform-overview)にある内容ほぼそのままです。

`SqPaymentForm` が定義されると自動的にHTMLフォームを生成します。そして注文するボタンを押した時の処理で `requestCardNonce` を呼び出すように指定していますので、その中で `paymentForm.requestCardNonce()` を呼び出します。このメソッドがSquareのAPIを実行し、クレジットカード情報から一時的なトークン（nonceと呼びます）を生成します。生成後 `cardNonceResponseReceived` が呼び出されます。

```javascript
// 注文するボタンが押された時の処理
function requestCardNonce(event) {
  event.preventDefault();
  paymentForm.requestCardNonce();
}

// Squareの決済フォーム周りの処理を行うオブジェクト
var paymentForm = new SqPaymentForm({
  // 個人の設定
  applicationId: applicationId,
  locationId: locationId,
  // Bootstrap用に変更
  inputClass: 'form-control',
  
  inputStyles: [{
      fontSize: '.9em'
  }],
  // プレイスホルダーを日本語に変更しています
  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: {
    methodsSupported: function (methods) {},
    createPaymentRequest: function () {
      var paymentRequestJson ;
      return paymentRequestJson ;
    },
    // カードの一時トークンを受け取った時の処理
    cardNonceResponseReceived: function(errors, nonce, cardData) {
      if (errors) {
        console.log("Encountered errors:");
        errors.forEach(function(error) {
          console.log('  ' + error.message);
        });
        return;
      }
      // 一時トークンを保存
      document.getElementById('card-nonce').value = nonce;
      // フォームを送信
      document.getElementById('nonce-form').submit();
    },

    unsupportedBrowserDetected: function() {
    },

    inputEventReceived: function(inputEvent) {
    },

    paymentFormLoaded: function() {
    }
  }
});
```

## 決済処理の実行

決済処理は生成されたトークンを使って行います。店舗IDに加えて、最低限必要な情報（ランダムな文字列、金額、nonce）を送信して完了です。決済処理を繰り返し行わないよう、別なページにリダイレクトさせます。

```javascript
// 決済処理です
router.post('/pay', (req, res, next) => {
  // Square APIから取得した一時的に使えるカードトークン
  const nonce = req.body.nonce;
  // 店舗ID
  const locationId = config.square.locationId;
  
  const api = new SquareConnect.TransactionsApi();
  
  // リクエストパラメータを組み立てます
  // ランダムなキー、金額、カードトークンが最低限必要です
  const body = {
    idempotency_key: `ORDER.${Math.random() * 10000000000000}`,
    amount_money: {
      amount: 1000,
      currency: 'JPY'
    },
    card_nonce: nonce
  }
  // 決済処理を実行します
  api.charge(locationId, body).then(data => {
    // 完了したらリダイレクトします（再読み込み防止のため）
    res.redirect(`/complete?id=${data.transaction.id}`);
  }, (err) => {
    console.error(err);
    res.render('error', {error: err})
  });
});
```

## 完了ページの表示

決済完了ページでは単に受け取ったIDを表示するだけです。

```javascript
// 注文完了ページ用です
router.get('/complete', (req, res, next) => {
  res.render('complete', {
    id: req.query.id
  });
});
```

```jade
extends layout

block content
  div.row
    div.col-md-12
      注文が完了しました。IDは #{id} になります。
```

![Screenshot_ 2017-12-13 18.34.46.png](https://qiita-image-store.s3.amazonaws.com/0/197026/8d7d98bb-d9e0-fdc7-113e-9db8b7b6f1b7.png)

## ここまでのコード

これだけでクレジットカードを使った決済処理が実装できます。カード番号に触れることもないので安全です。コードは [goofmint/SquareConnect-Express-Demo: Square Connect Node SDKでExpressを使ってクレジットカード決済を提供するデモです。](https://github.com/goofmint/SquareConnect-Express-Demo) にアップロードしてありますので実装時の参考にしてください。

[square/connect-javascript-sdk: Javascript client library for the Square Connect APIs https://docs.connect.squareup.com/](https://github.com/square/connect-javascript-sdk)