0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C#にNetSuiteのWebServiceを追加する

Last updated at Posted at 2022-11-25

業務でNetSuiteのWebServiceをC#に追加することになったけど、英語のドキュメントばかりで大変だったので記録。
コードはコピペと手打ちなので、ミスがあればご指摘いただければと思います。

前提

  • 認証方法はTBA(Token-Based Authentication)方式
  • 認証に必要なAccount, Token id, Token secret, Consumer key, Consumer secretの発行などは正しくされているとする
  • C#は.NET Framework 4.7.2

1. Postmanを使って確認

Postmanを使ってAPI叩けるかを確認します。
認証用のtokenとかが正しいかを確認できます。

1-1. サンプルアプリをダウンロード

以下のURLのAccountIDを入力し、サンプルアプリをダウンロードします。

https://<accountID>.app.netsuite.com/app/external/integration/integrationDownloadPage.nl

1-2. Postmanにサンプルアプリをインポート

Postmanはインストールしなくても使えるので、ブラウザで進めます。
左上の「import」からインポートできます。
image.png
インポートできると「NetSuite REST API Tutorial」のコレクションが作成されます。
image.png

1-3. 認証情報を追加

追加された「NetSuite REST API Tutorial」をクリックし、Authorizationを編集します。
画像と同じものを選択しつつ、Account, Token id, Token secret, Consumer key, Consumer secretを設定していきます。
image.png
image.png

オレンジ色の「{{xxx}}」は変数です。
「Variables」タブに切り替えれば、変数名の宣言と値の設定が可能です。
image.png

1-4. TBA認証で通過できるかを確認する

「0 Test」の「Example 0.1: Test Request」を開きます。
「REST_SERVISE」変数に「https://.suitetalk.api.netsuite.com/services/rest」を入れ、Sendを押します。
200番台のレスポンスが返ってくれば認証は通過できています。
image.png

2. .NETのサンプルアプリを実行

公式が用意している.NET用のサンプルアプリをダウンロードして、実行してみます。

2-1. .NETのサンプルアプリをダウンロードする

以下のサイトからサンプルアプリをダウンロードします。
リンクをクリックしてもダウンロードできないので、リンクをコピーしてから、ブラウザのアドレスバーに直接貼り付けてください。

2-2. サンプルアプリを修正する

サンプルアプリを実行するため、2つのファイルを修正します。

App.config
// 省略
  <appSettings>
    <!-- If set to false, login parameter values will be used from below. If set
         to true, the user will be prompted for values.  -->
    <add key="promptForLogin" value="false"/>
    <!-- Login field values -->
    <add key="login.email" value="EMAIL"/>
    <add key="login.password" value="PASSWORD"/>
    <add key="login.roleNSkey" value="3"/>
    <add key="login.acct" value="ACCOUNT"/>
    <add key="login.appId" value="8CA80836-4422-4A14-B91D-B386AE9815FD"/>
    <add key="login.useTba" value="false"/>
    <add key="login.tbaConsumerKey" value="CONSUMERKEY"/>
    <add key="login.tbaConsumerSecret" value="CONSUMERSECRET"/>
    <add key="login.tbaTokenId" value="TOKENID"/>
    <add key="login.tbaTokenSecret" value="TOKENSECRET"/>
  </appSettings>
// 省略

login.useTbaをtrueにし、ACCOUNT, CONSUMERKEY, CONSUMERSECRET, TOKENID, TOKENSECRETを指定します。
TBA認証を使うので、その他の項目は変更しなくて大丈夫です。

NSClient.cs
namespace NSClient
{
    public class NSClient
    {
        // 省略

		private TokenPassportSignature ComputeSignature(string compId, string consumerKey, string consumerSecret,  
										string tokenId, string tokenSecret, string nonce, long timestamp)
		{
			string baseString = compId + "&" + consumerKey + "&" + tokenId + "&" + nonce + "&" + timestamp;
			string key = consumerSecret + "&" + tokenSecret;
			string signature = "";
			var encoding = new System.Text.ASCIIEncoding();
			byte[] keyBytes = encoding.GetBytes(key);
			byte[] baseStringBytes = encoding.GetBytes(baseString);
            // HMAC-SHA1 -> HMAC-SHA256 に変更
			using (var hmacSha256 = new HMACSHA256(keyBytes))
			{
				byte[] hashBaseString = hmacSha1.ComputeHash(baseStringBytes);
				signature = Convert.ToBase64String(hashBaseString);
			}
			TokenPassportSignature sign = new TokenPassportSignature();
            // ここも HMAC-SHA1 -> HMAC-SHA256 に変更
			sign.algorithm = "HMAC-SHA256";
			sign.Value = signature;
			return sign;
		}

        // 省略
    }
}

2-3. サンプルアプリを実行する

VS 2022を管理者として実行し、NSClient.slnを開き、実行してみます。
カスタマ取得をしてみます。
Internal IDが一致すると、画像のように値を取得できます。
image.png

3. C#アプリにNetSuiteのWebServiceを追加

サンプルアプリを参考に、C#アプリに必要な処理を移植していきます。

3-1. サービス参照からWebServiceを追加する

ソリューションを右クリックし、「追加 > サービス参照」をクリックします。
次に左下の「詳細設計」をクリック。
image.png
「Web参照の追加」をクリック。
image.png
URLに「https://webservices.netsuite.com/wsdl/v2021_2_0/netsuite.wsdl 」を入力し、「参照の追加」をクリックして追加します。
image.png

次にTBA方式の認証をするにあたって必要なコードをサンプルアプリから移植。
とりあえずNSClient.csファイルを作成します。

NSClient.cs
using xxx.com.netsuite.WebService;
// 省略

namespace xxx.yyy
{
    public class NSClient
    {
        private NetSuiteService _service;
        
        public NetSuiteService Service {
            get
            {
                _service.tokenPassport = CreateTokenPassport();
                return _service;
            }
        }
        
        public bool IsAuthenticated { get; private set; }
        
        /// <summary>
        /// Constructor
        /// </summary>
        public NSClient()
        {
            IsAuthenticated = false;
            _service = new DataCenterAwareNetSuiteService();
            _service.Timeout = 1000 * 60 * 60 * 2;
        }
    
        public TokenPassport CreateTokenPassport()
        {
            string account = "Your Account";
            string consumerKey = "Your ConsumerKey";
            string consumerSecret = "Your ConsumerSecret";
            string tokenId = "Your TokenId";
            string tokenSecret = "Your TokenSecret";
                        
            string nonce = ComputeNonce();
            long timestamp = ComputeTimestamp();
            TokenPassportSignature signature = ComputeSignature(account, consumerKey, consumerSecret, tokenId, tokenSecret, nonce, timestamp);
    
            TokenPassport tokenPassport = new TokenPassport();
            tokenPassport.account = account;
            tokenPassport.consumerKey = consumerKey;
            tokenPassport.token = tokenId;
            tokenPassport.nonce = nonce;
            tokenPassport.timestamp = timestamp;
            tokenPassport.signature = signature;
            return tokenPassport;
        }
    
        private string ComputeNonce()
        {
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] data = new byte[20];
            rng.GetBytes(data);
            int value = Math.Abs(BitConverter.ToInt32(data, 0));
            return value.ToString();
        }
    
        private long ComputeTimestamp()
        {
            return ((long) (DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
        }

        private TokenPassportSignature ComputeSignature(string compId, string consumerKey, string consumerSecret,  
										string tokenId, string tokenSecret, string nonce, long timestamp)
		{
			string baseString = compId + "&" + consumerKey + "&" + tokenId + "&" + nonce + "&" + timestamp;
			string key = consumerSecret + "&" + tokenSecret;
			string signature = "";
			var encoding = new System.Text.ASCIIEncoding();
			byte[] keyBytes = encoding.GetBytes(key);
			byte[] baseStringBytes = encoding.GetBytes(baseString);
			using (var hmacSha256 = new HMACSHA256(keyBytes))
			{
				byte[] hashBaseString = hmacSha1.ComputeHash(baseStringBytes);
				signature = Convert.ToBase64String(hashBaseString);
			}
			TokenPassportSignature sign = new TokenPassportSignature();
			sign.algorithm = "HMAC-SHA256";
			sign.Value = signature;
			return sign;
		}

        public String GetStatusDetails(Status status)
		{
			System.Text.StringBuilder sb = new System.Text.StringBuilder();
			for (int i=0; i < status.statusDetail.Length; i++)
			{
				sb.Append( "[Code=" + status.statusDetail[i].code + "] " + status.statusDetail[i].message + "\n" );
			}
			return sb.ToString();
		}
    }

    class DataCenterAwareNetSuiteService : NetSuiteService
    {
        private System.Uri OriginalUri;

        public DataCenterAwareNetSuiteService()
            : base()
        {
            OriginalUri = new System.Uri(this.Url);
            string account = "Your Account";
            DataCenterUrls urls = getDataCenterUrls(account).dataCenterUrls;
            Uri dataCenterUri = new Uri(urls.webservicesDomain + OriginalUri.PathAndQuery);
            this.Url = dataCenterUri.ToString();
        }
    }

    class NSBase
    {
        protected static NSClient _client;

        public static NSClient Client
        {
            get
            {
                if (_client == null)
                {
                    _client = new NSClient();
                }
                return _client;
            }
            set
            {
                _client = value;
            }
        }
    }

    // NSEntity.csから移植
    class NSEntity : NSBase
    {
        // internalIdが一致するCustomerを一件取得
        public static string GetCustomer(string internalId)
        {
            RecordRef recordRef = new RecordRef();
            recordRef.internalId = internalId;
            // recordRef.type:テーブルの指定
            recordRef.type = RecordType.customer;
            recordRef.typeSpecified = true;

            ReadResponse response = Client.Service.get(recordRef);

            Customer customer = (Customer)response.record;
            ProcessCustomerReadResponse(response, customer);
        }

        // internalIdが一致するCustomerを全権取得
        public static List<string> getCustomerList(string[] internalIds)
        {
            RecordRef[] recordRef = new RecordRef[internalIds.Length];
            for (int i = 0; i < internalIds.Length; i++)
            {
                RecordRef recordRef = new RecordRef();
                recordRef.internalId = internalIds[i];
                recordRef.type = RecordType.customer;
                recordRef.typeSpecified = true;
            }
            ReadResponse[] getResponse = Client.Service.getList(recordRefs).readResponse;

            var returnStringList = new List<string>();
            for (int i = 0; i < getResponse.Length; i++ )
            {
                returnStringList.Add(ProcessCustomerReadResponse(getResponse[i], internalIds[i]));
            }
            return returnStringList;
        }

        private static bool ProcessCustomerReadResponse(ReadResponse response, Customer customer)
        {
            // Process the response
            if (response.status.isSuccess)
            {
                return (
                    "    internalId=" + customer.internalId +
                    "\n    entityId=" + customer.entityId +
                    (customer.companyName == null ? "" : ("\n    companyName=" + customer.companyName)) +
                    (customer.entityStatus == null ? "" : ("\n    status=" + customer.entityStatus.name)) +
                    (customer.email == null ? "" : ("\n    email=" + customer.email)) +
                    (customer.phone == null ? "" : ("\n    phone=" + customer.phone)) +
                    "\n    isInactive=" + customer.isInactive +
                    (!customer.dateCreatedSpecified ? "" : ("\n    dateCreated=" + customer.dateCreated.ToShortDateString())));
            }
            else
            {
                return Client.GetStatusDetails(response.status);
            }
        }
    }
}

あとは「NSEntity.GetCustomer(internalId)」のように呼べばOK

4. その他

4-1. WebServiceのリリース状況について

WebServiceのリリース状況に関しては、documentationタグの値を確認してください。
2021のWebService.png
2022のWebService.png

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?