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
2 changes: 1 addition & 1 deletion examples
Submodule examples updated 60 files
+7 −2 .github/CODEOWNERS
+35 −25 .github/workflows/ci.yml
+2 −1 Gemfile.lock
+0 −173 Makefile
+31 −11 README.md
+1 −1 go.mod
+2 −2 go.sum
+166 −0 justfile
+0 −3 official/docs/README.md
+1 −1 official/docs/csharp/current/batches/add-shipments.cs
+1 −1 official/docs/csharp/current/batches/remove-shipments.cs
+27 −0 official/docs/csharp/current/luma/buy.cs
+58 −0 official/docs/csharp/current/luma/one-call-buy.cs
+57 −0 official/docs/csharp/current/luma/promise.cs
+10 −0 official/docs/curl/current/customer-portals/create.sh
+13 −11 official/docs/curl/current/endshipper/create.sh
+13 −11 official/docs/curl/current/endshipper/update.sh
+2 −2 official/docs/curl/current/luma/buy.sh
+3 −5 official/docs/curl/current/luma/one-call-buy.sh
+37 −0 official/docs/curl/current/luma/promise.sh
+2 −0 official/docs/curl/current/trackers/delete.sh
+24 −0 official/docs/golang/current/luma/buy.go
+51 −0 official/docs/golang/current/luma/one-call-buy.go
+51 −0 official/docs/golang/current/luma/promise.go
+22 −0 official/docs/java/current/luma/buy.java
+53 −0 official/docs/java/current/luma/one-call-buy.java
+52 −0 official/docs/java/current/luma/promise.java
+15 −0 official/docs/node/current/luma/buy.js
+42 −0 official/docs/node/current/luma/one-call-buy.js
+45 −0 official/docs/node/current/luma/promise.js
+11 −0 official/docs/php/current/luma/buy.php
+39 −0 official/docs/php/current/luma/one-call-buy.php
+40 −0 official/docs/php/current/luma/promise.php
+12 −0 official/docs/python/current/luma/buy.py
+39 −0 official/docs/python/current/luma/one-call-buy.py
+38 −0 official/docs/python/current/luma/promise.py
+6 −0 official/docs/responses/customer-portals/customer-portal-create.json
+3 −0 official/docs/responses/trackers/trackers-delete.json
+16 −0 official/docs/ruby/current/luma/buy.rb
+41 −0 official/docs/ruby/current/luma/one-call-buy.rb
+40 −0 official/docs/ruby/current/luma/promise.rb
+0 −3 official/guides/README.md
+12 −0 official/guides/carrier-claims-guide/line_items.json
+2 −1 official/guides/create-carrier-curls/amazonshipping.sh
+10 −20 official/guides/create-carrier-curls/doordash.sh
+0 −17 official/guides/create-carrier-curls/fedexmailview.sh
+23 −4 official/guides/create-carrier-curls/maersk.sh
+17 −0 official/guides/create-carrier-curls/roadie.sh
+2 −1 official/guides/create-carrier-curls/royalmailv3.sh
+15 −0 official/guides/create-carrier-curls/uniuni.sh
+6 −22 official/guides/create-carrier-curls/ups.sh
+71 −0 official/guides/embeddables-guide/client.html
+7 −0 official/guides/embeddables-guide/csp-example.txt
+3 −0 official/guides/embeddables-guide/fonts-csssrc.js
+10 −0 official/guides/embeddables-guide/fonts-directsrc.js
+32 −0 official/guides/embeddables-guide/server.py
+88 −0 official/guides/embeddables-guide/theme-tokens.json
+259 −197 package-lock.json
+1 −0 style_guides/node/.eslintignore
+88 −4 tools/build_create_carrier_curl_requests/build_curls.py
2 changes: 2 additions & 0 deletions src/easypost.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import CustomsItemService from './services/customs_item_service';
import EmbeddableService from './services/embeddable_service';
import EndShipperService from './services/end_shipper_service';
import EventService from './services/event_service';
import FedExRegistrationService from './services/fedex_registration_service';
import InsuranceService from './services/insurance_service';
import LumaService from './services/luma_service';
import OrderService from './services/order_service';
Expand Down Expand Up @@ -375,6 +376,7 @@ EasyPostClient.SERVICES = {
Embeddable: EmbeddableService,
EndShipper: EndShipperService,
Event: EventService,
FedExRegistration: FedExRegistrationService,
Insurance: InsuranceService,
Luma: LumaService,
Order: OrderService,
Expand Down
169 changes: 169 additions & 0 deletions src/services/fedex_registration_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { v4 as uuid } from 'uuid';

import baseService from './base_service';

export default (easypostClient) =>
/**
* The FedExRegistrationService class provides methods for registering FedEx carrier accounts with MFA.
* @param {EasyPostClient} easypostClient - The pre-configured EasyPostClient instance to use for API requests with this service.
*/
class FedExRegistrationService extends baseService(easypostClient) {
/**
* Register the billing address for a FedEx account.
* @param {string} fedexAccountNumber - The FedEx account number.
* @param {Object} params - Map of parameters.
* @returns {Object}
*/
static async registerAddress(fedexAccountNumber, params) {
const wrappedParams = this._wrapAddressValidation(params);
const endpoint = `fedex_registrations/${fedexAccountNumber}/address`;

try {
const response = await easypostClient._post(endpoint, wrappedParams);
return this._convertToEasyPostObject(response.body, params);
} catch (e) {
return Promise.reject(e);
}
}

/**
* Request a PIN for FedEx account verification.
* @param {string} fedexAccountNumber - The FedEx account number.
* @param {string} pinMethodOption - The PIN delivery method: "SMS", "CALL", or "EMAIL".
* @returns {Object}
*/
static async requestPin(fedexAccountNumber, pinMethodOption) {
const wrappedParams = {
pin_method: {
option: pinMethodOption,
},
};
const endpoint = `fedex_registrations/${fedexAccountNumber}/pin`;

try {
const response = await easypostClient._post(endpoint, wrappedParams);
return this._convertToEasyPostObject(response.body, wrappedParams);
} catch (e) {
return Promise.reject(e);
}
}

/**
* Validate the PIN entered by the user for FedEx account verification.
* @param {string} fedexAccountNumber - The FedEx account number.
* @param {Object} params - Map of parameters.
* @returns {Object}
*/
static async validatePin(fedexAccountNumber, params) {
const wrappedParams = this._wrapPinValidation(params);
const endpoint = `fedex_registrations/${fedexAccountNumber}/pin/validate`;

try {
const response = await easypostClient._post(endpoint, wrappedParams);
return this._convertToEasyPostObject(response.body, params);
} catch (e) {
return Promise.reject(e);
}
}

/**
* Submit invoice information to complete FedEx account registration.
* @param {string} fedexAccountNumber - The FedEx account number.
* @param {Object} params - Map of parameters.
* @returns {Object}
*/
static async submitInvoice(fedexAccountNumber, params) {
const wrappedParams = this._wrapInvoiceValidation(params);
const endpoint = `fedex_registrations/${fedexAccountNumber}/invoice`;

try {
const response = await easypostClient._post(endpoint, wrappedParams);
return this._convertToEasyPostObject(response.body, params);
} catch (e) {
return Promise.reject(e);
}
}

/**
* Wraps address validation parameters and ensures the "name" field exists.
* If not present, generates a UUID (with hyphens removed) as the name.
* @private
* @param {Object} params - The original parameters map.
* @returns {Object} - A new map with properly wrapped address_validation and easypost_details.
*/
static _wrapAddressValidation(params) {
const wrappedParams = {};

if (params.address_validation) {
const addressValidation = { ...params.address_validation };
this._ensureNameField(addressValidation);
wrappedParams.address_validation = addressValidation;
}

if (params.easypost_details) {
wrappedParams.easypost_details = params.easypost_details;
}

return wrappedParams;
}

/**
* Wraps PIN validation parameters and ensures the "name" field exists.
* If not present, generates a UUID (with hyphens removed) as the name.
* @private
* @param {Object} params - The original parameters map.
* @returns {Object} - A new map with properly wrapped pin_validation and easypost_details.
*/
static _wrapPinValidation(params) {
const wrappedParams = {};

if (params.pin_validation) {
const pinValidation = { ...params.pin_validation };
this._ensureNameField(pinValidation);
wrappedParams.pin_validation = pinValidation;
}

if (params.easypost_details) {
wrappedParams.easypost_details = params.easypost_details;
}

return wrappedParams;
}

/**
* Wraps invoice validation parameters and ensures the "name" field exists.
* If not present, generates a UUID (with hyphens removed) as the name.
* @private
* @param {Object} params - The original parameters map.
* @returns {Object} - A new map with properly wrapped invoice_validation and easypost_details.
*/
static _wrapInvoiceValidation(params) {
const wrappedParams = {};

if (params.invoice_validation) {
const invoiceValidation = { ...params.invoice_validation };
this._ensureNameField(invoiceValidation);
wrappedParams.invoice_validation = invoiceValidation;
}

if (params.easypost_details) {
wrappedParams.easypost_details = params.easypost_details;
}

return wrappedParams;
}

/**
* Ensures the "name" field exists in the provided map.
* If not present, generates a UUID (with hyphens removed) as the name.
* This follows the pattern used in the web UI implementation.
* @private
* @param {Object} map - The map to ensure the "name" field in.
*/
static _ensureNameField(map) {
if (!map.name || map.name === null) {
const uuidValue = uuid().replace(/-/g, '');
map.name = uuidValue;
}
}
};
196 changes: 196 additions & 0 deletions test/services/fedex_registration.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { expect } from 'chai';

import EasyPostClient from '../../src/easypost';
import {
MockMiddleware,
MockRequest,
MockRequestMatchRule,
MockRequestResponseInfo,
} from '../helpers/mocking';

/* eslint-disable func-names */
describe('FedExRegistrationService', function () {
it('registers a billing address', async function () {
const fedexAccountNumber = '123456789';
const addressValidation = {
name: 'BILLING NAME',
street1: '1234 BILLING STREET',
city: 'BILLINGCITY',
state: 'ST',
postal_code: '12345',
country_code: 'US',
};

const easypostDetails = {
carrier_account_id: 'ca_123',
};

const params = {
address_validation: addressValidation,
easypost_details: easypostDetails,
};

const mockResponse = {
email_address: null,
options: ['SMS', 'CALL', 'INVOICE'],
phone_number: '***-***-9721',
};

const middleware = (request) => {
return new MockMiddleware(request, [
new MockRequest(
new MockRequestMatchRule(
'POST',
`v2\\/fedex_registrations\\/${fedexAccountNumber}\\/address`,
),
new MockRequestResponseInfo(200, mockResponse),
),
]);
};

const client = new EasyPostClient('test_api_key', {
requestMiddleware: middleware,
});

const response = await client.FedExRegistration.registerAddress(fedexAccountNumber, params);

expect(response.email_address).to.be.null;
expect(response.options).to.include('SMS');
expect(response.options).to.include('CALL');
expect(response.options).to.include('INVOICE');
expect(response.phone_number).to.equal('***-***-9721');
});

it('requests a pin', async function () {
const fedexAccountNumber = '123456789';

const mockResponse = {
message: 'sent secured Pin',
};

const middleware = (request) => {
return new MockMiddleware(request, [
new MockRequest(
new MockRequestMatchRule(
'POST',
`v2\\/fedex_registrations\\/${fedexAccountNumber}\\/pin`,
),
new MockRequestResponseInfo(200, mockResponse),
),
]);
};

const client = new EasyPostClient('test_api_key', {
requestMiddleware: middleware,
});

const response = await client.FedExRegistration.requestPin(fedexAccountNumber, 'SMS');

expect(response.message).to.equal('sent secured Pin');
});

it('validates a pin', async function () {
const fedexAccountNumber = '123456789';
const pinValidation = {
pin_code: '123456',
name: 'BILLING NAME',
};

const easypostDetails = {
carrier_account_id: 'ca_123',
};

const params = {
pin_validation: pinValidation,
easypost_details: easypostDetails,
};

const mockResponse = {
id: 'ca_123',
object: 'CarrierAccount',
type: 'FedexAccount',
credentials: {
account_number: '123456789',
mfa_key: '123456789-XXXXX',
},
};

const middleware = (request) => {
return new MockMiddleware(request, [
new MockRequest(
new MockRequestMatchRule(
'POST',
`v2\\/fedex_registrations\\/${fedexAccountNumber}\\/pin\\/validate`,
),
new MockRequestResponseInfo(200, mockResponse),
),
]);
};

const client = new EasyPostClient('test_api_key', {
requestMiddleware: middleware,
});

const response = await client.FedExRegistration.validatePin(fedexAccountNumber, params);

expect(response.id).to.equal('ca_123');
expect(response.object).to.equal('CarrierAccount');
expect(response.type).to.equal('FedexAccount');
expect(response.credentials.account_number).to.equal('123456789');
expect(response.credentials.mfa_key).to.equal('123456789-XXXXX');
});

it('submits details about an invoice', async function () {
const fedexAccountNumber = '123456789';
const invoiceValidation = {
name: 'BILLING NAME',
invoice_number: 'INV-12345',
invoice_date: '2025-12-08',
invoice_amount: '100.00',
invoice_currency: 'USD',
};

const easypostDetails = {
carrier_account_id: 'ca_123',
};

const params = {
invoice_validation: invoiceValidation,
easypost_details: easypostDetails,
};

const mockResponse = {
id: 'ca_123',
object: 'CarrierAccount',
type: 'FedexAccount',
credentials: {
account_number: '123456789',
mfa_key: '123456789-XXXXX',
},
};

const middleware = (request) => {
return new MockMiddleware(request, [
new MockRequest(
new MockRequestMatchRule(
'POST',
`v2\\/fedex_registrations\\/${fedexAccountNumber}\\/invoice`,
),
new MockRequestResponseInfo(200, mockResponse),
),
]);
};

const client = new EasyPostClient('test_api_key', {
requestMiddleware: middleware,
});

const response = await client.FedExRegistration.submitInvoice(fedexAccountNumber, params);

expect(response.id).to.equal('ca_123');
expect(response.object).to.equal('CarrierAccount');
expect(response.type).to.equal('FedexAccount');
expect(response.credentials.account_number).to.equal('123456789');
expect(response.credentials.mfa_key).to.equal('123456789-XXXXX');
});
});
Loading