Search code examples
javaspring-bootpaypalwebclient

Java Springboot WebClient call to API results in http-415


I am working with Java Spring boot - and I am trying to complete the integration of the paypal subscriptions api.

I've got a show subscription plan working in WebClient - although I am unable to complete the functionality for the cancel subscription method.

I get a 415 error --

"message": "415 Unsupported Media Type from POST https://api.sandbox.paypal.com/v1/billing/subscriptions/I-JJ0B8HRKSRYV/cancel",

https://developer.paypal.com/api/rest/responses/

https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_cancel -- when I look at the curl approach - it claims content-type and accept are in the header -- but it could also be the way I've tried to add the body?

https://reflectoring.io/spring-webclient/

//showSubscriptionDetails method

@CrossOrigin
@GetMapping("/api/showsubscriptiondetails")
@ResponseBody
public ResponseEntity<BaseResponse> showSubscriptionDetails(
        @RequestParam("subscriptionID") String subscriptionID
) throws PayPalRESTException {
    BaseResponse response = new BaseResponse();

    String bearerToken = generateAccessToken();
    String token = bearerToken.split(" ")[1].trim();

    WebClient client = WebClient.create(host);

    WebClient.ResponseSpec responseSpec = client.get()
            .uri("/v1/billing/subscriptions/"+subscriptionID+"?fields=plan")
            .headers(h -> {
                h.setBearerAuth(token);
            })
            .retrieve();

    String responseBody = responseSpec.bodyToMono(String.class).block();


    ObjectMapper mapper = new ObjectMapper();
    JsonNode root = null;
    try {
        root = mapper.readTree(responseBody);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
    JsonNode status = root.path("status");
    JsonNode plan = root.path("plan").get("name");

    BasicDBObject res = new BasicDBObject();
    res.put("status", status);
    res.put("plan", plan);

    response.setCode("OK");
    response.setData(res);
    response.setDescription(appMessageLocalUtil.getLanguageMessage("subscription.found"));
    return ResponseEntity.ok().body(response);
}

//cancelSubscription method

MultiValueMap<String, String> bodyValues = new LinkedMultiValueMap<>();
bodyValues.add("reason", "too expensive");


String bearerToken = generateAccessToken();
String token = bearerToken.split(" ")[1].trim();

WebClient client = WebClient.create(host);

WebClient.ResponseSpec responseSpec = client.post()
        .uri("/v1/billing/subscriptions/"+subscriptionID+"/cancel")
        .headers(h -> {
            h.setBearerAuth(token);
        })
        .retrieve();

String responseBody = responseSpec.bodyToMono(String.class).block();

System.out.println("responseSpec-- "+ responseSpec);
System.out.println("responseBody-- "+ responseBody);

... attempts

I've tried adding the content type in the header and as part of the main build but nothing seems to pass.

       WebClient client = WebClient.create(host);

        WebClient.ResponseSpec responseSpec = client.post()
                .uri("/v1/billing/subscriptions/"+subscriptionID+"/cancel")
                .headers(h -> {
                    h.setBearerAuth(token);
                    //h.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                    //h.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
                })
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                //.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                //.contentType(MediaType.APPLICATION_FORM_URLENCODED)
                //.accept(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromFormData(bodyValues))
                .retrieve();

-- 21st April 2023

working code was this - making body an object and using fromValue in the bodyinserter -- responseBody is null on outcome- but it does cancel the subscribe id

BasicDBObject body = new BasicDBObject();
body.put("reason", "xxx");

String bearerToken = generateAccessToken();
String token = bearerToken.split(" ")[1].trim();

WebClient client = WebClient.create(host);

WebClient.ResponseSpec responseSpec = client.post()
        .uri("/v1/billing/subscriptions/"+subscriptionID+"/cancel")
        .headers(h -> {
            h.setBearerAuth(token);
        })
        .contentType(MediaType.APPLICATION_JSON)
        .body(BodyInserters.fromValue(body))
        .retrieve();

String responseBody = responseSpec.bodyToMono(String.class).block();
System.out.println("responseSpec-- "+ responseSpec);
System.out.println("responseBody-- "+ responseBody);

Solution

  • The api doc you link to says the cancel body is json and contains {“reason”: “too expensive”}. You are sending a form. Hence 415: form ≠ json.

    Try this:

    String body = “{\”reason\”: \”too expensive\”}”;
    
    WebClient client = WebClient.create(host);
    
            WebClient.ResponseSpec responseSpec = client.post()
       .uri("/v1/billing/subscriptions/"+subscriptionID+"/cancel")
                .headers(h -> {
                    h.setBearerAuth(token);
                })
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(body))
                .retrieve();