[5.x]: Return line item order errors in PaymentsController::actionPay()
What happened?
Description
I am developing a React application that uses JSON requests to call the Commerce action end points.
I have a custom line item rule being for a custom ticket purchasable that determines whether the maximum quantity of that purchasable against a third-party system.
/**
* Register validation rules for when adding Ticket purchasable to a line item.
*
* This event is called before a line item is created and also when an order is
* updated, marked as completed or about to be paid.
*/
Event::on(LineItem::class, Entry::EVENT_DEFINE_RULES, function(DefineRulesEvent $e) {
/** @var LineItem $lineItem */
$lineItem = $e->sender;
if (!$lineItem->getPurchasable()) {
return;
}
if ($lineItem->getPurchasable()::class !== Ticket::class) {
return;
}
$maxPurchasableTicketQuantity = $lineItem->getPurchasable()->getMaxQuantity();
$e->rules[] = ['qty', 'integer', 'min' => 1, 'max' => $maxPurchasableTicketQuantity];
});
Say I have 5 tickets in my cart when I arrive at my payment step. I then complete the payment form and submit it. In the meantime (prior to submitting the payment form), it's possible that the maximum quantity of available tickets has now reduced to less than 5 (e.g. 1 max).
At some point in the PaymentsController::actionPay(), the line item validation is executing and detecting the qty is now invalid. This means the order now has errors. Consequently, this triggers the following response from actionPay() to be returned:
{
"cart": {
...
},
"paymentFormErrors": [],
"modelName": "paymentForm",
"paymentForm": {
"nonce": "tokencc_bj_znzy56_27bbxm_b5sr7x_37d5js_9q2",
"storeInVault": false,
"firstName": null,
"lastName": null,
"number": "",
"month": null,
"year": null,
"cvv": null,
"token": null,
"expiry": null,
"threeDSecure": false,
"savePaymentSource": false
},
"errors": [],
"message": "Invalid payment or order. Please review."
}
The problem is that the errors for the order are not accessible in the response, so there's no easy way to indicate to the user what the actual error is.
Debugging the PHP code in actionPay() shows that $order->getErrors() returns this:
{ ["lineItems.0.qty"]=> array(1) { [0]=> string(30) "Qty must be no greater than 1." } }
I would have expected the order errors are returned in the actionPay() response.
Craft CMS version
5.7.5
Craft Commerce version
5.3.12
PHP version
8.2
Operating system and version
No response
Database type and version
No response
Image driver and version
No response
Installed plugins and versions
See also https://github.com/craftcms/commerce/issues/601.
@timeverts thanks for the feedback. I agree we should be bubbling errors on the cart back during the payment response. Will look to make a fix soon.
@timeverts have a potential fix in PR #4081 - will let you know here once this is included in the next release.
Commerce 5.4.4 is out with that change.