Copied to clipboard

Flag this post as spam?

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


  • Finn Porter 11 posts 51 karma points
    Aug 25, 2020 @ 15:15
    Finn Porter
    0

    Finalizing order in callback

    Hi there,

    I'm relatively new to Vendr and I'm trying to figure out how to finialise an order manually after the callback from the payment provider comes in.

    We're using the normal vendr process up to the point of payment where we redirect the "go to payment" button to the form on the site of the payment provider. From what I understand that means that the order.Initialize() method is already called because it looks like that order has an order number assigned and everything. When the payment is done I'm trying to redirect the user to the payment confirmed page (this is a separate issue as it is usually just redirecting to the cart) and the payment provider sends their success confirmation to the callback url. It is there that I'd like to finalize the order and make sure it shows up in the backend and the basket is empty, etc.

    In the callback method, I can get the order (which has an order number and all the right order line items) but it has no paymentinfo or customerinfo,

    When I then call order.Finalize() the "IsFinalizing" property is set to true but then nothing happens after that.

    Does anyone have an idea what I'm missing here?

  • Matt Brailsford 2807 posts 14331 karma points MVP 7x c-trib
    Aug 25, 2020 @ 15:27
    Matt Brailsford
    0

    Hi Finn,

    I'm not entirely sure of your setup, or which URLs you are actually calling, but from within process callback method, returning a result with transaction info defined on it will finalize the order if it is not finalized already. You shouldn't need to call finalize there yourself.

        public override CallbackResult ProcessCallback(OrderReadOnly order, HttpRequestBase request, InvoicingSettings settings)
        {
            return new CallbackResult
            {
                TransactionInfo = new TransactionInfo
                {
                    AmountAuthorized = order.TotalPrice.Value.WithTax,
                    TransactionFee = 0m,
                    TransactionId = Guid.NewGuid().ToString("N"),
                    PaymentStatus = PaymentStatus.Authorized
                }
            };
        }
    

    I could probably do with knowing your setup in a bit more detail, including what URLs are getting called and from where to give you a more detailed answer.

    Matt

  • Finn Porter 11 posts 51 karma points
    Aug 25, 2020 @ 15:36
    Finn Porter
    0

    Hi Matt,

    I'll try to keep it brief and simple.

    We're using a payment provider that needs an xml string sent via a form to process payment. Part of that xml string hold a successURL and a callback url.

    The successURL is just to redirect the user back to the site instead of keeping them on the payment provider site, nothing fancy. I've tried to redirect to "/checkout/order-confirmation" but there seems to be an interal redirect that's probably caused by the payment not being processed via vendr or an approved payment provider.

    The callback Url (this goes to an umbraco surface controller) receives back an xml string in the Request Body that holds some of the payment information like a transactionId and the total amount paid but most importantly it holds the information about whether it was successful or not.

    So on that callback method I'm trying to get ahold of the current order and finalize it to make sure it shows up in the backend and can be processed by the team to send out the products to the customer.

    I hope this makes more sense. Please let me know if there is anything else I can provide to clarify.

  • Matt Brailsford 2807 posts 14331 karma points MVP 7x c-trib
    Aug 25, 2020 @ 15:50
    Matt Brailsford
    0

    Sorry, yea, it's still not that clear.

    I can't picture where you are getting these URLs from that your are redirecting to and I can't work out how much of the PaymentProvider pattern you are actually using and when you are breaking out from that.

    There are some pretty important steps in the lifestyle of a payment provider so if you are skipping them, or calling the wrong URL's then you are likely not going to get a finalized order.

    Maybe you can strip out the code that relates to your payment processing steps and share it so I can see exactly what is going on.

  • Finn Porter 11 posts 51 karma points
    Aug 26, 2020 @ 07:48
    Finn Porter
    0

    Thanks for trying to help, I really appreciate it.

    So we have several steps that are all done by the book through Vendr. 1) checkout/customer-info => collects name, address, etc. 2) checkout/shipping-method => let's customer choose how to ship products 3) checkout/payment-method => let's customer choose the payment method of which there is only one anyway 4)checkout/review-order => summary of all data to confirm all is correct

    on the 4) review page we have the following code

    <form id="frmtest1" name="frmtest1" method="post" action="https://paymentgatewayurl" enctype="application/x-www-form-urlencoded">
    
    <input type="hidden" name="requesttoken" value="foo" />
    <div class="border border-gray-300 rounded">
        <label class="flex items-center py-4 px-4 cursor-pointer hover:bg-gray-100">
            <input id="acceptTerms" type="checkbox" value="1" class="mr-3" required="required" />
            <span class="font-medium">I agree and accept the sites terms of service</span>
        </label>
    </div>
    
    <textarea cols="80" rows="40" name="xml" id="freetextxml" hidden>
            @xDoc.ToString()
        </textarea>
    @Html.VendrCheckoutPartial("VendrCheckoutPrevNext")
    

    When someone clicks on the "go to payment" button that is on the VendrCheckoutPrevNext partial, they're sent to the payment provider where they can enter card details, etc.

    the xDoc.ToString() is an xml string that holds information like the amount to pay, some public and secret keys, etc. it also holds

    <transactionreference><![CDATA[P637339439052773968:637339439052773968]]></transactionreference>
        <redirecturl><![CDATA[https://foobar.com/checkout/order-confirmation]]></redirecturl>
        <callbackurl><![CDATA[https://foobar.com/umbraco/surface/vendrcheckout/wpmcallback]]></callbackurl>
        <cancelurl><![CDATA[https://foobar.com/errors/payment-unsuccessful]]></cancelurl>
    

    So after a customer has paid on the payment providers website, the payment provider will send an xml string in the request body to the callback url to confirm payment has been made. Only this confirms the success of a payment so we can only then finalize the payment in vendr.

    And this is where it all falls down because I can't get it to do just that. I have tried using the vendrapi to GetCurrentOrder() but that gives me a unmutable OrderReadOnly object, so I'm getting it via the sessionManager like this

    using (var uow = _unitOfWorkProvider.Create())
            {
                var order = _sessionManager.GetOrCreateCurrentOrder(Guid.Parse(App.Current.Settings.StoreId))
                    .AsWritable(uow)}
    

    This gives me an Order object that I can call things like Finalize() on. However if I do that, all that happens is that the parameter called isFinalizing is set to true but it doesn't actually finish that process and so it never shows up in the list of orders in the backoffice of Umbraco.

  • Matt Brailsford 2807 posts 14331 karma points MVP 7x c-trib
    Aug 26, 2020 @ 08:23
    Matt Brailsford
    0

    Ok, it sounds like this should fit the regular Payment Providers model so I think it sounds like there is just some confusion about how a Payment Provider works.

    So first of all, on your review step you should be calling Vendr's BeginPaymentForm method (see example here https://github.com/vendrhub/vendr-demo-store/blob/main/src/Vendr.DemoStore.Web/Views/CheckoutReviewPage.cshtml#L9)

    This will request from the payment provider to generate a form via it's GenerateForm method, which this should prepare the form that redirects to the payment gateways processing page. In here you'd probably do whatever you need to do to generate XML string, and then return the form with it in a hidden field. On the front end, as per the example linked above, you can then add the other elements for agreeing to terms etc.

    An important thing to note in the payment providers GenerateForm method is that it is given a series of URLs continueUrl, cancelUrl and callbackUrl. These are the URLs that you should tell your payment gateway about as these are proxied by Vendr in order to perform certain tasks when they are requested (they are also fully qualified URLs so should be ready to be passed on). So, all those URLs you reference in the XML string, those should be using these URLs and you shouldn't need to generate these yourself.

    By using these URLs, when your payment gateway now makes an async request to your callbackUrl, this will be passed to Vendr where it will work out the order and payment provider that needs to handle it (which should be yours) and when it finds it, it will call the payment providers ProcessCallback method. Inside that method, you should then perform your verification's, and as mentioned in my first reply, return a CallbackResult with the relevant transaction info populated.

    Vendr then knows, that if it got transaction info back, that it should finalize the order. You should not need to call Finalize on the order yourself. Simply returning transaction info will do this for you.

    If you haven't done so already, I'd really urge you to take a look at the docs on the concept of a payment provider found here https://vendr.net/docs/core/1-2-0/key-concepts/payment-providers/ as this explains the purpose of the various payment provider methods, and what the flow of a provider is. In addition to this, I'd also take a look at the source code to some of the existing payment providers found here https://github.com/vendrhub?q=payment-provider to see how these work and so take inspirtation from them.

    I hope this helps clarify things for you

    /Matt

  • Finn Porter 11 posts 51 karma points
    Aug 26, 2020 @ 08:47
    Finn Porter
    0

    Hi Matt, thank you very much for this. I had looked at the payment provider documentation before but mostly confused myself with it when trying to fit our client's process into this.

    Your explanation is helping a lot and I'll get right onto implementing it that way.

    Thanks again for your help.

    Finn

  • Matt Brailsford 2807 posts 14331 karma points MVP 7x c-trib
    Aug 26, 2020 @ 08:51
    Matt Brailsford
    0

    No problem at all.

    Any feedback on what you found confusing about the docs would be handy as I'm always open to making improvements.

    I think I do need at least a "how to guide" as the concept docs don't paint the whole picture for sure.

    /Matt

  • Finn Porter 11 posts 51 karma points
    Aug 26, 2020 @ 11:14
    Finn Porter
    0

    Hi Matt,

    I'm still having a bit of an issue with this, unfortunately.

    I've implemented the BeginPaymentForm on the review step. That's actually what it was before I started screwing around with it.

    I've created a Payment Method of type Invoice and gave it a name and alias which I'm using in my PaymentProvider class as per your example.

    When I run through the checkout steps, I get to the review page, no problem. But then I'm taken immediately to the confirmation page, completely bypassing any and all payment structure. The GenerateForm method I've implemented pretty much in the same way you have it in your source code. But when I put breakpoints in that never gets called and I'm just moved on to the next step which is confirmation.

    I'm also not sure where to find that callback url. I can see that GenerateForm takes in a parameter of that name but there is no method for it like there is for eg continue url (GetContinueUrl()).

    If you have an idea what I might be forgetting to do, I'd really appreciate the input.

    Thanks, Finn

  • Matt Brailsford 2807 posts 14331 karma points MVP 7x c-trib
    Aug 26, 2020 @ 12:29
    Matt Brailsford
    0

    Hi Finn,

    Yea, whilst the Invoicing one is the simplest to follow, it's the one that causes most confusion as the job of the invoicing payment provider IS to allow the order straight through as it's designed such that it lets the order through and you are supposed to issue the customer an invoice manually outside of the system.

    The reason this does what it does is because of this line https://github.com/vendrhub/vendr-payment-provider-invoicing/blob/dev/src/Vendr.PaymentProviders.Invoicing/InvoicingPaymentProvider.cs#L26 So essentially the form that the invoicing payment provider is creating to display on the review page is a form that sends you straight through to the confirmation page, which is not what you want. Instead, you want that form to have the URL of your 3rd party gateway and you need to populate it with the hidden input fields your gateway is expecting. Thus when a customer clicks the "continue" button in that form, it sends them to the payment gateway to make payment.

    In terms of the URLs, the ones that are passed to the GenerateForm method are the ones you want to use at all times. These are generated by Vendr and passed in when the GenerateFrom method is called. These should be passed to the 3rd party either via their API to setup a payment you are about to process, or in your case I think encoded into the XML that you need to render in the form.

    You should NEVER access the continue URL / cancel URL from the GetContinueUrl or GetCancelUrl methods on the payment provider implementation directly. These are only for Vendr, as when the continue URL that is passed into the GenerateForm method is redirected to by the gateway, this will direct to a Vendr route which will run relevant logic to finalize the order etc, and then Vendr asks your payment provider for the continue / cancel URL at that point and performs a redirect. If you redirected to these manually, you would bypass the Vendr logic and likely not get the outcome you are expecting.

    /Matt

  • Finn Porter 11 posts 51 karma points
    Aug 26, 2020 @ 14:50
    Finn Porter
    100

    Hi Matt,

    thanks for that. This all makes sense. However, even if I modify the code for the form that gets returned in the PaymentFormResult, it doesn't change the outcome. I've added the inputs and the action url, etc.

    The problem is, I've got a breakpoint on the GenerateForm method overrride and it never gets hit, so I'm not sure any changes I make in there will make a difference at this point. I believe I'm not doing something correctly before that. Or it would hit the GenerateForm method, right?

  • Matt Brailsford 2807 posts 14331 karma points MVP 7x c-trib
    Aug 26, 2020 @ 16:12
    Matt Brailsford
    0

    Hey Finn,

    If it's not hitting your GenerateForm method, then I'd make sure you you have your payment method setup correctly to use your provider, and also make sure your order is configured to use your payment method (make sure you payment method is allowed in the country of your billing address).

    Assuming all these are correct, it should hit your payment provider.

    /Matt

  • Finn Porter 11 posts 51 karma points
    Aug 27, 2020 @ 09:04
    Finn Porter
    0

    Hi Matt,

    I've gone to "Payment Method" and created a new Invoice Payment Method because we're not using paypal or any of the already pre-configured payment methods that vendr so handily comes with. As you say it makes total sense that that would bypass the payment process as invoices usually don't need card details from the customer.

    So I guess this is where I went wrong and probably have to use a different payment method type?

    Thanks for all your help, Finn

  • Matt Brailsford 2807 posts 14331 karma points MVP 7x c-trib
    Aug 27, 2020 @ 09:15
    Matt Brailsford
    0

    Hi Finn,

    Yea, you'll need to create your own for your given payment gateway and hook it all up as defined above. Then make it available as a payment method in your store and select it during checkout. This should then use your payment provider and you can control what it does.

    /Matt

  • Finn Porter 11 posts 51 karma points
    Aug 28, 2020 @ 13:20
    Finn Porter
    0

    Hi Matt,

    I just wanted to say thank you for all your help. I can't believe how easy it was to do all that in the end.

    It's all set up now and working.

    Thanks again for being patient. I'll owe you a pizza.

    Thanks, Finn

  • Matt Brailsford 2807 posts 14331 karma points MVP 7x c-trib
    Aug 28, 2020 @ 13:30
    Matt Brailsford
    0

    No worries

    Glad we were able to get there in the end.

    Payment Providers are pretty confusing to build that mental model as to how all the pieces work together. But once you have it, they are pretty straight forward.

    Hope the rest of the build goes smoothly 👍

    Matt

Please Sign in or register to post replies

Write your reply to:

Draft