Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/easypost/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def initialize(api_key:, read_timeout: 60, open_timeout: 30, api_base: 'https://
EasyPost::Services::Embeddable,
EasyPost::Services::EndShipper,
EasyPost::Services::Event,
EasyPost::Services::FedexRegistration,
EasyPost::Services::Insurance,
EasyPost::Services::Luma,
EasyPost::Services::Order,
Expand Down
1 change: 1 addition & 0 deletions lib/easypost/services.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module EasyPost::Services
require_relative 'services/embeddable'
require_relative 'services/end_shipper'
require_relative 'services/event'
require_relative 'services/fedex_registration'
require_relative 'services/insurance'
require_relative 'services/luma'
require_relative 'services/order'
Expand Down
109 changes: 109 additions & 0 deletions lib/easypost/services/fedex_registration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# frozen_string_literal: true

require 'securerandom'

class EasyPost::Services::FedexRegistration < EasyPost::Services::Service
# Register the billing address for a FedEx account.
def register_address(fedex_account_number, params = {})
wrapped_params = wrap_address_validation(params)
endpoint = "fedex_registrations/#{fedex_account_number}/address"

response = @client.make_request(:post, endpoint, wrapped_params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::EasyPostObject)
end

# Request a PIN for FedEx account verification.
def request_pin(fedex_account_number, pin_method_option)
wrapped_params = {
pin_method: {
option: pin_method_option,
},
}
endpoint = "fedex_registrations/#{fedex_account_number}/pin"

response = @client.make_request(:post, endpoint, wrapped_params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::EasyPostObject)
end

# Validate the PIN entered by the user for FedEx account verification.
def validate_pin(fedex_account_number, params = {})
wrapped_params = wrap_pin_validation(params)
endpoint = "fedex_registrations/#{fedex_account_number}/pin/validate"

response = @client.make_request(:post, endpoint, wrapped_params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::EasyPostObject)
end

# Submit invoice information to complete FedEx account registration.
def submit_invoice(fedex_account_number, params = {})
wrapped_params = wrap_invoice_validation(params)
endpoint = "fedex_registrations/#{fedex_account_number}/invoice"

response = @client.make_request(:post, endpoint, wrapped_params)

EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::EasyPostObject)
end

private

# Wraps address validation parameters and ensures the "name" field exists.
# If not present, generates a UUID (with hyphens removed) as the name.
def wrap_address_validation(params)
wrapped_params = {}

if params.key?(:address_validation)
address_validation = params[:address_validation].dup
ensure_name_field(address_validation)
wrapped_params[:address_validation] = address_validation
end

wrapped_params[:easypost_details] = params[:easypost_details] if params.key?(:easypost_details)

wrapped_params
end

# Wraps PIN validation parameters and ensures the "name" field exists.
# If not present, generates a UUID (with hyphens removed) as the name.
def wrap_pin_validation(params)
wrapped_params = {}

if params.key?(:pin_validation)
pin_validation = params[:pin_validation].dup
ensure_name_field(pin_validation)
wrapped_params[:pin_validation] = pin_validation
end

wrapped_params[:easypost_details] = params[:easypost_details] if params.key?(:easypost_details)

wrapped_params
end

# Wraps invoice validation parameters and ensures the "name" field exists.
# If not present, generates a UUID (with hyphens removed) as the name.
def wrap_invoice_validation(params)
wrapped_params = {}

if params.key?(:invoice_validation)
invoice_validation = params[:invoice_validation].dup
ensure_name_field(invoice_validation)
wrapped_params[:invoice_validation] = invoice_validation
end

wrapped_params[:easypost_details] = params[:easypost_details] if params.key?(:easypost_details)

wrapped_params
end

# Ensures the "name" field exists in the provided hash.
# If not present, generates a UUID (with hyphens removed) as the name.
# This follows the pattern used in the web UI implementation.
def ensure_name_field(hash)
return if hash.key?(:name) && !hash[:name].nil?

uuid = SecureRandom.uuid.delete('-')
hash[:name] = uuid
end
end
162 changes: 162 additions & 0 deletions spec/fedex_registration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# frozen_string_literal: true

require 'spec_helper'

describe EasyPost::Services::FedexRegistration do
let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_PROD_API_KEY']) }

describe '.register_address' do
it 'registers a billing address' do
fedex_account_number = '123456789'
address_validation = {
name: 'BILLING NAME',
street1: '1234 BILLING STREET',
city: 'BILLINGCITY',
state: 'ST',
postal_code: '12345',
country_code: 'US',
}
easypost_details = {
carrier_account_id: 'ca_123',
}
params = {
address_validation: address_validation,
easypost_details: easypost_details,
}

json_response = {
'email_address' => nil,
'options' => %w[SMS CALL INVOICE],
'phone_number' => '***-***-9721',
}

allow(client).to receive(:make_request).with(
:post,
"fedex_registrations/#{fedex_account_number}/address",
params,
).and_return(json_response)

response = client.fedex_registration.register_address(fedex_account_number, params)

expect(response).to be_an_instance_of(EasyPost::Models::EasyPostObject)
expect(response.email_address).to be_nil
expect(response.options).to include('SMS')
expect(response.options).to include('CALL')
expect(response.options).to include('INVOICE')
expect(response.phone_number).to eq('***-***-9721')
end
end

describe '.request_pin' do
it 'requests a pin' do
fedex_account_number = '123456789'
wrapped_params = {
pin_method: {
option: 'SMS',
},
}

json_response = {
'message' => 'sent secured Pin',
}

allow(client).to receive(:make_request).with(
:post,
"fedex_registrations/#{fedex_account_number}/pin",
wrapped_params,
).and_return(json_response)

response = client.fedex_registration.request_pin(fedex_account_number, 'SMS')

expect(response).to be_an_instance_of(EasyPost::Models::EasyPostObject)
expect(response.message).to eq('sent secured Pin')
end
end

describe '.validate_pin' do
it 'validates a pin' do
fedex_account_number = '123456789'
pin_validation = {
pin_code: '123456',
name: 'BILLING NAME',
}
easypost_details = {
carrier_account_id: 'ca_123',
}
params = {
pin_validation: pin_validation,
easypost_details: easypost_details,
}

json_response = {
'id' => 'ca_123',
'object' => 'CarrierAccount',
'type' => 'FedexAccount',
'credentials' => {
'account_number' => '123456789',
'mfa_key' => '123456789-XXXXX',
},
}

allow(client).to receive(:make_request).with(
:post,
"fedex_registrations/#{fedex_account_number}/pin/validate",
params,
).and_return(json_response)

response = client.fedex_registration.validate_pin(fedex_account_number, params)

expect(response).to be_an_instance_of(EasyPost::Models::EasyPostObject)
expect(response.id).to eq('ca_123')
expect(response.object).to eq('CarrierAccount')
expect(response.type).to eq('FedexAccount')
expect(response.credentials['account_number']).to eq('123456789')
expect(response.credentials['mfa_key']).to eq('123456789-XXXXX')
end
end

describe '.submit_invoice' do
it 'submits details about an invoice' do
fedex_account_number = '123456789'
invoice_validation = {
name: 'BILLING NAME',
invoice_number: 'INV-12345',
invoice_date: '2025-12-08',
invoice_amount: '100.00',
invoice_currency: 'USD',
}
easypost_details = {
carrier_account_id: 'ca_123',
}
params = {
invoice_validation: invoice_validation,
easypost_details: easypost_details,
}

json_response = {
'id' => 'ca_123',
'object' => 'CarrierAccount',
'type' => 'FedexAccount',
'credentials' => {
'account_number' => '123456789',
'mfa_key' => '123456789-XXXXX',
},
}

allow(client).to receive(:make_request).with(
:post,
"fedex_registrations/#{fedex_account_number}/invoice",
params,
).and_return(json_response)

response = client.fedex_registration.submit_invoice(fedex_account_number, params)

expect(response).to be_an_instance_of(EasyPost::Models::EasyPostObject)
expect(response.id).to eq('ca_123')
expect(response.object).to eq('CarrierAccount')
expect(response.type).to eq('FedexAccount')
expect(response.credentials['account_number']).to eq('123456789')
expect(response.credentials['mfa_key']).to eq('123456789-XXXXX')
end
end
end