Skip to content

[5.x]: Return line item order errors in PaymentsController::actionPay() #4026

@timeverts

Description

@timeverts

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions