docs: update Create Fulfillment Provider guide (#4911)
This commit is contained in:
@@ -17,8 +17,10 @@ Adding a fulfillment provider is as simple as creating one [service](../../../de
|
||||
|
||||
1. `getFulfillmentOptions`: used to retrieve available fulfillment options provided by this fulfillment provider.
|
||||
2. `validateOption`: used to validate the shipping option when it’s being created by the admin.
|
||||
3. `validateFulfillmentData`: used to validate the shipping method when the customer chooses a shipping option on checkout.
|
||||
4. `createFulfillment`: used to perform any additional actions when fulfillment is being created for an order.
|
||||
3. `validateFulfillmentData`: used to validate a shipping method's data before it's created, typically during checkout.
|
||||
4. `createFulfillment`: used to perform any additional actions when fulfillment is being created for an order, such as communicating with a third-party service.
|
||||
|
||||
There are other [useful methods](#useful-methods) that can be implemented based on your fulfillment provider's use case.
|
||||
|
||||
Also, the fulfillment provider class should have a static property `identifier`. It is the name that will be used to install and refer to the fulfillment provider throughout Medusa.
|
||||
|
||||
@@ -88,11 +90,13 @@ class MyFulfillmentService extends FulfillmentService {
|
||||
|
||||
### getFulfillmentOptions
|
||||
|
||||
When the admin is creating shipping options available for customers during checkout, they choose one of the fulfillment options provided by underlying fulfillment providers.
|
||||
This method is used when retrieving the list of fulfillment options available in a region, particularly by the [List Fulfillment Options endpoint](https://docs.medusajs.com/api/admin#regions_getregionsregionfulfillmentoptions).
|
||||
|
||||
For example, if you’re integrating UPS as a fulfillment provider, you might support two fulfillment options: UPS Express Shipping and UPS Access Point.
|
||||
For example, if you’re integrating UPS as a fulfillment provider, you might support two fulfillment options: UPS Express Shipping and UPS Access Point. Each of these options can have different data associated with them.
|
||||
|
||||
These fulfillment options are defined in the `getFulfillmentOptions` method. This method should return an array of options.
|
||||
This method is expected to return an array of options. These options don't have any required format.
|
||||
|
||||
Later on, these options can be used when creating a shipping option, such as when using the [Create Shipping Option endpoint](https://docs.medusajs.com/api/admin#shipping-options_postshippingoptions). The chosen fulfillment option, which is one of the items in the array returned by this method, will be set in the `data` object of the shipping option.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -112,17 +116,13 @@ class MyFulfillmentService extends FulfillmentService {
|
||||
}
|
||||
```
|
||||
|
||||
When the admin chooses one of those fulfillment options, the data of the chosen fulfillment option is stored in the `data` property of the shipping option created. This property is used to add any additional data you need to fulfill the order with the third-party provider.
|
||||
|
||||
For that reason, the fulfillment option doesn't have any required structure and can be of any format that works for your integration.
|
||||
|
||||
### validateOption
|
||||
|
||||
Once the admin creates the shipping option, the data will be validated first using this method in the underlying fulfillment provider of that shipping option. This method is called when a `POST` request is sent to [`/admin/shipping-options`](https://docs.medusajs.com/api/admin#shipping-options_getshippingoptions).
|
||||
Once the admin creates the shipping option, the data of the shipping option will be validated first using this method. This method is called when the [Create Shipping Option endpoint](https://docs.medusajs.com/api/admin#shipping-options_postshippingoptions) is used.
|
||||
|
||||
This method accepts the `data` object that is sent in the body of the request. You can use this data to validate the shipping option before it is saved.
|
||||
This method accepts the `data` object that is sent in the body of the request, basically, the `data` object of the shipping option. You can use this data to validate the shipping option before it is saved.
|
||||
|
||||
This method returns a boolean. If the result is false, an error is thrown and the shipping option will not be saved.
|
||||
This method returns a boolean. If the returned value is `false`, an error is thrown and the shipping option will not be saved.
|
||||
|
||||
For example, you can use this method to ensure that the `id` in the `data` object is correct:
|
||||
|
||||
@@ -135,27 +135,27 @@ class MyFulfillmentService extends FulfillmentService {
|
||||
}
|
||||
```
|
||||
|
||||
If your fulfillment provider does not need to run any validation, you can simply return `true`.
|
||||
If your fulfillment provider doesn't need to run any validation, you can simply return `true`.
|
||||
|
||||
### validateFulfillmentOption
|
||||
### validateFulfillmentData
|
||||
|
||||
When the customer chooses a shipping option on checkout, the shipping option and its data are validated before the shipping method is created.
|
||||
|
||||
`validateFulfillmentOption` is called when a `POST` request is sent to [`/carts/:id/shipping-methods`](https://docs.medusajs.com/api/store#carts_postcartscartshippingmethod).
|
||||
This method is called when a shipping method is created. This typically happens when the customer chooses a shipping option during checkout, when a shipping method is created for an order return, or in other similar cases. The shipping option and its data are validated before the shipping method is created.
|
||||
|
||||
This method accepts three parameters:
|
||||
|
||||
1. The shipping option data.
|
||||
2. The `data` object passed in the body of the request.
|
||||
3. The customer’s cart data.
|
||||
1. The first parameter is the `data` object of the shipping option selected when creating the shipping method.
|
||||
2. The second parameter is `data` object passed in the body of the request.
|
||||
3. The third parameter is an object indicating the customer’s cart data. It may be empty if the shipping method isn't associated with a cart, such as when it's associated with a claim.
|
||||
|
||||
You can use these parameters to validate the chosen shipping option. For example, you can check if the `data` object includes all data needed to fulfill the shipment later on.
|
||||
You can use these parameters to validate the chosen shipping option. For example, you can check if the `data` object passed as a second parameter includes all data needed to fulfill the shipment later on.
|
||||
|
||||
If any of the data is invalid, you can throw an error. This error will stop Medusa from creating a shipping method and the error message will be returned as a result to the endpoint.
|
||||
|
||||
If everything is valid, this method must return a value that will be stored in the `data` property of the shipping method to be created. So, make sure the value you return contains everything you need to fulfill the shipment later on.
|
||||
If everything is valid, this method must return an object that will be stored in the `data` property of the shipping method to be created. So, make sure the value you return contains everything you need to fulfill the shipment later on.
|
||||
|
||||
For example:
|
||||
The returned value may also be used to calculate the price of the shipping method if it doesn't have a set price. It will be passed along to the [calculatePrice](#calculateprice) method.
|
||||
|
||||
Here's an example implementation:
|
||||
|
||||
```ts
|
||||
class MyFulfillmentService extends FulfillmentService {
|
||||
@@ -174,54 +174,53 @@ class MyFulfillmentService extends FulfillmentService {
|
||||
|
||||
### createFulfillment
|
||||
|
||||
After an order is placed, it can be fulfilled either manually by the admin or using automation.
|
||||
This method is used when a fulfillment is created for an order, a claim, or a swap.
|
||||
|
||||
This method gives you access to the fulfillment being created as well as other details in case you need to perform any additional actions with the third-party provider.
|
||||
It accepts four parameters:
|
||||
|
||||
This method accepts four parameters:
|
||||
|
||||
1. The data of the shipping method associated with the order.
|
||||
2. An array of items in the order to be fulfilled. The admin can choose all or some of the items to fulfill.
|
||||
3. The data of the order
|
||||
4. The data of the fulfillment being created.
|
||||
1. The first parameter is the `data` object of the shipping method associated with the resource, such as the order.
|
||||
2. The second parameter is the array of line item objects in the order to be fulfilled. The admin can choose all or some of the items to fulfill.
|
||||
3. The third parameter is an object that includes data related to the order, claim, or swap this fulfillment is being created for.
|
||||
1. If the resource the fulfillment is being created for is a claim, the `is_claim` property in the object will be `true`.
|
||||
2. If the resource the fulfillment is being created for is a swap, the `is_swap` property in the object will be `true`.
|
||||
3. Otherwise, the resource is an order.
|
||||
4. The fourth parameter is an object of type [Fulfillment](../../../references/entities/classes/Fulfillment.md), which is the fulfillment being created.
|
||||
|
||||
You can use the `data` property in the shipping method (first parameter) to access the data specific to the shipping option. This is based on your implementation of previous methods.
|
||||
|
||||
This method must return an object of data that will be stored in the `data` attribute of the created fulfillment.
|
||||
|
||||
Here is a basic implementation of `createFulfillment` for a fulfillment provider that does not interact with any third-party provider to create the fulfillment:
|
||||
|
||||
```ts
|
||||
class MyFulfillmentService extends FulfillmentService {
|
||||
// ...
|
||||
createFulfillment(
|
||||
async createFulfillment(
|
||||
methodData,
|
||||
fulfillmentItems,
|
||||
fromOrder,
|
||||
fulfillment
|
||||
) {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({})
|
||||
// No data to be stored in the fulfillment's data object
|
||||
return {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
This method is also used to create claims and swaps. The fulfillment object has the fields `claim_id`, `swap_id`, and `order_id`. You can check which isn’t null to determine what type of fulfillment is being created.
|
||||
|
||||
:::
|
||||
|
||||
### Useful Methods
|
||||
|
||||
The above-detailed methods are the required methods for every fulfillment provider. However, there are additional methods that you can use in your fulfillment provider to customize it further or add additional features.
|
||||
|
||||
#### canCalculate
|
||||
|
||||
This method validates whether a shipping option is calculated dynamically or flat rate. It is called if the `price_type` of the shipping option being created is set to `calculated`.
|
||||
This method is used to determine whether a shipping option is calculated dynamically or flat rate. It is called if the `price_type` of the shipping option being created is set to `calculated`.
|
||||
|
||||
If this method returns `true`, that means that the price should be calculated dynamically. The `amount` property of the shipping option will then be set to `null`. The amount will be created later when the shipping method is created on checkout using the `calculatePrice` method (explained next).
|
||||
This method accepts an object as a parameter, which is the `data` object of the shipping option being created. You can use this data to determine whether the shipping option should be calculated or not. This is useful if the fulfillment provider you are integrating has both flat rate and dynamically priced fulfillment options.
|
||||
|
||||
If the method returns `false`, an error is thrown as it means the selected shipping option can only be chosen if the price type is set to `flat_rate`.
|
||||
If this method returns `true`, that means that the price can be calculated dynamically and the shipping option can have the `price_type` set to `calculated`. The `amount` property of the shipping option will then be set to `null`. The amount will be created later when the shipping method is created on checkout using the [calculatePrice method](#calculateprice).
|
||||
|
||||
This method receives as a parameter the `data` object sent with the request that [creates the shipping option.](https://docs.medusajs.com/api/admin#shipping-options_postshippingoptions) You can use this data to determine whether the shipping option should be calculated or not. This is useful if the fulfillment provider you are integrating has both flat rate and dynamically priced fulfillment options.
|
||||
If the method returns `false`, an error is thrown as it means the selected shipping option is invalid and it can only have the `flat_rate` price type.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -236,31 +235,40 @@ class MyFulfillmentService extends FulfillmentService {
|
||||
|
||||
#### calculatePrice
|
||||
|
||||
This method is called on checkout when the shipping method is being created if the `price_type` of the selected shipping option is `calculated`.
|
||||
This method is used in different places, including:
|
||||
|
||||
1. When the shipping options for a cart are retrieved during checkout. If a shipping option has their `price_type` set to `calculated`, this method is used to set the `amount` of the returned shipping option.
|
||||
2. When a shipping method is created. If the shipping option associated with the method has their `price_type` set to `calculated`, this method is used to set the `price` attribute of the shipping method in the database.
|
||||
3. When the cart's totals are calculated.
|
||||
|
||||
This method receives three parameters:
|
||||
|
||||
1. The `data` parameter of the selected shipping option.
|
||||
2. The `data` parameter sent with [the request](https://docs.medusajs.com/api/store#carts_postcartscartshippingmethod).
|
||||
3. The customer’s cart data.
|
||||
1. The first parameter is the `data` object of the selected shipping option.
|
||||
2. The second parameter is a `data` object that is different based on the context it's used in:
|
||||
1. If the price is being calculated for the list of shipping options available for a cart, it's the `data` object of the shipping option.
|
||||
2. If the price is being calculated when the shipping method is being created, it's the data returned by the [validateFulfillmentData](#validatefulfillmentdata) method used during the shipping method creation.
|
||||
3. If the price is being calculated while calculating the cart's totals, it will be the `data` object of the cart's shipping method.
|
||||
3. The third parameter is either the [Cart](../../../references/entities/classes/Cart.md) or the [Order](../../../references/entities/classes/Order.md) object.
|
||||
|
||||
If your fulfillment provider does not provide any dynamically calculated rates you can keep the function empty:
|
||||
The method is expected to return a number that will be used to set the price of the shipping method or option, based on the context it's used in.
|
||||
|
||||
If your fulfillment provider does not provide any dynamically calculated rates you can keep the function empty. For example:
|
||||
|
||||
```ts
|
||||
class MyFulfillmentService extends FulfillmentService {
|
||||
// ...
|
||||
calculatePrice() {
|
||||
async calculatePrice(optionData, data, cart) {
|
||||
// leave empty
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Otherwise, you can use it to calculate the price with a custom logic. For example:
|
||||
Otherwise, you can use it to calculate the price with custom logic. For example:
|
||||
|
||||
```ts
|
||||
class MyFulfillmentService extends FulfillmentService {
|
||||
// ...
|
||||
calculatePrice(optionData, data, cart) {
|
||||
async calculatePrice(optionData, data, cart) {
|
||||
return cart.items.length * 1000
|
||||
}
|
||||
}
|
||||
@@ -270,19 +278,19 @@ class MyFulfillmentService extends FulfillmentService {
|
||||
|
||||
Fulfillment providers can also be used to return products. A shipping option can be used for returns if the `is_return` property is `true` or if an admin creates a Return Shipping Option from the settings.
|
||||
|
||||
This method is called when the admin [creates a return request](https://docs.medusajs.com/api/admin#orders_postordersorderreturns) for an order or when the customer [creates a return of their order](https://docs.medusajs.com/api/store#returns_postreturns).
|
||||
This method is used when the admin [creates a return request](https://docs.medusajs.com/api/admin#orders_postordersorderreturns) for an order, [creates a swap](https://docs.medusajs.com/api/admin#orders_postordersorderswaps) for an order, or when the customer [creates a return of their order](https://docs.medusajs.com/api/store#returns_postreturns). The fulfillment is created automatically for the order return.
|
||||
|
||||
It gives you access to the return being created in case you need to perform any additional actions with the third-party provider.
|
||||
The method receives as a parameter the [Return](../../../references/entities/classes/Return.md) object, which is the return that the fulfillment is being created for.
|
||||
|
||||
It receives the return created as a parameter. The value it returns is set to the `shipping_data` of the return instance.
|
||||
The method must return an object that will be used to set the value of the `shipping_data` attribute of the return being created.
|
||||
|
||||
This is the basic implementation of the method for a fulfillment provider that does not contact with a third-party provider to fulfill the return:
|
||||
|
||||
```ts
|
||||
class MyFulfillmentService extends FulfillmentService {
|
||||
// ...
|
||||
createReturn(returnOrder) {
|
||||
return Promise.resolve({})
|
||||
async createReturn(returnOrder) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -291,17 +299,15 @@ class MyFulfillmentService extends FulfillmentService {
|
||||
|
||||
This method is called when a fulfillment is cancelled by the admin. This fulfillment can be for an order, a claim, or a swap.
|
||||
|
||||
It gives you access to the fulfillment being canceled in case you need to perform any additional actions with your third-party provider.
|
||||
The method receives the `data` attribute of the fulfillment being canceled. The method isn't expected to return any specific data.
|
||||
|
||||
This method receives the fulfillment being cancelled as a parameter.
|
||||
|
||||
This is the basic implementation of the method for a fulfillment provider that does not interact with a third-party provider to cancel the fulfillment:
|
||||
This is the basic implementation of the method for a fulfillment provider that doesn't interact with a third-party provider to cancel the fulfillment:
|
||||
|
||||
```ts
|
||||
class MyFulfillmentService extends FulfillmentService {
|
||||
// ...
|
||||
cancelFulfillment(fulfillment) {
|
||||
return Promise.resolve({})
|
||||
async cancelFulfillment(fulfillment) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user