在 Medusa 裡有一個 Fulfillment Module(履約模組),它負責管理「訂單要怎麼出貨、建立出貨紀錄」這些核心概念。
但是,實際跟 第三方物流/出貨系統(像黑貓宅急便、DHL、UPS...) 溝通的,不是 Fulfillment Module 自己,而是 Fulfillment Module Provider(履約供應商提供者)。
在 Day12 我們有玩玩看我們的履約了,但是關於到貨卻是我們在設定的,如果是真正的電商網站,運送狀態應該是由第三方運貨公司在進行提供的。
所以今天我帶各位導讀 MedusaJS
製作第三方履約模組的方式。
如果要製作第三方履約,就需要繼承AbstractFulfillmentProviderService
類別,實作裡面的函式。
import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils"
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// TODO implement methods
}
export default MyFulfillmentProviderService
一個電商也不一定會只有一個運輸商,所以我們每一個運輸商都要給一個dentifier(識別碼)
作為認識。
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
static identifier = "my-fulfillment"
// ...
}
每一個運貨商有不同的價格標準,所以當購物車建立時候,可以將商品或者運輸速度傳送給第三方貨運商進行價格運算
import { CalculateShippingOptionPriceDTO } from "@medusajs/framework/types"
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async calculatePrice(
optionData: CalculateShippingOptionPriceDTO["optionData"],
data: CalculateShippingOptionPriceDTO["data"],
context: CalculateShippingOptionPriceDTO["context"]
): Promise<CalculatedShippingOptionPrice> {
// assuming the client can calculate the price using
// the third-party service
const price = await this.client.calculate(data)
return {
calculated_amount: price,
// Update this boolean value based on your logic
is_calculated_price_tax_inclusive: true,
}
}
}
此方法會驗證是否可以在結帳期間計算託運選項的價格。當管理員使用者建立類型的運送選項時,就會執行它。如果這個方法傳回錯誤,因為無法計算運送選項的價格。
並不是每個履約商都有「運費試算」功能。
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async canCalculate(data: CreateShippingOptionDTO): Promise<boolean> {
// assuming you have a client
return await this.client.hasRates(data.id)
}
}
當貨物取消運送的時候,就會需要此函式
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async cancelFulfillment(data: Record<string, unknown>): Promise<any> {
// assuming the client cancels a fulfillment
// in the third-party service
const { external_id } = data as {
external_id: string
}
await this.client.cancel(external_id)
}
}
這個函式名稱會讓人搞混,並不是自動發貨,而是商家按下建立出貨那一刻,此函式會對第三方運貨中心開一紀錄單,用來追蹤運送資訊。
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async createFulfillment(
data: Record<string, unknown>,
items: Partial<Omit<FulfillmentItemDTO, "fulfillment">>[],
order: Partial<FulfillmentOrderDTO> | undefined,
fulfillment: Partial<Omit<FulfillmentDTO, "provider_id" | "data" | "items">>
): Promise<CreateFulfillmentResult> {
// assuming the client creates a fulfillment
// in the third-party service
const externalData = await this.client.create(
fulfillment,
items
)
return {
data: {
...(fulfillment.data as object || {}),
...externalData
}
}
}
}
當為退貨建立出貨時,會使用到此函式。
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async createReturnFulfillment(fulfillment: Record<string, unknown>): Promise<CreateFulfillmentResult> {
// assuming the client creates a fulfillment for a return
// in the third-party service
const externalData = await this.client.createReturn(
fulfillment
)
return {
data: {
...(fulfillment.data as object || {}),
...externalData
}
}
}
}
對已經建立的履約,簡單說就是貨運商資料顯示
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async getFulfillmentDocuments(data: any): Promise<never[]> {
// assuming the client retrieves documents
// from a third-party service
return await this.client.documents(data)
}
}
這個方法會取得這個履約(fulfillment)供應商所支援的履約選項。
管理員(Admin 使用者)在建立「配送選項」(shipping option)時,就會從這些選項中挑選。
// other imports...
import { FulfillmentOption } from "@medusajs/framework/types"
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async getFulfillmentOptions(): Promise<FulfillmentOption[]> {
// assuming you have a client
const services = await this.client.getServices()
return services.map((service) => ({
id: service.service_id,
name: service.name,
service_code: service.code,
// can add other relevant data for the provider to later process the shipping option.
}))
}
}
當顧客要退貨時,有些物流商(第三方供應商)會要求「退貨必須附帶一些文件」,這個方法就是去取得這些文件
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async getReturnDocuments(data: any): Promise<never[]> {
// assuming the client retrieves documents
// from a third-party service
return await this.client.documents(data)
}
}
當你用第三方物流(例如 DHL、UPS、順豐)出貨時,系統會需要一些文件來隨貨或提供給倉庫/客戶,這個方法就是去取得這些出貨相關文件。
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async getShipmentDocuments(data: any): Promise<never[]> {
// assuming the client retrieves documents
// from a third-party service
return await this.client.documents(data)
}
}
retrieveDocuments
是用來 取得某個 fulfillment 的文件,但不是限定「出貨」或「退貨」,而是依照你傳入的 文件類型 (type) 來決定要拿什麼文件。
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async retrieveDocuments(
fulfillmentData: any,
documentType: any
): Promise<void> {
// assuming the client retrieves documents
// from a third-party service
return await this.client.documents(
fulfillmentData,
documentType
)
}
}
當建立一個 shipping method (配送方式)** 的時候,系統會呼叫 validateFulfillmentData
來檢查並整理這個配送方式帶的資料是否有錯誤。
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async validateFulfillmentData(
optionData: any,
data: any,
context: any
): Promise<any> {
// assuming your client retrieves an ID from the
// third-party service
const externalId = await this.client.getId()
return {
...data,
externalId
}
}
}
先來解釋以下 配送選項 與 配送方法 有什麼差別。
配送方法
:根據所選的配送選項,實際加到購物車(cart)或訂單(order)上的配送記錄。它代表這筆訂單最終會用哪一個配送選項來出貨。配送選項
:配送選項是由商店管理員在後台設定的具體配送方式,例如「標準運送」、「快遞運送」等。所以驗證履約選項也是當管理者建立履約選項時,系統自動帶入驗證資料是否正確。
class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
// ...
async validateFulfillmentData(
optionData: any,
data: any,
context: any
): Promise<any> {
// assuming your client retrieves an ID from the
// third-party service
const externalId = await this.client.getId()
return {
...data,
externalId
}
}
}
以上函式就是當要建立 第三方履約模組時,必要的函式以及其解釋。
介紹這一些函式,或許可以讓沒有做過電商平台或者已經有做的電商平台了解一般電商對於語出貨商串接嚴格的規範,也讓我漲了一堆知識。
接下來幾天我會輕鬆一點,篇幅可能會比較小。