PHPでSquare Eコマース決済を試す

世の中のインターネット向けサービスの多くでPHPが使われています（2017年12月現在、83.1%がPHPとされています）。WordPressは世界中の約30%のサイトで動いているなんてレポートもあります（via [W3Techs - extensive and reliable web technology surveys](https://w3techs.com/)）。

そうしたサイトの中に決済機能を持たせたいと思ったらぜひ[Square EコマースAPI](https://docs.connect.squareup.com/payments/sqpaymentform/sqpaymentform-overview)を使ってみてください。今回はその使い方を紹介します。

コードは [goofmint/SquareCommercePHPDemo: SquareのEコマースAPIをPHPで行うデモです。](https://github.com/goofmint/SquareCommercePHPDemo) にて公開しています。

## 処理の流れ

基本的な処理の流れです。Squareでは開発者がクレジットカード番号に触れないで済む仕組みを提供しています。注文処理を開始する際にまずクレジットカード情報をSquareに送信し、トークン化します（nonceと呼びます）。そのnonceと決済額を合わせて課金処理を行います。


トークン化はWebブラウザ（JavaScript）で行います。PHP側ではnonceを受け取り、課金処理を行うだけです。

## nonce生成

まずnonce生成について紹介します。HTMLは次のような形になります。 `#card-nonce` に取得したnonceが入ります。フォームの送信先は `process-card.php` になっていますが、ボタンを押した時にまず `requestCardNonce(event)` が実行されます。

このフォームの時点ではクレジットカード情報を入力する input タグがありません。後述するJavaScriptによって生成されます。

```html
<form id="nonce-form" novalidate action="/process-card.php" method="post">
  カードで支払います
  <table><tbody>
    <tr>
      <td>カード番号:</td>
      <td><div id="sq-card-number"></div></td>
    </tr>
    <tr>
      <td>CVV:</td>
      <td><div id="sq-cvv"></div></td>
    </tr>
    <tr>
      <td>有効期限:</td>
      <td><div id="sq-expiration-date"></div></td>
    </tr>
    <tr>
      <td>郵便番号:</td>
      <td><div id="sq-postal-code"></div></td>
    </tr>
    <tr>
      <td colspan="2">
        <button id="sq-creditcard" class="button-credit-card" onclick="requestCardNonce(event)">
          カードで支払う
        </button>
      </td>
    </tr>
  </tbody></table>
  <input type="hidden" id="card-nonce" name="nonce">
</form>
```

もし、MasterpassやApple Payを使う場合には、さらに次のように記述しておきます。

```html
<div id="sq-walletbox">
  デジタル通貨で支払う
  <div id="sq-apple-pay-label" class="wallet-not-enabled">Apple Pay for Web が有効ではありません</div>
  <button id="sq-apple-pay" class="button-apple-pay"></button>

  <div id="sq-masterpass-label" class="wallet-not-enabled">Masterpassが有効ではありません</div>
  <button id="sq-masterpass" class="button-masterpass"></button>
</div>
```

### Square決済フォームの表示

Squareの決済フォームを表示するには、まずスクリプトタグで以下のJavaScriptを読み込みます。

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

次にJavaScriptファイルを作成し、それを読み込みます。

```html
<script type="text/javascript" src="/sqpaymentform.js"></script>
```

`sqpaymentform.js` の内容は次のようになります。詳細はコメントを参照してください。二箇所 REPLACE_ME となっている部分があるので、それは Square Developer Portal で取得できるものと置き換えてください。

```javascript
var applicationId = "REPLACE_ME"; // アプリケーションIDと置き換えます
var locationId = "REPLACE_ME";    // 店舗IDと置き換えます

// ボタンを押したタイミングで実行される関数
function requestCardNonce(event) {
  event.preventDefault();
  paymentForm.requestCardNonce();
}

var paymentForm = new SqPaymentForm({
  // 以下は初期設定です
  applicationId: applicationId,
  locationId: locationId,
  inputClass: 'sq-input',
  inputStyles: [{
      fontSize: '.9em'
  }],

  // Apple Pay用
  applePay: {
    elementId: 'sq-apple-pay'
  },

  // MasterPass用
  masterpass: {
    elementId: 'sq-masterpass'
  },

  // クレジットカード情報のプレイスホルダー
  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: {
    
    // Apple Pay / MasterPassの有効/無効チェック
    methodsSupported: function (methods) {
      var applePayBtn = document.getElementById('sq-apple-pay');
      var applePayLabel = document.getElementById('sq-apple-pay-label');
      var masterpassBtn = document.getElementById('sq-masterpass');
      var masterpassLabel = document.getElementById('sq-masterpass-label');
      // Apple Payが有効だったら表示する
      if (methods.applePay === true) {
        applePayBtn.style.display = 'inline-block';
        applePayLabel.style.display = 'none' ;
      }
      // Masterpassが有効だったら表示する
      if (methods.masterpass === true) {
        masterpassBtn.style.display = 'inline-block';
        masterpassLabel.style.display = 'none';
      }
    },

    createPaymentRequest: function () {
    },
    // nonce 生成後に呼ばれるメソッド
    cardNonceResponseReceived: function(errors, nonce, cardData) {
      if (errors) {
        // エラーがあった場合
        console.log("エラーが発生しました。:");
        errors.forEach(function(error) {
          console.log('  ' + error.message);
        });
        return;
      }
      // nonceの値をhiddenの中に入れます
      document.getElementById('card-nonce').value = nonce;
      // 本来のフォームを送信します
      document.getElementById('nonce-form').submit();
    },
    // サポート外のぶらず艶アクセ視した場合
    unsupportedBrowserDetected: function() {
    },
    // イベントハンドリング
    inputEventReceived: function(inputEvent) {
    },
    // フォームを読み込んだ後のコールバック
    paymentFormLoaded: function() {
    }
  }
});


```

### フォームを読み込む

後は最低限のデザインを整えるCSSを読み込みます。

```css
/* Define how SqPaymentForm iframes should look */
.sq-input {
  border: 1px solid rgb(223, 223, 223);
  outline-offset: -2px;
  margin-bottom: 5px;
  display: inline-block;
}

/* Define how SqPaymentForm iframes should look when they have focus */
.sq-input--focus {
  outline: 5px auto rgb(59, 153, 252);
}

/* Define how SqPaymentForm iframes should look when they contain invalid values */
.sq-input--error {
  outline: 5px auto rgb(255, 97, 97);
}

/* Customize the "Pay with Credit Card" button */
.button-credit-card {
  min-width: 200px;
  min-height: 20px;
  padding: 0;
  margin: 5px;
  line-height: 20px;
  box-shadow: 2px 2px 1px rgb(200, 200, 200);
  background: rgb(255, 255, 255);
  border-radius: 5px;
  border: 1px solid rgb(200, 200, 200);
  font-weight: bold;
  cursor:pointer;
}


/* Customize the "{{Wallet}} not enabled" message */
.wallet-not-enabled {
  min-width: 200px;
  min-height: 40px;
  max-height: 64px;
  padding: 0;
  margin: 10px;
  line-height: 40px;
  background: #eee;
  border-radius: 5px;
  font-weight: lighter;
  font-style: italic;
  font-family: inherit;
  display: block;
}

/* Customize the Apple Pay on the Web button */
.button-apple-pay {
  min-width: 200px;
  min-height: 40px;
  max-height: 64px;
  padding: 0;
  margin: 10px;
  background-image: -webkit-named-image(apple-pay-logo-white);
  background-color: black;
  background-size: 100% 60%;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  border-radius: 5px;
  cursor:pointer;
  display: none;
}

/* Customize the Masterpass button */
.button-masterpass {
  min-width: 200px;
  min-height: 40px;
  max-height: 40px;
  padding: 0;
  margin: 10px;
  background-image: url(https://static.masterpass.com/dyn/img/btn/global/mp_chk_btn_147x034px.svg);
  background-color: black;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  border-radius: 5px;
  border-color: rgb(255, 255, 255);
  cursor:pointer;
  display: none;
}

#sq-walletbox {
  float:left;
  margin:5px;
  padding:10px;
  text-align: center;
  vertical-align: top;
  font-weight: bold;
}

#sq-ccbox {
  float:left;
  margin:5px;
  padding:10px;
  text-align: center;
  vertical-align: top;
  font-weight: bold;
}
```

これで画面が完成です。

## process-card.php の修正

実際に決済処理を行う process-card.php では以下の行を修正します。これは Square Developer Portal で取得できるアクセストークンと置き換えるだけです。本番であれば Personal Access Token の値を、開発環境であればサンドボックスのアクセストークン Sandbox Access Token を使います。

```
// 元
$access_token = 'REPLACE_ME';
// 修正後
$access_token = 'sandbox-sq0...ZBQ';
```

実際の決済処理は以下のコードで行われています。

```php
$transactions_api = new \SquareConnect\Api\TransactionsApi();

$request_body = array (
  "card_nonce" => $nonce,
  "amount_money" => array (
    "amount" => 100,
    "currency" => "JPY"
  ),
  "idempotency_key" => uniqid()
);

$result = $transactions_api->charge($location->getId(), $request_body);
echo "<pre>";
print_r($result);
echo "</pre>";
```

## 試す

では実際に試してみます。以下のコマンドで簡易的なPHPアプリケーションサーバが http://localhost:8080/ で立ち上がります。

```
$ php -S localhost:8080
```

Webブラウザで http://localhost:8080/ を開いて、以下の情報を入力してみましょう。

|名前|値|
|---|---|
|カード番号|4532759734545858|
|有効期限|12/20|
|CVV|111|
|郵便番号|111111|

カードで支払うボタンを押すと、まずSquareのサーバにクレジットカード情報が送信されて、代わりに nonce を受け取ります。そして、そのまま nonce をPHPへ送信して決済処理を実行します。

特に問題がなければTransactionオブジェクトのダンプが表示されます。

----

このような手順でPHP向けのWebアプリケーションに決済処理を追加できます。Webフォームのデザインは自由に設計できますので、既存のサービスに組み込んでも違和感なく使えるはずです。実装例としては、例えばWordPressにEコマース機能を追加する [WooCommerce — WordPress プラグイン](https://ja.wordpress.org/plugins/woocommerce/) があります。こちらを使うと簡単にSquareの決済機能が使えるようになります。ぜひ試してみてください。

今回のコードは [goofmint/SquareCommercePHPDemo: SquareのEコマースAPIをPHPで行うデモです。](https://github.com/goofmint/SquareCommercePHPDemo) にて公開しています。もし試してみて分からないところがあれば [Teratail](https://teratail.com/tags/Square) にて質問してください！

[Build with Square](https://docs.connect.squareup.com/)