通过 REST API 添加带 Pre-Authorized Payment 的 Sales Order 失败
结论
创建 Sales Order 时,不要在第一次 PUT 请求中同时提交 Payments。正确做法是分两步处理:
- 先创建 Sales Order,并从响应中获取 Acumatica 实际生成的
OrderType和OrderNbr。 - 再用这两个字段定位已创建的订单,提交
Payments,将 Pre-Authorized Payment 应用到该订单。
这个错误并不表示信用卡授权号不存在。Acumatica 在处理 Payments 时,需要将付款应用到一张已经存在的订单。第一次请求执行到付款逻辑时,新订单可能还没有保存完成,或者请求中没有可用于重新定位订单的 OrderNbr,所以系统找不到对应的订单类型和订单号组合。
为什么本地可以,而 Acumatica.com 失败
两个环境的行为不同,通常与以下差异有关:
- Acumatica 版本或补丁级别不同。
- Contract-Based REST Endpoint 的版本或扩展字段定义不同。
- 本地环境安装了定制代码,而 Acumatica.com 使用了另一套事件处理逻辑。
- 两个环境的 Sales Order 编号规则、支付方式、Processing Center 或 Payment Method 配置不同。
- 本地环境可能恰好允许在保存父记录的过程中插入付款,但这种处理顺序并不可靠。
Payments 不是普通的订单明细。添加付款时,Acumatica 会执行”将付款应用到订单”的业务逻辑,因此必须能通过 OrderType + OrderNbr 找到已经持久化的订单。
第一次请求中只有:
"OrderType": {
"value": "SO"
}
但没有确定的 OrderNbr。如果订单号由 Acumatica 自动生成,付款处理开始时可能还没有有效的目标订单,于是系统报错:
The order cannot be applied, the specified combination of the order type and order number cannot be found in the system.
正确的处理流程
第一步:创建 Sales Order
第一次调用只发送订单头、地址和明细,不发送 Payments:
var request = _mapper.Map<DataModel.SalesOrder>(order);
// 新订单创建阶段不要提交 Payments
request.Payments = null;
DataModel.SalesOrder createdOrder =
await api.PutEntityAsync(request, null, null, null, null);
string orderType = createdOrder.OrderType?.Value;
string orderNbr = createdOrder.OrderNbr?.Value;
if (string.IsNullOrWhiteSpace(orderType) ||
string.IsNullOrWhiteSpace(orderNbr))
{
throw new InvalidOperationException(
"Acumatica did not return OrderType or OrderNbr.");
}
后续操作必须使用 Acumatica 响应中的 OrderNbr。不要把外部系统的 order.OrderNumber 当成 Acumatica 已经生成的 Sales Order 编号,除非当前环境明确允许外部指定订单号。
第二步:向已创建的订单添加付款
接下来构造更新请求,用明确的订单键定位刚创建的订单:
var payment = new DataModel.SalesOrderPayment
{
Description = new StringValue
{
Value = $"Payment for {orderNbr}"
},
PaymentMethod = new StringValue
{
Value = "CUSTOMPAYMC"
},
CardAccountNbr = new StringValue
{
Value = order.PaymentDetails.CardID
},
CashAccount = new StringValue
{
Value = "10600"
},
ProcessingCenterID = new StringValue
{
Value = "CUSTOMPAY"
},
PaymentAmount = new DecimalValue
{
Value = order.Header.TotalAmount
},
AppliedToOrder = new DecimalValue
{
Value = order.Header.TotalAmount
}
};
var transaction = new DataModel.SalesOrderCreditCardTransactionDetail
{
AuthNbr = new StringValue
{
Value = order.PaymentDetails.AuthorizationNumber
},
NeedValidation = new BooleanValue
{
Value = false
},
TranDate = new DateTimeValue
{
Value = order.PaymentDetails.AuthorizationDateTime
},
TranNbr = new StringValue
{
Value = order.PaymentDetails.TransactionID
},
TranType = new StringValue
{
Value = "Authorize Only"
}
};
payment.CreditCardTransactionInfo =
new List<DataModel.SalesOrderCreditCardTransactionDetail>
{
transaction
};
var paymentRequest = new DataModel.SalesOrder
{
OrderType = new StringValue
{
Value = orderType
},
OrderNbr = new StringValue
{
Value = orderNbr
},
Payments = new List<DataModel.SalesOrderPayment>
{
payment
}
};
DataModel.SalesOrder updatedOrder =
await api.PutEntityAsync(paymentRequest, null, null, null, null);
即使 SDK 的 PutEntityAsync 支持通过 URL 参数传入实体键,也建议在请求体中保留 OrderType 和 OrderNbr,以免付款应用逻辑取不到订单键。具体的方法签名取决于所使用的 Acumatica REST SDK 和 Endpoint 定义。
原代码中还需要修正的问题
原代码虽然创建了 detail,却没有把它加入集合:
payment.CreditCardTransactionInfo = new List();
// payment.CreditCardTransactionInfo.Add(detail);
由于 Add 被注释掉,实际请求中不会包含授权交易明细。可以改为:
payment.CreditCardTransactionInfo =
new List<DataModel.SalesOrderCreditCardTransactionDetail>
{
detail
};
也可以先创建集合,再添加明细:
payment.CreditCardTransactionInfo =
new List<DataModel.SalesOrderCreditCardTransactionDetail>();
payment.CreditCardTransactionInfo.Add(detail);
另外,除非当前 Endpoint 明确将以下字段声明为可写,否则不建议主动写入这类由系统计算或只读的字段:
payment.Status = new StringValue
{
Value = "Pre-Authorized"
};
Status 通常应由支付处理结果决定。仅将它设为 "Pre-Authorized",并不会创建有效的预授权交易。订单中的 OrderTotal、OrderedQty、OpenQty、UnbilledAmount 和行金额合计等字段,也应尽量交给 Acumatica 计算。
配置检查
即使拆成两次请求,以下配置不一致仍可能导致付款失败:
CUSTOMPAYMC是否存在,并且允许用于目标客户。CardAccountNbr是否是该客户在 Acumatica 中有效的 Payment Profile,而不是任意拼接的字符串。CUSTOMPAYProcessing Center 是否存在,并且已与 Payment Method 正确关联。10600是否是当前 Branch 可用的 Cash Account。- Endpoint 是否公开了
Payments、AppliedToOrder和CreditCardTransactionInfo。 TranType是否允许使用"Authorize Only"。- 当前 Acumatica 版本是否允许通过 Contract-Based REST API 导入外部授权交易。
最后一点需要特别确认。部分版本或支付中心只允许通过 Acumatica 的支付处理 Action 生成预授权记录,不允许直接写入 CreditCardTransactionInfo。如果第二次 PUT 能添加付款,却无法建立 Pre-Authorized 状态,需要检查对应实例的 Endpoint 是否提供相关 Action,并按该版本支持的”授权”、“记录外部交易”或付款创建流程调用。不能只靠写入 Status、AuthNbr 和 TranNbr 来模拟预授权。
处理时应先保存订单,让它获得有效的 OrderNbr,再创建或关联付款。预授权状态则应通过该实例实际支持的支付业务流程生成。
备注:内容仅供参考。
