SBPayment(リンク型)のフロント対応備忘録

## 前置き
こんにちは。suginokoです。[弊社](https://brewus.co.jp/)でSBPaymentサービス(以下SBPayment)を使って対応する予定があるかもということで調査しました。
SBPaymentではかなり豊富な数の決済方法ができるようで、その分だけ多くの取扱説明書(以下仕様書)を読み込む必要がありました。

個人としてはGMOの決済対応したことがあったのですが、SBPaymentは初めてでした。
基本的にはSBPaymentで提供されている仕様書をDLして読めばいい(契約後にアカウント教えてもらえるんだと思われる)と思いますが、読むものが多かったので弊社webフロントがどのような対応をすればよいのかを今後苦労しないでもいいように備忘録を残すことにしました。

## 決済方法
ここでは**クレジットカード決済**の**リンク型**の対応について紹介します。
※他にAPI型というものがあります。

##### リンク型
* SBPayment側に決済画面を用意してもらう。
* デザインの自由度は低い
* 決済方法が豊富

##### API型
* デザインや遷移を自由に決められる
* APIを組み合わせてもろもろ設計できる
* 決済外システムと連携ができる

## 環境
実装するうえで環境設定が必要だと思いますが、SBPaymentでは**共用環境**、**専用環境**、本番、という分かれ方をしているようで。
本番は言わずもがなですので、**共用環境**、**専用環境**を軽く紹介します。

##### 共用環境
弊社だけでなく、他社も使う共通のテスト環境です。
SBPayment側に提供されたアカウントやカード情報を使って決済のテストを行っていきます。

今回試したのは共用環境のみ。

##### 専用環境
アカウントやカードは自社のものを使います。
自社のカードを専用アカウントで使用できるようになるまですごく時間がかかるっぽい。(何カ月かかかるっぽい)


## リンク型を使ってみる
ここまで分かればあとは「リンク型共通仕様(購入要求)」の資料をベースとして、それぞれの決済資料を読めばOKで。
必要なパラメータを入れていきます。

### 開発環境
* React 16.13.1
* axios 0.21.1
* crypto-js 4.0.0

### 試したこと1
提供されている資料に逆らってformData使って送信できないか試しました。
reactでview側を実装していて、サーバーとの連携でaxiosを使って実装していたため、axios.post()でformDataで送信出来たらいいな～～と思って<form>タグを使わずに実装したら、
クロスドメインエラーでSBpaymentにたどりつけなかったのでやはり仕様書に従う形で実装する形に変更。

### 試したこと2
<form>タグを使って実装。
結局仕様書通りformタグで上手くいった感じです。SBPaymentに遷移はするようになったのでformタグで実装ことに。

### 実装
SBpayment側に送る各パラメータのほかに
＜input type=”hidden” name=”sps_hashcode” value=”ab8be32d9602・・・”＞
という**送信する各パラメータを文字列にした値**と**SBPayment側から提供されるhash**を
さらに合体させて暗号化したパラメータをsps_hashcodeの値にあてて送信しなければなりません。


仕様書には
`リクエストの各パラメーターの値を項目定義順に文字列結合し、 最後に当社より払い出されたハッシュキーを結合した値を「UTF-8」へ変換して、 「SHA1」にてハッシュ演算を行ってください。`
とのことでしたので、[crypto-js](https://github.com/brix/crypto-js)を使って暗号化実装してます。
Javascriptで実装するとこんな感じ。


```javascript
/**
 * SBPaymentに関連するlib
 */
import * as CryptoJS from 'crypto-js'
// import Encoding from 'encoding-japanese'


/**
  * 
  * @param {arr} SBpaymentでのparamsの配列
  * @return 連結された文字列にUTF8に変換された値
  * memo: arrの中には吐き出されたhashKeyがあるらしく、それも混ぜる必要がある
  */
 export const SBConvertToUTF8 = (arr) => {
   const str = arr.join('')
   return CryptoJS.enc.Utf8.parse(str)
 }

/**
 * @param {string} utf8に変換されたもの
 * @return SHA1でハッシュ演算された値
 */
 export const SbHash = (utf8str) => {
  return CryptoJS.SHA1(utf8str)
}
```

これらを使って

```Javascript
  const creditParams = [
    { key: 'pay_method', value: your pay method },
    { key: 'merchant_id', value: your merchant id },
    { key: 'service_id', value: your service id },
    { key: 'cust_code', value: your custcode },
    .....
  ]

  let utfData = []
  creditParams.forEach((item, index) => {
    utfData.push(item.value)
  })
  // hash追加
  utfData.push(yourhash)
  // change UTF8
  const toUtf8 = SBConvertToUTF8(utfData)
  // hash
  const hashData = SbHash(toUtf8)
```

hashData をsps_hashcodeのパラメータとして差し込めばOKです。

```html
　　　＜form action={ Const.TEST_POST_BUY_INFO } method="POST" acceptCharset="Shift_JIS"＞
      {
        creditParams.map((item, index) => {
          return (
            <input type="hidden" name={ item.key } value={ item.value } key={ index } />
          )
        })
      }
      <input type="hidden" name="sps_hashcode" value={ hashData }/>
      <input type="submit" value="購入"/>
    ＜/form＞

```

## 困ったこと
* 暗号化
    * 上記のことがJavascriptで出来るまで少々時間かかったが、「接続支援テストページ」があるので何とかなった感じです。
* pagecon_url(必須項目)が意味不明だった
    * ここのURLをミスると決済ができずエラーになる
    * SBPaymentの管理画面上(SBPaymentから提供されるサービスIDでログインすると決済情報が確認できる)は「決済エラー」表示になる
    * 弊社ではサーバーチームにコールバック用APIを用意してもらって対応したことで解決。(pagecon_urlが正しく渡されてなかったときも「接続支援テストページ」でわかるので相談できた。)
* (20210409追記)日本語が文字化け
    * formタグのacceptCharset="Shift_JIS"を入れないと日本語文字列は文字化けします。

正しく接続ができるかどうかは「接続テスト」を使って最初行うといいかもしれません。
誤ったパラメータを指摘してくれます。[こちら](https://developer.sbpayment.jp/)で確認できます。
接続テストについての資料も「リンク型接続支援サイト利用手引き」があるので、試してみてください。
そのあとに本チャン購入テストを行う感じで。

## 以上
Javascriptで暗号化する資料がなかったので書いてみました。
それでは。