关于 b2c-ordering
面向 Salesforce B2C Commerce 开发者的订单管理技能,涵盖 OrderMgr 创建、查询及状态转换,并说明 SCAPI 支付授权与订单放置流程。
主要功能
- 01订单生命周期与状态转换
- 02创建及查询订单
- 03取消、失败与恢复购物篮
- 04SCAPI 支付和订单放置指导
适用场景
- 01实现结账后的订单创建
- 02处理支付失败并允许重试
- 03按状态或日期检索订单
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ salesforcecommercecloud/ b2c-developer-tooling --skill b2c-ordering安装后在 Claude Code 中输入/b2c-ordering确认已加载。
附带文件evals/trigger-evals.json
详细介绍
原文在翻译后有更新,译文可能不是最新。
B2C 订购
OrderMgr API 提供订单创建、状态管理和查询功能。了解订单生命周期对于结账实现和订单处理至关重要。
订单生命周期
订单会经历以下状态:
Basket → CREATED → NEW → (COMPLETED or CANCELLED or FAILED)
| 状态 | 描述 | 可转换为 |
|---|---|---|
CREATED | 订单已创建,尚未提交 | NEW、FAILED |
NEW | 订单已提交,等待履约 | OPEN、COMPLETED、CANCELLED、FAILED |
OPEN | 订单正在处理中 | COMPLETED、CANCELLED |
COMPLETED | 订单已履约 | - |
CANCELLED | 订单已取消 | NEW(通过 undoCancel) |
FAILED | 订单失败(支付、验证) | -(无法重新打开) |
重要: 订单一旦进入 FAILED 状态,就无法重新打开或取消。请使用 failOrder(order, true) 重新打开购物篮以便重试。
创建订单
标准流程(同步)
var OrderMgr = require('dw/order/OrderMgr');
var Transaction = require('dw/system/Transaction');
var Status = require('dw/system/Status');
function createOrder(basket) {
var order;
Transaction.wrap(function() {
// Create order from basket (status: CREATED)
order = OrderMgr.createOrder(basket);
});
if (!order) {
return { error: true, message: 'Order creation failed' };
}
// Authorize payment
var paymentResult = authorizePayment(order);
if (!paymentResult.success) {
Transaction.wrap(function() {
OrderMgr.failOrder(order, true); // Reopen basket
});
return { error: true, message: 'Payment failed' };
}
// Place the order (status: CREATED → NEW)
var placeResult;
Transaction.wrap(function() {
placeResult = OrderMgr.placeOrder(order);
});
if (placeResult.error) {
return { error: true, message: 'Order placement failed' };
}
// Set confirmation status
Transaction.wrap(function() {
order.setConfirmationStatus(order.CONFIRMATION_STATUS_CONFIRMED);
});
return { error: false, order: order };
}
SCAPI 流程:创建、通过 Order PI 授权,然后提交
优先使用 Shopper Orders 状态机,而不要从 order.afterPOST 手动提交:
POST /checkout/shopper-orders/v1/organizations/{orgId}/orders
-> commits order in CREATED
PATCH /checkout/shopper-orders/v1/organizations/{orgId}/orders/{orderNo}/payment-instruments/{piId}
-> invokes dw.order.payment.authorizeCreditCard or dw.order.payment.authorize
-> passes successfullyAuthorized to order.payment_instrument.afterPATCH
-> default afterPATCH places when authorized coverage reaches the order total
NEW -> success
CREATED -> not fully placed; authorize remaining PIs, fail safely, or reconcile
成功的自定义订单 PI afterPATCH 实现应返回 undefined,这样平台就会执行基于覆盖范围的默认提交行为。非空的 Status 会终止执行并抑制该实现。
发生确定性的支付失败时,请使用 Shopper Orders fail 操作,并设置 reopenBasket=true。
如果网关结果不确定,应保留 CREATED 订单以便对账,而不要冒重复授权的风险。
当结账流程确实需要通过单个服务器端阶段创建订单、授权并执行 Commerce 端的提交或失败转换,或者处理方无法参与订单 PI 授权时,可使用 dw.ocapi.shop.order.afterPOST 作为替代方案。该钩子在订单创建事务中运行:不要添加嵌套的 Transaction.wrap,并且要了解 Status.ERROR 会回滚 Commerce 订单,但不会回滚外部网关副作用。请参阅
b2c-hooks。
OrderMgr API 参考
订单创建
| 方法 | 描述 |
|---|---|
createOrder(basket) | 创建订单并自动生成订单号 |
createOrder(basket, orderNo) | 使用指定编号创建订单 |
createOrderNo() | 生成下一个订单号 |
createOrderSequenceNo() | 获取下一个序列号(用于自定义格式) |
订单状态
| 方法 | 描述 |
|---|---|
placeOrder(order) | 提交订单(CREATED → NEW) |
failOrder(order, reopenBasket) | 将订单设为失败(状态设为 FAILED) |
cancelOrder(order) | 取消订单(状态设为 CANCELLED) |
undoCancelOrder(order) | 将已取消的订单恢复为 NEW |
注意: 没有 undoFailOrder() 方法。失败的订单无法重新打开。请使用 failOrder(order, true) 重新打开购物篮以便重试。
订单查询
| 方法 | 描述 |
|---|---|
getOrder(orderNo) | 按编号获取订单 |
searchOrder(query, ...args) | 搜索单个订单 |
searchOrders(query, sortString, ...args) | 搜索多个订单 |
queryOrder(query, ...args) | 查询单个订单 |
queryOrders(query, sortString, ...args) | 查询多个订单 |
查询订单
按编号获取订单
var OrderMgr = require('dw/order/OrderMgr');
var order = OrderMgr.getOrder('00001234');
if (order) {
var status = order.status.value;
var total = order.totalGrossPrice;
}
搜索订单
var OrderMgr = require('dw/order/OrderMgr');
// Search by customer email
var orders = OrderMgr.searchOrders(
'customerEmail = {0} AND status != {1}',
'creationDate desc',
'[email protected]',
dw.order.Order.ORDER_STATUS_FAILED
);
while (orders.hasNext()) {
var order = orders.next();
// Process order
}
orders.close();
按日期范围查询
var OrderMgr = require('dw/order/OrderMgr');
var Calendar = require('dw/util/Calendar');
var startDate = new Calendar();
startDate.add(Calendar.DAY_OF_YEAR, -7);
var orders = OrderMgr.searchOrders(
'creationDate >= {0} AND status = {1}',
'creationDate desc',
startDate.time,
dw.order.Order.ORDER_STATUS_NEW
);
while (orders.hasNext()) {
var order = orders.next();
// Process order
}
orders.close();
订单状态管理
取消订单
var OrderMgr = require('dw/order/OrderMgr');
var Transaction = require('dw/system/Transaction');
var Order = require('dw/order/Order');
function cancelOrder(orderNo) {
var order = OrderMgr.getOrder(orderNo);
if (!order) {
return { error: true, message: 'Order not found' };
}
// Can only cancel NEW or OPEN orders
if (order.status.value !== Order.ORDER_STATUS_NEW &&
order.status.value !== Order.ORDER_STATUS_OPEN) {
return { error: true, message: 'Order cannot be cancelled' };
}
Transaction.wrap(function() {
OrderMgr.cancelOrder(order);
});
return { error: false };
}
使订单失败
var OrderMgr = require('dw/order/OrderMgr');
var Transaction = require('dw/system/Transaction');
function failOrder(order, reopenBasket) {
// reopenBasket: true = customer can retry checkout
// false = basket is lost
Transaction.wrap(function() {
OrderMgr.failOrder(order, reopenBasket);
});
}
处理失败的订单
失败的订单无法重新打开。 请改用 failOrder(order, true) 重新打开购物篮:
var OrderMgr = require('dw/order/OrderMgr');
var Transaction = require('dw/system/Transaction');
// When payment fails, fail the order and reopen basket
function handlePaymentFailure(order) {
Transaction.wrap(function() {
// reopenBasket=true allows customer to retry checkout
OrderMgr.failOrder(order, true);
});
// Basket is now available again for the customer
return { error: true, message: 'Payment failed. Please try again.' };
}
SCAPI:失败订单并重新打开购物篮
对于 SCAPI 集成,在确定支付失败后使用 Shopper Orders 失败操作:
POST /checkout/shopper-orders/v1/organizations/{orgId}/orders/{orderNo}/actions/fail?siteId={siteId}&reopenBasket=true
Authorization: Bearer {token}
Content-Type: application/json
{
"reasonCode": "payment_auth_failure"
}
当前面向支付的原因代码包括 payment_auth_failure、payment_confirm_failure 和
payment_capture_failure。如果订单已不处于可失败的状态,端点会返回 409。生成特定版本的代码前,请确认当前的 Shopper Orders 架构。
撤销已取消的订单
可以使用 undoCancelOrder() 重新打开已取消的订单:
var OrderMgr = require('dw/order/OrderMgr');
var Transaction = require('dw/system/Transaction');
var Order = require('dw/order/Order');
function reopenCancelledOrder(orderNo) {
var order = OrderMgr.getOrder(orderNo);
if (order.status.value !== Order.ORDER_STATUS_CANCELLED) {
return { error: true, message: 'Order is not cancelled' };
}
Transaction.wrap(function() {
// Revert to NEW status
OrderMgr.undoCancelOrder(order);
});
return { error: false, order: order };
}
订单属性
| 属性 | 描述 |
|---|---|
orderNo | 订单编号 |
status | 当前订单状态 |
confirmationStatus | 确认状态 |
exportStatus | OMS 的导出状态 |
paymentStatus | 支付状态 |
shippingStatus | 配送状态 |
customerEmail | 客户邮箱 |
customerName | 客户姓名 |
totalGrossPrice | 订单总额(含税) |
totalNetPrice | 订单总额(不含税) |
totalTax | 税额合计 |
creationDate | 订单创建日期 |
productLineItems | 订单中的商品项 |
shipments | 订单配送单 |
paymentInstruments | 支付方式 |
自定义订单编号
使用 dw.order.createOrderNo hook 自定义订单编号生成:
// hooks.json
{
"hooks": [
{
"name": "dw.order.createOrderNo",
"script": "./hooks/orderNo.js"
}
]
}
// hooks/orderNo.js
var OrderMgr = require('dw/order/OrderMgr');
var Site = require('dw/system/Site');
exports.createOrderNo = function() {
var seqNo = OrderMgr.createOrderSequenceNo();
var prefix = Site.current.ID.toUpperCase();
var year = new Date().getFullYear();
return prefix + '-' + year + '-' + seqNo;
};
最佳实践
应该
- 仅当调用方尚未提供事务时,才将 Script API 订单修改操作包装在事务中
- 在状态转换前检查订单状态
- 完成后关闭订单迭代器
- 使用
failOrder(order, true)以便客户重试 - 实现幂等的订单创建(使用特定订单编号)
- 设置适当的导出状态和确认状态
不应该
- 在支付授权成功前下单
- 未处理退款就取消订单
- 让订单无限期处于 CREATED 状态
- 忘记处理并发订单修改
- 状态转换前跳过状态验证
错误处理
| 场景 | 解决方案 |
|---|---|
| 购物篮为空 | 在调用 createOrder() 前验证购物篮 |
| 购物篮无效 | 检查是否缺少配送地址或账单地址 |
| 支付失败 | 使用 failOrder(order, true) 重新打开购物篮 |
| 订单编号已存在 | 使用自动生成的编号或验证其唯一性 |
| 状态转换无效 | 调用状态方法前检查当前状态 |
相关技能
- b2c-hooks - 订单 hooks(calculate、payment、createOrderNo)