Node.js使ってSquareで支払ってみよう

## はじめに

この記事は[【マイスター・ギルド】本物の Advent Calendar 2021](https://adventar.org/calendars/6579)の6日目の記事です。
Node.jsを使ったSquareの支払い方についてまとめてみました。

Squareについて、まとめてる記事が少なかったことがきっかけで、
自分で調べたことをまとめた備忘録のようなものになります。
参考になれば幸いです。

## 基礎的な支払い

基礎的な支払いの仕方になります。

### クレジット払いでの決済

あくまでも例です。

まずSquareのSDKのインストール。

```
$ npm install square
```

```js
const { ApiError, Client, Environment } = require('square')

const client = new Client({
  timeout:3000,
  environment: Environment.Sandbox,
  accessToken: Square Developerのアプリのアクセストークン,
})

let PaymentId;

try {
    const { result, ...httpResponse } = await client.paymentsApi.createPayment({
      sourceId: クライアント側から送られるnonce,
      // ↓uuidを使用しています
      idempotencyKey: uuidv4(),
      amountMoney: {
        amount: 100,
        currency: 'JPY'
      },
      // ↓は記述しなくてもOKです。書かない場合はデフォルトでtrueになります
      // ちなみにfalseにすると、決済は承認されたけど料金は自体は支払われてない状態の仮決済状態になります。
      // 別途実決済の手続きが必要になります。
      autocomplete: false
    });
    // paymentsApiからcreatePaymentリクエストをすると、
    // 返ってくるプロミスのreslutにはpayment.idがあります。
    // 仮決済時にはこのpayment.idを使用します。
    // そのため、どこかで保存しておく必要があります
    // ↓はただ簡易的に変数に入れてるだけです。
    PaymentId = result.payment.id;

  } catch(error) {
    console.log(error);
  }
```

paymentsApiからcreatePaymentリクエストをするとき、
仮決済、遅延払いをする場合は、返ってくるプロミスのresultには**payment.id**を使用するので、保存する必要がありますので、注意。

参考：[Take Card Payments](https://developer.squareup.com/docs/payments-api/take-payments/card-payments#delayed-capture-of-a-card-payment), 
[Delayed capture of a card payment](https://developer.squareup.com/docs/payments-api/take-payments/card-payments#delayed-capture-of-a-card-payment), 
[square / square-nodejs-sdk](https://github.com/square/square-nodejs-sdk/blob/master/doc/api/payments.md)

- - -

### 遅延払い、仮決済後に実決済を行う場合

```js
try {
// PaymentIdは仮決済のときに発行されたpayment.idのこと。決済を完了したいpayment.idを渡せば、完了する
    const { result, ...httpResponse } = await client.paymentsApi.completePayment(PaymentId);
  
  } catch(error) {
    console.log(error);
  }
```

paymentsApiからcompletePaymentを実行する際に、仮決済の時に返ってきた**payment.id**を渡せば決済が完了します。

参考：[Delayed capture of a card payment ](https://developer.squareup.com/docs/payments-api/take-payments/card-payments#delayed-capture-of-a-card-payment)
[Complete Payment](https://github.com/square/square-nodejs-sdk/blob/master/doc/api/payments.md#complete-payment)

- - -

### 金額の変更

```js
try {
// こちらもPaymentIDを使うので、注意
    const { result, ...httpResponse } = await client.paymentsApi.updatePayment(PaymentId, {
      idempotencyKey: uuidv4(),
      payment: {
        amountMoney: {
          amount: 変更した金額,
          currency: 'JPY'
        }
      }
    });
    console.log(result);
  } catch(error) {
    console.log(error);
  }
```

仮決済の支払いには、金額変更が可能です。

- 通常支払い時にpaymentsApiからcreatePayment実行した際はautocompleteパラメーターが**true**の設定になってます。その際にレスポンスとしてPayment objectが返ってきます。statusパラメーターが**"COMPLETED"**になります。
- 仮決済時では、paymentsApiからcreatePayment実行した際にautocompleteパラメーターが**false**として設定されてます。その際にレスポンスのPayment objectのstatusパラメーターは**"APPROVED"**になります。
 - 別途paymentsApiからcompletePaymentを実行すると実決済が行われ、statusパラメーターも**"COMPLETED"**になります。

**金額変更はstatusパラメーターが"APPROVED"のものしか適用できません。**

金額変更してもstatusパラメーターは"APPROVED"のままなので、
仮決済後に実決済をしなければならないのと同じく、
[paymentsApiからcompletePaymentを実行しなければなりません](https://meisterguild.docbase.io/posts/1946182#%E9%81%85%E5%BB%B6%E6%89%95%E3%81%84%E4%BB%AE%E6%B1%BA%E6%B8%88%E5%BE%8C%E3%81%AB%E5%AE%9F%E6%B1%BA%E6%B8%88%E3%82%92%E8%A1%8C%E3%81%86%E5%A0%B4%E5%90%88)ので、注意。


参考：[Update Payments](https://developer.squareup.com/docs/payments-api/update-payments), [Update Payment Node-sdk](https://github.com/square/square-nodejs-sdk/blob/master/doc/api/payments.md#update-payment)

- - -

## 注意点

注意点をまとめてみました。

### 支払いの最低金額は100円

100円未満だとエラーになります。

参考：[Payment minimums](https://developer.squareup.com/docs/payments-api/additional-payment-considerations#payment-minimums), [Take Payments](https://developer.squareup.com/docs/payments-api/take-payments)

- - -

### 仮決済の期間は最大で7日

> For card payments, you have the <span style="color: #d61b09">following default time thresholds to capture</span> (or cancel) an authorized payment:
> 
> - 36 hours for in-person (card present) payments, where the card was swiped, dipped, or tapped using a card reader.
> 
> - <span style="color: #d61b09">7 days</span> for online (card not present) payments, where the buyer used a card on file or typed the card number.
> 
> After the time period expires, if the payment is not in a terminal state (COMPLETED, CANCELED, or FAILED), Square cancels the payment. The delayed_until field provides the date and time on Square servers when Square cancels the payment.
> 
> You can change the preceding default thresholds by specifying the delay_duration field in a CreatePayment request. <span style="color: #d61b09">The time periods you specify must be</span> at least 1 minute and<span style="color: #d61b09"> less than the preceding threshold values.</span>
> 

仮決済の期間（delay_duration）の最大値は、
PT168H( RFC 3339形式)=168時間=24時間 × 7 = 7日間
最大で7日の範囲内でしか設定できません。

delay_durationをP8D(8日)としてリクエストを送ると、
エラーになります。

引用：[Delayed capture of a card payment](https://developer.squareup.com/docs/payments-api/take-payments/card-payments#delayed-capture-of-a-card-payment)

- - -

## 応用的な決済の仕方

通常な決済の仕方ではなくて、複数のAPIを組み合わせた支払いの例になります。

### 初めに決済を行わずにクレジットカード情報を保存し、後に決済だけをする場合


**まず初めに、決済したいユーザーを顧客リストに追加します。
そのためには、SquareのCustomers APIからCreateCustomerを実行します。**

> You must provide at least one of the following values in your request to this endpoint:
- given_name
- family_name
- company_name
- email_address
- phone_number

リクエストを送る際には、given_name、family_name、company_name、email_address、phone_numberのいずれかを指定して、送らないといけません。
今回の例では、given_nameを指定します。


リクエストの例

```js
try {
  const response = await client.customersApi.createCustomer({
    givenName: 'お名前',
  });

  console.log(response.result);
} catch(error) {
  console.log(error);
}
```

- customersApiからcreateCustomerを実行した際に、レスポンスとして、**customer.id**が発行されます。それを後の決済に使用していきます。

- レスポンスの例

```json
{
  "customer": {
  // ↓このIDのことです。
    "id": "◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯",
    "created_at": "2021-03-31T18:27:07.803Z",
    "updated_at": "2021-03-31T18:27:07Z",
    "given_name": 登録された名前,
    略〜
  }
}
```

- Squareのデータのダッシュボードの顧客リストに追加されます。


参考：[Step 1: Create a customer record in the target seller account](https://developer.squareup.com/docs/cards-api/walkthrough-seller-card#step-1-create-a-customer-record-in-the-target-seller-account)、[Create Customer](https://github.com/square/square-nodejs-sdk/blob/master/doc/api/customers.md#create-customer)

- - -

**次に、クレジットカードを先ほど登録したユーザーのデータと結びつけます。
そのためには、SquareのCards APIからCreateCardを実行します。**

リクエストを送る際に、**sourceIdをクライアント側に送られるクレジットを入力したときのトークン**、
**customerIdを前の手順で発行されたcustomer.idを指定します。**

```js
try {
  const response = await client.cardsApi.createCard({
    idempotencyKey: uuidv4(),
    sourceId: クライアント側から送られるnonce,
    card: {
      customerId: 前の手順で発行されたcustomer.id,
    }
  });

  console.log(response.result);
} catch(error) {
  console.log(error);
}
```

- cardsApiからcreateCardを実行した際に、レスポンスとして、**card.id**が発行されます。それを後の決済に使用していきます。
- レスポンスの例

```json
{
  "card": {
   // ↓このIDのことです。
    "id": "◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯",
    "bin": "411111",
    "card_brand": "VISA",
    "card_type": "CREDIT",
    "cardholder_name": "Amelia Earhart",
    "customer_id": 入力したcustomer.id,
    "enabled": true,
    "exp_month": 11,
    "exp_year": 2024,
    "last_4": "1111",
    "prepaid_type": "NOT_PREPAID",
    "reference_id": "user-id-1",
    "version": 1
  }
}
```

- Squareのデータのダッシュボードの顧客リストの保存済みカード情報として保存されます。

参考：[Step 2: Save a card on file in the seller Square account](https://developer.squareup.com/docs/cards-api/walkthrough-seller-card#step-2-save-a-card-on-file-in-the-seller-square-account)、[Create Card](https://github.com/square/square-nodejs-sdk/blob/master/doc/api/cards.md#create-card)

- - -

**そして、今までの手順で発行されたcustomer.idとcard.idを用いてることで、
登録された任意のユーザーとカードを指定して決済することが可能です。**


```js
try {
  const response = await client.paymentsApi.createPayment({
    sourceId: 今までの手順で発行されたcard.id,
    idempotencyKey: uuidv4(),
    amountMoney: {
      amount: 100,
      currency: 'JPY'
    },
      customerId: 今までの手順で発行されたcustomer.id,
  });

  console.log(response.result);
} catch(error) {
  console.log(error);
}

```

- 通常時の決済のように、クライアント側でクレジット情報を入力した際に発行されるトークンを渡す必要ありません。
 -  sourceIdには、通常の決済時、↑を指すトークンを渡しますが、今回の決済の場合では代わりにcard.idを渡します。

参考：[Step 3: Create a payment using the card on file](https://developer.squareup.com/docs/cards-api/walkthrough-seller-card#step-3-create-a-payment-using-the-card-on-file)

- - -

### 決済と同時に顧客情報とカード情報を登録する場合

大まかに実行する処理は、以下の通りになります。

1. 通常の決済処理を行う
 - paymentsApiからcreatePaymentを使用する。
 - レスポンスとして返ってくる**payment.id**をカード情報の登録の際に使用する
1. 顧客情報を登録する
 - customersApiからcreateCustomerを使用する。
 - レスポンスとして返ってくる**customer.id**をカード情報の登録の際に使用する
1. カード情報を登録する
 - cardsApiからcreateCardを使用する
 - リクエスト送る際に、source_idパラメーターを1の手順で発行されたpayment.idを指定する
 - リクエスト送る際に、customer_idパラメーターを２の手順で発行されたcustomer.idを指定する

上記の複数の処理を一回の実行にまとめることで、本目的の、決済と同時に顧客情報とカード情報を登録するというのが達成できます。
(登録した顧客情報に登録したカード情報が結びつきます。)

参考：[Create a Card on File from a Payment ID](https://developer.squareup.com/docs/cards-api/walkthrough/card-from-payment-id)
(↑のドキュメントでは、それぞれ分けて処理を行っていますが、ここでは一つにまとめています。)


```js
try {
// ↓決済の処理
    const paymentResult = await client.paymentsApi.createPayment({
      sourceId: クライアント側から送られるnonce,
      idempotencyKey: uuidv4(),
      amountMoney: {
        amount: 100,
        currency: 'JPY'
      }
    });

    let  PaymentId = paymentResult.result.payment.id;
  

// 顧客情報登録の処理
    const customerResult = await client.customersApi.createCustomer({
      givenName: お名前,
    });
    
    let CustomerId = customerResult.result.customer.id

// カード情報登録の処理
    const { result, ...httpResponse } = await client.cardsApi.createCard({
      idempotencyKey: uuidv4(),
      // 決済の処理のレスポンスのPaymentIdを指定する
      sourceId: PaymentId,
      card: {
      // 顧客情報登録の処理のレスポンスのCustomerIdを指定する
        customerId: CustomerId,
      }
    });
    
  } catch(error) {
    console.log(error);
  }
```

- - -


実際に処理を行うと、Squareデータの取引履歴に決済情報が残ります。
Squareの顧客リストを確認すると、
顧客のカード情報として保存されています。

注意点としては、一回目の決済が取引履歴として残らないところです。

- - -

## 詰まりそうなポイント

個人的に詰まり、調べても特に欲しい情報がなかったものになります。

### 顧客リストから特定の顧客の情報を取得する

**電話検索の場合**

1. 正確に一致するように検索
1. あいまい検索

上記２パターンが存在します。

> If your seller account is outside the US, you might need to use international versions of an E.164-compliant phone number.

注意：国際バージョンのE.164準拠に準拠した電話番号指定しないと、正確一致の検索では取得できません。

Squareデータの顧客リストから任意の顧客を選択して、個人情報の編集のリンクを押下すると、準拠した電話番号が出てきます。

例：
+81 800000000になります
(080-0000-0000から日本の国際番号の＋081と元の番号の080-0000-0000から最初の0を除いた番号になります)

リクエスト例(完全一致検索：exact)

```js
try {
  const response = await client.customersApi.searchCustomers({
    query: {
      filter: {
        phoneNumber: {
          exact: '+818000000000'
        }
      }
    }
  });

  console.log(response.result);
} catch(error) {
  console.log(error);
}
```

レスポンス例

```json
{
  "customers": [
    {
      "id": CustmerIdになります,
      "created_at": "2021-06-24T04:30:20.849Z",
      "updated_at": "2021-06-24T08:15:08Z",
      "cards": [
        {
          "id": CardIdになります,
          "card_brand": "DISCOVER_DINERS",
          "last_4": "0004",
          "exp_month": 11,
          "exp_year": 2022,
          "billing_address": {
            "postal_code": "99987"
          }
        }
      ],
      "given_name": "お名前",
      "phone_number": "+818000000000",
      "preferences": {
        "email_unsubscribed": false
      },
      "creation_source": "THIRD_PARTY",
      "version": 5
    }
  ]
}
```


参考：[Search by phone number](https://developer.squareup.com/docs/customers-api/use-the-api/search-customers#search-by-phone-number)

- - -

## おわり

以上、Squareのまとめ記事でした。
Squareを使ってみて、とても使いやすいなーという印象を受けました。
Squareについて調べてみて、まとめてある記事が少なかったので、本記事が誰かの助けになれば嬉しいです。
最後までご覧いただきありがとうございました。