Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Lesley 107 posts 349 karma points
    Nov 03, 2016 @ 21:01
    Lesley
    0

    Noninline payment provider - handle cancel/decline

    We are using a custom payment provider that sends user to external site to enter their credit card details and on success redirects to the receipt page.

    If user cancels or credit card is declined we want to redirect them to either the credit card payment form or the payment method page. The redirect is working, but how do we display the Order Summary and address details again?

    Redirecting to payment method page displays Invalid Checkout Stage where the Apply Discount and Order Summary details should be.

    Redirecting to payment page displays Invalid Checkout Stage where the Payment Method and Order Summary details should be.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 04, 2016 @ 15:21
    Rusty Swayne
    0

    What is happening -

    The checkout manager's job is essentially done since the invoice has been generated and saved so that the payment (attempt) can be made against the invoice.

    The redirecting providers are slightly trickier than the providers local to the application ...

    In a local setup, you can simply do the retry directly against the invoice with one of the invoice extensions.

    e.g.

      if (!paymentResult.Payment.Success)
    
      {
          ... redirect to a retry form
      }
    

    Then something like:

     var retry = invoice.AuthorizeCapture(...
    

    In the redirect scenario you are faced with the choice of working out a workflow to do the same as above OR if you've not cleared the basket, simply deleting the previously created invoice and redirecting to the beginning of the Checkout process to create a new CheckoutManager from the basket.

    The good news is this is pretty configurable (see https://merchello.readme.io/docs/checkoutcontext) - in which there are options to retain the information the customer previously entered into the checkout forms between checkout manager resets (which happens every time there is a changed detected in the basket or the information is transferred and saved to an invoice).

  • Lesley 107 posts 349 karma points
    Nov 07, 2016 @ 23:58
    Lesley
    0

    Thanks for the info. Our site is based on FastTrack, and I still can't figure out how to redirect to the payment method page and have it show the Discount and Order Summary partial views with the correct invoice items data.

    It just shows "Invalid Checkout Stage" but I don't know how to set the checkout stage...

    I've tried looking at the PayPal example in the Merchello solution but it's still not clear.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 08, 2016 @ 16:45
    Rusty Swayne
    0

    The checkout stage is not something required by Merchello. It's just the way the FastTrack starter keeps track of where in the checkout workflow the customer is at any particular point in time.

    The invalid checkout stage indicates that you are hitting a page where the CheckoutManager does not have enough information (or a parameter was not passed) to do the specific operation that needs to be performed. Most often this is due to either a change in the basket, or as described above, an invoice being saved and the checkout cleared.

    One thing that is done in the PayPal express implementation is the CheckoutManager is directed to not empty the basket before the redirect even though the payment method will save the created invoice befor the redirect: https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web.Store/Controllers/Payment/PayPalExpressPaymentController.cs#L57

    This allows for the checkout to be started over in the event of a cancellation or failed payment. https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web.Store/Controllers/Payment/PayPalExpressPaymentController.cs#L169

    Note: This technique does have the consequence that in the event of a failure or cancellation, invoice numbers get skipped (because of the deletion).

  • Lesley 107 posts 349 karma points
    Nov 10, 2016 @ 02:47
    Lesley
    0

    Got a bit further, however, when the external payment page redirects back to the site with Cancel/Decline, the customer ExtendedData no longer contains merchBillingAddress, merchPaymentMethod and merchShippingDestinationAddress.

    The CheckoutManager is directed to not empty the basket before the redirect and if I redirect to the Basket page then I can see the basket items are still there. However, the shipping and payment method details have gone.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 10, 2016 @ 14:58
    Rusty Swayne
    0

    There are a bunch of settings that can be configured in the CheckoutContext: https://merchello.readme.io/docs/checkoutcontext

    However, it sounds like CheckoutManager.Context.SettingsResetCustomerManagerDataOnVersionChange == true. That was the default setting in early versions ... however it was later changed to have a default of false on install.

    Check your merchello.config to ensure this setting is indeed false and that you are not overriding the setting if you are using a custom context.

  • Lesley 107 posts 349 karma points
    Nov 10, 2016 @ 19:57
    Lesley
    0

    Checked the config and CheckoutManager.Context.SettingsResetCustomerManagerDataOnVersionChange is set to false and i can't see anywhere where we are overriding the setting.

  • Lesley 107 posts 349 karma points
    Nov 15, 2016 @ 23:44
    Lesley
    0

    After stepping through the code and checking the extendedData values in merchAnonymousCustomer, it seems that something in the following method on CheckoutPaymentManagerBase is causing the extendedData to 'lose' the address and payment method details:

            /// <summary>
        /// Raises the Finalizing event.
        /// </summary>
        /// <param name="result">
        /// The result.
        /// </param>
        protected void OnFinalizing(IPaymentResult result)
        {
    
            if (Finalizing != null)
            {
                Finalizing.RaiseEvent(new CheckoutEventArgs<IPaymentResult>(Context.Customer, result), this);
            }
        }
    

    I haven't been able to determine what it is though

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 16, 2016 @ 16:58
    Rusty Swayne
    0

    The OnFinalizing event is handled here:

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/UmbracoApplicationEventHandler.cs#L439

    If the result is successful the CheckoutManager will reset since the checkout has been completed and it's job is done.

    However, this event should not be raised in the event of a cancel. If the payment was declined, the result of the payment attempt should have a success value of false which would prevent the reset in the event handler.

  • Lesley 107 posts 349 karma points
    Nov 17, 2016 @ 00:05
    Lesley
    0

    We have a two step payment process (AuthorizePayment and CapturePayment) as we are using non-inline payment provider.

    The AuthorizePayment step is returning a successful payment result to indicate that the AuthorizePayment was successful - this is causing the CheckoutManager to reset before we have attempted to capture the payment.

  • Lesley 107 posts 349 karma points
    Nov 17, 2016 @ 02:45
    Lesley
    1

    I was able to resolve the issue by creating a custom version of the Basket's AuthorizePayment method that does not finalize the payment.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 17, 2016 @ 15:52
    Rusty Swayne
    0

    Hey Lesley

    I'd love to see your solution - did you just sub-class the BasketCheckoutPaymentManager ... or did you do it some other way?

  • Lesley 107 posts 349 karma points
    Nov 20, 2016 @ 19:40
    Lesley
    100

    I tried to sub-class the BasketCheckoutPaymentManager but was unable to as it is an internal class.

    I then tried to create my own version by taking a copy of the BasketCheckoutPaymentManager code, but found that some of the other forms (from memory I think it was the Discount form) expected a BasketCheckoutPaymentManager.

    I ended up creating my own AuthorizePayment method in my payment method controller and based it on the BasketCheckoutPaymentManager's code

            public IPaymentResult AuthorizePayment(Guid paymentMethodKey, ProcessorArgumentCollection args)
        {
            var paymentManager = this.CheckoutManager.Payment;
    
            var paymentGatewayMethod = this.CheckoutManager.Context.Gateways.Payment.GetPaymentGatewayMethods().FirstOrDefault(x => x.PaymentMethod.Key.Equals(paymentMethodKey));
    
            if (!paymentManager.IsReadyToInvoice()) return new PaymentResult(Attempt<IPayment>.Fail(new InvalidOperationException("SalesPreparation is not ready to invoice")), null, false);
    
            // invoice
            var invoice = paymentManager.PrepareInvoice();
    
            paymentManager.Context.Services.InvoiceService.Save(invoice);
    
            var result = invoice.AuthorizePayment(paymentGatewayMethod, args);
    
            // Removed steps that emptied basket and finalized payment
    
            return result;
        }
    
Please Sign in or register to post replies

Write your reply to:

Draft