Developer forum

Forum » Development » Checkout handler POST callback

Checkout handler POST callback

Reynir Viðar Ingason
Reply

Hey there,

 

I am developing a checkout handler and the pattern of this payment provider is as follows:

1. The store sends a POST request for the creation of a new payment order with information about user and a single callback url.
2. The payment provider then processes the payment, if all is successful he then sends a POST request to the callback url with information about the transaction in the request body in JSON format.
3. The store can then compare a hash from the body to verify that the request is coming from the payment provider and mark the order as completed and send a receipt to the payment provider.

If the process times out at the payment provider no request to the callback is performed so the store will need to do a timing operation and react after a certain time passes.

 

So I have two questions:

1. How can I access the POST request and the body of it in the callback from the payment provider?
2. How would you recommend I measure the time since the store POSTs a payment in order to react to a timeout?

 

I have several examples of payment providers which I got from this site, however all of the ones I have seen use seperate callbackurls(differend parameters in url) to differentiate between successful and unsuccessful payments.
I have caught the callback in the redirect method of the checkoutHandler but that appears to be a GET request with no body.

 

Reynir


Replies

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi Reynir,

I'm not completely sure about the flow here. After you send the POST request to the gateway, do you then redirect the user to the gateway to enter their card information or do they stay in the cart waiting for the response?

If you redirect the user, then it should be fairly simple to follow the examples you have. If you don't redirect and the user needs to wait in the cart while the processing is happening, then you need to add some wait logic in your checkout handler's StartCheckout method. Here you'd make the request to the gateway passing them a callback url that looks something like this:"<URL_TO_CART_PAGE>?CheckoutHandlerOrderId=<ORDER_ID>". The querystring parameter needs to be there even though it may be a POST. When the gateway is finished processing the payment, it will call the cart page, which will then call the Redirect method in your checkout handler. Here you set some global value that the payment was processed successfully. The waiter in StartCheckout will periodically check the global value to see if the payment is complete. After a specified time, the waiter will have to break if the payment hasn't been processed yet and you need to decide how to handle that: redirect the user back to the step before checkout or render an error template. If the payment was successful, you need set the order.Complete property to "true" and call CheckoutDone(order) followed by RedirectToCart(order).

I'm leaving a lot of details up to you and I don't quite understand the behavior of the gateway. It seems very unstable to have a gateway the may or may not respond as part of its expected behavior. You might have customers who try to buy something but the gateway doesn't respond in time. The order is then cancelled, but shortly thereafter, they may still get billed for it.

Anyway, I hope this helps you.

- Jeppe

 
Reynir Viðar Ingason
Reply

Hey Jeppe,

thank you for the prompt response.

I hadn't thought of implementing a wait in the checkoutStarted method and will look into that using an accessible variable to determine the state of the payment reqeust.

The main issue I am dealing with right now is that when I receive the callback in the redirect method I don't seem to be catching a POST request but rather a GET request.
I am unsure why this is but I have had confirmation from the provider that a POST request was indeed sent with the appropriate values in the body of the request.

 

I may be accessing the request incorrectly but I am using "Context.Current.Request.RequestType" to discern the type of the received request.

The user is not redirected while the payment is being processed, he gets a notification on an app where he finishes the transaction and subsequently the gateway sends the POST request to the callback url.

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

If the gateway actually POSTs, you should also get see a POST.

What happens if you try to read the value from the POST instead of checking the request type? Is it null, or does it exist? What if you don't send the SEO friendly url, but the "old school" url, "<DOMAIN>?ID=<CART_PAGE_ID>&CheckoutHandlerOrderId=<ORDER_ID>" to the gateway? What happens if you try to POST to the same page using Postman or similar? Do you still see a GET or is it a POST?

- Jeppe

 
Reynir Viðar Ingason
Reply

I just ran the attached Postman request against my locally hosted development site.
You will notice that there is a body to the request with JSON content.

When I attach to the project in visual studio and break in the redirect method I can see that the "Context.Current.Response" has a httpMethod of GET and when I read the input stream the output is an empty string.

 

Reynir

 
Nicolai Pedersen
Reply

I think it is a post - see attached. So you need to do a request.form and not request querystring.

If that does not work, please provide your code here so we can check it out.

Capture.PNG
 
Reynir Viðar Ingason
Reply

Hey Nicolai,

 

you are correct, the request I am sending with Postman(and attached previously) is a POST request.
That is the point, I.e. the request being sent is POST but the request I observe in the checkouthandler.redirect method is not a POST request and the observed request does not have a body.

So the issue is that I am not being served the request with the data I need in the checkouthandler but a different request.
The parameters in the URL match the ones sent as callbackUrl to the gateway. Beyond that there is no match between the request I observe in the redirect method and the request I am expecting to receive.

 

 

Reynir

 
Reynir Viðar Ingason
Reply

I have attached the project I am working with. It may require sume configuration to get it running since it was part of a larger solution.

 

 
Nicolai Pedersen
Reply

Hi Reynir

Well, cannot tell you what is wrong. I cannot setup your checkouthandler. We do it like you - see attachment from our DIBS checkouthandler.

If you can, please post all the values from HttpContext.Current.Request object when attaching a debugger on the callback. Dynamicweb cannot manipulate the namevalue collection of the request body on its way in...

Capture.PNG
 
Reynir Viðar Ingason
Reply

Hey Nicolai,

 

I have been refering to the DIBS checkouthandler thusfar. The point where my checkouthandler differes is when I need to access the body of the request to get to data whereas the DIBS checkouthandler only uses parts of the callback url to determine a success or failure.

The input stream on the incoming request is empty so no request body can be read from it.

Attached is a screenshot from my development environment of the request object and the post request in postman.

 

Reynir

 

23-5-2019_12-57-22.png
 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi Reynir,

The issue seems to be how your POST body is defined. In Postman, if you choose body type "raw", then Dynamicweb will see that as a GET. If you set it to "form-data" then your request is processed as as POST.

I'm not exactly sure why this is, but you can try and work around it by creating a WebApi controller in your project, send the url to the WebApi action to the gateway and have the controller set the OrderComplete variable in the CheckoutHandler. It's a bit more cumbersome, but creating a WebApi controller will circumvent all of the Dynamicweb frontend and allow you to handle any type of request.

You'll need to add a notification subscriber like this one to your project in order to let your controller handle its route:

using Dynamicweb.Extensibility.Notifications;

namespace CustomProject.WebApi
{
    [Subscribe(Dynamicweb.Notifications.Standard.Application.AuthenticateRequest)]
    public class WebApiEnabler : NotificationSubscriber
    {
        private const string RoutePrefix = "api/payment";
        private const bool EnableSessionState = true;

        public override void OnNotify(string notification, NotificationArgs args)
        {
            var authenticateArgs = args as Dynamicweb.Notifications.Standard.Application.AuthenticateRequestArgs;
            var request = authenticateArgs.Application.Context.Request;
            var isHandled = request.Path.StartsWith($"/{RoutePrefix}");
            if (EnableSessionState && isHandled)
            {
                // Enable session
                System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
            }

            // When this is set to true then Dynamicweb will skip the default handling of the current request
            authenticateArgs.Handled = isHandled;
        }
    }
}

I hope this helps.

- Jeppe

 
Nicolai Pedersen
Reply
This post has been marked as an answer

Hi both

I think I've located the problem. It is the url engine in Dynamicweb that do a redirect it is not supposed to do. If you see my markings of your debugging dump, you can see it.

I have made a fix which will be in next hotfix so only get requests can be redirected (it was like this previously, but was broken). 

To work around it, you can try to add &redirect=false to the callback URL.

BR Nicolai

Capture.PNG
Votes for this answer: 1
 
Reynir Viðar Ingason
Reply

Hey guys,

 

thank you for the help. Adding "&redirect=false" to the callbackUrl worked, I now have a POST request and the request body.

 

Regards,
Reynir

 

You must be logged in to post in the forum