Salesforce B2C CommerceのヘッドレスAPIを呼んでみる(ショッパー系API - OCAPI編) 

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

***
この記事はヘッドレス 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)
***

**今回は OCAPI を利用したショッパー系 API の呼び方を紹介したいと思います。**

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

この記事では、OCAPI で ３ つの顧客タイプが商品をカートに追加するユースケースとクッキーを利用したトークンの取得方法を説明します。


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




## ショッパー系APIの呼び方


### 1. API クライアントの作成
### 2. スコープの設定

１、２ は、「[ショッパー系API - SCAPIプライベートクライアント編](https://qiita.com/syunyama/items/34bd2a11dabb93bb1dc1)」と同じなので割愛

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

手順は「[3.(Federated Login のために) Google OAuth2.0 の設定](https://qiita.com/syunyama/items/34bd2a11dabb93bb1dc1#3-federated-login-%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AB-google-oauth20-%E3%81%AE%E8%A8%AD%E5%AE%9A)」 と同じだが、リダイレクト URL にはコールバックで処理する自身のアプリケーションのエンドポイントを設定してください。(例：`http://localhost:3000/callback`)

### 4. アクセストークンを取得

#### ゲストユーザ

![ocapi-guest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/e9ac47d7-e26c-aa58-75b2-c0d2c4650c02.png)


* 下記のようなリクエストを行い、レスポンスヘッダーの `Authorization` に `Bearer {token}` のフォーマットで値が入っています。
    * `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)」 を参照してください。
    * `clientID` は **「1.API クライアントの作成」** で作成した API クライアントの ID です。

```http
REQUEST:
POST /s/{siteID}/dw/shop/v22_4/customers/auth?client_id={clientID} HTTP/1.1
Host: {realmID}.dx.commercecloud.salesforce.com
Content-Type: application/json

REQUEST BODY:
{
    "type": "guest"
}
```



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


![ocapi-registered.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/2495971c-00fb-cab3-62c5-50cb43ca1c18.png)



* 下記のようなリクエストを行い、レスポンスヘッダーの `Authorization` に `Bearer {token}` のフォーマットで値が入っています。
    * `userCredential` は ユーザ ID /パスワード、それらを `ID:password`のようにコロン(:)で結合したものを Base64 でエンコードした値です。

```http
REQUEST:
POST /s/{siteID}/dw/shop/v22_4/customers/auth?client_id={clientID} HTTP/1.1
Host: {realmID}.dx.commercecloud.salesforce.com
Authorization: Basic {userCredential}
Content-Type: application/json

REQUEST BODY: 
{
    "type": "credentials"
}
```



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

![ocapi-federated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/9cf55a89-b433-0829-4fc5-71b444dbd93a.png)


* まずはゲストユーザのフローを使ってゲストトークンを取得する。
* まずは下記のようなリクエストをブラウザから行う。
    * `googleClientID` は Google Cloud Platform で作成したクライアント ID のページ右上の **[クライアント ID]** から確認または、 **[JSONをダウンロード]** ボタンからそれらが記入された JSON をダンロードできます。
    * `redirectUri` はコールバックで処理する自身のアプリケーションのエンドポイントを入力

```http
REQUEST: 
GET /o/oauth2/v2/auth
?response_type=code
&scope=openid%20email%20profile
&redirect_uri={redirect_url}
&client_id={googleClientID} HTTP/1.1
Host: accounts.google.com
```



* ブラウザからリクエストを行ったら下の画像のように Google のログイン画面に遷移するので、ログイン。

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

![スクリーンショット 2022-04-11 17.01.22 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/ee0ff894-6f66-2951-576b-177c588cdd32.png)

* ログイン成功すると `{redirect_url}?code={}&scope={}&prompt=consent` のフォーマットの URL にブラウザでリダイレクトされる。
* 自身のアプリケーションで、`/callback` を実装し、`code` の値を利用して下記のリクエストを行い、レスポンスボディ内の `access_token` を取得
    * `code` はリダイレクト先 URL のパラメータに入っている `code` を入力
    * `googleClientSecret` は Google Cloud Platform で作成したクライアント ID のページ右上の **[クライアント ID]** から確認または、 **[JSONをダウンロード]** ボタンからそれらが記入された JSON をダンロードできます。

```http
REQUEST: 
POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded 

REQUEST PARAMETER: 
grant_type=authorization_code
&redirect_uri={redirectUri}
&code={code}
&client_id={googleClientID}
&client_secret={googleClientSecret}
```

* 取得した `access_token` を利用して下記のようにユーザ情報を取得します。
```http
REQUEST: 
GET /oauth2/v3/userinfo HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer {token}
```



* 取得したユーザ情報を B2C Commerce に登録するために Account Manager のアクセストークンを取得します。取得方法については、「[データ系API編](https://qiita.com/syunyama/items/060f5777b0fc04e4cea1#3-%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AE%E5%8F%96%E5%BE%97)」と同様なので、割愛します。
* そのアクセストークンを利用して下記のようにリクエストし、すでに登録済みではないか確認します。登録済みの場合、ユーザ情報と共にステータス 200 が返ってきます。
    * `providerId` は任意の ID プロバイダーを示す一意の ID です。（例：`google`）
    * `externalId` は ID プロバイダーから取得したユーザ情報の ID です。今回は Google のユーザ情報内のEメールを利用します。

```http
REQUEST: 
GET /s/{siteID}/dw/shop/v22_4/customers/ext_profile
?authentication_provider_id={providerId}
&external_id={externalId} HTTP/1.1
Host: {realmID}.dx.commercecloud.salesforce.com
Authorization: Bearer {token}
```



* （もし登録されていなかった場合）下記のリクエストを送り、ユーザ情報を登録します。ユーザ情報と共にステータス 200 が返ってきます。
    * `email`, `given_name`, `family_name` は ID プロバイダーから取得したユーザ情報のEメールです。今回は Google のユーザ情報内の`email`, `given_name`, `family_name` を利用します。

```http
REQUEST:
POST /s/{siteID}/dw/shop/v22_4/customers/ext_profile HTTP/1.1
Host: {realmID}.dx.commercecloud.salesforce.com
Authorization: Bearer {token}
Content-Type: application/json

REQUEST BODY:
{
 "authentication_provider_id" : "{providerId}",
 "external_id" : "{email}",
 "email" : "{email}",
 "first_name" : "{given_name}",
 "last_name" : "{family_name}"
}
```



* ユーザ情報の登録情報確認、または登録が完了したら、登録したユーザ情報をもとに下記のようなリクエストを送りトークンを取得します。

```http
REQUEST:
POST /s/{siteID}/dw/shop/v22_4/customers/auth/trustedsystem HTTP/1.1
Host: {realmID}.dx.commercecloud.salesforce.com
Authorization: Bearer {token}
Content-Type: application/json

REQUEST BODY:
{
  "login" : "{providerId}-{externalId}",
  "client_id": "{clientID}"
}
```



#### クッキーを利用したトークンの取得方法（セッションブリッジ）

![ocapi-session.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2110894/8090eb1e-dc54-07bb-aee9-ff32bd2e29da.png)


ここでは、ゲスト・登録済みユーザ・ Federated ログインユーザ問わず、クッキーを利用したトークンの取得方法を紹介します。

* 下記のようなリクエストを行うと、レスポンスヘッダー内の `Set-Cookie` に必要な値がセットされて返ってくる。
    * `token` は上記で説明済みの、ゲスト・登録済みユーザ・ Federated ログインユーザのいずれかのフローで取得したトークン

```http
REQUEST:
POST /s/{siteID}/dw/shop/v22_4/sessions HTTP/1.1
Host: {realmID}.dx.commercecloud.salesforce.com
Authorization: Bearer {token}
```



* 下記のようなリクエストを行い、レスポンスヘッダーの `Authorization` に `Bearer {token}` のフォーマットで値が入っています。
    * `cookies` は上記リクエストで `Set-Cookie` に入っていたの値

```http
REQUEST:
POST /s/{siteID}/dw/shop/v22_4/customers/auth?client_id={clientID} HTTP/1.1
Host: {realmID}.dx.commercecloud.salesforce.com
Content-Type: application/json
Cookie: {cookies}

BODY:
{
    "type": "session"
}
```





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

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

:::note
その他のAPIについては[ OCAPI の API ドキュメント](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-commerce-ocapi/opencommerceapi.html)の Shop API をご参照ください。
:::

```http
REQUEST:
POST /s/{siteID}/dw/shop/v22_4/baskets HTTP/1.1
Host: {realmID}.dx.commercecloud.salesforce.com
Authorization: Bearer {token}
Content-Type: application/json

BODY:
{}
```