Squareでは従来POSレジを提供してきましたが、今年から各種Web APIも提供しています。このWeb APIを使うことでオンライン決済なども可能になります。そのEコマースAPIは、サービス提供事業者はサービス利用者のカード番号を知らないままに決済が提供できるのが利点です。しかもよくある決済ASPのように別画面にいったりすることもなく、デザインの自由度も非常に高いものになっています。
今回はそんなEコマースAPIをPHPで実装する方法を紹介します。
フローについて
最初に処理フローについて紹介します。
- SquareのJavaScriptライブラリがフォームを生成する
 - 入力されたカード番号などをSquareのサーバに送信する
 - Squareのサーバから一時的に利用できるトークン(nonce)が返される
 - 入力された顧客情報(名前、住所など)とトークンを自社サーバに送信する
 - 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();
サーバでの処理について
ではここからPHPの処理になります。PHP向けにライブラリとして square-connect を提供しています。composer を使ってインストール可能です。
{
  "require": {
    "square/connect": "2.1.*"
  }
}
まずアクセストークンを用意しておきます。
$access_token = 'REPLACE_ME';
次に先のJavaScriptの処理で手に入れたnonce(テンポラリトークン)を得ます。ない場合はエラーです。
$nonce = $_POST['nonce'];
if (is_null($nonce)) {
  echo "Invalid card data";
  http_response_code(422);
  return;
}
次に決済を行う店舗情報を得ます。あらかじめ決まっている場合はここは読み飛ばしても良いでしょう。 CREDIT_CARD_PROCESSING があるかどうかが肝になります。
$locations_api = new \SquareConnect\Api\LocationsApi();
$locations = $locations_api->listLocations();
$location = current(array_filter($locations->getLocations(), function($location) {
  $capabilities = $location->getCapabilities();
  return is_array($capabilities) &&
    in_array('CREDIT_CARD_PROCESSING', $capabilities);
}));
ではリクエスト情報を生成します。 idempotency_key はユニークなIDです。日本円で1000円の決済を要求しています。
$transactions_api = new \SquareConnect\Api\TransactionsApi();
$request_body = array (
  "card_nonce" => $nonce,
  "amount_money" => array (
    "amount" => 1000,
    "currency" => "JPY"
  ),
  "idempotency_key" => uniqid()
);
最後に決済処理を実行します。
$result = $transactions_api->charge($location->getId(), $request_body);
これで完了になります。カード情報は一度しか使えないnonceとなっていますので、番号などを知ることはありません。なお、あらかじめ決済が通るかどうかはチェックしていませんので、限度額に達している場合などはエラーになる可能性があります。 $result の内容によって処理分けしてください。
カード番号を知らずに決済できる仕組みは幾つかありますが、SquareのEコマースAPIはフォームも自前で持てるので自由度が高い仕組みになっています。JavaScriptの知識も必要ですが、ぜひ導入してみてください!
Embedding the Square Payment Form - Square Connect API Documentation