Unexpected PayPal order status CREATED

Created on 18 November 2022, over 2 years ago
Updated 3 January 2025, 4 months ago

Hi, thank you for your wonderful module.

Since the upgrade yesterday (17 November) Paypal on my site gives errors when users checkout, so does not fire payments anymore.

Error in dblog: Unexpected PayPal order status CREATED

commerce-paypal/checkout-approve/paypal/3570?skip_payment_creation=1

I'm reverting to 8.x-1.1, version before 2 most previous updates.

🐛 Bug report
Status

Active

Version

1.0

Component

PayPal Checkout

Created by

🇫🇷France liliplanet Cannes

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇩🇪Germany Anybody Porta Westfalica

    I can confirm this issue, just had this in the production shop logs:

    Unexpected PayPal order status CREATED.

    The URL logged is: /commerce-paypal/checkout-approve/paypal_checkout/4385?skip_payment_creation=1

  • 🇭🇺Hungary ibis

    Anyone is using together with these module the webform_product module?
    My scenario is:
    I have some normal products and one webform with webform_product handler assigned.
    If the webform_product is on, the users cannot buy the normal products because this issue (the webform product works good).
    If i switch off the webform_product, then the normal products works fine.
    And ofc. the issue is do not coming in paypal sandbox mode, only in live mode, so the testing is more complicated..

  • 🇩🇪Germany Anybody Porta Westfalica

    No, we're having this in regular Drupal Commerce 2.x

  • 🇮🇱Israel jsacksick

    @anybody: Some payments are failing or all of them? I believe this just means the payment wasn't approved or something?
    This is the code:

        if (!in_array($paypal_order['status'], ['APPROVED', 'SAVED'])) {
          throw new PaymentGatewayException(sprintf('Unexpected PayPal order status %s.', $paypal_order['status']));
        }

    The expected PayPal order statuses are 'approved' or 'saved', so this probably simply means that the payment wasn't approved?

  • 🇩🇪Germany Anybody Porta Westfalica

    @jsacksick some payments are failing, I think about 5% (which is quite a lot).

    I guess we need to investigate what CREATED means and how it should be handled? Doesn't sound like something that should create an exception. The api docs say:

    status	
    string [ 1 .. 255 ] characters ^[0-9A-Z_]+$
    
    The order status.
    Enum Value 	Description
    CREATED	
    The order was created with the specified context.
    
    SAVED	
    The order was saved and persisted. The order status continues to be in progress until a capture is made with final_capture = true for all purchase units within the order.
    APPROVED	
    
    The customer approved the payment through the PayPal wallet or another form of guest or unbranded payment. For example, a card, bank account, or so on.
    
    VOIDED	
    All purchase units in the order are voided.
    
    COMPLETED	
    The intent of the order was completed and a payments resource was created. Important: Check the payment status in purchase_units[].payments.captures[].status before fulfilling the order. A completed order can indicate a payment was authorized, an authorized payment was captured, or a payment was declined.
    
    PAYER_ACTION_REQUIRED	
    The order requires an action from the payer (e.g. 3DS authentication). Redirect the payer to the "rel":"payer-action" HATEOAS link returned as part of the response prior to authorizing or capturing the order. Some payment sources may not return a payer-action HATEOAS link (eg. MB WAY). For these payment sources the payer-action is managed by the scheme itself (eg. through SMS, email, in-app notification, etc). 
    

    So this also looks related to 🐛 Needs to redirect users on UNPROCESSABLE_ENTITY > PAYER_ACTION_REQUIRED - No checkout possible Active where additional handling of PAYER_ACTION_REQUIRED is needed.

  • 🇮🇱Israel jsacksick

    First the PayPal order is created when clicking on the smart payments button. When the payment is approved the expected state is APPROVED or SAVED. But if it's still CREATED, we can''t assume the payment was approved...

  • 🇩🇪Germany Grevil

    Yea, I am with @jsacksick on this one. Once the user approves a payment during the PayPal checkout process, our "onApprove" js function is automatically called. This method makes an ajax call to our "CheckoutController", which invokes the "onApprove" method. This method invokes "onReturn". Where we have the code in question in place:

        if (!in_array($paypal_order['status'], ['APPROVED', 'SAVED'])) {
          throw new PaymentGatewayException(sprintf('Unexpected PayPal order status %s.', $paypal_order['status']));
        }
    

    At this stage, we don't want any other order-state besides "APPROVED" or "SAVED". It is still unclear to me, how we can have any other state at this point. Since the whole validation starts AFTER the user approves the payment. And at this point I expect paypal to switch states from CREATED to APPROVED / SAVED.

    The only reason I see, would be a race condition, where the state has not yet switched from CREATED to APPROVED / SAVED and "onApprove" has already started. But the react implementation for example also doesn't check the state before processing "onApprove" (https://github.com/paypal/paypal-js/blob/a2e716e0a0982fe79c8c51e8c04e187...) so I guess this is pretty safe...

    Another reason could be, that the JSON sent to PayPal at the start of the transaction is broken / incorrect, but that should leave us with "PAYER_ACTION_REQUIRED" like in 🐛 Needs to redirect users on UNPROCESSABLE_ENTITY > PAYER_ACTION_REQUIRED - No checkout possible Active and is also very unlikely, since at the time "onApprove" fires PayPal approves the payment...

    Lastly, eventually the user or some random js plugin removes the "skip_payment_creation" url query parameter, which would lead to a new payment creation at the end of the "onApprove" php callback:

        if ($flow === 'mark' && !$request->query->has('skip_payment_creation')) {
          $payment_storage = $this->entityTypeManager->getStorage('commerce_payment');
          /** @var \Drupal\commerce_payment\Entity\PaymentInterface $payment */
          $payment = $payment_storage->create([
            'state' => 'new',
            'amount' => $order->getBalance(),
            'payment_gateway' => $this->parentEntity->id(),
            'payment_method' => $payment_method->id(),
            'order_id' => $order->id(),
          ]);
          $this->createPayment($payment);
        }
    

    But the logged example has that query parameter in place... No idea....

  • 🇩🇪Germany Anybody Porta Westfalica

    Thanks @jsacksick and @grevil! At least we have some further information now. @grevil should keep track of this issue and let's also see if others run into it or if it's somehow specific to our case.

    Regarding:

    Lastly, eventually the user or some random js plugin removes the "skip_payment_creation" url query parameter, which would lead to a new payment creation at the end of the "onApprove" php callback

    No, I can't see any case where that could happen and for our case I know, and you already confirmed that, so we can leave out that option.

    Still it happens and presumably breaks checkouts for the affected users, so it needs to be fixed (some day, some way).

  • 🇩🇪Germany Anybody Porta Westfalica

    @amh5514 and @ibis if I get your comments right, you also get exactly this error? Could you please re-check that and let us know, how frequently that happens? Any ideas for the reasons in your case?

  • 🇩🇪Germany Grevil

    I almost think, that this might be a paypal issue...

    https://github.com/paypal/paypal-js/issues/628
    https://github.com/paypal/paypal-js/issues/596

    Clear steps to reproduce would definitely help to fix this issue....

Production build 0.71.5 2024