how to prevent create new order when payment failed

Help for integrating the Laravel package
Forum rules
Always add your Laravel, Aimeos and PHP version as well as your environment (Linux/Mac/Win)
Spam and unrelated posts will be removed immediately!
Ahmad
Posts: 72
Joined: 05 Jul 2017, 15:19

how to prevent create new order when payment failed

Post by Ahmad » 17 Feb 2021, 16:31

Aimeos: 2020.10
Laravel: 7
PHP: 7.4

when customer redirected to payment provider and redirect back with an error, they can try again for payment (even without make any change to order/basket) but when they click on try again button new order with a new order id was added.
I want to prevent this and make change on it such a way that they try again to pay same order with same order id, how can I do this?

User avatar
aimeos
Administrator
Posts: 8380
Joined: 01 Jan 1970, 00:00

Re: how to prevent create new order when payment failed

Post by aimeos » 18 Feb 2021, 07:41

It's not possible because payment gateways require a new order ID for each payment. Already used order IDs are not accepted and the will return a "duplicate order ID" error in that case.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

Ahmad
Posts: 72
Joined: 05 Jul 2017, 15:19

Re: how to prevent create new order when payment failed

Post by Ahmad » 18 Feb 2021, 11:57

aimeos wrote: 18 Feb 2021, 07:41 It's not possible because payment gateways require a new order ID for each payment. Already used order IDs are not accepted and the will return a "duplicate order ID" error in that case.
my problem is when user goes back from payment gateway and I call verify method of payment gateway from updateSync method of my PaymentServiceProvider i get an error from gateway that say "another verify request is in process for this payment" but I don't know what happen, I think my customer refresh page manually when they back from gateway payment and in this moment I get multiple verification request error from gateway and throw an exception and customer see error message and try again button and he/she click on try again button and another order create in this moment and verification of previous order failed.

Furthermore my payment gateway provide this feature in api to send again order with same id (with created ref id I get from api on process request) to pay it, for example customer goes to gateway and mistakenly click on cancel and goes back to confirm page and try again to pay it.

how can i do for my problem with verifying of payment?

User avatar
aimeos
Administrator
Posts: 8380
Joined: 01 Jan 1970, 00:00

Re: how to prevent create new order when payment failed

Post by aimeos » 20 Feb 2021, 08:43

You should check the docs of the payment gateway or call their support.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

Ahmad
Posts: 72
Joined: 05 Jul 2017, 15:19

Re: how to prevent create new order when payment failed

Post by Ahmad » 20 Feb 2021, 14:02

aimeos wrote: 20 Feb 2021, 08:43 You should check the docs of the payment gateway or call their support.
no it there is no need to do this, i check the docs with payment gateway and after that realized that aimeos send several times requests to gateway (run separately process method of Payment Provider) when basket checkout, also it seems the checkout form submitted several times when customer click submit order button.
why this happen?

User avatar
aimeos
Administrator
Posts: 8380
Joined: 01 Jan 1970, 00:00

Re: how to prevent create new order when payment failed

Post by aimeos » 22 Feb 2021, 14:21

Did you implement a payment service provider on your own? Can you show the code of that service provider?
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

Ahmad
Posts: 72
Joined: 05 Jul 2017, 15:19

Re: how to prevent create new order when payment failed

Post by Ahmad » 28 Feb 2021, 16:04

aimeos wrote: 22 Feb 2021, 14:21 Did you implement a payment service provider on your own? Can you show the code of that service provider?
yes i implement a payment service provider, please read my description and help me to solve this problem.
here is process method of my service provider:

Code: Select all

    public function process(\Aimeos\MShop\Order\Item\Iface $order, array $params = array() ): \Aimeos\MShop\Common\Helper\Form\Iface
    {
        $basket = $this->getOrderBase( $order->getBaseId(), \Aimeos\MShop\Order\Item\Base\Base::PARTS_ADDRESS  );
        $total = $basket->getPrice()->getValue() + $basket->getPrice()->getCosts();

        $dataArray = [
            'payerName' => "",
            'amount' => intval($total),
            'payerIdentity' => "",
            'returnUrl' => $this->getConfigValue( array( 'payment.url-success' ) ),
            'description' => "Order No." . $order->getBaseId(),
            'clientRefId' => $order->getId(),
        ];

        $basketAddress = $basket->getAddresses()->first()[0];

        if($basketAddress) {
            $dataArray["payerName"] = $basketAddress->getFirstName() . " " . $basketAddress->getLastName();
            $dataArray["payerIdentity"] = $basketAddress->getTelephone();
        }

        $providerToken = "*******************************************************94";

        $response = Http::withHeaders([
            'Accept' => 'application/json',
            'Content-Type' => 'application/json',
            'Authorization' => 'bearer ' . $providerToken,
        ])->post('https://api.providerdomain/v2/pay', $dataArray);
        $checkTransactionResultResponseObj = json_decode($response->body());

        if($response->status() == 400 && $checkTransactionResultResponseObj->Error) throw new \Aimeos\MShop\Service\Exception( "error from gateway: " . $checkTransactionResultResponseObj->Error);

        if(isset($checkTransactionResultResponseObj->code)) {
            // define the payment information that should be sent to the external payment gateway
            $list = array(
                'MyOnlinePaymentProvider.Token' => new \Aimeos\MW\Criteria\Attribute\Standard( array(
                    'label' => 'Token ID',
                    'code' => 'MyOnlinePaymentProvider.code',
                    'internalcode' => 'code',
                    'internaltype' => 'string',
                    'type' => 'string',
                    'default' => $checkTransactionResultResponseObj->code,
                    'public' => false,
                ) ),
            );

            $orderBaseItem = $this->getOrderBase( $order->getBaseId(), \Aimeos\MShop\Order\Item\Base\Base::PARTS_ALL );
            $type = \Aimeos\MShop\Order\Item\Base\Service\Base::TYPE_PAYMENT;
            $serviceItem = $this->getBasketService( $orderBaseItem, $type, $this->getServiceItem()->getCode() );
            $this->setAttributes( $serviceItem, ['code' => $checkTransactionResultResponseObj->code], 'payment/providerName' );
            $this->saveOrderBase( $orderBaseItem );

            $status = \Aimeos\MShop\Order\Item\Base::PAY_PENDING;
            $order->setPaymentStatus( $status );
            $this->saveOrder( $order );

            $gatewayUrl = $this->getConfigValue( array( 'myprovider.url' ), 'https://api.providerdomain/v2/pay/gotoipg/' . $checkTransactionResultResponseObj->code );
            return new \Aimeos\MShop\Common\Helper\Form\Standard( $gatewayUrl, 'GET', $list );
        } else {
            throw new \Aimeos\MShop\Service\Exception( "payment error" );
        }
the process of this method is executed correctly and the reference id that received from provider saved correctly in the payment service attributes and customer redirect to payment provider URL to pay, but i think in this step customer tap the refresh button in its own browser, after it another order was created and the process method of my own payment provider execute again.
my customer mostly open my shop from link in Instagram story and they see shop page in their in-app Instagram browser and process of redirection to payment gateway URL may take a few seconds and customers refresh page in this moment.

I say this because I see several similar orders from 1 person with a time difference of a few seconds (1 to 5 seconds) in my shop admin orders list, those processed my own payment provider process method. (because they have reference id that received from provider and have status PAY_PENDING that I set in the process method).


Because of this I want to prevent create new order in this situation and just redirect again customer with reference id that received from provider and saved in to order payment service attributes which is still valid and accepted by the payment provider.

several similar orders caused the product to run out of stock by mistake.
please help me to solve this.

User avatar
aimeos
Administrator
Posts: 8380
Joined: 01 Jan 1970, 00:00

Re: how to prevent create new order when payment failed

Post by aimeos » 03 Mar 2021, 09:03

We are still evaluating how we can prevent creating new orders when customers are reloading the "process" page. Our current idea is to use the getSessionLock/setSessionLock() methods in the order base manager:
https://github.com/aimeos/aimeos-core/b ... #L103-L171

We could use them here to set and test the lock:
https://github.com/aimeos/ai-controller ... #L122-L190

Problem is that the session might be written at the end of the request and under heavy load, there is still a race condition that could create more than one order.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

User avatar
aimeos
Administrator
Posts: 8380
Joined: 01 Jan 1970, 00:00

Re: how to prevent create new order when payment failed

Post by aimeos » 03 Mar 2021, 09:07

Ahmad wrote: 28 Feb 2021, 16:04 several similar orders caused the product to run out of stock by mistake.
If you execute the cronjob for removing unpaid orders, the items are returned to stock:
https://aimeos.org/docs/latest/laravel/ ... once-a-day

Maybe you have to adapt the timeframe how long orders are kept but don't set the time frame too short:
https://aimeos.org/docs/2020.x/config/c ... keep-hours
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

Post Reply