结账流程和事件
本文档概述了 WooCommerce 结账模块中的结账流程,并提供了一些通用的架构概览。
结账模块的架构源于以下原则:
- 在结账流程中,数据拥有单一的权威来源。
- 为扩展集成提供一致的界面(例如,付款方式)。此界面保护结账过程的完整性,并将扩展逻辑与结账逻辑隔离。结账模块负责处理所有与服务器的通信,用于处理订单。扩展可以通过提供的界面与结账模块交互并进行通信。
- 结账流程的状态由结账状态跟踪。
- 扩展可以通过订阅发出的事件来与结账流程进行交互。
以下是流程的高层概览:

通用概念
通过状态跟踪流程
在结账生命周期的任何阶段,组件都应能够准确检测结账流程的状态。这包括以下内容:
- 是否正在加载内容?正在加载什么内容?
- 是否存在错误?是什么错误?
- 结账是否正在计算总额?
在某些情况下,使用简单的布尔值可能就足够了,但在其他情况下,它可能会导致复杂的条件判断和容易出错的代码(尤其是在处理对各种流程状态做出反应的逻辑行为时)。
为了显示流程状态,该模块使用状态,这些状态在各种上下文中进行跟踪。尽可能地,这些状态是内部设置的,以响应各种操作,因此子组件不需要实现(组件只需要_消费_状态,而不需要设置状态)。
以下是在 Checkout 中存在的状态。
结账数据存储状态
通过选择器,结账数据存储会暴露各种状态。所有选择器的详细信息如下,并在 结账 API 文档 中进行说明。
您可以在组件中使用它们,例如:
const { useSelect } = window.wp.data;
const { checkoutStore } = window.wc.wcBlocksData;
const MyComponent = ( props ) => {
const isComplete = useSelect( ( select ) =>
select( checkoutStore ).isComplete()
);
// 对 isComplete 进行操作
};
以下是与状态相关的可用布尔标志:
isIdle: 当结账状态为 IDLE 时,此标志为真。在块加载后,任何对结账状态的更改都会使结账处于此状态。当在处理过程中出现错误后,如果可以重试购买,结账也会处于此状态。
isBeforeProcessing: 当结账状态为 BEFORE_PROCESSING 时,此标志为真。当用户提交结账以进行处理时,结账将处于此状态。
isProcessing: 当结账状态为 PROCESSING 时,此标志为真。当所有在 BEFORE_PROCESSING 状态下触发的事件观察器完成且没有错误时,结账将处于此状态。在此状态下,块会向服务器发送请求,访问结账端点以处理订单。注意: 在此状态下,还会发生一些付款状态的更改(请参阅 PaymentProvider 暴露的状态部分)。
isAfterProcessing: 当结账状态为 AFTER_PROCESSING 时,此标志为真。当块从服务器端处理请求接收到响应后,结账将处于此状态。
isComplete: 当结账状态为 COMPLETE 时,此标志为真。当所有在 AFTER_PROCESSING 状态下触发的事件观察器成功完成后,结账将处于此状态。当结账处于此状态时,购物者的浏览器将被重定向到 redirectUrl 的值(通常是 order-received 路由)。
特殊状态
以下布尔值通过结账提供程序暴露。 它们彼此独立,并且与结账状态不同,但可以组合使用,以响应结账过程中的各种状态。
isCalculating
isCalculating 在以下两种情况下为真:当订单的总价正在重新计算时,或者当一个插件使用 disableCheckoutFor 动作(将在下一节中介绍)有意地禁用结账流程时。
有很多因素可能触发总价的重新计算,例如添加或删除优惠券、更新运费、选择运费等。与其检查每个单独的状态,您可以可靠地检查这个布尔值是否为真(计算中)或假(未计算中)。
isCalculating 影响:
- 禁用结账区块中的“提交订单”按钮。
- 禁用购物车区块中的“继续结账”按钮。
- 在计算过程中,显示快速支付方式的加载状态。
使用 disableCheckoutFor 控制 isCalculating
您可以使用 disableCheckoutFor 函数来编程地控制 isCalculating:
const { dispatch } = window.wp.data;
const { checkoutStore } = window.wc.wcBlocksData;
// 示例:在执行异步操作时禁用结账
dispatch( checkoutStore ).disableCheckoutFor( async () => {
// 在此处执行您的异步操作,例如使用 API 验证数据
await myAsyncOperation();
// 无需返回任何值 - 我们只关心 Promise 是否已解析
} );
该函数控制内部状态,确保客户端在提供的 Promise 解析之前无法尝试完成流程,无论成功与否。
hasError
hasError 在结账流程的任何部分出现错误状态时为真。这可能包括验证错误、请求错误、优惠券应用错误、支付处理错误等。
ShippingProvider 暴露的 Status
shipping 上下文提供者暴露了与 shipping 相关的全部信息。这包括一组错误 status,用于指示 shipping 上下文所处的错误状态。该错误状态受服务器对地址更改、费率检索和选择的 requests 影响。
当前,错误 status 可能为 NONE、INVALID_ADDRESS 或 UNKNOWN(请注意,此值可能会在未来更改)。
status 通过 useShippingDataContext Hook 提供的 currentErrorStatus 对象暴露。该对象具有以下 property:
isPristine和isValid:这两个布尔值都与相同的错误status相关联。当status为NONE时,这两个布尔值的取值为true。它基本上意味着没有shipping错误。hasInvalidAddress:当提供的shipping地址无效时,此值为true。hasError:当shipping的错误status为UNKNOWN或hasInvalidAddress时,此值为true。
付款方式数据存储状态
付款的状态存储在付款数据存储中。您可以使用以下选择器查询状态:
const { select } = window.wp.data;
const { paymentStore } = window.wc.wcBlocksData;
const MyComponent = ( props ) => {
const isPaymentIdle = select( paymentStore ).isPaymentIdle();
const isExpressPaymentStarted =
select( paymentStore ).isExpressPaymentStarted();
const isPaymentProcessing = select( paymentStore ).isPaymentProcessing();
const isPaymentReady = select( paymentStore ).isPaymentReady();
const hasPaymentError = select( paymentStore ).hasPaymentError();
// 对布尔值进行操作
};
这里的状态将有助于了解_客户端_处理的当前****状态,并且会在结账处理周期的不同阶段通过商店操作进行更新。_客户端_是指在注册的、活动的付款方式中,当结账表单通过这些注册的客户端组件提交时,处理任何付款的状态。 尽管如此,付款方式在处理订单时可能仍然存在额外的服务器端处理,但这不会反映在这些状态中(更多信息请参见付款方式集成文档)。
可能设置的_内部_状态包括:
IDLE: 这是在初始化结账且存在未执行任何操作的付款方式时的状态。 每当结账状态更改为IDLE时,也会设置此状态。EXPRESS_STARTED: 仅限快速 付款方式 - 当用户点击其按钮触发快速付款方式时,将使用此状态。 此流程发生在处理之前,通常在一个模态窗口中。PROCESSING: 当结账状态为PROCESSING,结账hasError为false,结账未进行计算,并且当前****付款状态不是FINISHED时,将设置此状态。 当设置此状态时,将触发付款处理事件发射器。READY: 在所有已连接到付款处理事件的观察者成功完成后,将设置此状态。CheckoutProcessor组件使用此状态以及结账PROCESSING状态来表示准备好将订单数据发送到服务器进行处理,并进行付款。ERROR: 当连接到付款处理事件的观察者返回错误响应后,将设置此状态。 这将导致结账hasError标志设置为true。
发送事件
另一个影响可扩展性的关键点是,提供既具有指导性又灵活的界面,以便扩展程序可以响应和处理流程中的特定事件。 为了保证系统的稳定性,核心结账流程应_控制_所有与服务器的通信,这些通信与结账/订单处理相关,并将扩展程序特定的需求留给扩展程序自行处理。 这样可以使扩展程序能够根据需要,以可预测的方式与结账数据和流程进行交互,而不会影响其他与之挂钩的扩展程序。
实现这种类型可扩展性的最可靠方法是使用事件系统。 因此,各种上下文提供器:
- 提供订阅 API,允许扩展程序订阅对特定事件做出反应的_观察者_。
- 在结账流程的特定点触发事件,这些事件会将数据传递给已注册的观察者,并在某些情况下,根据观察者的回复做出相应的反应。
当在任何事件发射器中注册观察者时,有一个_非常重要_的规则,那就是它们_不能_更新上下文状态。 仅对特定组件的局部状态进行更新是可以的,但不能修改任何上下文或全局状态。 原因是观察者回调函数是按顺序在特定点执行的,因此,注册到同一事件的后续观察者将无法响应早期执行的观察者中全局/上下文状态的任何更改。
const unsubscribe = emitter(myCallback);
您可以将您正在注册的任何发射器替换为 emitter 函数。 例如,如果您正在注册 onCheckoutValidation 事件发射器,则代码如下:
const unsubscribe = onCheckoutValidation(myCallback);
您还可以指定希望您的观察者以何种优先级执行。 较低的优先级在较高的优先级之前运行,因此您可以控制您的观察者在已注册到发射器的观察者堆栈中何时运行。 您可以通过在第二个参数中指定一个数值来指示优先级:
const unsubscribe = onCheckoutValidation(myCallback, 10);
在示例中,myCallback 是您的订阅函数。 订阅函数可以从事件发射器接收数据(如在“发射器详情”中描述的),并且可能需要以特定的格式返回一个回复(也在“特定发射器详情”中描述)。 订阅函数可以是 Promise,并且当事件发射器循环遍历已注册的观察者时,它将等待任何已注册的 Promise 完成。
最后,调用发射器函数的返回值是一个取消订阅函数,该函数可用于取消注册您的观察者。 这在 React 组件上下文中特别有用,您需要在组件卸载时确保取消注册观察者。 例如,可以在 useEffect 钩子中使用:
Event Emitter Utilities
这里有一些与事件相关的实用方法。这些方法位于 assets/js/base/context/event-emit/utils.ts 文件中,可以按以下方式导入:
import {
noticeContexts,
responseTypes,
shouldRetry,
} from '@woocommerce/base-context';
import {
isSuccessResponse,
isErrorResponse,
isFailResponse,
} from '@woocommerce/types';
以下是辅助函数的描述:
isSuccessResponse,isErrorResponse和isFailResponse: 这些是辅助函数,它们接收一个值,并通过布尔值报告该对象是否为预期的响应类型。对于接收来自注册观察者的响应的事件发射器,返回对象的type属性指示响应的类型,事件发射器会根据该类型做出反应。例如,如果观察者返回{ type: 'success' },则发射器可以将该值传递给isSuccessResponse,该函数将返回true。您可以在 支付处理事件发射器 的实现中看到一个示例。noticeContexts: 这是一个对象,包含引用可以在结账过程中定位通知的区域的属性。该对象具有以下属性:PAYMENTS: 这是对支付方式步骤中通知区域的引用。EXPRESS_PAYMENTS: 这是对快速支付方式步骤中通知区域的引用。
responseTypes: 这是一个对象,包含引用某些事件发射器可以返回的各种响应类型的属性。 这使得自动完成类型更容易,并避免了由于人为错误导致的拼写错误。 类型为SUCCESS、FAIL和ERROR。 这些类型的对应值与 结账端点响应 中的 付款状态类型。shouldRetry: 这是一个函数,包含在结账流程中是否允许用户在之前的付款失败后重试付款的逻辑。 它接收response对象,并且默认情况下检查retry属性是否为true、undefined或false。 有关更多详细信息,请参阅onCheckoutSuccess文档。
技术文档
注意: noticeContexts 和 responseTypes 通过传递给其组件的 emitResponse 属性,暴露给 付款方式。
const MyPaymentMethodComponent = ( { emitResponse } ) => {
const { noticeContexts, responseTypes } = emitResponse;
// 其他 `付款方式` 的逻辑...
};
以下 活动 允许 扩展 注册观察者:
onCheckoutValidation
注册到此事件发射器的观察者将不会接收任何参数。 此外,所有观察者将在结账处理来自发射器的回复之前执行。 注册到此发射器的观察者可以返回 true,表示它们没有需要返回给结账的信息;返回 false,表示希望结账返回到 IDLE 状态;或者返回一个包含以下属性的对象:
errorMessage: 这将作为错误通知添加到结账上下文。validationErrors: 这将设置为结账字段中的内联验证错误。 如果您的观察者希望触发验证错误,可以使用以下结构:- 这是一个对象,其中键是验证错误的属性名称(对应于结账字段,例如
country或coupon),值是描述验证问题的错误消息。
- 这是一个对象,其中键是验证错误的属性名称(对应于结账字段,例如
当结账状态为 BEFORE_PROCESSING 时(这发生在验证时,在用户触发结账表单提交或使用 Express 付款方式之后),会触发此事件。
如果所有观察者都为该事件返回 true,则结账状态将更改为 PROCESSING。
可以通过使用 useCheckoutContext 钩子从结账上下文或作为属性在已注册的组件中,获取此事件发射器的订阅者:
用于内部开发:
import { useCheckoutContext } from '@woocommerce/base-contexts';
import { useEffect } from '@wordpress/element';
const Component = () => {
const { onCheckoutValidation } = useCheckoutContext();
useEffect( () => {
const unsubscribe = onCheckoutValidation( () => true );
return unsubscribe;
}, [ onCheckoutValidation ] );
return null;
};
用于已注册的付款方式组件:
const { useEffect } = window.wp.element;
const PaymentMethodComponent = ( { eventRegistration } ) => {
const { onCheckoutValidation } = eventRegistration;
useEffect( () => {
const unsubscribe = onCheckoutValidation( () => true );
return unsubscribe;
}, [ onCheckoutValidation ] );
};
用于其他情况:
const { onCheckoutValidation } = wc.blocksCheckoutEvents;
useEffect( () => {
const unsubscribe = onCheckoutValidation( () => true );
return unsubscribe;
}, [ onCheckoutValidation ] );
onPaymentProcessing
onPaymentProcessing此功能已过时,并已替换为 onPaymentSetup 事件发射器。
onPaymentSetup
当支付方式上下文的 Status 为 PROCESSING 时,此事件触发器会被激活。Status 的设置条件是:结账的 Status 为 PROCESSING,结账没有 Error,结账没有正在计算,并且当前的支付 Status 不是 FINISHED。
此事件触发器会依次执行所有已注册的观察者(不传递任何参数)。当某个观察者返回非真值时,事件触发器会停止执行后续的已注册观察者。
当支付方式返回非真值时,如果返回的是有效的响应类型,事件触发器会根据响应更新各种内部 Status。以下是事件触发器可能处理的响应类型:
成功
当用户的输入数据正确且支付验证成功时,应返回成功的响应。一个响应被认为是成功的,至少应是具有以下结构的 Object:
const successResponse = { type: 'success' };
当返回成功的响应时,支付方式上下文的 Status 会更改为 SUCCESS。此外,包含以下任何额外的 Property 会触发额外的 Actions:
paymentMethodData: 此对象的Contents将作为payment_data的值,当结账向服务器发送请求以处理订单时,该值会被包含在请求中。如果支付方式需要进行额外的服务器端处理,则此功能非常有用。billingAddress: 允许支付方式更新结账中的任何账单信息(通常由 Express 支付方式使用),以便将其包含在发送给服务器的结账处理请求中。此数据应符合 此处 描述的结构。shippingAddress: 允许支付方式更新订单的任何配送信息(通常由 Express 支付方式使用),以便将其包含在发送给服务器的结账处理请求中。此数据应符合 此处 描述的结构。
如果响应对象中没有 billingAddress 或 shippingAddress Property,则相关数据的 Status 将保持不变。
失败
当支付处理出现错误时,应返回失败的回复。一个回复被认为是失败回复,当它是一个具有以下结构的 JSON 对象时:
const failResponse = { type: 'failure' };
当观察者返回一个失败回复时,支付方法上下文的状态将被更改为 FAIL。此外,包含以下任何属性会导致额外的操作:
message: 这里提供的字符串将被设置为结账中的错误提示。messageContext: 如果提供,这将指定错误提示的目标区域(这与前面提到的noticeContexts相关)。否则,提示将添加到noticeContexts.PAYMENTS区域。paymentMethodData: (与成功回复相同)。billingAddress: (与成功回复相同)。
错误
当用户在结账表单中输入存在错误时,应返回错误响应。 响应被认为是错误响应,当它是一个具有以下结构的 对象时:
const errorResponse = { type: 'error' };
当观察者返回错误响应时,支付方法上下文状态将更改为 ERROR。 此外,包含以下任何属性将导致额外的操作:
message: 在此处提供的字符串将被设置为错误提示。messageContext: 如果提供,这将针对指定的区域显示错误提示(这与前面提到的noticeContexts相关)。 否则,提示将添加到noticeContexts.PAYMENTS区域。validationErrors: 这将设置为结账字段中的内联验证错误。 如果观察者希望触发验证错误,可以使用以下结构来定义错误:- 这是一个对象,其中键是验证错误的属性名称(对应于结账字段,例如
country或coupon),值是描述验证问题的错误消息。
- 这是一个对象,其中键是验证错误的属性名称(对应于结账字段,例如
如果响应对象不符合上述任何条件,则默认情况下将支付状态设置为 SUCCESS。
当支付状态设置为 SUCCESS 并且结账状态为 PROCESSING 时,CheckoutProcessor 组件将触发向服务器发送请求以处理订单。
可以通过使用 usePaymentEventsContext 钩子获取结账上下文,或者作为已注册组件的属性,将此事件发射器订阅者提供给支付方法扩展:
用于内部开发:
import { usePaymentEventsContext } from '@woocommerce/base-contexts';
import { useEffect } from '@wordpress/element';
const Component = () => {
const { onPaymentSetup } = usePaymentEventsContext();
useEffect( () => {
const unsubscribe = onPaymentSetup( () => true );
return unsubscribe;
}, [ onPaymentSetup ] );
return null;
};
用于已注册的支付方法组件:
const { useEffect } = window.wp.element;
const PaymentMethodComponent = ( { eventRegistration } ) => {
const { onPaymentSetup } = eventRegistration;
useEffect( () => {
const unsubscribe = onPaymentSetup( () => true );
return unsubscribe;
}, [ onPaymentSetup ] );
};
onCheckoutSuccess
当结账状态为 AFTER_PROCESSING 且结账的 hasError 状态为 false 时,会触发此事件发射器。AFTER_PROCESSING 状态由 CheckoutProcessor 组件在接收到服务器对结账处理请求的响应后设置。
注册到此事件发射器的观察者将接收以下对象作为参数:
const onCheckoutProcessingData = {
redirectUrl,
orderId,
customerId,
orderNotes,
paymentResult,
};
属性如下:
redirectUrl: 这是一个字符串,表示结账将重定向到的 URL,由服务器上的处理过程返回。orderId: 是当前正在处理的订单的 ID。customerId: 是进行购买的客户的 ID(该 ID 与订单相关联)。orderNotes: 这是客户在订单上留下的任何自定义备注。paymentResult: 这是来自/checkoutStoreApi 响应的payment_result的值 (https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/34e17c3622637dbe8b02fac47b5c9b9ebf9e3596/src/RestApi/StoreApi/Schemas/CheckoutSchema.php#L103-L138)。 此对象中暴露的数据(通过对象的属性):paymentStatus: 支付在服务器端处理后的状态。可能的值为success、failure、pending或error。paymentDetails: 这将是一个任意对象,包含付款方式处理服务器端在结账处理响应中返回给客户端的任何数据。付款方式可以通过钩子在服务器端进行处理,并设置此数据。
此事件发射器将依次调用所有已注册的观察者,直到任何一个观察者的响应不等于 true。 此时,任何未调用的观察者将被跳过,并且触发中止的观察者的响应将被处理。
此发射器将处理 success 类型的响应 ({ type: success }),并将结账状态设置为 COMPLETE。 此外,如果响应包含 redirectUrl,则结账将重定向到指定的地址。
此发射器还将处理 failure 类型的响应或 error 类型的响应,如果检测到无效的类型,则将其视为 error 类型的响应。
在所有情况下,如果响应中包含以下属性,则会执行额外的操作:
结账状态处理
以下描述了如何处理结账状态。
message: 此字符串将被添加为错误通知。
messageContext: 如果存在,则该通知将被配置为显示在指定的通知区域(否则,它将只是结账块的通用通知)。
retry: 如果此值为 true 或未定义,则结账状态将被设置为 IDLE。 这基本上意味着该错误是可以恢复的(例如,尝试不同的支付方式),因此结账将被重置为 IDLE 以供购物者再次尝试。 如果此值为 false,则结账状态将被设置为 COMPLETE,并且结账将重定向到当前设置为 redirectUrl 的 URL。
redirectUrl: 如果此参数存在,则当状态为 COMPLETE 时,结账将重定向到此 URL。
如果所有观察者都返回 true,则结账状态将仅设置为 COMPLETE。
此事件发射器订阅者可以通过使用 useCheckoutContext 钩子获取结账上下文,或者作为已注册组件的属性提供给支付方式扩展。
用于内部开发:
import { useCheckoutContext } from '@woocommerce/base-contexts';
import { useEffect } from '@wordpress/element';
const Component = () => {
const { onCheckoutSuccess } = useCheckoutContext();
useEffect( () => {
const unsubscribe = onCheckoutSuccess( () => true );
return unsubscribe;
}, [ onCheckoutSuccess ] );
return null;
};
用于已注册的支付方式组件:
const { useEffect } = window.wp.element;
const PaymentMethodComponent = ( { eventRegistration } ) => {
const { onCheckoutSuccess } = eventRegistration;
useEffect( () => {
const unsubscribe = onCheckoutSuccess( () => true );
return unsubscribe;
}, [ onCheckoutSuccess ] );
};
用于其他情况:
const { onCheckoutSuccess } = wc.blocksCheckoutEvents;
useEffect( () => {
const unsubscribe = onCheckoutSuccess( () => true );
return unsubscribe;
}, [ onCheckoutSuccess ] );
onCheckoutFail
当结账状态为 AFTER_PROCESSING 且结账的 hasError 状态为 true 时,会触发此事件发射器。AFTER_PROCESSING 状态由 CheckoutProcessor 组件在接收到服务器对结账处理请求的响应后设置。
注册到此发射器的观察者将接收到与注册到 onCheckoutSuccess 相同的的数据包。
来自第一个观察者的响应,如果返回的值不 === true,则处理方式与 onCheckoutSuccess 类似,但仅处理类型为 error 或 failure 的情况。
如果所有观察者都返回 true,则结账状态将直接设置为 IDLE,并在结账上下文中显示默认错误提示。
可以通过结账上下文使用 useCheckoutContext 钩子,或作为属性传递给已注册的支付方式组件来获取此事件发射器的订阅者:
用于内部开发:
import { useCheckoutContext } from '@woocommerce/base-contexts';
import { useEffect } from '@wordpress/element';
const Component = () => {
const { onCheckoutFail } = useCheckoutContext();
useEffect( () => {
const unsubscribe = onCheckoutFail( () => true );
return unsubscribe;
}, [ onCheckoutFail ] );
return null;
};
用于已注册的支付方式组件:
const { useEffect } = window.wp.element;
const PaymentMethodComponent = ( { eventRegistration } ) => {
const { onCheckoutFail } = eventRegistration;
useEffect( () => {
const unsubscribe = onCheckoutFail( () => true );
return unsubscribe;
}, [ onCheckoutFail ] );
};
用于其他情况:
const { onCheckoutFail } = wc.blocksCheckoutEvents;
useEffect( () => {
const unsubscribe = onCheckoutFail( () => true );
return unsubscribe;
}, [ onCheckoutFail ] );
onShippingRateSuccess
当运费信息未加载,且运费数据上下文的错误状态为 NONE,并且存在可用的运费信息时,会触发此事件发射器。
此事件发射器不关心任何已注册的观察者的响应,而是简单地执行所有已注册的观察者,并将从服务器检索到的当前运费信息传递给它们。
onShippingRateFail
当运费信息未加载,且运费数据上下文的错误状态为 UNKNOWN 或 INVALID_ADDRESS 时,会触发此事件发射器。
此事件发射器不关心任何已注册的观察者的响应,而是简单地执行所有已注册的观察者,并将当前的错误状态传递给它们。
onShippingRateSelectSuccess
当运费选择未持久化到服务器,并且存在已选择的运费信息,且当前上下文中的错误状态为 NONE 时,会触发此事件发射器。
此事件发射器不关心任何已注册的观察者的响应,而是简单地执行所有已注册的观察者,并将当前的已选择运费信息传递给它们。
onShippingRateSelectFail
当配送费率选择未能保存到服务器,并且配送数据上下文的错误状态为 UNKNOWN 或 INVALID_ADDRESS 时,会触发此事件发射器。
此事件发射器不关心任何已注册的观察者的响应,它将简单地执行所有已注册的观察者,并将当前的错误状态传递给它们。