From 2871ff39c577fc878c7d2b62b0f1e7dce69340e0 Mon Sep 17 00:00:00 2001 From: Evgeniy Sinev Date: Tue, 29 Sep 2020 17:31:45 +0300 Subject: [PATCH] #88911 Added refund() operation --- pom.xml | 9 ++- server/pom.xml | 5 ++ .../payneteasy/pos/proxy/IPaymentService.java | 3 + .../com/payneteasy/pos/proxy/WebServer.java | 22 ++++++ .../pos/proxy/impl/InpasNetworkManager.java | 69 +++++++++++++++++ .../pos/proxy/impl/PaymentServiceImpl.java | 77 +++++++++---------- .../pos/proxy/messages/PaymentRequest.java | 16 +++- .../pos/proxy/messages/PaymentResponse.java | 19 ++++- .../pos/proxy/messages/RefundRequest.java | 26 +++++++ .../main/resources/swagger-ui/pos-proxy.json | 69 +++++++++++++++++ swagger/messages/RefundRequest.yaml | 30 ++++++++ swagger/pos-proxy.yaml | 16 ++++ swagger/run-ui.sh | 1 + 13 files changed, 312 insertions(+), 50 deletions(-) create mode 100644 server/src/main/java/com/payneteasy/pos/proxy/impl/InpasNetworkManager.java create mode 100644 server/src/main/java/com/payneteasy/pos/proxy/messages/RefundRequest.java create mode 100644 swagger/messages/RefundRequest.yaml diff --git a/pom.xml b/pom.xml index 403b932..d00ef96 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ 4.1.1.4 - 1.4-59 + 1.4-90 @@ -211,6 +211,13 @@ test + + org.projectlombok + lombok + 1.18.12 + provided + + diff --git a/server/pom.xml b/server/pom.xml index 7c0fbc5..3d3f3e6 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -100,6 +100,11 @@ one-nio + + org.projectlombok + lombok + provided + diff --git a/server/src/main/java/com/payneteasy/pos/proxy/IPaymentService.java b/server/src/main/java/com/payneteasy/pos/proxy/IPaymentService.java index 27897c1..a756807 100644 --- a/server/src/main/java/com/payneteasy/pos/proxy/IPaymentService.java +++ b/server/src/main/java/com/payneteasy/pos/proxy/IPaymentService.java @@ -2,9 +2,12 @@ import com.payneteasy.pos.proxy.messages.PaymentRequest; import com.payneteasy.pos.proxy.messages.PaymentResponse; +import com.payneteasy.pos.proxy.messages.RefundRequest; public interface IPaymentService { PaymentResponse pay(PaymentRequest aRequest); + PaymentResponse refund(RefundRequest aRequest); + } diff --git a/server/src/main/java/com/payneteasy/pos/proxy/WebServer.java b/server/src/main/java/com/payneteasy/pos/proxy/WebServer.java index 037b4f5..bb0e5b7 100644 --- a/server/src/main/java/com/payneteasy/pos/proxy/WebServer.java +++ b/server/src/main/java/com/payneteasy/pos/proxy/WebServer.java @@ -2,6 +2,7 @@ import com.payneteasy.pos.proxy.messages.PaymentRequest; import com.payneteasy.pos.proxy.messages.PaymentResponse; +import com.payneteasy.pos.proxy.messages.RefundRequest; import one.nio.http.*; import one.nio.server.AcceptorConfig; import one.nio.util.ByteArrayBuilder; @@ -68,6 +69,27 @@ public void pay(Request aRequest, HttpSession aSession) throws IOException { }); } + @Path("/pos-proxy/refund") + public void refund(Request aRequest, HttpSession aSession) throws IOException { + PAYMENT_EXECUTOR.execute(() -> { + try { + String apiKey = aRequest.getHeader("X-API-Key"); + + if(!EXPECTED_API_KEY.equals(apiKey)) { + LOG.error("Expected X-API-Key is '{}' but was '{}'", EXPECTED_API_KEY, apiKey); + aSession.sendError(Response.FORBIDDEN, "Wrong X-API-Key"); + return; + } + JsonMessages json = new JsonMessages(aRequest); + RefundRequest request = json.parse(RefundRequest.class); + PaymentResponse response = paymentService.refund(request); + aSession.sendResponse(json.response(response)); + } catch (Exception e) { + sendError("Error while processing pay", aSession, e); + } + }); + } + @Override public void handleDefault(Request aRequest, HttpSession aSession) throws IOException { if(aRequest.getURI().startsWith("/pos-proxy/swagger-ui")) { diff --git a/server/src/main/java/com/payneteasy/pos/proxy/impl/InpasNetworkManager.java b/server/src/main/java/com/payneteasy/pos/proxy/impl/InpasNetworkManager.java new file mode 100644 index 0000000..aaeccc8 --- /dev/null +++ b/server/src/main/java/com/payneteasy/pos/proxy/impl/InpasNetworkManager.java @@ -0,0 +1,69 @@ +package com.payneteasy.pos.proxy.impl; + +import com.payneteasy.android.sdk.reader.inpas.config.InpasTerminalConfiguration; +import com.payneteasy.android.sdk.reader.inpas.network.InpasNetworkClient; +import com.payneteasy.inpas.sa.client.handlers.DefaultClientPacketOptions; +import com.payneteasy.pos.proxy.messages.PaymentResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class InpasNetworkManager { + + private static final Logger LOG = LoggerFactory.getLogger(InpasNetworkManager.class); + + private final String amount; + private final String currency; + private final String posAddress; + + public InpasNetworkManager(String amount, String currency, String aPosAddress) { + this.amount = amount; + this.currency = currency; + posAddress = aPosAddress; + } + + public interface IInpasOperationHandler { + PaymentResponse invokeOperation(InpasNetworkClient aClient) throws IOException; + } + + public PaymentResponse makeOperation(IInpasOperationHandler aHandler) { + InpasNetworkClient client = new InpasNetworkClient(posAddress, new InpasTerminalConfiguration.Builder() + .packetOptions(new DefaultClientPacketOptions()) + .throwExceptionIfCannotConnect(true) + .build() + ); + try { + client.connect(); + try { + + return aHandler.invokeOperation(client); + + } catch (Exception e) { + client.sendEot(); + LOG.error("Cannot process a payment", e); + return error("-1"); + } finally { + client.closeConnection(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted"); + } catch (Exception e) { + LOG.error("Cannot connect", e); + return error("-2"); + } + + } + + private PaymentResponse error(String aErrorCode) { + return PaymentResponse.builder() + .amount ( amount ) + .currency ( currency ) + .orderId ( null ) + .responseCode ( aErrorCode ) + .build(); + } + + +} diff --git a/server/src/main/java/com/payneteasy/pos/proxy/impl/PaymentServiceImpl.java b/server/src/main/java/com/payneteasy/pos/proxy/impl/PaymentServiceImpl.java index bdd5662..81cda84 100644 --- a/server/src/main/java/com/payneteasy/pos/proxy/impl/PaymentServiceImpl.java +++ b/server/src/main/java/com/payneteasy/pos/proxy/impl/PaymentServiceImpl.java @@ -1,17 +1,14 @@ package com.payneteasy.pos.proxy.impl; -import com.payneteasy.android.sdk.reader.inpas.config.InpasTerminalConfiguration; -import com.payneteasy.android.sdk.reader.inpas.config.InpasTerminalPacketOptions; -import com.payneteasy.android.sdk.reader.inpas.network.InpasNetworkClient; import com.payneteasy.inpas.sa.messages.sale.Sa1PaymentResponse; -import com.payneteasy.inpas.sa.network.handlers.DefaultClientPacketOptions; +import com.payneteasy.inpas.sa.messages.sale.Sa29ReversalResponse; import com.payneteasy.pos.proxy.IPaymentService; import com.payneteasy.pos.proxy.messages.PaymentRequest; import com.payneteasy.pos.proxy.messages.PaymentResponse; +import com.payneteasy.pos.proxy.messages.RefundRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.math.BigDecimal; public class PaymentServiceImpl implements IPaymentService { @@ -20,46 +17,44 @@ public class PaymentServiceImpl implements IPaymentService { @Override public PaymentResponse pay(PaymentRequest aRequest) { - InpasNetworkClient client = new InpasNetworkClient(aRequest.posAddress, new InpasTerminalConfiguration.Builder() - .packetOptions(new DefaultClientPacketOptions()) - .throwExceptionIfCannotConnect(true) - .build() - ); - try { - client.connect(); - try { - Sa1PaymentResponse saResponse = client.makeSale(aRequest.currency, new BigDecimal(aRequest.amount)); + InpasNetworkManager manager = new InpasNetworkManager(aRequest.getAmount(), aRequest.getCurrency(), aRequest.getPosAddress()); - PaymentResponse response = new PaymentResponse(); - response.amount = saResponse.get_00_amount().toString(); - response.currency = "RUB"; - response.orderId = getPaynetOrderId(saResponse); - response.responseCode = saResponse.get_15_responseCode(); - return response; + return manager.makeOperation(aClient -> { + Sa1PaymentResponse saResponse = aClient.makeSale(aRequest.getCurrency(), new BigDecimal(aRequest.getAmount())); - } catch (Exception e) { - client.sendEot(); - LOG.error("Cannot process a payment", e); - return error(aRequest, "-1"); - } finally { - client.closeConnection(); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException("Interrupted"); - } catch (Exception e) { - LOG.error("Cannot connect", e); - return error(aRequest, "-2"); - } + return PaymentResponse.builder() + .amount ( saResponse.get_00_amount().toString() ) + .currency ( aRequest.getCurrency() ) + .orderId ( getPaynetOrderId(saResponse) ) + .responseCode ( saResponse.get_15_responseCode() ) + .build(); + }); } - private PaymentResponse error(PaymentRequest aRequest, String aErrorCode) { - PaymentResponse response = new PaymentResponse(); - response.amount = aRequest.amount; - response.currency = "RUB"; - response.orderId = null; - response.responseCode = aErrorCode; - return response; + @Override + public PaymentResponse refund(RefundRequest aRequest) { + InpasNetworkManager manager = new InpasNetworkManager(aRequest.getRefundAmount(), aRequest.getCurrency(), aRequest.getPosAddress()); + + return manager.makeOperation(aClient -> { + Sa29ReversalResponse saResponse = aClient.makeReversal(aRequest.getCurrency(), new BigDecimal(aRequest.getRefundAmount()), toRrn(aRequest.getOrderId())); + + return PaymentResponse.builder() + .amount ( saResponse.get_00_amount().toString() ) + .currency ( aRequest.getCurrency() ) + .orderId ( aRequest.getOrderId() ) + .responseCode ( saResponse.get_15_responseCode() ) + .build(); + }); + } + + public static String toRrn(long aOrderId) { + StringBuilder sb = new StringBuilder(); + sb.append(aOrderId); + while(sb.length() < 11) { + sb.insert(0, "0"); + } + sb.insert(0, "P"); + return sb.toString(); } private Long getPaynetOrderId(Sa1PaymentResponse saResponse) { diff --git a/server/src/main/java/com/payneteasy/pos/proxy/messages/PaymentRequest.java b/server/src/main/java/com/payneteasy/pos/proxy/messages/PaymentRequest.java index 61f5f2b..b886f07 100644 --- a/server/src/main/java/com/payneteasy/pos/proxy/messages/PaymentRequest.java +++ b/server/src/main/java/com/payneteasy/pos/proxy/messages/PaymentRequest.java @@ -1,13 +1,21 @@ package com.payneteasy.pos.proxy.messages; +import lombok.Data; +import lombok.NonNull; + +@Data public class PaymentRequest { - public String amount; + @NonNull + private final String amount; - public String currency; + @NonNull + private final String currency; - public String posAddress; + @NonNull + private final String posAddress; - public String posType; + @NonNull + private final String posType; } diff --git a/server/src/main/java/com/payneteasy/pos/proxy/messages/PaymentResponse.java b/server/src/main/java/com/payneteasy/pos/proxy/messages/PaymentResponse.java index 83a50fd..27b886d 100644 --- a/server/src/main/java/com/payneteasy/pos/proxy/messages/PaymentResponse.java +++ b/server/src/main/java/com/payneteasy/pos/proxy/messages/PaymentResponse.java @@ -1,12 +1,23 @@ package com.payneteasy.pos.proxy.messages; +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; + +@Data +@Builder public class PaymentResponse { - public String amount; - public String currency; - public String responseCode; + @NonNull + private final String amount; + + @NonNull + private final String currency; + + @NonNull + private final String responseCode; - public Long orderId; + private final Long orderId; } diff --git a/server/src/main/java/com/payneteasy/pos/proxy/messages/RefundRequest.java b/server/src/main/java/com/payneteasy/pos/proxy/messages/RefundRequest.java new file mode 100644 index 0000000..545552c --- /dev/null +++ b/server/src/main/java/com/payneteasy/pos/proxy/messages/RefundRequest.java @@ -0,0 +1,26 @@ +package com.payneteasy.pos.proxy.messages; + +import lombok.Data; +import lombok.NonNull; + +@Data +public class RefundRequest { + + @NonNull + private final String refundAmount; + + /** + * Paynet order id to refund + */ + private final long orderId; + + @NonNull + private final String currency; + + @NonNull + private final String posAddress; + + @NonNull + private final String posType; + +} diff --git a/server/src/main/resources/swagger-ui/pos-proxy.json b/server/src/main/resources/swagger-ui/pos-proxy.json index dca4037..2f60ac0 100644 --- a/server/src/main/resources/swagger-ui/pos-proxy.json +++ b/server/src/main/resources/swagger-ui/pos-proxy.json @@ -90,6 +90,75 @@ } } } + }, + "/refund": { + "post": { + "tags": [ + "proxy" + ], + "summary": "Make a refund", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "title": "Refund Request", + "type": "object", + "additionalProperties": false, + "properties": { + "refundAmount": { + "type": "string", + "description": "Refund amount", + "example": "10.00" + }, + "currency": { + "type": "string", + "description": "Currency", + "example": "RUB" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "posType": { + "type": "string", + "enum": [ + "INPAS" + ], + "description": "POS terminal type", + "example": "INPAS" + }, + "posAddress": { + "type": "string", + "description": "POS terminal address", + "example": "10.0.1.20:27015" + } + } + } + } + ], + "responses": { + "200": { + "description": "Status", + "schema": { + "title": "Control Response", + "type": "object", + "additionalProperties": false, + "properties": { + "responseCode": { + "type": "string", + "description": "00 - Approved, other codes - errors" + }, + "orderId": { + "type": "integer", + "format": "int64" + } + } + } + } + } + } } } } \ No newline at end of file diff --git a/swagger/messages/RefundRequest.yaml b/swagger/messages/RefundRequest.yaml new file mode 100644 index 0000000..8b22203 --- /dev/null +++ b/swagger/messages/RefundRequest.yaml @@ -0,0 +1,30 @@ +title: "Refund Request" +type: "object" +additionalProperties: false +properties: + refundAmount: + type: "string" + description: "Refund amount" + example: "10.00" + + currency: + type: "string" + description: "Currency" + example: "RUB" + + orderId: + type: "integer" + format: "int64" + + posType: + type: "string" + enum: + - "INPAS" + description: "POS terminal type" + example: "INPAS" + + posAddress: + type: "string" + description: "POS terminal address" + example: "10.0.1.20:27015" + diff --git a/swagger/pos-proxy.yaml b/swagger/pos-proxy.yaml index c81a696..df47e31 100644 --- a/swagger/pos-proxy.yaml +++ b/swagger/pos-proxy.yaml @@ -36,3 +36,19 @@ paths: description: Status schema: $ref: './messages/PaymentResponse.yaml' + + /refund: + post: + tags: ['proxy'] + summary: Make a refund + parameters: + - in: body + name: body + required: true + schema: + $ref: './messages/RefundRequest.yaml' + responses: + 200: + description: Status + schema: + $ref: './messages/PaymentResponse.yaml' diff --git a/swagger/run-ui.sh b/swagger/run-ui.sh index cedf680..b5a2ca2 100755 --- a/swagger/run-ui.sh +++ b/swagger/run-ui.sh @@ -1 +1,2 @@ +#!/usr/bin/env bash swagger-ui-watcher pos-proxy.yaml