由于几个不同的原因,此屏幕难以通过 Web 服务使用:
- 屏幕上没有可用于定位付款方式的唯一键。内部键是一个名为 PMInstanceID 的字段,虽然技术上您可以通过 Web 服务命令引用它,但使用起来并不明显
- 在 Acumatica 中,一旦记录了使用此付款方式的一笔交易,付款方式详细信息就会被锁定。这意味着要对其进行修改,您需要创建一种新的付款方式,并将旧的付款方式设为无效。
- 屏幕中有一个错误,该错误仅在通过 Web 服务使用时才会出现,这与上述项目有关。加载支付方式时,如果有任何交易,系统将禁用支付方式详细信息,但如果没有,系统将无法重新启用它。从网络浏览器使用 Acumatica 时,这绝不是问题,因为在每次往返之间会自动重新启用这些字段。但是,当通过 Web 服务自动执行此屏幕时,您实际上是在一次往返中执行所有操作。已报告此问题并将很快修复(内部 JIRA 参考为 AC-54456)
话虽如此,我们可以使用一些东西来使此屏幕与其他屏幕一样易于通过 Web 服务使用,并解决其局限性。通过创建自定义项目,我们可以在屏幕上添加 PMInstanceID 字段(以下简称 Token ID 字段)。您可以将此令牌存储在您的系统上,并将其用于付款方式的未来操作。在同一个定制项目中,我们还可以始终启用付款方式详细信息,让您可以更新现有卡的到期日期。这样做还解决了上面提到的系统不允许您向系统添加任何新付款方式的错误。自定义分为两部分:
- 覆盖
CustomerPaymentMethod DAC 以使 PMInstanceID 字段可从 UI 访问。这是通过将 PXUIField 属性附加到 PMInstanceID 字段来完成的:[PXUIField(DisplayName="Token ID", Visibility=PXUIVisibility.SelectorVisible)]。之后,可以使用布局编辑器将字段添加到屏幕。
-
处理CustomerPaymentMethod_RowSelected 事件以强制始终启用付款方式详细信息。在添加新的支付方式时,我们还使用此事件处理程序来隐藏 Token ID 字段,因为在保存支付方式之前,该字段将显示int.MinValue。该事件的完整代码如下所示:
protected void CustomerPaymentMethod_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
{
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
// Force the payment method details to always be enabled to facilitate working via web services
PXUIFieldAttribute.SetEnabled(Base.Details.Cache, null, true);
// When adding a new method, field will have a temporary value corresponding to int.MinValue - don't show it
PXUIFieldAttribute.SetVisible<CustomerPaymentMethod.pMInstanceID>(cache, e.Row, cache.GetStatus(e.Row) != PXEntryStatus.Inserted);
}
定制发布后,更新现有支付方式会变得容易得多。下面的代码显示了如何添加付款方式,并检索您稍后将用于更新付款方式的令牌 ID:
public int AddCreditCard(string customerID, string paymentMethod, string cardNumber, string expirationDate, string cvv, string nameOnCard)
{
if(_AR303010 == null) _AR303010 = _context.AR303010GetSchema();
_context.AR303010Clear();
var commands = new Command[]
{
new Value { Value = customerID, LinkedCommand = _AR303010.PaymentMethodSelection.Customer },
new Value { Commit = true, LinkedCommand = _AR303010.Actions.Insert },
new Value { Value = paymentMethod, LinkedCommand = _AR303010.PaymentMethodSelection.PaymentMethod },
new Value { Value = "CCDNUM", LinkedCommand = _AR303010.PaymentMethodDetails.Description },
new Value { Value = cardNumber, LinkedCommand = _AR303010.PaymentMethodDetails.Value, Commit = true },
new Value { Value = "EXPDATE", LinkedCommand = _AR303010.PaymentMethodDetails.Description },
new Value { Value = expirationDate, LinkedCommand = _AR303010.PaymentMethodDetails.Value, Commit = true},
new Value { Value = "CVV", LinkedCommand = _AR303010.PaymentMethodDetails.Description },
new Value { Value = cvv, LinkedCommand = _AR303010.PaymentMethodDetails.Value, Commit = true },
new Value { Value = "NAMEONCC", LinkedCommand = _AR303010.PaymentMethodDetails.Description },
new Value { Value = nameOnCard, LinkedCommand = _AR303010.PaymentMethodDetails.Value, Commit = true },
_AR303010.Actions.Save,
_AR303010.PaymentMethodSelection.TokenID
};
var result = _context.AR303010Submit(commands.ToArray());
return int.Parse(result[0].PaymentMethodSelection.TokenID.Value);
}
public void UpdateCreditCardExpirationDate(string customerID, string paymentMethod, int tokenID, string expirationDate)
{
if (_AR303010 == null) _AR303010 = _context.AR303010GetSchema();
_context.AR303010Clear();
var commands = new Command[]
{
new Value { Value = customerID, LinkedCommand = _AR303010.PaymentMethodSelection.Customer },
new Value { Commit = true, LinkedCommand = _AR303010.Actions.Insert },
new Value { Value = paymentMethod, LinkedCommand = _AR303010.PaymentMethodSelection.PaymentMethod },
new Value { Value = tokenID.ToString(), LinkedCommand = _AR303010.PaymentMethodSelection.TokenID },
new Value { Value = "EXPDATE", LinkedCommand = _AR303010.PaymentMethodDetails.Description },
new Value { Value = expirationDate, LinkedCommand = _AR303010.PaymentMethodDetails.Value, Commit = true},
_AR303010.Actions.Save,
};
var result = _context.AR303010Submit(commands.ToArray());
}
public void MakeCardInactive(string customerID, string paymentMethod, int tokenID)
{
if (_AR303010 == null) _AR303010 = _context.AR303010GetSchema();
_context.AR303010Clear();
var commands = new Command[]
{
new Value { Value = customerID, LinkedCommand = _AR303010.PaymentMethodSelection.Customer },
new Value { Commit = true, LinkedCommand = _AR303010.Actions.Insert },
new Value { Value = paymentMethod, LinkedCommand = _AR303010.PaymentMethodSelection.PaymentMethod },
new Value { Value = tokenID.ToString(), LinkedCommand = _AR303010.PaymentMethodSelection.TokenID },
new Value { Value = "False", LinkedCommand = _AR303010.PaymentMethodSelection.Active },
_AR303010.Actions.Save,
};
var result = _context.AR303010Submit(commands.ToArray());
}
我把这3个函数封装成一个类,实际使用就变得很简单了:
var paymentMethodManager = new PaymentMethodManager(context);
int tokenID = paymentMethodManager.AddCreditCard("ABARTENDE", "MASTERCARD", "5111111111111118", "122016", "123", "John Doe");
paymentMethodManager.UpdateCreditCardExpirationDate("ABARTENDE", "MASTERCARD", tokenID, "032017");
paymentMethodManager.MakeCardInactive("ABARTENDE", "MASTERCARD", tokenID);
目前,无法删除现有的付款方式,您必须将其设为非活动状态。我已经提出了这个增强的请求,它可能会在未来出现。
注意:我已将此答案中使用的所有代码放在 GitHub 上 https://github.com/gmichaud/acumatica-paymentmethod-ws-extensions。