Salesforce B2C Commerce のヘッドレス API を呼んでみる(ショッパー系 API - SCAPI プライベートクライアント編)


※ これから記載する事項は、私が所属する会社とは一切関係のない事柄です。

***
この記事はヘッドレス API の使い方のシリーズの一つです。その他の記事は下記を参照してください。
* [ショッパー系 API - SCAPI パブリッククライアント編](https://qiita.com/syunyama/items/89b204486ace9fd10440)
* [ショッパー系 API - SCAPI プライベートクライアント編](https://qiita.com/syunyama/items/34bd2a11dabb93bb1dc1)
* [ショッパー系 API - OCAPI 編](https://qiita.com/syunyama/items/0bb4a6e095a53dac7267)
* [データ系 API 編](https://qiita.com/syunyama/items/060f5777b0fc04e4cea1)
***


**今回は SCAPI プライベートクライアントを利用したショッパー系 API の呼び方を紹介したいと思います。**

:::note
データ系とショッパー系の違いについては「 [データ系 API 編](https://qiita.com/syunyama/items/060f5777b0fc04e4cea1) 」を参照してください。
:::

## クライアントタイプ

SCAPI のショッパー系の API の認証する際には ２ つのクライアントタイプがあります。

* **プライベートクライアント** ・・・ クライアントシークレットを安全に保管しておけるアプリケーション。例えば、主な処理をサーバー側で行うような従来型のウェブアプリケーションや backend-for-frontend (BFF) を持つウェブまたはモバイルアプリケーション
* **パブリッククライアント** ・・・ クライアントシークレットを安全に保管しておけないアプリケーション。例えば、シングルページアプリケーションやネイティブアプリ

この記事では、SCAPI のプライベートクライアントで ３ つの顧客タイプが商品をカートに追加するユースケースを説明します。

* **ゲストユーザ** ・・・ ログインしていないユーザ
* **登録済みユーザ(B2C Login)** ・・・ B2C Commerce に登録済みユーザ
* **登録済みユーザ(Federated Login)** ・・・ Google や Facebook といったIDプロバイダーを利用して(Federated Login)ログインしたユーザ




## ショッパー系APIの呼び方
### 1. API クライアントの作成

 「[API クライアントの作成](https://qiita.com/syunyama/items/060f5777b0fc04e4cea1#1-api-%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90)」と同様なので割愛


### 2. スコープの設定

「[スコープの設定](https://qiita.com/syunyama/items/060f5777b0fc04e4cea1#ascapi-account-manager-%E3%81%A7%E3%81%AE%E8%A8%AD%E5%AE%9A)」と同じ手順だが、で **「データ」** ではなく、 **「ストアフロント」** を選択。


### 3. (Federated Login のために) Google OAuth2.0 の設定

:::note
現状 SLAS でサポートされている IDP は[こちら](https://developer.salesforce.com/docs/commerce/commerce-api/guide/slas-identity-providers.html)を確認してください。
:::

* [Google Cloud Platformの認証情報ページ](https://console.developers.google.com/apis/credentials)へ遷移
* 画面上部の **[+認証情報を作成]** をクリックし、リストから **[OAuthクライアントIDの作成}** をクリックし、タブからアプリケーションの種類として **「ウェブアプリケーション」** を選択。
* 任意の名前を入力し、さらに承認済みのリダイレクト URI に`https://{shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/idp/callback/{IDPID}`のフォーマットのURLを入力し保存します。
    * `IDPID` は **「4.(Federated Loginのために) IDP を登録」** で説明した IDP の登録時に設定する ID ですが、ここでは `google` としておきます。
    * `shortCode`、`tenantID`、`orgID`、`siteID`、`realmID` については 「[B2C Commerce に関わる ID を把握](https://qiita.com/syunyama/items/58433ae8e3c5fda7f37e#a--b2c-commerce-%E3%81%AB%E9%96%A2%E3%82%8F%E3%82%8B-id-%E3%82%92%E6%8A%8A%E6%8F%A1)」 を参照してください。

![image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/3b1ad53c-c83e-9027-3d26-b4d0f7480145.png)




### 4. (Federated Loginのために) IDP を登録

* 下記のようなリクエストを行います
    * `token` は [sfcc-ci にてトークンを取得](https://qiita.com/syunyama/items/58433ae8e3c5fda7f37e#c--sfcc-ci%E3%81%AB%E3%81%A6%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E5%8F%96%E5%BE%97)で取得したトークンを利用してください。
    * `IDPID` は `google` にします。（利用できる値は[API リファレンス](https://developer.salesforce.com/docs/commerce/commerce-api/references/slas-admin?meta=registerIdentityProvider)を確認してください。）
    * `googleClientID` と `googleClientSecret` は Google Cloud Platform で作成したクライアント ID のページ右上の「クライアント ID」と「クライアント シークレット」から確認または、「JSON をダウンロード」ボタンからそれらが記入された JSON をダンロードできます。

```http
REQUEST:
PUT /shopper/auth-admin/v1/tenants/{tenantID}/idps/{IDPID} HTTP/1.1
Host: {shortCode}.api.commercecloud.salesforce.com
Authorization: Bearer {token}
Content-Type: application/json

REQUEST BODY:
{
  "name": "{IDPID}",
  "clientId": "{googleClientID}",
  "clientSecret": "{googleClientSecret}",
  "authUrl": "https://accounts.google.com/o/oauth2/auth",
  "tokenInfoUrl": "https://www.googleapis.com/oauth2/v3/tokeninfo",
  "userInfoUrl": "https://www.googleapis.com/oauth2/v3/userinfo",
  "tokenUrl": "https://oauth2.googleapis.com/token",
  "redirectUrl": "https://{shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/idp/callback/{IDPID}",
  "preferenceValue": true
}
```





### 5. SLAS テナントの登録

「[Create a SLAS Client](https://developer.salesforce.com/docs/commerce/commerce-api/guide/authorization-for-shopper-apis.html#create-a-slas-client)」 を行ってください。
この際に、 **[Private?]** の値を **true** に **[Redirect URI]** の値にコールバックで処理する自身のアプリケーションのエンドポイントを設定してください。(例：`http://localhost:3000/callback`)

:::note
この時生成した `Client Id` と `Secret` をメモしておいてください。
:::


### 6. アクセストークンの取得

#### ゲストユーザ

![private1-7094a761-001e-44b4-9bd3-7e43e22c7d8a-62d61b45-b68e-4935-9593-2d05a0dd5088 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/6d08d3d1-ac33-b4b9-88d6-86b2c58398b7.png)


* 下記のようなリクエストを行い、レスポンスボディの中から `access_token` を取得します。
    * `credential` は **「5. SLASテナントの登録」** で取得した `clientId` と `secret` で、それらを `clientId:secret` のようにコロン(:)で結合したものを Base64 でエンコードした値です。

```http
REQUEST: 
POST /shopper/auth/v1/organizations/{orgID}/oauth2/token HTTP/1.1
Host: {shortCode}.api.commercecloud.salesforce.com 
Authorization: Basic {credential}
Content-Type: application/x-www-form-urlencoded 

REQUEST PARAMETER: 
grant_type=client_credentials
```



#### 登録済みユーザ(B2C Login)

![private3-dead9087-e8f4-415b-b77b-edddd6f0d7ee-f97d6619-2a1d-4f7e-ba29-f3bd0597f0c0 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/f240b386-5fa5-02b6-28d5-73ab0255dd66.png)



* まずは下記のようなリクエストでユーザーがログインをする。
    * `redirectUri` はコールバックで処理する自身のアプリケーションのエンドポイントを入力
    * `code_challenge` は `code_verifier` という 43 ～ 128 文字の文字列を SHA256 でハッシュ化したものを入力。（ロジックは公開されている [Postman コレクション](https://www.postman.com/salesforce-developers/workspace/salesforce-developers/request/15394816-99057296-4e2f-4474-8ff6-4374bb368f98)の Pre-request Script を参照）
    * `usid` はゲストユーザのトークン取得時のレスポンスボディにある `usid` に値を入力
    * `userCredential` は ユーザ ID / パスワード、それらを `id:password`のようにコロン(:)で結合したものを Base64 でエンコードした値です。

```http
REQUEST: 
POST /shopper/auth/v1/organizations/{orgID}/oauth2/login HTTP/1.1
Host: {shortCode}.api.commercecloud.salesforce.com 
Authorization: Basic {userCredential}
Content-Type: application/x-www-form-urlencoded 

REQUEST PARAMETER: 
grant_type=client_credentials
&client_id={clientID}
&redirect_uri={redirectUri}
&channel_id={siteID}
&code_challenge={codeChallenge}
&usid={usid}
```




* 上記のリクエストをすると HTTP ステータスが 303 でレスポンスヘッダーの `Location` に `{redirectUri}/callback?usid={}&state={}&scope=openid%20offline_access&code={}` のフォーマットでURLが入っているので、その URL にブラウザでリダイレクトさせる。
* 自身のアプリケーションで、`/callback` を実装し、`code` の値を利用して下記のリクエストを行い、レスポンスボディ内の `access_token` を取得
    * `code_verifier` は `code_challenge` 生成時に利用した文字列。（ロジックは公開されている[Postmanコレクション](https://www.postman.com/salesforce-developers/workspace/salesforce-developers/request/15394816-99057296-4e2f-4474-8ff6-4374bb368f98)の Pre-request Script を参照）
    * `code` はリダイレクト先 URL のパラメータに入っている `code` を入力

```http
REQUEST: 
POST /shopper/auth/v1/organizations/{orgID}/oauth2/token HTTP/1.1
Host: {shortCode}.api.commercecloud.salesforce.com 
Authorization: Basic {credential}
Content-Type: application/x-www-form-urlencoded 

REQUEST PARAMETER: 
grant_type=authorization_code_pkce
&client_id={clientID}
&redirect_uri={redirectUri}
&code_verifier={codeVerifier}
$code={code}
```





#### 登録済みユーザ(Federated Login)

![private-registered-user-federated-login-218978e3-33bf-4703-b2c8-a74a26601e28 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/f677446a-c121-c287-f951-37472a47823f.png)



* まずは下記のようなリクエストでユーザーがログインをする。
    * `IDPID` は **「4.(Federated Loginのために) IDP を登録」** で作成した `IDPID`。今回は `google`

```http
REQUEST: 
GET /shopper/auth/v1/organizations/{orgID}/oauth2/authorize
?redirect_uri={redirect_url}
&response_type=code
&client_id={clientID}
&hint={IDPID}
&channel_id={siteID}
&usid={usid} HTTP/1.1
Host: {shortCode}.api.commercecloud.salesforce.com
```

* 上記のリクエストをすると HTTP ステータスが 303 でレスポンスヘッダーの `Location` に`{https://accounts.google.com/o/oauth2/auth?scope=openid%20email%20profile&response_type=code&client_id={}&redirect_uri={}&state={}`のフォーマットで URL が入っているので、その URL にブラウザでリダイレクトさせる。
* すると画像のように Google のログイン画面に遷移するので、ログイン。

:::note warn
Google Cloud Platform の [OAuth 同意画面](https://console.cloud.google.com/apis/credentials/consent)ページで公開ステータスを「テスト」でテストユーザを設定している場合は、そのユーザでログインしてください。
:::

![スクリーンショット 2022-04-11 17.01.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/68b330a0-4c92-4e51-d6c4-af71879f9530.png)



* ログイン成功すると `http://localhost:3000/callback?usid={}&code={}`のフォーマットの URL にブラウザでリダイレクトさせる。
* 自身のアプリケーションで、`/callback` を実装し、`code` の値を利用して下記のリクエストを行い、レスポンスボディ内の `access_token` を取得

```http
REQUEST: 
POST /shopper/auth/v1/organizations/{orgID}/oauth2/token HTTP/1.1
Host: {shortCode}.api.commercecloud.salesforce.com 
Authorization: Basic {credential}
Content-Type: application/x-www-form-urlencoded 

REQUEST PARAMETER: 
grant_type=authorization_code
&redirect_uri={redirectUri}
&code={code}
```




### 7.  ショッパー系 API を呼ぶ

* 下記のようにサンプルとして `/baskets` を呼ぶ。
* `token` に上記 3 つのパターンのいずれかで取得したアクセストークンを入力しリクエストすると無事 200 で返ってくる。

:::note
その他のAPIについては[ SCAPI の API ドキュメント](https://developer.salesforce.com/docs/commerce/commerce-api/references?meta=assignments:Summary)の「Shopper〜」から始まっているものを参照ください。
:::

```http
REQUEST:
POST /checkout/shopper-baskets/v1/organizations/{orgID}/baskets
?siteId={siteID} HTTP/1.1
Host: {shortCode}.api.commercecloud.salesforce.com
Authorization: Bearer {token}
Content-Type: application/json

REQUEST BODY:
{}
```