diff --git a/src/main/java/conekta/io/client/impl/OrdersClient.java b/src/main/java/conekta/io/client/impl/OrdersClient.java index d4686ea..16bd45a 100644 --- a/src/main/java/conekta/io/client/impl/OrdersClient.java +++ b/src/main/java/conekta/io/client/impl/OrdersClient.java @@ -7,6 +7,7 @@ import conekta.io.config.Constants; import conekta.io.error.ConektaErrorResponse; import conekta.io.model.PaginatedConektaObject; +import conekta.io.model.request.OrderRefundReq; import conekta.io.model.request.OrderReq; import conekta.io.model.response.Order; import conekta.io.model.submodel.Charge; @@ -65,6 +66,17 @@ public ConektaResponse retrieveOrder(String orderId) { .build(); } + public ConektaResponse getOrderCharge(String orderId, String chargeId) { + HttpResponse customerResponse = doRequest(null, Constants.ORDERS_PATH + Constants.SLASH + orderId + Constants.CHARGES + Constants.SLASH + chargeId, Constants.GET); + return ConektaResponse.builder() + .response(customerResponse) + .statusCode(customerResponse.statusCode()) + .data(ConektaObjectMapper.getInstance().stringJsonToObject(customerResponse.body(), new TypeReference() { + })) + .error(ConektaObjectMapper.getInstance().stringJsonToObject(customerResponse.body(), ConektaErrorResponse.class)) + .build(); + } + public ConektaResponse> getOrderCharges(String orderId, String next) { HttpResponse customerResponse = doRequest(null, Constants.ORDERS_PATH + Constants.SLASH + orderId + Constants.CHARGES + (next != null ? next : ""), Constants.GET); return ConektaResponse.>builder() @@ -86,4 +98,14 @@ public ConektaResponse> getOrders(String next) { .error(ConektaObjectMapper.getInstance().stringJsonToObject(customerResponse.body(), ConektaErrorResponse.class)) .build(); } + + public ConektaResponse refundOrder(String orderId, OrderRefundReq orderRefundReq) { + HttpResponse orderResponse = doRequest(orderRefundReq, Constants.ORDERS_PATH + Constants.SLASH + orderId + Constants.REFUNDS, Constants.POST); + return ConektaResponse.builder() + .response(orderResponse) + .statusCode(orderResponse.statusCode()) + .data(ConektaObjectMapper.getInstance().stringJsonToObject(orderResponse.body(), Order.class)) + .error(ConektaObjectMapper.getInstance().stringJsonToObject(orderResponse.body(), ConektaErrorResponse.class)) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/conekta/io/config/Constants.java b/src/main/java/conekta/io/config/Constants.java index e675173..f0d5fe9 100644 --- a/src/main/java/conekta/io/config/Constants.java +++ b/src/main/java/conekta/io/config/Constants.java @@ -12,6 +12,7 @@ public class Constants { public static final String NEXT = "?next="; public static final String EVENTS_PATH = "/events"; public static final String CHARGES = "/charges"; + public static final String REFUNDS = "/refunds"; public static final int HTTP_CLIENT_TIMEOUT = 15; public static final String PAYMENT_SOURCES = "/payment_sources"; diff --git a/src/main/java/conekta/io/model/request/OrderRefundReq.java b/src/main/java/conekta/io/model/request/OrderRefundReq.java new file mode 100644 index 0000000..d4456ab --- /dev/null +++ b/src/main/java/conekta/io/model/request/OrderRefundReq.java @@ -0,0 +1,12 @@ +package conekta.io.model.request; + +import conekta.io.model.ConektaObject; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class OrderRefundReq extends ConektaObject { + private String reason; + private BigDecimal amount; +} diff --git a/src/test/java/conekta/io/client/impl/OrdersClientTest.java b/src/test/java/conekta/io/client/impl/OrdersClientTest.java index 4dd2e0b..646f4a8 100644 --- a/src/test/java/conekta/io/client/impl/OrdersClientTest.java +++ b/src/test/java/conekta/io/client/impl/OrdersClientTest.java @@ -7,6 +7,7 @@ import conekta.io.config.Constants; import conekta.io.error.ConektaError; import conekta.io.model.PaginatedConektaObject; +import conekta.io.model.request.OrderRefundReq; import conekta.io.model.request.OrderReq; import conekta.io.model.response.Order; import conekta.io.model.submodel.Charge; @@ -171,7 +172,48 @@ void getChargesOrderFail() throws IOException, URISyntaxException { Assertions.assertNotNull(order.getError()); Assertions.assertEquals(order.getError().getCode(), orderResp.getCode()); } - + + @Test + void getChargeOrder() throws IOException, URISyntaxException { + // Arrange + String orderChargeResponse = Utils.readFile("Orders/orderChargeResponse.json"); + String orderCharge = Utils.readFile("Orders/orderCharge.json"); + + Charge charge = ConektaObjectMapper.getInstance().stringJsonToObject(orderCharge, Charge.class); + mockWebServer.enqueue(new MockResponse() + .addHeader(Constants.CONTENT_TYPE, Constants.APPLICATION_JSON_CHARSET_UTF_8) + .addHeader(Constants.ACCEPT, Constants.APPLICATION_VND_CONEKTA_V_2_0_0_JSON) + .setBody(orderChargeResponse) + .setResponseCode(200)); + + // Act + ConektaResponse order = ordersClient.getOrderCharge("1", "anything"); + + // Assert + Assertions.assertNotNull(order.getData()); + Assertions.assertEquals(order.getData().getAmount().toString(), charge.getAmount().toString()); + } + + @Test + void getChargeOrderFail() throws IOException, URISyntaxException { + // Arrange + String orderChargeFailResponse = Utils.readFile("Orders/orderChargeFailResponse.json"); + ConektaError orderResp = ConektaObjectMapper.getInstance().stringJsonToObject(orderChargeFailResponse, ConektaError.class); + + mockWebServer.enqueue(new MockResponse() + .addHeader(Constants.CONTENT_TYPE, Constants.APPLICATION_JSON_CHARSET_UTF_8) + .addHeader(Constants.ACCEPT, Constants.APPLICATION_VND_CONEKTA_V_2_0_0_JSON) + .setBody(orderChargeFailResponse) + .setResponseCode(404)); + + // Act + ConektaResponse order = ordersClient.getOrderCharge("1", "nothing"); + + // Assert + Assertions.assertNotNull(order.getError()); + Assertions.assertEquals(order.getError().getCode(), orderResp.getCode()); + } + @Test void getOrders() throws IOException, URISyntaxException { // Arrange @@ -210,4 +252,46 @@ void getOrdersFail() throws IOException, URISyntaxException { Assertions.assertNotNull(orderResponse.getError()); Assertions.assertEquals(orderResponse.getError().getCode(), orderResp.getCode()); } + + @Test + void refundOrder() throws URISyntaxException, IOException, InterruptedException { + // Arrange + String orderRefundRequestJson = Utils.readFile("Orders/orderRefundRequest.json"); + String orderRefundResponseJson = Utils.readFile("Orders/orderRefundResponse.json"); + OrderRefundReq orderRefundReq = ConektaObjectMapper.getInstance().stringJsonToObject(orderRefundRequestJson, OrderRefundReq.class); + Order orderResp = ConektaObjectMapper.getInstance().stringJsonToObject(orderRefundResponseJson, Order.class); + mockWebServer.enqueue(new MockResponse() + .addHeader(Constants.CONTENT_TYPE, Constants.APPLICATION_JSON_CHARSET_UTF_8) + .addHeader(Constants.ACCEPT, Constants.APPLICATION_VND_CONEKTA_V_2_0_0_JSON) + .setBody(orderRefundResponseJson) + .setResponseCode(200)); + + // Act + ConektaResponse order = ordersClient.refundOrder("anything", orderRefundReq); + + // Assert + Assertions.assertEquals(order.getData(), orderResp); + } + + @Test + void refundOrdersFail() throws IOException, URISyntaxException { + // Arrange + String orderRefundRequestJson = Utils.readFile("Orders/orderRefundRequest.json"); + String orderRefundFailResponse = Utils.readFile("Orders/orderRefundFailResponse.json"); + OrderRefundReq orderRefundReq = ConektaObjectMapper.getInstance().stringJsonToObject(orderRefundRequestJson, OrderRefundReq.class); + ConektaError orderResp = ConektaObjectMapper.getInstance().stringJsonToObject(orderRefundFailResponse, ConektaError.class); + + mockWebServer.enqueue(new MockResponse() + .addHeader(Constants.CONTENT_TYPE, Constants.APPLICATION_JSON_CHARSET_UTF_8) + .addHeader(Constants.ACCEPT, Constants.APPLICATION_VND_CONEKTA_V_2_0_0_JSON) + .setBody(orderRefundFailResponse) + .setResponseCode(404)); + + // Act + ConektaResponse orderResponse = ordersClient.refundOrder("anything", orderRefundReq); + + // Assert + Assertions.assertNotNull(orderResponse.getError()); + Assertions.assertEquals(orderResponse.getError().getCode(), orderResp.getCode()); + } } \ No newline at end of file diff --git a/src/test/resources/Orders/orderChargeFailResponse.json b/src/test/resources/Orders/orderChargeFailResponse.json new file mode 100644 index 0000000..6279fe2 --- /dev/null +++ b/src/test/resources/Orders/orderChargeFailResponse.json @@ -0,0 +1,12 @@ +{ + "details": [ + { + "debug_message": "The object order \"ord_2sLgKeSCQibNS7Hm\" could not be found.", + "message": "El recurso no ha sido encontrado.", + "code": "conekta.errors.resource_not_found.entity" + } + ], + "object": "error", + "type": "resource_not_found_error", + "log_id": "630531496cd84c0d4807c615" +} \ No newline at end of file diff --git a/src/test/resources/Orders/orderChargeResponse.json b/src/test/resources/Orders/orderChargeResponse.json new file mode 100644 index 0000000..6e1f2d3 --- /dev/null +++ b/src/test/resources/Orders/orderChargeResponse.json @@ -0,0 +1,29 @@ +{ + "id": "62fbffbb9e9c315e01b36ada", + "livemode": false, + "created_at": 1660682171, + "currency": "MXN", + "device_fingerprint": "zptcxk4p6w1ijwz85snf1l3bqe5g09ie", + "payment_method": { + "name": "erererere ererere ", + "exp_month": "06", + "exp_year": "24", + "auth_code": "272780", + "object": "card_payment", + "type": "credit", + "last4": "4607", + "brand": "visa", + "issuer": "BANAMEX", + "account_type": "Credit", + "country": "MX", + "fraud_indicators": [] + }, + "object": "charge", + "description": "Payment from order", + "status": "paid", + "amount": 81568, + "paid_at": 1660682171, + "fee": 3033, + "customer_id": "", + "order_id": "ord_2sLgKeSCQibNS7Hms" +} \ No newline at end of file diff --git a/src/test/resources/Orders/orderRefundFailResponse.json b/src/test/resources/Orders/orderRefundFailResponse.json new file mode 100644 index 0000000..4cd1afd --- /dev/null +++ b/src/test/resources/Orders/orderRefundFailResponse.json @@ -0,0 +1,12 @@ +{ + "details": [ + { + "debug_message": "The object Order \"ord_2sB7DCyt6hWpZnME\" could not be found.", + "message": "El recurso no ha sido encontrado.", + "code": "conekta.errors.resource_not_found.entity" + } + ], + "object": "error", + "type": "resource_not_found_error", + "log_id": "630508819e9c31064875b472" +} \ No newline at end of file diff --git a/src/test/resources/Orders/orderRefundRequest.json b/src/test/resources/Orders/orderRefundRequest.json new file mode 100644 index 0000000..611f806 --- /dev/null +++ b/src/test/resources/Orders/orderRefundRequest.json @@ -0,0 +1,4 @@ +{ + "amount": 10, + "reason": "requested_by_client" +} \ No newline at end of file diff --git a/src/test/resources/Orders/orderRefundResponse.json b/src/test/resources/Orders/orderRefundResponse.json new file mode 100644 index 0000000..216864c --- /dev/null +++ b/src/test/resources/Orders/orderRefundResponse.json @@ -0,0 +1,195 @@ +{ + "livemode": false, + "amount": 81568, + "currency": "MXN", + "payment_status": "partially_refunded", + "amount_refunded": 20, + "customer_info": { + "email": "test@test.com", + "phone": "5556581111", + "name": "John Doe", + "corporate": true, + "customer_id": "cus_2rYuZoAh4cSWMpjP3", + "object": "customer_info" + }, + "shipping_contact": { + "receiver": "Monse Torres", + "phone": "5556581111", + "address": { + "street1": "Av. Jardín", + "country": "mx", + "residential": true, + "object": "shipping_address", + "postal_code": "02970" + }, + "id": "ship_cont_2sB7DCyt6hWpZnMES", + "object": "shipping_contact", + "created_at": 0 + }, + "channel": { + "segment": "Checkout", + "checkout_request_id": "f8192408-999f-48b6-8d0a-bbbfc0b84b33", + "checkout_request_type": "HostedPayment", + "id": "channel_2sB7DCyt6hWpZnMEU" + }, + "checkout": { + "id": "f8192408-999f-48b6-8d0a-bbbfc0b84b33", + "name": "Concepto quemado", + "livemode": false, + "emails_sent": 0, + "success_url": "https://www.google.com/payment/", + "failure_url": "https://www.mysite.com/payment/", + "paid_payments_count": 0, + "sms_sent": 0, + "status": "Issued", + "type": "HostedPayment", + "recurrent": false, + "starts_at": 1658120400, + "expires_at": 1658379599, + "allowed_payment_methods": [ + "card", + "cash", + "bank_transfer" + ], + "exclude_card_networks": [], + "needs_shipping_contact": false, + "monthly_installments_options": [ + 3, + 6, + 9, + 12 + ], + "monthly_installments_enabled": true, + "is_redirect_on_failure": true, + "force_3ds_flow": true, + "metadata": {}, + "can_not_expire": false, + "object": "checkout", + "slug": "f8192408999f48b68d0abbbfc0b84b33", + "url": "https://pay.stg.conekta.io/checkout/f8192408999f48b68d0abbbfc0b84b33" + }, + "object": "order", + "id": "ord_2sB7DCyt6hWpZnMET", + "metadata": {}, + "created_at": 1658151139, + "updated_at": 1661273884, + "line_items": { + "object": "list", + "has_more": false, + "total": 1, + "data": [ + { + "name": "It is fine dog cup", + "unit_price": 80000, + "quantity": 1, + "tags": [ + "physical goods" + ], + "object": "line_item", + "id": "line_item_2sB7DCyt6hWpZnMEN", + "parent_id": "ord_2sB7DCyt6hWpZnMET", + "metadata": {}, + "antifraud_info": {} + } + ] + }, + "shipping_lines": { + "object": "list", + "has_more": false, + "total": 1, + "data": [ + { + "amount": 0, + "carrier": "Fedex", + "method": "Airplane", + "tracking_number": "TRACK000000000123", + "object": "shipping_line", + "id": "ship_lin_2sB7DCyt6hWpZnMEP", + "parent_id": "ord_2sB7DCyt6hWpZnMET" + } + ] + }, + "tax_lines": { + "object": "list", + "has_more": false, + "total": 1, + "data": [ + { + "description": "IVA MX 16 ", + "amount": 1568, + "object": "tax_line", + "id": "tax_lin_2sB7DCyt6hWpZnMEQ", + "parent_id": "ord_2sB7DCyt6hWpZnMET" + } + ] + }, + "charges": { + "object": "list", + "has_more": false, + "total": 1, + "data": [ + { + "id": "62d5610a6cd84c51ce9441a0", + "livemode": false, + "created_at": 1658151178, + "currency": "MXN", + "channel": { + "segment": "Checkout", + "checkout_request_id": "f8192408-999f-48b6-8d0a-bbbfc0b84b33", + "checkout_request_type": "HostedPayment", + "id": "channel_2sB7DhmqQUU7sTLSX" + }, + "payment_method": { + "name": "TEST", + "exp_month": "12", + "exp_year": "25", + "auth_code": "878062", + "object": "card_payment", + "type": "credit", + "last4": "4242", + "brand": "visa", + "issuer": "BANAMEX", + "account_type": "Credit", + "country": "MX", + "fraud_indicators": [] + }, + "object": "charge", + "description": "Payment from order", + "status": "partially_refunded", + "amount": 81568, + "paid_at": 1658151179, + "fee": 5890, + "customer_id": "cus_2rYuZoAh4cSWMpjP3", + "order_id": "ord_2sB7DCyt6hWpZnMET", + "refunds": { + "object": "list", + "has_more": false, + "total": 3, + "data": [ + { + "object": "refund", + "amount": -10, + "auth_code": "878062", + "id": "6305071c6cd84c3fc17086be", + "created_at": 1661273884 + }, + { + "object": "refund", + "amount": -10, + "auth_code": "878062", + "id": "6304dd746cd84c121d49af7f", + "created_at": 1661263220 + }, + { + "object": "refund", + "amount": -10, + "auth_code": "878062", + "id": "62d56c206cd84c51d1db81c7", + "created_at": 1658154016 + } + ] + } + } + ] + } +} \ No newline at end of file