- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
How to Create a Fulfillment Provider Module
In this document, you’ll learn how to create a fulfillment provider module and the methods you must implement in its main service.
1. Create Module Directory#
Start by creating a new directory for your module. For example, src/modules/my-fulfillment
.
2. Create the Fulfillment Provider Service#
Create the file src/modules/my-fulfillment/service.ts
that holds the module's main service. It must extend the AbstractFulfillmentProviderService
class imported from @medusajs/framework/utils
:
constructor#
The constructor allows you to access resources from the module's container using the first parameter, and the module's options using the second parameter.
If you're creating a client or establishing a connection with a third-party service, do it in the constructor.
Example
1import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils"2import { Logger } from "@medusajs/framework/types"3 4type InjectedDependencies = {5 logger: Logger6}7 8type Options = {9 apiKey: string10}11 12class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {13// other properties...14 protected logger_: Logger15 protected options_: Options16 // assuming you're initializing a client17 protected client18 19 constructor(20 { logger }: InjectedDependencies,21 options: Options22 ) {23 super()24 25 this.logger_ = logger26 this.options_ = options27 28 // TODO initialize your client29 }30}31 32export default MyFulfillmentProviderService
identifier#
Each fulfillment provider has a unique identifier defined in its class. The provider's ID
will be stored as fp_{identifier}_{id}
, where {id}
is the provider's id
property in the medusa-config.ts
.
Example
getFulfillmentOptions#
This method retrieves a list of fulfillment options that this provider supports. Admin users will then choose from these options when
they're creating a shipping option. The chosen fulfillment option's object is then stored within the created shipping option's data
property.
The data
property is useful to store data relevant for the third-party provider to later process the fulfillment.
This method is useful if your third-party provider allows you to retrieve support options, carriers, or services from an API. You can then retrieve those and return then in the method, allowing the admin user to choose from the services provided by the third-party provider.
Example
1// other imports...2import { FulfillmentOption } from "@medusajs/framework/types"3 4class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {5 // ...6 async getFulfillmentOptions(): Promise<FulfillmentOption[]> {7 // assuming you have a client8 const services = await this.client.getServices()9 10 return services.map((service) => ({11 id: service.service_id,12 name: service.name,13 service_code: service.code,14 // can add other relevant data for the provider to later process the shipping option.15 }))16 }17}
Returns
Promise
Promise<FulfillmentOption[]>The list of fulfillment options. Each object in the array should have an id
property unique to an item, and a name
property
that's used to display the option in the admin.
Promise
Promise<FulfillmentOption[]>id
property unique to an item, and a name
property
that's used to display the option in the admin.validateFulfillmentData#
This method validates the data
property of a shipping method and returns it. The returned data
is stored in the shipping method's data
property.
Your fulfillment provider can use the data
property to store additional information useful for
handling the fulfillment later. For example, you may store an ID from the third-party fulfillment
system.
Example
1class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {2 // ...3 async validateFulfillmentData(4 optionData: any,5 data: any,6 context: any7 ): Promise<any> {8 // assuming your client retrieves an ID from the9 // third-party service10 const externalId = await this.client.getId()11 12 return {13 ...data,14 externalId15 }16 }17}
Parameters
optionData
Record<string, unknown>data
property of the shipping option.data
Record<string, unknown>data
property of the shipping method.Context details, such as context of the cart or customer.
Returns
Promise
Promise<any>the data to store in the data
property of the shipping method.
Promise
Promise<any>data
property of the shipping method.validateOption#
This method validates the data
property of a shipping option when it's created.
The data
property can hold useful information that's later added to the data
attribute
of shipping methods created from this option.
Example
Parameters
data
Record<string, unknown>Returns
Promise
Promise<boolean>Whether the data is valid.
Promise
Promise<boolean>canCalculate#
This method validates whether a shippin option's price can be calculated during checkout. It's executed when the admin user creates a shipping
option of type calculated
. If this method returns false
, an error is thrown as the shipping option's price can't be calculated.
You can perform the checking using the third-party provider if applicable. The data
parameter will hold the shipping option's data
property, which
includes the data of a fulfillment option returned by getFulfillmentOptions.
Example
Parameters
The data
property of the shipping option.
data
property of the shipping option.Returns
Promise
Promise<boolean>Whether the price can be calculated for the shipping option.
Promise
Promise<boolean>calculatePrice#
This method calculates the price of a shipping method when it's created or its cart is refreshed.
In this method, you can send a request to your third-party provider to retrieve the prices. The first
parameters holds the data
property of the shipping method's shipping option, which has fulfillment
object data returned by getFulfillmentOptions.
The second parameter holds the data
property of the shipping method, which has data returned by validateFulfillmentData.
It can also hold custom data passed from the frontend during checkout.
So, using both of these data, assuming you're storing in them data related to the third-party service, you can retrieve the calculated price of the shipping method.
Example
1import { CalculateShippingOptionPriceDTO } from "@medusajs/framework/types"2class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {3 // ...4 async calculatePrice(5 optionData: CalculateShippingOptionPriceDTO["optionData"],6 data: CalculateShippingOptionPriceDTO["data"],7 context: CalculateShippingOptionPriceDTO["context"]8 ): Promise<CalculatedShippingOptionPrice> {9 // assuming the client can calculate the price using10 // the third-party service11 const price = await this.client.calculate(data)12 return {13 calculated_amount: price,14 // Update this boolean value based on your logic15 is_calculated_price_tax_inclusive: true,16 }17 }18}
Parameters
optionData
Record<string, unknown>data
property of a shipping option.data
Record<string, unknown>data
property with custom data passed from the frontend.context
CartPropsForFulfillment & objectThe context details, such as the cart details.
context
CartPropsForFulfillment & objectReturns
Promise
Promise<CalculatedShippingOptionPrice>The calculated price's details.
Promise
Promise<CalculatedShippingOptionPrice>createFulfillment#
This method is used when a fulfillment is created. If the method returns in the object a
data
property, it's stored in the fulfillment's data
property.
The data
property is useful when handling the fulfillment later,
as you can access information useful for your integration, such as the ID in the
third-party provider.
You can also use this method to perform an action with the third-party fulfillment service since a fulfillment is created, such as purchase a label.
Example
1class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {2 // ...3 async createFulfillment(4 data: any,5 items: any,6 order: any,7 fulfillment: any8 ): Promise<CreateFulfillmentResult> {9 // assuming the client creates a fulfillment10 // in the third-party service11 const externalData = await this.client.create(12 fulfillment,13 items14 )15 16 return {17 data: {18 ...(fulfillment.data as object || {}),19 ...externalData20 }21 }22 }23}
Parameters
data
Record<string, unknown>data
property of the shipping method this fulfillment is created for.items
Partial<Omit<FulfillmentItemDTO, "fulfillment">>[]order
undefined | Partial<FulfillmentOrderDTO>fulfillment
Partial<Omit<FulfillmentDTO, "data" | "items" | "provider_id">>The fulfillment's details.
fulfillment
Partial<Omit<FulfillmentDTO, "data" | "items" | "provider_id">>Returns
Promise
Promise<CreateFulfillmentResult>An object whose data
property is stored in the fulfillment's data
property.
Promise
Promise<CreateFulfillmentResult>data
property is stored in the fulfillment's data
property.cancelFulfillment#
This method is used when a fulfillment is canceled. Use it to perform operations with the third-party fulfillment service.
Example
1class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {2 // ...3 async cancelFulfillment(data: Record<string, unknown>): Promise<any> {4 // assuming the client cancels a fulfillment5 // in the third-party service6 const { external_id } = data as {7 external_id: string8 }9 await this.client.cancel(external_id)10 }11}
Parameters
data
Record<string, unknown>data
property.Returns
Promise
Promise<any>This method is used when a fulfillment is canceled. Use it to perform operations
with the third-party fulfillment service.
Promise
Promise<any>getFulfillmentDocuments#
This method retrieves the documents of a fulfillment.
Example
Parameters
data
Record<string, unknown>data
property of the fulfillment.Returns
Promise
Promise<never[]>The fulfillment's documents.
Promise
Promise<never[]>createReturnFulfillment#
This method is used when a fulfillment is created for a return. If the method returns in the object a
data
property, it's stored in the fulfillment's data
property.
The data
property is useful when handling the fulfillment later,
as you can access information useful for your integration. For example, you
can store an ID for the fulfillment in the third-party service.
Use this method to perform actions necessary in the third-party fulfillment service, such as purchasing a label for the return fulfillment.
Example
1class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {2 // ...3 async createReturnFulfillment(fulfillment: Record<string, unknown>): Promise<CreateFulfillmentResult> {4 // assuming the client creates a fulfillment for a return5 // in the third-party service6 const externalData = await this.client.createReturn(7 fulfillment8 )9 10 return {11 data: {12 ...(fulfillment.data as object || {}),13 ...externalData14 }15 }16 }17}
Parameters
fulfillment
Record<string, unknown>Returns
Promise
Promise<CreateFulfillmentResult>An object containing data
which is stored in the fulfillment's data
property and labels
array which is used to create FulfillmentLabels.
Promise
Promise<CreateFulfillmentResult>data
which is stored in the fulfillment's data
property and labels
array which is used to create FulfillmentLabels.getReturnDocuments#
This method retrieves documents for a return's fulfillment.
Example
Parameters
data
Record<string, unknown>data
property of the fulfillment.Returns
Promise
Promise<never[]>The fulfillment's documents.
Promise
Promise<never[]>getShipmentDocuments#
This method retrieves the documents for a shipment.
Example
Parameters
data
Record<string, unknown>data
property of the shipmnet.Returns
Promise
Promise<never[]>The shipment's documents.
Promise
Promise<never[]>retrieveDocuments#
This method retrieves the documents of a fulfillment of a certain type.
Example
1class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {2 // ...3 async retrieveDocuments(4 fulfillmentData: any,5 documentType: any6 ): Promise<void> {7 // assuming the client retrieves documents8 // from a third-party service9 return await this.client.documents(10 fulfillmentData,11 documentType12 )13 }14}
Parameters
fulfillmentData
Record<string, unknown>data
property of the fulfillment.documentType
stringinvoice
.Returns
Promise
Promise<void>3. Create Module Definition File#
Create the file src/modules/my-fulfillment/index.ts
with the following content:
This exports the module's definition, indicating that the MyFulfillmentProviderService
is the module's service.
4. Use Module#
To use your Fulfillment Module Provider, add it to the providers
array of the Fulfillment Module in medusa-config.ts
:
1module.exports = defineConfig({2 // ...3 modules: [4 {5 resolve: "@medusajs/medusa/fulfillment",6 options: {7 providers: [8 // default provider9 {10 resolve: "@medusajs/medusa/fulfillment-manual",11 id: "manual",12 },13 {14 resolve: "./src/modules/my-fulfillment",15 id: "my-fulfillment",16 options: {17 // provider options...18 },19 },20 ],21 },22 },23 ]24})
5. Test it Out#
Before you use your fulfillment provider, in the Medusa Admin:
- Add the fulfillment provider to a location.
- Add in the location a delivery shipping option that uses the provider.
Then, place an order, choosing the shipping option you created during checkout, and create a fulfillment in the Medusa Admin. The fulfillment is created using your provider.