見出し画像

REST APIでSalesforceのApexクラスを呼ぶ

自前のWebサーバーに設置したフォームで登録された情報をSaleforceに登録するにはどうしたらいいでしょうか?

・Webサーバー側でフォーム登録時に定型メールを作成し、Salesforceのメールサービス機能で連携する
・SalesforceのREST APIまたはSOAP APIを使い、Webサーバー側でフォーム登録時にAPI経由で登録する
・Salesforceのメールtoリード、メールtoケースの機能を利用する
・Pardotを導入する

どの方法でも可能ですが、フォームの見栄えは重視し、かつ、あまり費用を掛けずに実現するには、前の1つが候補となります。掛かる手間は同じくらいです。

今時、システム間の連携と言えば、REST APIを使うことが多いので、2番目の方法をご紹介したいと思います。

証明書を作成する

SalesforceのREST APIを使うには、事前に認証を行う必要があります。認証の方法はいくつかありますが、サーバー同士の連携を実現したいので、JWTを使ったOAuth連携を使います。

そのためには、デジタル証明書が必要です。openssl コマンドで自己証明書を作成できますので、それを利用します。

# 証明書を保存するフォルダを作成する
>> mkdir cert
>> cd cert

# RSA非公開鍵を生成する
>> openssl genrsa -des3 -passout pass:<<任意のパスワード>> -out server.pass.key 2048
Generating RSA private key, 2048 bit long modulus
........................+++
..................................................+++
e is 65537 (0x10001)

# 鍵ファイルを作成する
>> openssl rsa -passin pass:<<任意のパスワード>> -in server.pass.key -out server.key
writing RSA key

# RSA非公開鍵ファイルを削除する
>> rm server.pass.key 

# 証明書を作成する
>> openssl req -new -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:Japan
string is too long, it needs to be less than  2 bytes long
Country Name (2 letter code) []:jp
State or Province Name (full name) []:iwate
Locality Name (eg, city) []:takizawa
Organization Name (eg, company) []:pitadigi
Organizational Unit Name (eg, section) []:.
Common Name (eg, fully qualified host name) []:.
Email Address []:.

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:

# SSL証明書を作成する
>> openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
Signature ok
subject=/C=jp/ST=iwate/L=takizawa/O=pitadigi
Getting Private key

# 作成したファイルを確認する
>> ls
server.crt      server.csr      server.key

Salesforceで設定する

接続アプリケーションを作成する

次にSalesforceで接続アプリケーションを作成します。設定-アプリケーション-アプリケーションマネージャーを開き、新規接続アプリケーションをクリックします。

画像1

OAuth設定の有効化

ON

コールバックURL

JWTの場合は不要ですが、必須項目なので「https://localhost:8443/RestTest/oauth/_callback」でOK

デジタル署名を使用

ONにして作成した証明書(server.crt)を選択

選択したOAuth範囲

・データのアクセスと管理
・ユーザに代わっていつでも要求を実行

Webサーバフローの秘密が必要

ON

接続アプリケーションを設定する

作成した接続アプリケーションの設定を変更します。設定-アプリケーション接続アプリケーション-接続アプリケーションを管理するを選択します。

作成した接続アプリケーションを開き、以下の設定を行います。

画像2

許可されているユーザ

サーバー同士の連携で利用しますので、使用するユーザは限定した方がいいため、管理者が承認したユーザのみで利用できるようにします。
「管理者が承認したユーザは事前承認済み」を選択します。

プロファイル

使用するユーザが属しているプロファイルを選択します。ここではシステム管理者としていますが、より特定するには専用のプロファイルを作成した方がいいです。

Apexクラスを呼び出す側のプログラムを準備する

REST APIでApexクラスを呼び出す手順は以下の通りです。

・作成した秘密鍵を使ってJWTを作成する
・作成したJWTを使ってSalesforceに認証を要求しアクセストークンを取得する
・取得したアクセストークンを使ってApexクラスを呼び出す

今回はPHPで実装してみます(全く使ったことはありませんが・・・)

JWTを作成する

JWTを作成するためのライブラリは、lcobucci/jwtを使います。

必要なライブラリをインストールする

composerを使って、必要なライブラリをインストールします。

>> composer require lcobucci/jwt

JWTを作るコードを書く

JWTを作るコードを書きます。

SalesforceのヘルプにJWTに関するものがあります。ここを見るとJWTのペーロードに何を設定するかが書いてあります。

これを見ながらJWTを作成します。ログインURLは使用する環境によって異なります。

<?php

require_once './vendor/autoload.php';

use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;

// ログインURL
// 本番: https://login.salesforce.com
// Sandbox: https://test.login.salesforce.com
// スクラッチ組織: https://test.saleforce.com
define('LOGIN_URL', 'https://test.salesforce.com');
// コンシューマ鍵
define('CLIENT_ID', <<接続アプリケーションのコンシューマ鍵>>);
// ユーザID
define('USER_ID', 'xxxxx@example.com');

function createjwt() {

   $signer = new Sha256();
   $privateKey = new Key('file://cert/server.key');
   $time = time();
   
   $token = (new Builder())->issuedBy(CLIENT_ID) // iss: コンシューマ鍵
                           ->permittedFor(LOGIN_URL) // aud: SalesforceログインURL
                           ->relatedTo(USER_ID) // sub: SalesforceユーザID
                           ->expiresAt($time + 3 * 60) // exp: 3分以内
                           ->getToken($signer,  $privateKey);

   return $token;
}

$jwt = createjwt();

echo $jwt;

Salesforceにアクセスする

認証を行う

作成したJWTを使ってSalesforceに認証を投げるコードを書きます。

REST APIを呼び出すにはいくつかの認証がサポートされています。JWTを使うメリットは以下の通りです。

・ユーザのパスワードを使わないため、セキュアな情報を渡す必要がない
・証明書を使うため、パスワードよりセキュア
・標準的なテクノロジーを使うので、他のAPIを使う時に役立つ

これで、アクセスするURLと使用するBearerトークンが取得できます。

<?php

require_once './vendor/autoload.php';

use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;

// ログインURL
// 本番: https://login.salesforce.com
// Sandbox: https://test.login.salesforce.com
// スクラッチ組織: https://test.saleforce.com
define('LOGIN_URL', 'https://test.salesforce.com');
// 認証URL
define('AUTH_URL', LOGIN_URL . '/services/oauth2/token');
// コンシューマ鍵
define('CLIENT_ID', <<接続アプリケーションのコンシューマ鍵>>);
// ユーザID
define('USER_ID', 'xxxxxxx@example.com');
// 認証タイプ
define('GRANT_TYPE', 'urn:ietf:params:oauth:grant-type:jwt-bearer');


function createjwt() {

   $signer = new Sha256();
   $privateKey = new Key('file://cert/server.key');
   $time = time();
   
   $token = (new Builder())->issuedBy(CLIENT_ID) // iss: コンシューマ鍵
                           ->permittedFor(LOGIN_URL) // aud: SalesforceログインURL
                           ->relatedTo(USER_ID) // sub: SalesforceユーザID
                           ->expiresAt($time + 3 * 60) // exp: 3分以内
                           ->getToken($signer,  $privateKey);

   return $token;
}

function auth() {
   $jwt = createjwt();

   $post = array(
       'grant_type' => GRANT_TYPE,
       'assertion' => $jwt,
   );

   $curl = curl_init();
   curl_setopt( $curl, CURLOPT_URL, AUTH_URL );
   curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 );
   curl_setopt( $curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
   curl_setopt( $curl, CURLOPT_POSTFIELDS, $post );
   $buf = curl_exec( $curl );
   if ( curl_errno( $curl ) ) {
       exit;
   }
   curl_close( $curl );
   
   $json = json_decode( $buf );

   $accinfo = array(
       // アクセスするためのURL
       'instance_url' => $json->instance_url,
       // アクセスするために使用するBearerトークン
       'access_token' => $json->access_token,
   );

   return $accinfo;
}

$accinfo = auth();

Apexクラスを呼び出す

取引先責任者に登録するApexクラスを作成します。

先頭で@RestResource(urlMapping='/resttest/*')と宣言します。これで認証で取得したURL+@RestResource(urlMapping='/resttest/*')でアクセスできます。

呼び出すメソッドは@HttpPostアノテーションで宣言します。パラメータは1項目ずつがを独立して宣言します。REST APIで呼び出す時は、JSONでパラメータを受け渡します。

{
  "firstname": "苗字",
  "lastname": "名",
  "email": "Eメールアドレス"
}
// アクセスするURL(/services/apexrest/resttest/)
@RestResource(urlMapping='/resttest/*')
global with sharing class RestTest {
   // POSTで呼び出すメソッド
   // パラメータはjsonで渡す
   @HttpPost
   global static String doPost(
       String firstname,   // 名
       String lastname,    // 苗字
       String email        // Eメール
   ) {
       // 取引先オブジェクトを作成する
       Contact con = new Contact();

       // 指定されたパラメータを設定する
       con.FirstName = firstname;
       con.LastName = lastname;
       con.Email = email;
   
       // 取引先責任者を保存する
       try {
           upsert con;
       }
       catch(DmlException e) {
           return e.getMessage();
       }

       return 'OK';
   }
}

Apexクラスを呼び出すには、認証で取得したURLとアクセストークンを使います。

ヘッダー

Content-Type: application/json;charset=UTF-8
Authorization: Bearer 取得したアクセストークン

URL

取得したURL /services/apexrest/resttest/

    $url = $accinfo['instance_url'] . '/services/apexrest/resttest/';

   $data = array(
       'firstname' => 'Koji',
       'lastname' => 'Matae',
       'email' => 'test@test.jp',
   );

   $header = array(
       'Content-Type: application/json;charset=UTF-8',
       'Authorization: Bearer ' . $accinfo['access_token'],
   );

   $curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, $url);
   curl_setopt($curl, CURLOPT_HEADER, false);
   curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
	curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
	curl_setopt($curl, CURLOPT_POST, true);
   curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
   curl_setopt($curl, CURLOPT_RETURNTRANSFER, true );
   curl_setopt($curl, CURLOPT_VERBOSE, true);
	$result = curl_exec($curl);
   curl_close($curl);
   
   echo var_dump($result);

これで、REST API経由で取引先を登録するApexクラスを呼び出す事ができます。

REST API経由でSOQLを実行してデータを取得、更新することも可能です。

SOQLを直接呼び出すと、REST API経由のアクセスのバリエーションが必要以上に増え、メンテナンス性が低下する可能性があります。

こうした状況を避けるためにも、Apexクラスを作成し、その中で必要な処理を書いてREST APIで呼び出すことをオススメします。

最後に、PHPの全ソースです。

<?php

require_once './vendor/autoload.php';

use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;

// ログインURL
// 本番: https://login.salesforce.com
// Sandbox: https://test.login.salesforce.com
// スクラッチ組織: https://test.saleforce.com
define('LOGIN_URL', 'https://test.salesforce.com');
// 認証URL
define('AUTH_URL', LOGIN_URL . '/services/oauth2/token');
// コンシューマ鍵
define('CLIENT_ID', <<コンシューマ鍵>>);
// ユーザID
define('USER_ID', 'test@example.com');
// 認証タイプ
define('GRANT_TYPE', 'urn:ietf:params:oauth:grant-type:jwt-bearer');

function createjwt() {

   $signer = new Sha256();
   $privateKey = new Key('file://cert/server.key');
   $time = time();
   
   $token = (new Builder())->issuedBy(CLIENT_ID) // iss: コンシューマ鍵
                           ->permittedFor(LOGIN_URL) // aud: SalesforceログインURL
                           ->relatedTo(USER_ID) // sub: SalesforceユーザID
                           ->expiresAt($time + 3 * 60) // exp: 3分以内
                           ->getToken($signer,  $privateKey);

   return $token;
}

function auth() {
   $jwt = createjwt();

   $post = array(
       'grant_type' => GRANT_TYPE,
       'assertion' => $jwt,
   );

   $curl = curl_init();
   curl_setopt( $curl, CURLOPT_URL, AUTH_URL );
   curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 );
   curl_setopt( $curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
   curl_setopt( $curl, CURLOPT_POSTFIELDS, $post );
   $buf = curl_exec( $curl );
   if ( curl_errno( $curl ) ) {
       exit;
   }
   curl_close( $curl );
   
   $json = json_decode( $buf );

   $accinfo = array(
       // アクセスするためのURL
       'instance_url' => $json->instance_url,
       // アクセスするために使用するBearerトークン
       'access_token' => $json->access_token,
   );

   return $accinfo;
}

function callapex($accinfo){
   $url = $accinfo['instance_url'] . '/services/apexrest/resttest/';

   $data = array(
       'firstname' => 'Koji',
       'lastname' => 'Matae',
       'email' => 'test@test.jp',
   );

   $header = array(
       'Content-Type: application/json;charset=UTF-8',
       'Authorization: Bearer ' . $accinfo['access_token'],
   );

   $curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, $url);
   curl_setopt($curl, CURLOPT_HEADER, false);
   curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
	curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
	curl_setopt($curl, CURLOPT_POST, true);
   curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
   curl_setopt($curl, CURLOPT_RETURNTRANSFER, true );
   curl_setopt($curl, CURLOPT_VERBOSE, true);
	$result = curl_exec($curl);
   curl_close($curl);
   
   echo var_dump($result);
}

$accinfo = auth();

callapex($accinfo);


この記事が気に入ったらサポートをしてみませんか?