You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby脚本调用NetSuite接口返回401无效登录,但Postman请求正常的问题排查求助

Ruby脚本调用NetSuite接口返回401无效登录,但Postman请求正常的问题排查求助

我尝试用Ruby脚本获取销售订单的状态,但NetSuite返回了Invalid login attempt (401)错误。不过用Postman请求同一个接口却能得到正确的200响应结果,有没有大佬能帮我找找代码里的问题?

netsuite.rb

require 'openssl'
require 'base64'
require 'securerandom'
require 'uri'
require 'net/https' # Ensures HTTPS is used
require 'json'
require_relative 'oauth_testf'
require 'erb'

module Buyplans
  module OrderStatus
    module StatusServices
      class Netsuite
        attr_reader :account, :params, :http_request_method, :oauth, :url
              
        CONSUMER_KEY = "190bc7de20cf6b3c8baa3992e974f1b5bbd864a0ca1ad1a2fd3969675861f55d"
        CONSUMER_SECRET = "04a695a183ececb95ace384ac7bf35dab33b6d925990a8a5afdbeebeba9146ff"
        TOKEN_ID = "b7609df83213ba4ff45e0eb2b033ad644a143453eca6b8c3e1a3a3cc5cedfc40"
        TOKEN_SECRET = "e1696cd286a6b9d993c625c657a3fd004b576d26f2232dd3bb5a9815c1ae6656"
        BASE_URL = "https://TD2951254.suitetalk.api.netsuite.com" # HTTPS URL

        def fetch_status(buyplan, sales_log)
          url = "#{BASE_URL}/services/rest/record/v1/salesOrder/3?expandSubResources=true"
          response = make_request(url)
          puts "Response from make_request: #{response.code} - #{response.message}"
          $stdout.flush

          if response.code == '200'
            parsed_body = JSON.parse(response.body)
            puts "Parsed Response Body: #{parsed_body}"
            $stdout.flush
            parsed_body
          else
            error_message = { error: "Request failed with status #{response.code}", body: response.body }
            puts "Error Message: #{error_message}"
            $stdout.flush
            error_message
          end
        end

        private

        def make_request(url)
          # Initialize OAuth with credentials
          @oauth = Oauth.new(
            consumer_key: CONSUMER_KEY,
            consumer_secret: CONSUMER_SECRET,
            token_id: TOKEN_ID,
            token_secret: TOKEN_SECRET,
            account: "TD2951254",
            method_type: 'GET',
            url: url,
            params: {}
          )

          # Parse URL and enforce HTTPS
          uri = URI.parse(url)
          http = Net::HTTP.new(uri.host, uri.port)
          http.use_ssl = true # Enforce SSL/TLS
          http.verify_mode = OpenSSL::SSL::VERIFY_PEER # Verify SSL certificate

          # Set up the GET request with headers
          request = Net::HTTP::Get.new(uri)
          request['Authorization'] = oauth.authorization_string
          request['Accept'] = 'application/json'
          request['Host'] = uri.host
          request['Content-Type'] = 'application/json'
          
          # Debugging output for request components
          puts "Request URL: #{uri}"
          puts "Authorization Header: #{request['Authorization']}"
          puts "All Headers: #{request.to_hash}"
          $stdout.flush
          
          # Execute the HTTPS request
          response = http.request(request)
          puts "Response Code: #{response.code}"
          puts "Response Body: #{response.body}"
          $stdout.flush
          response
        end
      end
    end
  end
end

# Example invocation
netsuite = Buyplans::OrderStatus::StatusServices::Netsuite.new
netsuite.fetch_status("buyplan_sample", "sales_log_sample")

oauth.rb

class Oauth
  require 'openssl'
  require 'base64'
  require 'erb'
  require 'cgi'

  attr_reader :consumer_key, :consumer_secret, :token_id, :token_secret,
              :account, :url_text, :oauth_timestamp, :oauth_nonce,
              :params, :http_request_method

  def initialize(consumer_key:, consumer_secret:, token_id:, token_secret:,
                 account:, method_type:, url:, params:)
    @consumer_key = consumer_key
    @consumer_secret = consumer_secret
    @token_id = token_id
    @token_secret = token_secret
    @account = account
    @url_text = url
    @params = params || {}
    @oauth_timestamp = timestamp
    @oauth_nonce = generate_nonce
    @http_request_method = method_type

    # Debugging Outputs
    puts "Initialized OAuth with:"
    puts "Consumer Key: #{@consumer_key}"
    puts "Token ID: #{@token_id}"
    puts "Account: #{@account}"
    puts "URL: #{@url_text}"
    puts "HTTP Method: #{@http_request_method}"
    $stdout.flush
  end

  def authorization_string
    # Construct Authorization header as per NetSuite requirements
    authorization = [
      "realm=\"#{account}\"",
      "oauth_consumer_key=\"#{consumer_key}\"",
      "oauth_token=\"#{token_id}\"",
      "oauth_signature_method=\"#{oauth_signature_method}\"",
      "oauth_timestamp=\"#{oauth_timestamp}\"",
      "oauth_nonce=\"#{oauth_nonce}\"",
      "oauth_version=\"#{version}\"",
      "oauth_signature=\"#{hmac_signature}\""
    ].join(', ')
    
    # Debugging Outputs
    puts "Authorization String: #{authorization}"
    $stdout.flush
    
    "OAuth #{authorization}"
  end

  private

  def hmac_signature
    # Construct the base string following OAuth 1.0a rules
    base_string = "#{http_request_method.upcase}&#{CGI.escape(url_text)}&#{CGI.escape(normalized_request_parameters)}"
    secret_key = "#{CGI.escape(consumer_secret)}&#{CGI.escape(token_secret)}"
    
    # Debugging Outputs
    puts "Base String for Signature: #{base_string}"
    puts "Secret Key for HMAC: #{secret_key}"
    $stdout.flush
    
    # Generate HMAC-SHA256 signature
    signature = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', secret_key, base_string))
    encoded_signature = CGI.escape(signature)
    
    # Debugging Outputs
    puts "Generated HMAC Signature: #{encoded_signature}"
    $stdout.flush
    
    encoded_signature
  end

  def normalized_request_parameters
    # Prepare OAuth parameters for signature base string
    request_params = {
      oauth_consumer_key: consumer_key,
      oauth_nonce: oauth_nonce,
      oauth_signature_method: oauth_signature_method,
      oauth_timestamp: oauth_timestamp,
      oauth_token: token_id,
      oauth_version: version
    }
    
    # Merge additional parameters in case they exist
    params.each { |k, v| request_params[k] = CGI.escape(v.to_s) }
    
    # Sort parameters by name, then by value, and construct parameter string
    sorted_params = request_params.sort_by { |k, _| k }
    params_text = sorted_params.map { |k, v| "#{k}=#{v}" }.join('&')
    
    # Debugging Outputs
    puts "Normalized Request Parameters: #{params_text}"
    $stdout.flush
    
    params_text
  end

  def oauth_signature_method
    'HMAC-SHA256'
  end

  def version
    '1.0'
  end

  def generate_nonce
    nonce = CGI.escape(SecureRandom.base64(8))[0, 11]
    puts "Generated Nonce: #{nonce}"
    $stdout.flush
    nonce
  end

  def timestamp
    time = Time.now.to_i
    puts "Generated Timestamp: #{time}"
    $stdout.flush
    time
  end
end

Postman请求配置截图

Postman请求配置截图

备注:内容来源于stack exchange,提问作者Kevin Paul Babu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:14:32