Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 52 additions & 22 deletions src/main/java/com/starkbank/PaymentRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.starkbank.utils.Rest;
import com.starkcore.utils.SubResource;
import com.starkcore.utils.GsonEvent;
import com.google.gson.annotations.JsonAdapter;

import java.lang.reflect.Type;
import java.util.ArrayList;
Expand All @@ -15,9 +16,10 @@
import java.util.Map;


@JsonAdapter(PaymentRequest.Deserializer.class)
public final class PaymentRequest extends Resource {
static ClassData data = new ClassData(PaymentRequest.class, "PaymentRequest");

static {
GsonEvent.registerTypeAdapter(PaymentRequest.class, new PaymentRequest.Deserializer());
}
Expand Down Expand Up @@ -171,47 +173,75 @@ public PaymentRequest(){
public static class Deserializer implements JsonDeserializer<PaymentRequest> {
@Override
public PaymentRequest deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctw) throws JsonParseException {
JsonElement resourceElement = json.getAsJsonObject().get("payment");
json.getAsJsonObject().remove("payment");
PaymentRequest request = new Gson().fromJson(json, PaymentRequest.class);
Gson gson = GsonEvent.getInstance();
JsonObject obj = json.getAsJsonObject();

// Build the base request manually to avoid recursive deserialization
PaymentRequest request = new PaymentRequest();
request.id = obj.has("id") && !obj.get("id").isJsonNull() ? obj.get("id").getAsString() : null;
request.centerId = obj.has("centerId") && !obj.get("centerId").isJsonNull() ? obj.get("centerId").getAsString() : null;
request.type = obj.has("type") && !obj.get("type").isJsonNull() ? obj.get("type").getAsString() : null;
request.due = obj.has("due") && !obj.get("due").isJsonNull() ? obj.get("due").getAsString() : null;
request.amount = obj.has("amount") && !obj.get("amount").isJsonNull() ? obj.get("amount").getAsLong() : null;
request.description = obj.has("description") && !obj.get("description").isJsonNull() ? obj.get("description").getAsString() : null;
request.status = obj.has("status") && !obj.get("status").isJsonNull() ? obj.get("status").getAsString() : null;
request.updated = obj.has("updated") && !obj.get("updated").isJsonNull() ? obj.get("updated").getAsString() : null;
request.created = obj.has("created") && !obj.get("created").isJsonNull() ? obj.get("created").getAsString() : null;

if (obj.has("tags") && obj.get("tags").isJsonArray()) {
JsonArray tagsArray = obj.getAsJsonArray("tags");
String[] tags = new String[tagsArray.size()];
for (int i = 0; i < tagsArray.size(); i++) {
tags[i] = tagsArray.get(i).isJsonNull() ? null : tagsArray.get(i).getAsString();
}
request.tags = tags;
}

// Deserialize payment according to type
JsonElement resourceElement = obj.get("payment");
Resource resource = null;
switch (request.type) {
case "transfer":
resource = new Gson().fromJson(resourceElement, Transfer.class);
resource = gson.fromJson(resourceElement, Transfer.class);
break;
case "transaction":
resource = new Gson().fromJson(resourceElement, Transaction.class);
resource = gson.fromJson(resourceElement, Transaction.class);
break;
case "boleto-payment":
resource = new Gson().fromJson(resourceElement, BoletoPayment.class);
resource = gson.fromJson(resourceElement, BoletoPayment.class);
break;
case "utility-payment":
resource = new Gson().fromJson(resourceElement, UtilityPayment.class);
resource = gson.fromJson(resourceElement, UtilityPayment.class);
break;
case "tax-payment":
resource = new Gson().fromJson(resourceElement, TaxPayment.class);
resource = gson.fromJson(resourceElement, TaxPayment.class);
break;
case "darf-payment":
resource = new Gson().fromJson(resourceElement, DarfPayment.class);
resource = gson.fromJson(resourceElement, DarfPayment.class);
break;
case "brcode-payment":
resource = new Gson().fromJson(resourceElement, BrcodePayment.class);
resource = gson.fromJson(resourceElement, BrcodePayment.class);
break;
default:
break;
}

request.payment = resource;

resourceElement = json.getAsJsonObject().get("actions");
for (LinkedTreeMap<Object, Object> action : (List<LinkedTreeMap<Object, Object>>) new Gson().fromJson(resourceElement, List.class)){
request.actions.add(new PaymentRequest.Action(
(String) action.get("name"),
(String) action.get("action"),
(String) action.get("type"),
(String) action.get("id")
));
// Parse actions with safe initialization
List<PaymentRequest.Action> parsedActions = new ArrayList<>();
JsonElement actionsElement = obj.get("actions");
if (actionsElement != null && !actionsElement.isJsonNull()) {
for (LinkedTreeMap<Object, Object> action : (List<LinkedTreeMap<Object, Object>>) gson.fromJson(actionsElement, List.class)) {
parsedActions.add(new PaymentRequest.Action(
(String) action.get("name"),
(String) action.get("action"),
(String) action.get("type"),
(String) action.get("id")
));
}
}
request.actions = parsedActions;

return request;
}
Expand Down Expand Up @@ -477,7 +507,7 @@ private static String getType(Resource payment) throws Exception{
return "brcode-payment";

throw new Exception("Payment must either be a Transfer, a Transaction, a BoletoPayment, a BrcodePayment, " +
"a UtilityPayment, a TaxPayment or a DarfPayment.");
"a UtilityPayment, a TaxPayment or a DarfPayment.");
}

public final static class Log extends Resource {
Expand Down Expand Up @@ -562,9 +592,9 @@ public final static class Action extends SubResource {

/**
* PaymentRequest.Action object
*
*
* Used to define a action in the payment request
*
*
* Parameters:
* @param name [string]: name of the user that took the action. ex: "Stark Project"
* @param action [string]: action type. ex "requested", "approved"
Expand Down
90 changes: 83 additions & 7 deletions src/test/java/TestPaymentRequest.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import static org.junit.Assert.*;

import com.starkbank.PaymentRequest;
import com.starkbank.Settings;
import com.starkbank.Transaction;
Expand All @@ -10,6 +12,7 @@
import org.junit.Assert;
import org.junit.AssumptionViolatedException;
import org.junit.Test;
import com.google.gson.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
Expand Down Expand Up @@ -39,10 +42,83 @@ public void testCreate() throws Exception{
}

for(PaymentRequest request : requests) {
Assert.assertNotNull(request.id);
assertNotNull(request.id);
}
}

@Test
public void testDeserializeCreateResponsePayload() {
String payload = "{\n" +
" \"message\" : \"Requisição(ões) de pagamento(s) criada(s) com sucesso\",\n" +
" \"requests\" : [ {\n" +
" \"actions\" : [ {\n" +
" \"action\" : \"requested\",\n" +
" \"email\" : \"\",\n" +
" \"id\" : \"12345454444\",\n" +
" \"name\" : \"Proj\",\n" +
" \"pictureUrl\" : \"\",\n" +
" \"status\" : \"active\",\n" +
" \"type\" : \"project\"\n" +
" }, {\n" +
" \"action\" : \"required\",\n" +
" \"email\" : \"fgomes@helpnei.com\",\n" +
" \"id\" : \"12345454444\",\n" +
" \"name\" : \"Fernando Gomes\",\n" +
" \"pictureUrl\" : \"\",\n" +
" \"status\" : \"active\",\n" +
" \"type\" : \"member\"\n" +
" }, {\n" +
" \"action\" : \"required\",\n" +
" \"email\" : \"vpaz@helpnei.com\",\n" +
" \"id\" : \"12345454444\",\n" +
" \"name\" : \"Vinicius Paz\",\n" +
" \"pictureUrl\" : \"\",\n" +
" \"status\" : \"active\",\n" +
" \"type\" : \"member\"\n" +
" } ],\n" +
" \"amount\" : 12334,\n" +
" \"attachments\" : [ ],\n" +
" \"centerId\" : \"12345671234567\",\n" +
" \"created\" : \"2025-11-23T12:32:36.965218+00:00\",\n" +
" \"description\" : \"fERNANDO teste manual (011.222.333-40)\",\n" +
" \"due\" : \"2025-11-23T12:32:36.952898+00:00\",\n" +
" \"id\" : \"12345454444\",\n" +
" \"payment\" : {\n" +
" \"accountNumber\" : \"*IEQ1ATFCgZwH8JFsfAdeurjYW9AR3JpFIwm4v7iTVnpYkY140znoQZt/1u6SyhCtQ7FlmKwgyVsAsVzR7EhEwdtLhSq4NDPLsj3NxQTFakw=\",\n" +
" \"accountType\" : \"payment\",\n" +
" \"amount\" : 12334,\n" +
" \"bankCode\" : \"123456\",\n" +
" \"bankName\" : \"NU PAGAMENTOS - IP\",\n" +
" \"branchCode\" : \"*7at/HwNgLRQDwejLYsmwPHRe4qlN/WToRlf/3aNi22Q=\",\n" +
" \"description\" : \"Cash-out request ID: e53a40d3-76f2-4da2-a217-b2829faa1c05\",\n" +
" \"externalId\" : \"e53a40d3-76f2-4da2-a217-b2829faa1c05\",\n" +
" \"name\" : \"fERNANDO teste manual\",\n" +
" \"tags\" : [ \"requestId:e53a40d3-76f2-4da2-a217-b2829faa1c05\" ],\n" +
" \"taxId\" : \"011.222.333-40\"\n" +
" },\n" +
" \"status\" : \"pending\",\n" +
" \"tags\" : [ \"requestid:e53a40d3-76f2-4da2-a217-b2829faa1c05\" ],\n" +
" \"type\" : \"transfer\",\n" +
" \"updated\" : \"2025-11-23T12:32:36.965225+00:00\"\n" +
" } ]\n" +
"}";

JsonObject obj = JsonParser.parseString(payload).getAsJsonObject();
JsonArray arr = obj.getAsJsonArray("requests");
JsonElement first = arr.get(0);

// This must not throw (previously it would fail trying to instantiate abstract Resource)
PaymentRequest request = new Gson().fromJson(first, PaymentRequest.class);

assertNotNull(request);
assertEquals("transfer", request.type);
assertNotNull(request.payment);
assertTrue(request.payment instanceof com.starkbank.Transfer);
assertEquals("pending", request.status);
assertNotNull(request.actions);
assertEquals(3, request.actions.size());
}

@Test
public void testQuery() throws Exception {
Settings.user = utils.User.defaultProject();
Expand All @@ -57,7 +133,7 @@ public void testQuery() throws Exception {
int i = 0;
for (PaymentRequest request : requests) {
i += 1;
Assert.assertNotNull(request.id);
assertNotNull(request.id);
}
}

Expand Down Expand Up @@ -104,13 +180,13 @@ public void testPaymentRequestEventParse() throws Exception{
try {
event = (Event.PaymentRequestEvent) Event.parse(content, valid_signature);
} catch (InvalidSignatureError e) {
Assert.fail(e.getMessage());
fail(e.getMessage());
}

Assert.assertEquals(event.getClass(), Event.PaymentRequestEvent.class);
Assert.assertEquals(event.log.getClass(), PaymentRequest.Log.class);
Assert.assertEquals(event.log.request.getClass(), PaymentRequest.class);
Assert.assertEquals(event.log.request.payment.getClass(), BoletoPayment.class);
assertEquals(event.getClass(), Event.PaymentRequestEvent.class);
assertEquals(event.log.getClass(), PaymentRequest.Log.class);
assertEquals(event.log.request.getClass(), PaymentRequest.class);
assertEquals(event.log.request.payment.getClass(), BoletoPayment.class);
}

static PaymentRequest example() throws Exception{
Expand Down