diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..bfae5a130d974eb9a2fc4fe67ef8dad0ba8f48d8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM maven:3.8.4-openjdk-8-slim AS build + +WORKDIR /app + +COPY pom.xml . +COPY src ./src + +RUN mvn clean package -DskipTests + +CMD ["java", "-jar", "target/soap-0.0.jar"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a1da0d2f58152f42020f11d530eb45fe947dc135..43abf344115ba9a736c857ec7564c7d260cf0c85 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,14 @@ version: "3.1" services: - db: + mysql: image: mysql:8.2 restart: always + healthcheck: + test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] + timeout: 12s + retries: 10 + interval: 1s environment: MYSQL_DATABASE: 'db' MYSQL_USER: 'user' @@ -13,5 +18,27 @@ services: - '3306:3306' volumes: - my-db:/var/lib/mysql + networks: + - all-bridge + soap-service: + build: + context: . + dockerfile: Dockerfile + environment: + REST_API_KEY: "REST_API_KEY" + MONOLITH_API_KEY: "MONOLITH_API_KEY" + sender_email: kms.bizzzzzz@gmail.com + sender_password: + depends_on: + mysql: + condition: service_healthy + ports: + - "8080:8080" + networks: + - all-bridge + volumes: - my-db: \ No newline at end of file + my-db: +networks: + all-bridge: + driver: bridge \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8ba5d3047f79443d7ce8f83dfa73a7adb5cd038d..19ba5675fdd3a947c84e78ab36b342c009692c08 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,8 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <maven.compiler.source>21</maven.compiler.source> - <maven.compiler.target>21</maven.compiler.target> + <maven.compiler.source>8</maven.compiler.source> + <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> @@ -27,91 +27,50 @@ <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>com.sun.xml.ws</groupId> - <artifactId>jaxws-rt</artifactId> - <version>2.3.6</version> </dependency> <dependency> <groupId>com.j256.ormlite</groupId> <artifactId>ormlite-jdbc</artifactId> <version>6.1</version> </dependency> - <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.2.0</version> </dependency> - + <dependency> + <groupId>com.sun.mail</groupId> + <artifactId>javax.mail</artifactId> + <version>1.6.2</version> + </dependency> + <dependency> + <groupId>org.freemarker</groupId> + <artifactId>freemarker</artifactId> + <version>2.3.30</version> + </dependency> </dependencies> <build> - <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> - <plugins> - <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> - <plugin> - <artifactId>maven-clean-plugin</artifactId> - <version>3.1.0</version> - </plugin> - <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> - <plugin> - <artifactId>maven-resources-plugin</artifactId> - <version>3.0.2</version> - </plugin> - <plugin> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.8.0</version> - </plugin> - <plugin> - <artifactId>maven-surefire-plugin</artifactId> - <version>2.22.1</version> - </plugin> - <plugin> - <artifactId>maven-jar-plugin</artifactId> - <version>3.0.2</version> - </plugin> - <plugin> - <artifactId>maven-install-plugin</artifactId> - <version>2.5.2</version> - </plugin> - <plugin> - <artifactId>maven-deploy-plugin</artifactId> - <version>2.8.2</version> - </plugin> - <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> - <plugin> - <artifactId>maven-site-plugin</artifactId> - <version>3.7.1</version> - </plugin> - <plugin> - <artifactId>maven-project-info-reports-plugin</artifactId> - <version>3.0.0</version> - </plugin> - <plugin> - <groupId>org.jvnet.jax-ws-commons</groupId> - <artifactId>jaxws-maven-plugin</artifactId> - <version>2.2</version> - <executions> - <execution> - <id>SomeId</id> - <goals> - <goal>wsgen</goal> - </goals> - <phase>prepare-package</phase> - <configuration> - <sei>com.kms.service.PaymentService</sei> - <genWsdl>true</genWsdl> - <keep>true</keep> - <inlineSchemas>true</inlineSchemas> - </configuration> - </execution> - </executions> - </plugin> - - </plugins> - </pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <configuration> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>com.kms.App</mainClass> + </transformer> + </transformers> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> </build> </project> diff --git a/src/main/java/com/kms/App.java b/src/main/java/com/kms/App.java index 1b28d905343e33e24cfcf13c2dc24dd844f809d1..94732180675731af0c8838d688c18bd3da1fdbfa 100644 --- a/src/main/java/com/kms/App.java +++ b/src/main/java/com/kms/App.java @@ -4,6 +4,7 @@ import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.DaoManager; import com.j256.ormlite.jdbc.JdbcConnectionSource; import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.TableUtils; import com.kms.crosscut.LoggingHelper; import com.kms.model.Log; import com.kms.model.Payment; @@ -15,20 +16,29 @@ import javax.xml.ws.Endpoint; import java.sql.SQLException; import java.util.Timer; + public class App { - public static void main( String[] args ) throws SQLException { - String databaseUrl = "jdbc:mysql://localhost:3306/db?user=user&password=password"; - ConnectionSource connectionSource = new JdbcConnectionSource(databaseUrl); - Dao<Payment,Integer> paymentDao = - DaoManager.createDao(connectionSource, Payment.class); - Dao<Log,Integer> logDao = DaoManager.createDao(connectionSource, Log.class); - LoggingHelper.init(logDao); - - Timer time = new Timer(); - PaymentCompletorTask task = new PaymentCompletorTask(paymentDao); - time.schedule(task, 0, 25000); - - Endpoint.publish("http://localhost:8080/paymentservice", new PaymentService(paymentDao)); - Endpoint.publish("http://localhost:8080/paymenthistory", new PaymentHistoryService(paymentDao)); + public static void main( String[] args ) { + try { + final String databaseUrl = "jdbc:mysql://mysql:3306/db?user=user&password=password"; + final ConnectionSource connectionSource = new JdbcConnectionSource(databaseUrl); + final Dao<Payment,Integer> paymentDao = DaoManager.createDao(connectionSource, Payment.class); + final Dao<Log,Integer> logDao = DaoManager.createDao(connectionSource, Log.class); + LoggingHelper.init(logDao); + + TableUtils.dropTable(connectionSource, Payment.class, true); + TableUtils.dropTable(connectionSource, Log.class, true); + + TableUtils.createTableIfNotExists(connectionSource, Payment.class); + TableUtils.createTableIfNotExists(connectionSource, Log.class); + + final Timer time = new Timer(); + PaymentCompletorTask task = new PaymentCompletorTask(paymentDao); + time.schedule(task, 0, 25000); + + Endpoint.publish("http://0.0.0.0:8080/paymentservice", new PaymentService(paymentDao)); + Endpoint.publish("http://0.0.0.0:8080/paymenthistory", new PaymentHistoryService(paymentDao)); + } catch (SQLException e) { + } } } diff --git a/src/main/java/com/kms/crosscut/LoggingHelper.java b/src/main/java/com/kms/crosscut/LoggingHelper.java index f5ee1b1c8950141c6dcdf6871d5d4f748e1c5e70..7fc6181538c1741865da2752e50ce764356b02d5 100644 --- a/src/main/java/com/kms/crosscut/LoggingHelper.java +++ b/src/main/java/com/kms/crosscut/LoggingHelper.java @@ -23,18 +23,19 @@ public class LoggingHelper { return instance; } - public void log(String description, String ip, String endpoint) { - Log log = Log.builder() + public void log(String description, String ip, String endpoint, String callerId, String annotation) { + final Log log = Log.builder() .endpoint(endpoint) .originIp(ip) .requestDesc(description) .requestTime(new Timestamp(System.currentTimeMillis())) + .callerId(callerId) + .annotation(annotation) .build(); try { logDao.create(log); } catch (SQLException ignored) { - System.out.println(ignored); } } diff --git a/src/main/java/com/kms/dto/EmailReq.java b/src/main/java/com/kms/dto/EmailReq.java index bb417f5664f9f9f4494351060eabee0f351db0aa..5d7527bd9fe3505432b90597bb10d1c5c80a8dbd 100644 --- a/src/main/java/com/kms/dto/EmailReq.java +++ b/src/main/java/com/kms/dto/EmailReq.java @@ -13,7 +13,5 @@ public class EmailReq { private String recipient; private String cc; - private String subject; - private String body; } diff --git a/src/main/java/com/kms/dto/PaymentHistoryResp.java b/src/main/java/com/kms/dto/PaymentHistoryResp.java index 754d58650fb4ab51b59aa102370c6be675d81021..983b680318e8d46b8c905a9f19f8456c4050ddf9 100644 --- a/src/main/java/com/kms/dto/PaymentHistoryResp.java +++ b/src/main/java/com/kms/dto/PaymentHistoryResp.java @@ -5,8 +5,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import java.sql.Timestamp; - @Getter @Setter @AllArgsConstructor diff --git a/src/main/java/com/kms/dto/PaymentInitReq.java b/src/main/java/com/kms/dto/PaymentInitReq.java index 2bf3707b8a5d1a7b5badd35eb3d9cfe1e75484f0..5fea4af57a6e2520e91711e14494f9f3fe6b9d2c 100644 --- a/src/main/java/com/kms/dto/PaymentInitReq.java +++ b/src/main/java/com/kms/dto/PaymentInitReq.java @@ -5,17 +5,15 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; @Getter @Setter @AllArgsConstructor @NoArgsConstructor -@XmlRootElement public class PaymentInitReq { private double amount; private String description; private String initiatorId; + private String idempotentId; } diff --git a/src/main/java/com/kms/dto/PaymentInitResp.java b/src/main/java/com/kms/dto/PaymentInitResp.java index e9e18c3750839fd5255b4b928f46f0e95e494a52..32aca6f496b1984fd3fd48dd388f9c9fa111ac2e 100644 --- a/src/main/java/com/kms/dto/PaymentInitResp.java +++ b/src/main/java/com/kms/dto/PaymentInitResp.java @@ -13,5 +13,7 @@ public class PaymentInitResp { private Integer paymentId; private boolean success; + private int responseCode; // 1 for success, 2 for unexpected error, 3 for invalid idempotency key + private String annotation; } diff --git a/src/main/java/com/kms/dto/PaymentStatusResp.java b/src/main/java/com/kms/dto/PaymentStatusResp.java index d24d0cdc16ad5762cb338e1cf93ccef5cf1f123f..4e0fa895e358d0ed7c8418002ec1952d57f29963 100644 --- a/src/main/java/com/kms/dto/PaymentStatusResp.java +++ b/src/main/java/com/kms/dto/PaymentStatusResp.java @@ -5,7 +5,6 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; - @Getter @Setter @AllArgsConstructor diff --git a/src/main/java/com/kms/handler/AuthHandler.java b/src/main/java/com/kms/handler/AuthHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..7f57cabab3e835ad972de1f82744f89d80d9fab2 --- /dev/null +++ b/src/main/java/com/kms/handler/AuthHandler.java @@ -0,0 +1,59 @@ +package com.kms.handler; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPElement; +import javax.xml.soap.SOAPException; +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.handler.soap.SOAPHandler; +import javax.xml.ws.handler.soap.SOAPMessageContext; +import java.util.Iterator; +import java.util.Set; + +public class AuthHandler implements SOAPHandler<SOAPMessageContext> { + + private static final String REST_API_KEY = System.getenv("REST_API_KEY"); + private static final String MONOLITH_API_KEY = System.getenv("MONOLITH_API_KEY"); + private static final String NAMESPACE = "http://service.kms.com/"; + + @Override + public Set<QName> getHeaders() { + return null; + } + + @Override + public boolean handleMessage(SOAPMessageContext soapMessageContext) { + try { + final SOAPHeader header = soapMessageContext.getMessage().getSOAPHeader(); + final QName apiKeyQName = new QName(NAMESPACE, "apiKey"); + String apiKey = ""; + final Iterator x = header.getChildElements(apiKeyQName); + if (x.hasNext()) { + SOAPElement apiKeyElement = (SOAPElement) x.next(); + apiKey = apiKeyElement.getValue(); + } + + String callerId = "null"; + if (apiKey.equals(REST_API_KEY)) { + callerId = "REST"; + }else if (apiKey.equals(MONOLITH_API_KEY)) { + callerId = "MONOLITH"; + } + + soapMessageContext.put("callerId", callerId); + } catch (SOAPException e) { + } + + return true; + } + + @Override + public boolean handleFault(SOAPMessageContext soapMessageContext) { + return false; + } + + @Override + public void close(MessageContext messageContext) { + + } +} diff --git a/src/main/java/com/kms/handler/LogHandler.java b/src/main/java/com/kms/handler/LogHandler.java index d9d5a495863506f38db0e5cadbd7944fced2f8f9..8975be10d24311c9b24dc62070e1211fab1466c2 100644 --- a/src/main/java/com/kms/handler/LogHandler.java +++ b/src/main/java/com/kms/handler/LogHandler.java @@ -2,14 +2,16 @@ package com.kms.handler; import com.kms.crosscut.LoggingHelper; import com.sun.net.httpserver.HttpExchange; -import com.sun.xml.ws.developer.JAXWSProperties; import javax.xml.namespace.QName; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPException; +import javax.xml.soap.SOAPFactory; +import javax.xml.soap.SOAPFault; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; +import javax.xml.ws.soap.SOAPFaultException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -18,6 +20,16 @@ import java.util.Set; public class LogHandler implements SOAPHandler<SOAPMessageContext> { + private static final SOAPFactory soapFactory; + + static { + try { + soapFactory = SOAPFactory.newInstance(); + } catch (SOAPException e) { + throw new RuntimeException(e); + } + } + @Override public Set<QName> getHeaders() { return null; @@ -25,7 +37,8 @@ public class LogHandler implements SOAPHandler<SOAPMessageContext> { @Override public boolean handleMessage(SOAPMessageContext soapMessageContext) { - boolean messageHandled = (boolean) soapMessageContext.getOrDefault("messageHandled", false); + final boolean messageHandled = + (boolean) soapMessageContext.getOrDefault("messageHandled", false); if (messageHandled) { return true; @@ -33,19 +46,31 @@ public class LogHandler implements SOAPHandler<SOAPMessageContext> { soapMessageContext.put("messageHandled", true); - HttpExchange exchange = (HttpExchange)soapMessageContext.get(JAXWSProperties.HTTP_EXCHANGE); + HttpExchange exchange = (HttpExchange)soapMessageContext.get("com.sun.xml.internal.ws.http.exchange"); InetSocketAddress remoteAddress = exchange.getRemoteAddress(); - String remoteHost = remoteAddress.getHostName(); - + final String address = String.valueOf(remoteAddress.getAddress()); try { SOAPBody soapBody = soapMessageContext.getMessage().getSOAPBody(); OutputStream outputStream = new ByteArrayOutputStream(); soapMessageContext.getMessage().writeTo(outputStream); + final String callerId = (String) soapMessageContext.getOrDefault("callerId", "null"); + + final LoggingHelper loggingHelper = LoggingHelper.getInstance(); + if (callerId.equals("null")) { + SOAPFault customFault = soapFactory.createFault(); + customFault.setFaultCode("InvalidRequest"); + customFault.setFaultString("API key is invalid."); + loggingHelper.log(outputStream.toString(), address, + soapBody.getChildNodes().item(1).getNodeName(), callerId, "invalid api key"); - LoggingHelper loggingHelper = LoggingHelper.getInstance(); - loggingHelper.log(outputStream.toString(), remoteHost, soapBody.getChildNodes().item(1).getNodeName()); + throw new SOAPFaultException(customFault); + } + + loggingHelper.log(outputStream.toString(), address, + soapBody.getChildNodes().item(1).getNodeName(), callerId, ""); } catch (SOAPException | IOException ignored) { } + return true; } diff --git a/src/main/java/com/kms/model/Log.java b/src/main/java/com/kms/model/Log.java index a2e683d3cadd4bd22382e8678d1c3528f27d5c5f..539bc7b853a2b2abb0cfeb1a9b01668842b728b4 100644 --- a/src/main/java/com/kms/model/Log.java +++ b/src/main/java/com/kms/model/Log.java @@ -10,6 +10,8 @@ import lombok.Setter; import java.sql.Timestamp; +import static java.time.LocalTime.now; + @AllArgsConstructor @Getter @Setter @@ -22,13 +24,15 @@ public class Log { private Integer id; @DatabaseField(columnName = "request_description") private String requestDesc; - @DatabaseField(columnName = "origin_ip") + @DatabaseField(columnName = "origin_ip", canBeNull = false) private String originIp; - @DatabaseField(columnName = "endpoint") + @DatabaseField(columnName = "endpoint", canBeNull = false) private String endpoint; - @DatabaseField(columnName = "request_time") + @DatabaseField(columnName = "request_time", columnDefinition = "TIMESTAMP DEFAULT NOW() NOT NULL") private Timestamp requestTime; - @DatabaseField(columnName = "caller_id") + @DatabaseField(columnName = "caller_id", canBeNull = false) private String callerId; + @DatabaseField(columnName = "annotation") + private String annotation; } diff --git a/src/main/java/com/kms/model/Payment.java b/src/main/java/com/kms/model/Payment.java index 89fba01d3a284a5b3dafd177a06748ca5b97b908..b766dc03f97d9e6eab527c080913e0462c6a3139 100644 --- a/src/main/java/com/kms/model/Payment.java +++ b/src/main/java/com/kms/model/Payment.java @@ -19,18 +19,20 @@ public class Payment { @DatabaseField(generatedId = true, columnName = "payment_id") private Integer paymentId; - @DatabaseField(columnName = "amount") + @DatabaseField(columnName = "amount", canBeNull = false) private double amount; - @DatabaseField(columnName = "payment_init_time") + @DatabaseField(columnName = "payment_init_time", canBeNull = false, + columnDefinition = "TIMESTAMP DEFAULT NOW() NOT NULL") private Timestamp paymentInitTime; - @DatabaseField(columnName = "status_paid") + @DatabaseField(columnName = "status_paid", defaultValue = "0", canBeNull = false) private boolean paid; @DatabaseField(columnName = "payment_paid_time") private Timestamp paymentPaidTime; @DatabaseField(columnName = "description") private String description; - @DatabaseField(columnName = "initiator_id") + @DatabaseField(columnName = "initiator_id", canBeNull = false) private String initiatorId; - + @DatabaseField(columnName = "idempotent_id", canBeNull = false) + private String idempotentId; } diff --git a/src/main/java/com/kms/service/EmailService.java b/src/main/java/com/kms/service/EmailService.java index fbf023487737e499034375f243869d491c42c750..7d02a57eb990f7f17b0ea49ebe7ee73d78223f74 100644 --- a/src/main/java/com/kms/service/EmailService.java +++ b/src/main/java/com/kms/service/EmailService.java @@ -2,21 +2,79 @@ package com.kms.service; import com.kms.util.EmailUtil; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import java.util.Properties; + public class EmailService { - public static void send(String recipient, String cc, String subject, String body) { - if (recipient == null || recipient.isEmpty() || !EmailUtil.isEmailValid(recipient)) { + private static final Session session; + private static final Properties properties; + private static final String SENDER_EMAIL; + private static final String SENDER_PASSWORD; + + static { + properties = new Properties(); + properties.put("mail.smtp.host", "smtp.gmail.com"); + properties.put("mail.smtp.socketFactory.port", "465"); + properties.put("mail.smtp.socketFactory.class", + "javax.net.ssl.SSLSocketFactory"); + properties.put("mail.smtp.auth", "true"); + properties.put("mail.smtp.port", "465"); + + SENDER_EMAIL = System.getenv("sender_email"); + SENDER_PASSWORD = System.getenv("sender_password"); + session = Session.getDefaultInstance(properties, + new javax.mail.Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(SENDER_EMAIL,SENDER_PASSWORD); + } + }); + } + + + public static void send(String recipient, String cc, String subject, String body){ + if (recipient == null || recipient.isEmpty()) { return; } - for (String x: cc.split(",")) { - x = x.trim(); - if (!EmailUtil.isEmailValid(x)) { + try { + MimeMessage message = new MimeMessage(session); + + int toCount = 0; + for (String email: recipient.split(",")) { + email = email.trim(); + if (!EmailUtil.isEmailValid(email)) { + continue; + } + ++toCount; + message.addRecipient(Message.RecipientType.TO,new InternetAddress(email)); + } + if (toCount == 0) { return; } - } - // TODO: send with smtp + for (String email: cc.split(",")) { + email = email.trim(); + if (!EmailUtil.isEmailValid(email)) { + continue; + } + message.addRecipient(Message.RecipientType.CC, new InternetAddress(email)); + } + message.setSubject(subject); + message.setContent(body, "text/html"); + + Transport.send(message); + + System.out.println("email sent to: " + recipient); + } catch (MessagingException e) { + System.out.println(e.getMessage()); + } } } diff --git a/src/main/java/com/kms/service/PaymentHistoryService.java b/src/main/java/com/kms/service/PaymentHistoryService.java index 81fd853108f5a7c874177723fa03297e8ea4870c..b29db99e5aa23c9586618e0e2e15ed6788a6f180 100644 --- a/src/main/java/com/kms/service/PaymentHistoryService.java +++ b/src/main/java/com/kms/service/PaymentHistoryService.java @@ -5,14 +5,23 @@ import com.kms.dto.EmailReq; import com.kms.dto.PaymentHistoryResp; import com.kms.model.Payment; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateExceptionHandler; import lombok.RequiredArgsConstructor; import javax.jws.HandlerChain; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; +import java.io.IOException; +import java.io.StringWriter; import java.sql.SQLException; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @WebService @RequiredArgsConstructor @@ -21,6 +30,14 @@ public class PaymentHistoryService { private final Dao<Payment,Integer> paymentDao; + private static final Configuration cfg = new Configuration(Configuration.VERSION_2_3_30); + + static { + cfg.setClassForTemplateLoading(PaymentHistoryService.class, "/"); + cfg.setDefaultEncoding("UTF-8"); + cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + } + @WebMethod public List<PaymentHistoryResp> getPaymentHistory(@WebParam(name = "initiatorId") String initiatorId, @WebParam(name = "sendMail") boolean sendMail, @@ -33,16 +50,28 @@ public class PaymentHistoryService { List<Payment> payments = paymentDao.queryForMatching(filter); if (sendMail && emailReq != null) { - EmailService.send(emailReq.getRecipient(), emailReq.getCc(), emailReq.getSubject(), emailReq.getBody()); + EmailService.send(emailReq.getRecipient(), emailReq.getCc(), + "KMS Payment History", formHistoryMailBody(payments)); + } return payments.stream() .map(el -> new PaymentHistoryResp(el.getPaymentInitTime().toString(), el.getPaymentPaidTime().toString(), el.getAmount(), el.getDescription())) - .toList(); - } catch (SQLException e) { + .collect(Collectors.toList()); + } catch (SQLException | TemplateException | IOException e) { throw new RuntimeException(e); } } + private String formHistoryMailBody(List<Payment> payments) throws IOException, TemplateException { + Template template = cfg.getTemplate("history.ftl"); + Map<String, Object> dataModel = new HashMap<>(); + dataModel.put("payments", payments); + + StringWriter stringWriter = new StringWriter(); + template.process(dataModel, stringWriter); + return stringWriter.toString(); + } + } diff --git a/src/main/java/com/kms/service/PaymentService.java b/src/main/java/com/kms/service/PaymentService.java index 1ebe4e5eae23885c1c6cdacf7bf8e69338b41042..a02e9300ca5dbcd802765ef38f5f66db812b570b 100644 --- a/src/main/java/com/kms/service/PaymentService.java +++ b/src/main/java/com/kms/service/PaymentService.java @@ -7,13 +7,13 @@ import com.kms.dto.PaymentStatusResp; import com.kms.model.Payment; import lombok.RequiredArgsConstructor; -import javax.annotation.Resource; import javax.jws.HandlerChain; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import java.sql.SQLException; import java.sql.Timestamp; +import java.util.List; @WebService @HandlerChain(file = "handlers.xml") @@ -22,23 +22,35 @@ public class PaymentService { private final Dao<Payment,Integer> paymentDao; - @WebMethod public PaymentInitResp initPayment(@WebParam(name = "req") PaymentInitReq req) { - Payment payment = Payment.builder() - .amount(req.getAmount()) - .paymentInitTime(new Timestamp(System.currentTimeMillis())) - .initiatorId(req.getInitiatorId()) - .description(req.getDescription()) - .build(); - try { + List<Payment> idempotentCheck = paymentDao.queryForMatching(Payment.builder() + .initiatorId(req.getInitiatorId()) + .idempotentId(req.getIdempotentId()).build()); + + if (!idempotentCheck.isEmpty()) { + Payment payment = idempotentCheck.get(0); + if (payment.getAmount() != req.getAmount() || payment.isPaid()) { + return new PaymentInitResp(null, false, 3, + "You're trying to create another payment request with duplicate idempotency key"); + } + return new PaymentInitResp(payment.getPaymentId(), true, 1, ""); + } + + Payment payment = Payment.builder() + .amount(req.getAmount()) + .paymentInitTime(new Timestamp(System.currentTimeMillis())) + .initiatorId(req.getInitiatorId()) + .idempotentId(req.getIdempotentId()) + .description(req.getDescription()) + .build(); + paymentDao.create(payment); + return new PaymentInitResp(payment.getPaymentId(), true, 1, ""); } catch (SQLException e) { - return new PaymentInitResp(null, false); + return new PaymentInitResp(null, false, 2, "Unexpected error has happened"); } - - return new PaymentInitResp(payment.getPaymentId(), true); } @WebMethod @@ -49,7 +61,6 @@ public class PaymentService { if (payment == null) { return new PaymentStatusResp(null, "Payment id not found"); } - return new PaymentStatusResp(payment.isPaid(), null); } catch (SQLException e) { return new PaymentStatusResp(null, "Unexpected error has happened"); diff --git a/src/main/java/com/kms/task/PaymentCompletorTask.java b/src/main/java/com/kms/task/PaymentCompletorTask.java index 592a2f94b181389668a0b8be502cfd1b707b4cf8..f41207dff0d60f7f42f4f5c206c04e9d8bba7c17 100644 --- a/src/main/java/com/kms/task/PaymentCompletorTask.java +++ b/src/main/java/com/kms/task/PaymentCompletorTask.java @@ -4,6 +4,7 @@ import com.j256.ormlite.dao.Dao; import com.kms.model.Payment; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.List; import java.util.TimerTask; @@ -18,10 +19,11 @@ public class PaymentCompletorTask extends TimerTask { @Override public void run() { try { - List<Payment> paymentsNotDone = paymentDao.queryForEq("status_paid", 0); + final List<Payment> paymentsNotDone = paymentDao.queryForEq("status_paid", 0); for (Payment p: paymentsNotDone) { p.setPaid(true); + p.setPaymentPaidTime(new Timestamp(System.currentTimeMillis())); paymentDao.update(p); } } catch (SQLException e) { diff --git a/src/main/java/com/kms/task/PdfCleanerTask.java b/src/main/java/com/kms/task/PdfCleanerTask.java new file mode 100644 index 0000000000000000000000000000000000000000..6e4833405abff7fc346f4fa1912f189c7059d2e1 --- /dev/null +++ b/src/main/java/com/kms/task/PdfCleanerTask.java @@ -0,0 +1,13 @@ +package com.kms.task; + +import java.util.TimerTask; + +public class PdfCleanerTask extends TimerTask { + + private static final String PDF_FILE_LOCATION = System.getenv("PDF_FILE_LOCATION"); + + @Override + public void run() { + + } +} diff --git a/src/main/resources/handlers.xml b/src/main/resources/handlers.xml index 033b4d965df9095b8bc7d3020ade2099bc62a05d..2036a9603bf99a236599613fcfacf2bcbff548c5 100644 --- a/src/main/resources/handlers.xml +++ b/src/main/resources/handlers.xml @@ -4,5 +4,8 @@ <handler> <handler-class>com.kms.handler.LogHandler</handler-class> </handler> + <handler> + <handler-class>com.kms.handler.AuthHandler</handler-class> + </handler> </handler-chain> </handler-chains> diff --git a/src/main/resources/history.ftl b/src/main/resources/history.ftl new file mode 100644 index 0000000000000000000000000000000000000000..8153e98b0d0d37fdbf27af333b7e789246fa40d9 --- /dev/null +++ b/src/main/resources/history.ftl @@ -0,0 +1,7 @@ +<#list payments as payment> + + ${payment.paymentPaidTime} + <br> + ${payment.amount} + <br> +</#list> \ No newline at end of file