WHOATEMYNOODLES
WHOATEMYNOODLES

Reputation: 2631

Retrofit call returning 400, cURL request working perfectly fine, syntax issue

I've tried making a retrofit call to an API endpoint, but it's returning a 400 error, however my curl request is working perfectly fine. I can't seem to spot the error, could someone double check my work to see where I made a mistake?

The curl call that works:

curl --request POST https://connect.squareupsandbox.com/v2/payments \
    --header "Content-Type: application/json" \
    --header "Authorization: Bearer accesstoken112233" \
    --header "Accept: application/json" \
    --data '{
    "idempotency_key": "ab2a118d-53e2-47c6-88e2-8c48cb09bf9b",
    "amount_money": {
    "amount": 100,
    "currency": "USD"},
    "source_id": "cnon:CBASEITjGLBON1y5od2lsdxSPxQ"}'

My Retrofit call:

public interface IMakePayment {

        @Headers({
                "Accept: application/json",
                "Content-Type: application/json",
                "Authorization: Bearer accesstoken112233"
        })
        @POST(".")
        Call<Void> listRepos(@Body DataDto dataDto);
    }

DataDto class:

public class DataDto {

    private String idempotency_key;
    private String amount_money;
    private String source_id;

    public DataDto(String idempotency_key, String amount_money, String source_id) {
        this.idempotency_key = idempotency_key;
        this.amount_money = amount_money;
        this.source_id = source_id;
    }
}

And lastly making the retrofit call:

 DataDto dataDto = new DataDto("ab2a118d-53e2-47c6-88e2-8c48cb09bf9b", "{\"amount\": 100, \"currency\": \"USD\"}", "cnon:CBASEITjGLBON1y5od2lsdxSPxQ");

        RetrofitInterfaces.IMakePayment service = RetrofitClientInstance.getRetrofitInstance().create(RetrofitInterfaces.IMakePayment.class);
        Call<Void> call = service.listRepos(dataDto);
        call.enqueue(new Callback<Void>() {
            @Override
            public void onResponse(@NonNull Call<Void> call, @NonNull Response<Void> response) {
                Log.d(TAG, "onResponse: " + response.toString());
            }

            @Override
            public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
                Log.d(TAG, "onFailure: Error: " + t);
            }
        });

Retrofit Instance:

public class RetrofitClientInstance {

    private static Retrofit retrofit;
    private static final String BASE_URL = "https://connect.squareupsandbox.com/v2/payments/";


    public static Retrofit getRetrofitInstance() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

Edit 1: Changing to second parameter to JSON Object

JSONObject jsonObject = new JSONObject();
        try{
            jsonObject.put("amount", 100);
            jsonObject.put("currency", "USD");
        }catch (Exception e){
            Log.d(TAG, "onCreate: " + e);
        }
        DataDto dataDto = new DataDto("ab2a118d-53e2-47c6-88e2-8c48cb09bf9b", jsonObject, "cnon:CBASEITjGLBON1y5od2lsdxSPxQ");

Upvotes: 7

Views: 1015

Answers (4)

Jasurbek
Jasurbek

Reputation: 2966

First of all, let's see what 400 means

The HyperText Transfer Protocol (HTTP) 400 Bad Request response status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

Now we are sure, the problem stands in our request (not server fault), most probably it is because you are trying to convert JSON in request (do not do this explicitly GSON will convert automatically)

Use interceptor to verify your outgoing network requests (Tell the result here)

you use @POST(".") which does not make sense, please understand BASE_URL is your server URL NOT MORE

The problem could be translating this post request

So a possible solution

  • Change base URL into "https://connect.squareupsandbox.com/"
  • Replace @POST(".") with @POST("v2/payments/")

PS. @NaveenNiraula mentioned right thing even though it did not help you, please follow his instruction, it is the correct way parsing data using GSON (make sure you include it and configure it correctly) converter

EDIT

I make it work (I eliminated 400 error code that is what you want as long as question title is concerned) partially which means I detect why 400 error was occurred and fixed it but unfortunately, I stuck the UNAUTHORIZED issue. The problem was relating to converting json and data type

data class DataDTO(
    val idempotency_key: String,
    val source_id: String,
    val amount_money: MoneyAmount

)

data class MoneyAmount(
    val amount: Int,
    val currency: String
)

I gist all code here you can refer

Upvotes: 3

GV_FiQst
GV_FiQst

Reputation: 1567

Please check your base url.

In your curl you have https://connect.squareupsandbox.com/v2/payments

But in the code you have

private static final String BASE_URL = "https://connect.squareupsandbox.com/v2/payments/";

There is extra / (slash) in the end. I've seen cases where it was the issue. Could be your problem :)

Upvotes: 0

k_o_
k_o_

Reputation: 6298

Most likely the passed JSON structure is not serialized in the same format.

"amount_money": {
    "amount": 100,
    "currency": "USD"},

I would at first use for private String amount_money; a real DTO having the amount and currency fields. This should give progress. I'm not 100% sure how the underscore mapping of attributes looks like, but this is the next step.

Add logging to be able to see the passed data. A quick search reveals this tutorial: https://futurestud.io/tutorials/retrofit-2-log-requests-and-responses. When seeing the transmitted data it should be easy to compare the expected and sent data.

Upvotes: 0

Naveen Niraula
Naveen Niraula

Reputation: 771

You need two DTO classes as below:

public class Amount_money
{
    private String amount;

    private String currency;

    public String getAmount ()
    {
        return amount;
    }

    public void setAmount (String amount)
    {
        this.amount = amount;
    }

    public String getCurrency ()
    {
        return currency;
    }

    public void setCurrency (String currency)
    {
        this.currency = currency;
    }

    @Override
    public String toString()
    {
        return "ClassPojo [amount = "+amount+", currency = "+currency+"]";
    }
}

And

public class DataDto
{
    private String idempotency_key;

    private Amount_money amount_money;

    private String source_id;

    public String getIdempotency_key ()
    {
        return idempotency_key;
    }

    public void setIdempotency_key (String idempotency_key)
    {
        this.idempotency_key = idempotency_key;
    }

    public Amount_money getAmount_money ()
    {
        return amount_money;
    }

    public void setAmount_money (Amount_money amount_money)
    {
        this.amount_money = amount_money;
    }

    public String getSource_id ()
    {
        return source_id;
    }

    public void setSource_id (String source_id)
    {
        this.source_id = source_id;
    }

    @Override
    public String toString()
    {
        return "ClassPojo [idempotency_key = "+idempotency_key+", amount_money = "+amount_money+", source_id = "+source_id+"]";
    }
}

You need to create object for each like under :

Amount_money am = new Amount_money();
am.setAmount("100");
am.setCurrency("USD");

DataDto dto = new DataDto();
dto.setIdempotency_key("your key");
dto.setsource_id("your id");
dto.setAmount_money(am);

RetrofitInterfaces.IMakePayment service = RetrofitClientInstance.getRetrofitInstance().create(RetrofitInterfaces.IMakePayment.class);
        Call<Void> call = service.listRepos(dataDto);
// yo get the point follow along

Upvotes: 0

Related Questions