Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interrupt if API call to payment processor takes over 60 seconds

I'm integrating with a payments processor and am trying to deal with the scenario where:

  • user clicks pay and a request is made to our server
  • our server makes a request to the payment processor
  • there is a significant delay on the payment processor side
  • after a certain threshold e.g. 60 seconds, we alert the user that their payment was unsuccessful
  • after 70 seconds the payment processor returns a successful response.

So I need to start an API call to the payment processor from within the HTTP call from the UI, then if it takes more than 60 seconds, end the HTTP call and return an error to the user, then if the API call to the payment processor eventually succeeds (say after 70 seconds), send an email to the admin team.

I'm thinking of something like this:

    import javax.ws.rs.client.*;
    import java.util.Timer;
    import java.util.TimerTask;

    ...

    boolean overThreshold = false;
    int timeout = 60; // seconds
    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            overThreshold = true;
            // return a message to user here saying their payment could not be processed
        }
    };

    new Timer(true).schedule(task, timeout * 1000);

    Client client = ClientBuilder.newClient();
    WebTarget webTarget
            = client.target({url of payment processor});
    Invocation.Builder builder = webTarget.request()
            .header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON);

    final Response response = builder.post(Entity.json(new Gson().toJson(request)));

    if (overThreshold) {
        // send alert email here
    }

There are a few problems, e.g. the run() method has void return value, error with overThreshold as being accessed from a inner class. Is there a more elegant way of doing this?

like image 311
Mark Avatar asked Apr 09 '19 17:04

Mark


1 Answers

Using Future.get(timeout) from an ExecutorService should handle this quite cleanly.

For example:

    ExecutorService executor = Executors.newCachedThreadPool();

    // ... set up builder as before ...
    Future<Response> responseFuture = executor.submit(
            () -> builder.post(Entity.json(new Gson().toJson(request))));
    try {
        Response response = responseFuture.get(timeout, TimeUnit.SECONDS);
        // return normal response here
    } catch (TimeoutException ex) {
        executor.submit( () -> {
            Response lateResponse = responseFuture.get();
            // send overThreshold alert email here
            // Dummy return - prefer Callable to Runnable here for exception handling
            return null;
        } );
        // return a message to user here saying their payment could not be processed
    }

The choice of ExecutorService could be tuned to fit, or equally be a shared thread pool elsewhere in the application.

like image 136
df778899 Avatar answered Nov 01 '22 01:11

df778899