Copied to clipboard

Flag this post as spam?

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


  • Tom W 39 posts 96 karma points
    Feb 18, 2022 @ 09:46
    Tom W
    0

    Using the Callback URL and other URLs in a PaymentProvider

    i Matt

    Loving Vendr more than ever. We're using Vendr Checkout - you've done a great job trying to make this work for every scenario. We're currently on 2.0.x but will get it to 2.1.0 before live I think.

    We're doing a more "standard" checkout this time, using a horrible payment provider called GlobalPay. They're really quite rubbish. Docs are average: https://developer.globalpay.com/ecommerce/hosted-solution/HPP-Guide#hpp-guide

    I'm using the ctx.Urls.CallbackUrl which is working pretty well (love the way it gets the contextual data for you), however the way GlobalPay works, is it actually renders the content of a single URL (so not async) to the browser, on their domain, after POSTing to it, so I'm having to render some Javascript to do a redirect back to the site. The only PP I've ever seen that does this.

    Could you just confirm for me whether I should be sending the user back to the ctx.Urls.ContinueUrl if successful, or should I be manually updating the order to be Completed and sending them to /checkout/order-confirmation? Or /basket with an error if it failed?

    For some reason, this is setting my order status to Error, but payment is Captured, so it's kind of working. If I return anything but a 200 to GlobalPay, it dies. I can't see anything that tells me why it's in Error, but I suspect because I'm not (and can't) use return CallbackResult.OK as you have in the source I've seen.

    It's not that clear to me what the Continue/Cancel/Error URLs should be used for (if anything) - sorry if I'm being daft.

    I could do this with a simple SurfaceController and manually update the order, but I don't want to do that if I don't need to. Feel it may well give me a bit more control though, so if it's not clear what I'm asking, I'll probably go that way.

    Thank you!

    Tom

            var url = "?";
            var qs = postDataAsNameValueCollection;
            var isComplete = qs["RESULT"] == "00";
           // ... other logic to ensure the payment response if valid
    
            var response = request.CreateResponse(HttpStatusCode.OK);
            response.Content = new StringContent($"<html><head><script>window.location.href='{url}';</script></head><body><p>Redirecting back to the website</body></html>");
    
            // should I complete the order manually here? Or does the consumer of the CallbackResult do that?
    
            return new CallbackResult()
            {
                HttpResponse = response,
                TransactionInfo = new TransactionInfo() 
                {
                    AmountAuthorized = priceFromPayment,
                    PaymentStatus = isComplete ? PaymentStatus.Captured : PaymentStatus.Error,
                    TransactionFee = 0m,
                    TransactionId = qs["ORDER_ID"]
                },
                MetaData = new Dictionary<string, string>()
                {
                    { "globalPayResult", qs["RESULT"] },
                    { "globalPayAuthCode", qs["AUTHCODE"] },
                    { "globalPayMessage", qs["MESSAGE"] },
                    { "globalPayOrderId", qs["ORDER_ID"] }
                }
            };
    
  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Feb 18, 2022 @ 11:54
    Matt Brailsford
    1

    Hey Tom,

    So payment providers can kinda work in 2 ways, which dictate which URLs get used, and how.

    Secenario 1 - Webhook Confirmation
    By far the most popular approach of payment gateways is to redirect the customer to their payment portal, the customer completes the purchase, and then the payment gateways returns the customer to the site. In the background though, the payment gateway sends another request to a webhook handler to actually be notified of successful payment.

    By notifying you of payment status over webhook, it ultimately means that should anything go wrong in the actual checkout flow (maybe the customer just closes the browser) you still get notified regardless.

    In this scenario, you'll generally use the 3 URLs that Vendr exposes like so

    1. ctx.Urls.CancelUrl - Where to redirect to if the user cancels out of the payment portal
    2. ctx.Urls.ContinueUrl - Where to redirect to once they are done at the payment portal.
    3. ctx.Urls.CallbackUrl - The webhook handler that actually checks for successful transaction.

    The important thing to note here is that the continue URL is just responsible for forwarding the customer to a confirmation page. It's not actually responsible for finalizing the order, and in fact, due to the nature of webhooks this could actually happen a couple of seconds or even minutes later when the webhook arrives.

    Needless to say though, the order confirmation emails etc won't be sent until the order is finalized via the webhook.

    Scenario 2 - Continue URL Confirmation
    In the second scenario, rather than the webhook being the thing that finalizes the order, we can actually get the Continue URL to perform this task too.

    This is generally used by payment gateways that send transaction details on the querystring back on the continue URL, rather than via a separate webhook handler.

    In this scenario, you'll generally use the 2 URLs that Vendr exposes like so

    1. ctx.Urls.CancelUrl - Where to redirect to if the user cancels out of the payment portal
    2. ctx.Urls.ContinueUrl - Where to redirect to once they are done at the payment portal, but also extract transaction info and finalize the order.

    In order to get the Continue URL within Vendr to do that work of finalizing the order though (remember from scenario 1, it's normal job is just to send you to the confirmation page) you need to override the FinalizeAtContinueUrl property in your payment provider and have it return a true value.

    With this set, it now means accessing the Continue URL will also trigger a call to your ProcessCallbackAsync method in which you can perform the relevant logic necessary to extract the transaction info from the request, and return to Vendr the transaction details.

    Vendr will perform this task first, and then perform the redirect to the Continue URL.

    So these are the 2 scenarios. I get the feeling option 2 will be what is required by your gateway.

    RE Preparing Transaction
    Just to touch on your issue with having to notify the payment gateway and then render some javascript to trigger the payment, I think you should be able to do this from your payment providers GenerateFormAsync

    You should ultimately perform the server tasks within this method, notifying the gateway of the transaction coming, which this should then give you back the JSON you need for the js.

    From GenerateFormAsync though, you can return JS as part of this form (the Stripe payment provider does this for example https://github.com/vendrhub/vendr-payment-provider-stripe/blob/v2/dev/src/Vendr.PaymentProviders.Stripe/StripeCheckoutPaymentProvider.cs#L314-L330) which I think should handle your setup.

    return new PaymentFormResult()
    {
        Form = new PaymentForm(ctx.Urls.ContinueUrl, PaymentFormMethod.Post)
            .WithJsFile("https://url/to/rxp-hpp.js")
            .WithJs(@"
                var gpServerJson = \"{responseJson}\";
                RealexHpp.setHppUrl(\"https://pay.sandbox.realexpayments.com/pay\");
                RealexHpp.redirect.init(\"payButtonId\", \"{ctx.Urls.ContinueUrl}", gpServerJson);
            ")
    };
    

    It kind of requires you to know the ID of the button being rendered on the "review" step, but if it's just a one off provider that might be ok.

    Otherwise you could always render a css hidden button and use that, but then trigger it via javascript.

    Anyways, I hope this points you in the right direction.

    Matt

  • Liam Dilley 155 posts 381 karma points
    Jul 15, 2022 @ 07:36
    Liam Dilley
    0

    Hi Matt, What about when you have something like eWay so you need an inline form and your handling requests and responses that way?

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Jul 15, 2022 @ 08:17
    Matt Brailsford
    0

    In those scenarios I'd say render a PaymentForm that redirects you to your on site page and have that do what it needs to do to take payment, but then when it's done, send a request back to the payment provider via it's CallbackUrl to update the transaction in Vendr and then redirect to Vendrs ContinueUrl to then continue the checkout process.

    I'd say maybe in the PaymentForm render some JS out containing Vendr's continue / callback URL so you have easy reference those URLs in your JS code.

  • Liam Dilley 155 posts 381 karma points
    Jul 18, 2022 @ 00:07
    Liam Dilley
    0

    Thanks Matt, In a future update it would be great to be able to have the checkout extension support that natively so you can just click to payment and have the form render there.

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Jul 18, 2022 @ 07:33
    Matt Brailsford
    0

    Hi Liam,

    The problem with this is that pretty much every payment gateway does this kind of stuff differently and the only semi standard API they have is the hosted payment option. The hosted payment option puts much less requirements on the implementor too from a PCI complience perspective, so these are the main reasons we target the hosted options by default.

    I do think we can try and make these types of checkouts easier though, so hopefuly we can review this and see what we can to do at some point 👍

  • Liam Dilley 155 posts 381 karma points
    Jul 19, 2022 @ 08:01
    Liam Dilley
    0

    Hi Matt, Yeah. Everyone does it differently but it would be nice to be able to build say an eWay solution as we are and be able to package that up better to provide as an available solution on your site for example. We have NAB working in most recent case but with as discussed its not something you could package up.

    For you guys it would be more of the means to have a "self hosted" method, checkout being able to also handle that so you can then call your own form and process then handle the return which there is very close to being fine for a more seamless package already. A true error "page" as well to handle that.

    Then it would be the payment gateway dll and the rest view the payment provider that would generate the form and good to package up.

  • Tom W 39 posts 96 karma points
    Feb 18, 2022 @ 12:08
    Tom W
    0

    Amazing Matt, thank you very much. A very well supported package, this one!

    Great explanation, I'll be working on this again next week so will try it out. Definitely option 2 for this scenario, but I'm sure your explanation of option 1 will help us (and others!) in future.

    I'd much rather be using Stripe! No support for our little island yet though.

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Feb 18, 2022 @ 12:25
    Matt Brailsford
    0

    Hey Tom,

    No worries. Really hope it helps.

    And thanks for moving this to the forums. It's so much easier to support these types of questions here 🤘

    Let me know how you get on next week.

    Matt

  • Derek 17 posts 71 karma points
    Feb 22, 2022 @ 12:12
    Derek
    0

    Hi Tom,

    Would you mind if I asked how you are getting on with this? I was just about to post the same question but it sounds like you are a bit ahead of us! :)

    We are have real trouble getting the Global Payments stuff to work with Vendr/Umbraco. I think like you mentioned, I can get the HPP documentation demo code working in a standalone, surface controller solution but as soon as I try to involve the Payment Provider and passing Json responses back we're hitting a brick wall.

    Thanks, Derek

  • Tom W 39 posts 96 karma points
    Feb 22, 2022 @ 13:00
    Tom W
    0

    Hi Derek

    I'm not quite there yet, but hope to have something by the end of the week. As soon as I do I'll let you know how I achieved it, assuming I do.

  • Tom W 39 posts 96 karma points
    Mar 03, 2022 @ 11:15
    Tom W
    0

    Hi Derek

    I was wondering whether you've had any joy with this? It's a bit of a nightmare isn't it...

    The main problem seems to be that, rather than any other provider, where the success URL is used as a POST or GET, what GP have decided to do is "download" the HTML from the URL after a POST, rather than POST to it. Why, I've no idea.

    So if there is any kind of redirect in there, it fails with the awesome message

    Your transaction has been successful but there was a problem connecting back to the merchant's web site. Please contact the merchant and advise them that you received this error message.
    

    So I think what we're going to have to do, is have the URL that's sent in RealexHpp.redirect.init('continue', '" + url + @"', hppJson); to a SurfaceController render, which then looks at the POST data, and renders a javascript redirect to either the ContinueUrl if successful, or the CancelUrl if not.

    The URLs can be passed over to GP using HostedPaymentData

    var hostedPaymentData = new HostedPaymentData()
                {
                    SupplementaryData = new Dictionary<string, string>()
                    {
                        { ctx.Settings.SupplementaryDataKey, ctx.Order.OrderNumber  },
                        { "ContinueUrl", ctx.Urls.ContinueUrl },
                        { "CancelUrl", ctx.Urls.CancelUrl }
                    },
    

    I also don't think it likes the localhost:port URLs. I've tried using ngrok without much success so far

    I'll let you know if I make any progress - this is getting quite urgent for us now, so I'm going to do my best to come up with a solution today.

    Let me know if you've found a way around this weirdness.

    Thanks

    Tom

  • Tom W 39 posts 96 karma points
    Mar 03, 2022 @ 11:44
    Tom W
    0

    I was hoping to return a javascript redirect from the surface controller, which would go to ContinueUrl if successful, CancelUrl if not.

    So for now, I'm just rendering all Request.Form keys to the GP complete page, which actually renders it on GP domain...

    https://pay.sandbox.realexpayments.com/card.html?guid=TEST-3308-4847-9593-94791aa4a16a

    Redirect URL: https://XXX/umbraco/vendr/payment/continue/globalpay/ec1aa178-fb19-4cca-b73d-017f4f8f6fda/ORDER-00792-41788-R65D5/d7fe0af1e4e24f0f88777203297f9c9ca8a76aa819c33fbd2287f89677fe3107
    
    RESULT: 00
    AUTHCODE: 12345
    MESSAGE: [ test system ] Authorised
    PASREF: 16463074047204395
    AVSPOSTCODERESULT: U
    AVSADDRESSRESULT: U
    CVNRESULT: U
    ACCOUNT: internet
    MERCHANT_ID: XXX
    ORDER_ID: R9RHlJEof0-cEmgqroREUg
    TIMESTAMP: 20220303113628
    AMOUNT: 1519
    MERCHANT_RESPONSE_URL: https://XXX:443/umbraco/surface/globalpaysurface/paymentresponse
    pas_uuid: 6dc8cb52-3308-4847-9593-94791aa4a16a
    HPP_CUSTOMER_PHONENUMBER_MOBILE: XXX
    BILLING_CODE: |
    BILLING_CO: GB
    SRD: AROiA4I3pQ3lCvqS
    SHA1HASH: d680fcdac8b421a313f6772e518b2ce6fdb19012
    HPP_BILLING_STREET1: XXX
    ...
    HPP_ADDRESS_MATCH_INDICATOR: FALSE
    HPP_FRAUDFILTER_MODE: PASSIVE
    Client-Order: ORDER-00792-41788-R65D5
    CancelUrl: https://XXX/umbraco/vendr/payment/cancel/globalpay/ec1aa178-fb19-4cca-b73d-017f4f8f6fda/ORDER-00792-41788-R65D5/39226f56308a063ffb141418c26179a2dd6094f18c469404f141bc14147ede79
    ContinueUrl: https://XXX/umbraco/vendr/payment/continue/globalpay/ec1aa178-fb19-4cca-b73d-017f4f8f6fda/ORDER-00792-41788-R65D5/d7fe0af1e4e24f0f88777203297f9c9ca8a76aa819c33fbd2287f89677fe3107
    BATCHID: 1059121
    

    However, when I then try to manually go to the ContinueUrl, I'm being redirected to the Error page (set up in the payment provider) but no errors are being logged anywhere, so I'm not sure what's going wrong.

    I think I'm going to have to manually Finalize the order in the SurfaceController, and perform the redirect to /checkout/order-confirmation/ if successful, and back to the basket with a message if not.

    Not ideal, but I can't think of any other way to work out why the ContinueUrl is failing/erroring.

  • Derek 17 posts 71 karma points
    Mar 03, 2022 @ 13:18
    Derek
    1

    Hi Tom,

    Yes, we've made a bit of progress. Admittedly we are not doing much during the payment process - just credit card only on the Payment Gateway and we're using the Global Payments Hosted Payment Page solution:

    https://developer.globalpay.com/ecommerce/hosted-solution/HPP-overview

    So, following their code examples in the step-by-step pages, I've got this working using the Vendr Payment Provider and Payment Provider Settings. Initially I had it working with Surface Controllers but I was then running into problems trying to get native Payment Provider & Settings properties and values, so ended going back to the Payment Provider solution.

    Anyway, to get it working we are:

    • in the Provider, setting FinalizeAtContinueUrl to true
    • in the Provider Generate Form method, essentially using the Global Payments HPP .net code (HostedService, HostedPaymentData etc).
    • Passing the JSON request back in the PaymentFormResult in a similar way to what Matt suggested above using WithJs and passing in the continue url along with it.

    (We're using the embedded iframe option but I also had the lightbox version working.)

    Then, upon the response return then ProcessCallback method is firing and, again, we are using the HPP .net code as a basis to handle the response Json and proceed accordingly.

    It all seems to be working so far. Couple of issues testing with 3DSecure v2 stuff like ensuring a mobile number is added to the request AND that it had the international dialing code in the preferred Global Payments format with it.

    Derek

  • Tom W 39 posts 96 karma points
    Mar 03, 2022 @ 13:38
    Tom W
    0

    Thanks for coming back Derek.

    I figured it would be easier to use the Full Page Redirect, but because of how GlobalPay handles the success/redirect, it's a real pain with Session management etc.

    So, if I can't get this working well using a custom Surface Controller, I'll probably see if I can make it work with the embedded iframe.

    Are you using a custom Checkout, or Vendr Checkout? Just interested to know where your embedded form is rendered.

    Cheers, Tom

  • Derek 17 posts 71 karma points
    Mar 03, 2022 @ 13:51
    Derek
    0

    Hi Tom,

    Yeah, a custom checkout process. Name/billing address page (online only so no shipping page required) -> Confirmation page -> Payment Page (with embedded iframe and vendr beginpaymentform) -> Order Complete page if response successful.

    If error is returned then back to Payment Page with error message displayed.

    Cheers, Derek

Please Sign in or register to post replies

Write your reply to:

Draft