Skip to content

Commit 21c0999

Browse files
implemented the view details of generated invoicesworking code
1 parent 7a9669d commit 21c0999

File tree

15 files changed

+408
-10
lines changed

15 files changed

+408
-10
lines changed

pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
</exclusion>
4141
</exclusions>
4242
</dependency>
43+
<dependency>
44+
<groupId>org.springframework.boot</groupId>
45+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
46+
</dependency>
4347

4448
<dependency>
4549
<groupId>org.springframework.boot</groupId>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.bitscoderdotcom.link_generator_system.controller;
2+
3+
import com.bitscoderdotcom.link_generator_system.dto.ApiResponse;
4+
import com.bitscoderdotcom.link_generator_system.dto.InvoiceDto;
5+
import com.bitscoderdotcom.link_generator_system.entities.Invoice;
6+
import com.bitscoderdotcom.link_generator_system.service.service.InvoiceService;
7+
import lombok.AllArgsConstructor;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.stereotype.Controller;
10+
import org.springframework.ui.Model;
11+
import org.springframework.web.bind.annotation.*;
12+
13+
import java.security.Principal;
14+
15+
@Controller
16+
@AllArgsConstructor
17+
@RequestMapping("/lgsApp/v1/invoice")
18+
public class InvoiceController {
19+
20+
private InvoiceService invoiceService;
21+
22+
@PostMapping("/generateInvoice")
23+
public ResponseEntity<ApiResponse<InvoiceDto.Response>> generateInvoice(@RequestBody InvoiceDto invoiceDto, Principal principal) {
24+
return invoiceService.generateInvoice(invoiceDto, principal);
25+
}
26+
27+
@GetMapping("/{invoiceId}/payment")
28+
public String showPaymentPage(@PathVariable String invoiceId, Model model) {
29+
30+
Invoice invoice = invoiceService.getInvoiceById(invoiceId);
31+
32+
model.addAttribute("invoice", invoice);
33+
34+
return "payment";
35+
}
36+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.bitscoderdotcom.link_generator_system.controller;
2+
3+
import com.bitscoderdotcom.link_generator_system.service.service.InvoiceService;
4+
import lombok.AllArgsConstructor;
5+
import org.springframework.stereotype.Controller;
6+
import org.springframework.ui.Model;
7+
import org.springframework.web.bind.annotation.PathVariable;
8+
import org.springframework.web.bind.annotation.PostMapping;
9+
import org.springframework.web.bind.annotation.RequestParam;
10+
11+
@Controller
12+
@AllArgsConstructor
13+
public class PaymentController {
14+
15+
private final InvoiceService invoiceService;
16+
17+
@PostMapping("/payment")
18+
public String processPayment(@RequestParam String linkId, @RequestParam String invoiceId, Model model) {
19+
String message = invoiceService.processPayment(linkId, invoiceId);
20+
21+
model.addAttribute("message", message);
22+
23+
return "paymentSuccess";
24+
}
25+
}

src/main/java/com/bitscoderdotcom/link_generator_system/entities/Invoice.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class Invoice {
2929
private double totalAmount;
3030
private Date invoiceGenerationDate;
3131
private Date paymentDueDate;
32+
@Enumerated(EnumType.STRING)
3233
private Status status;
3334
@OneToOne(mappedBy = "invoice", cascade = CascadeType.ALL)
3435
private PaymentLink paymentLink;

src/main/java/com/bitscoderdotcom/link_generator_system/repository/CompanyRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ public interface CompanyRepository extends JpaRepository<Company, String> {
1212
boolean existsByCompanyEmail(String email);
1313

1414
boolean existsByUserName(String username);
15+
16+
Optional<Company> findUserByCompanyEmail(String email);
1517
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
import com.bitscoderdotcom.link_generator_system.entities.PaymentLink;
44
import org.springframework.data.jpa.repository.JpaRepository;
55

6-
public interface TransactionLinkRepository extends JpaRepository<PaymentLink, String> {
6+
public interface PaymentLinkRepository extends JpaRepository<PaymentLink, String> {
77
}

src/main/java/com/bitscoderdotcom/link_generator_system/security/WebSecurityConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
8585
.authorizeHttpRequests()
8686
.requestMatchers("/lgsApp/v1/auth/**").permitAll()
8787
.requestMatchers("/h2-console/**").permitAll()
88+
.requestMatchers("/lgsApp/v1/invoice/{invoiceId}/payment", "/payment").permitAll()
8889
.anyRequest().authenticated()
8990
.and()
9091
.formLogin();

src/main/java/com/bitscoderdotcom/link_generator_system/security/service/UserDetailsImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public String getPassword() {
7979

8080
@Override
8181
public String getUsername() {
82-
return userName;
82+
return email;
8383
}
8484

8585
@Override
Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.bitscoderdotcom.link_generator_system.service;
22

33
import com.bitscoderdotcom.link_generator_system.dto.EmailDetails;
4+
import jakarta.mail.MessagingException;
5+
import jakarta.mail.internet.MimeMessage;
46
import lombok.extern.slf4j.Slf4j;
57
import org.springframework.beans.factory.annotation.Autowired;
68
import org.springframework.beans.factory.annotation.Value;
79
import org.springframework.mail.MailException;
810
import org.springframework.mail.SimpleMailMessage;
911
import org.springframework.mail.javamail.JavaMailSender;
12+
import org.springframework.mail.javamail.MimeMessageHelper;
1013
import org.springframework.stereotype.Service;
1114

1215
@Service
@@ -20,19 +23,21 @@ public class EmailService {
2023
private String emailSender;
2124

2225
public void sendEmail (EmailDetails emailDetails) {
23-
2426
try {
25-
SimpleMailMessage mailMessage = new SimpleMailMessage();
26-
mailMessage.setFrom(emailSender);
27-
mailMessage.setTo(emailDetails.getRecipient());
28-
mailMessage.setSubject(emailDetails.getMessageBody());
29-
mailMessage.setText(emailDetails.getMessageBody());
27+
MimeMessage mimeMessage = mailSender.createMimeMessage();
28+
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, "utf-8");
29+
30+
helper.setFrom(emailSender);
31+
helper.setTo(emailDetails.getRecipient());
32+
helper.setSubject(emailDetails.getSubject());
33+
helper.setText(emailDetails.getMessageBody(), true);
3034

31-
mailSender.send(mailMessage);
35+
mailSender.send(mimeMessage);
3236
log.info("Message sent to: {}", emailDetails.getRecipient());
3337
log.info("Message sender: {}", emailSender);
34-
} catch (MailException e) {
38+
} catch (MessagingException e) {
3539
throw new RuntimeException(e);
3640
}
3741
}
42+
3843
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.bitscoderdotcom.link_generator_system.service.service;
2+
3+
import com.bitscoderdotcom.link_generator_system.dto.ApiResponse;
4+
import com.bitscoderdotcom.link_generator_system.dto.InvoiceDto;
5+
import com.bitscoderdotcom.link_generator_system.entities.Invoice;
6+
import org.springframework.http.ResponseEntity;
7+
8+
import java.security.Principal;
9+
10+
public interface InvoiceService {
11+
12+
ResponseEntity<ApiResponse<InvoiceDto.Response>> generateInvoice(InvoiceDto invoiceDto, Principal principal);
13+
String processPayment(String linkId, String invoiceId);
14+
Invoice getInvoiceById(String id);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package com.bitscoderdotcom.link_generator_system.service.service.serviceImpl;
2+
3+
import com.bitscoderdotcom.link_generator_system.constant.Status;
4+
import com.bitscoderdotcom.link_generator_system.dto.ApiResponse;
5+
import com.bitscoderdotcom.link_generator_system.dto.EmailDetails;
6+
import com.bitscoderdotcom.link_generator_system.dto.InvoiceDto;
7+
import com.bitscoderdotcom.link_generator_system.entities.Company;
8+
import com.bitscoderdotcom.link_generator_system.entities.Invoice;
9+
import com.bitscoderdotcom.link_generator_system.entities.PaymentLink;
10+
import com.bitscoderdotcom.link_generator_system.exception.ResourceNotFoundException;
11+
import com.bitscoderdotcom.link_generator_system.repository.CompanyRepository;
12+
import com.bitscoderdotcom.link_generator_system.repository.InvoiceRepository;
13+
import com.bitscoderdotcom.link_generator_system.repository.PaymentLinkRepository;
14+
import com.bitscoderdotcom.link_generator_system.service.EmailService;
15+
import com.bitscoderdotcom.link_generator_system.service.service.InvoiceService;
16+
import lombok.AllArgsConstructor;
17+
import lombok.extern.slf4j.Slf4j;
18+
import org.springframework.http.ResponseEntity;
19+
import org.springframework.stereotype.Service;
20+
import org.springframework.transaction.annotation.Transactional;
21+
import org.thymeleaf.TemplateEngine;
22+
import org.thymeleaf.context.Context;
23+
24+
import java.security.Principal;
25+
import java.time.LocalDate;
26+
import java.time.LocalDateTime;
27+
import java.util.UUID;
28+
29+
@Service
30+
@Slf4j
31+
@AllArgsConstructor
32+
public class InvoiceServiceImpl implements InvoiceService {
33+
34+
private final InvoiceRepository invoiceRepository;
35+
private final CompanyRepository companyRepository;
36+
private final PaymentLinkRepository paymentLinkRepository;
37+
private final EmailService emailService;
38+
private final TemplateEngine templateEngine;
39+
40+
@Transactional
41+
public ResponseEntity<ApiResponse<InvoiceDto.Response>> generateInvoice(InvoiceDto invoiceDto, Principal principal) {
42+
log.info("Generating invoice for company");
43+
44+
Company company = companyRepository.findUserByCompanyEmail(principal.getName())
45+
.orElseThrow(() -> new ResourceNotFoundException("Company", "User email", principal.getName()));
46+
47+
// Create a new invoice
48+
Invoice invoice = new Invoice();
49+
invoice.setCompany(company);
50+
invoice.setCustomerName(invoiceDto.getCustomerName());
51+
invoice.setCustomerEmail(invoiceDto.getCustomerEmail());
52+
invoice.setDescription(invoiceDto.getDescription());
53+
invoice.setQuantity(invoiceDto.getQuantity());
54+
invoice.setUnitPrice(invoiceDto.getUnitPrice());
55+
invoice.setTotalAmount(invoiceDto.getTotalAmount());
56+
invoice.setInvoiceGenerationDate(invoiceDto.getInvoiceGenerationDate());
57+
invoice.setPaymentDueDate(invoiceDto.getPaymentDueDate());
58+
invoice.setStatus(Status.Unpaid);
59+
60+
invoiceRepository.save(invoice);
61+
62+
PaymentLink paymentLink = new PaymentLink();
63+
paymentLink.setInvoice(invoice);
64+
paymentLinkRepository.save(paymentLink);
65+
String baseUrl = "http://localhost:8080";
66+
paymentLink.setUrl(baseUrl + generateUniqueUrl(invoice.getId()));
67+
paymentLinkRepository.save(paymentLink);
68+
69+
// Send the payment link to the customer via email
70+
EmailDetails emailDetails = new EmailDetails();
71+
emailDetails.setRecipient(invoice.getCustomerEmail());
72+
emailDetails.setSubject("Invoice Payment Link");
73+
emailDetails.setMessageBody("Please click the following link to view and pay your invoice: <a href=\"" + paymentLink.getUrl() + "\">" + paymentLink.getUrl() + "</a>");
74+
emailService.sendEmail(emailDetails);
75+
76+
log.info("Invoice generated successfully for company with id: {}", company.getId());
77+
78+
InvoiceDto.Response invoiceResponseDto = convertEntityToDto(invoice);
79+
80+
ApiResponse<InvoiceDto.Response> apiResponse = new ApiResponse<>(
81+
LocalDateTime.now(),
82+
UUID.randomUUID().toString(),
83+
true,
84+
"Invoice generated successfully",
85+
invoiceResponseDto
86+
);
87+
88+
return ResponseEntity.ok(apiResponse);
89+
}
90+
91+
@Override
92+
public Invoice getInvoiceById(String id) {
93+
return invoiceRepository.findById(id)
94+
.orElseThrow(() -> new ResourceNotFoundException("Invoice", "id", id));
95+
}
96+
97+
@Transactional
98+
public String processPayment(String linkId, String invoiceId) {
99+
// Find the payment link by linkId
100+
PaymentLink paymentLink = paymentLinkRepository.findById(linkId)
101+
.orElseThrow(() -> new ResourceNotFoundException("PaymentLink", "id", linkId));
102+
103+
if (!paymentLink.getInvoice().getId().equals(invoiceId)) {
104+
throw new IllegalArgumentException("The provided invoiceId does not match the invoiceId associated with the payment link.");
105+
}
106+
107+
Invoice invoice = paymentLink.getInvoice();
108+
109+
// Check if the invoice has already been paid
110+
if (invoice.getStatus() == Status.Paid) {
111+
return "This invoice has already been paid for.";
112+
}
113+
114+
// If the invoice has not been paid, process the payment
115+
invoice.setStatus(Status.Paid);
116+
invoiceRepository.save(invoice);
117+
118+
String receipt = generateReceipt(invoice);
119+
120+
// Send a success email to the customer with the receipt
121+
EmailDetails emailDetails = new EmailDetails();
122+
emailDetails.setRecipient(invoice.getCustomerEmail());
123+
emailDetails.setSubject("Payment Successful, please check your email for details. Thank you and please come again.");
124+
emailDetails.setMessageBody(receipt);
125+
emailService.sendEmail(emailDetails);
126+
127+
return "Payment for invoice " + invoice.getId() + " was successful.";
128+
}
129+
130+
private String generateUniqueUrl(String invoiceId) {
131+
String baseUrl = "/lgsApp/v1";
132+
return baseUrl + "/invoice/" + invoiceId + "/payment";
133+
}
134+
135+
private InvoiceDto.Response convertEntityToDto(Invoice invoice) {
136+
return new InvoiceDto.Response(
137+
invoice.getId(),
138+
invoice.getCustomerName(),
139+
invoice.getCustomerEmail(),
140+
invoice.getDescription(),
141+
invoice.getQuantity(),
142+
invoice.getUnitPrice(),
143+
invoice.getTotalAmount(),
144+
invoice.getInvoiceGenerationDate(),
145+
invoice.getPaymentDueDate(),
146+
invoice.getStatus()
147+
);
148+
}
149+
150+
private String generateReceipt(Invoice invoice) {
151+
Context context = new Context();
152+
context.setVariable("invoice", invoice);
153+
context.setVariable("paymentDate", LocalDate.now());
154+
return templateEngine.process("receipt", context);
155+
}
156+
}

src/main/resources/application.properties

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,10 @@ spring.mail.properties.mail.smtp.ssl.enable=true
2828
# =========================================================
2929
lgs.jwtExpirationMs=89999999
3030
lgs.jwtSecretKey=ulw+8TNdn7IQSFh4YU5RkF9lRHriLg7udv5Pxn2DaCiZOOHKsHXSmqmNDeaVQ0lwg/a4bJCRWw5lXghr8vY9Eg==
31+
32+
33+
# =========================================================
34+
# - THYMELEAF
35+
# =========================================================
36+
spring.thymeleaf.prefix=classpath:/templates/
37+
spring.thymeleaf.suffix=.html

0 commit comments

Comments
 (0)