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
45 changes: 33 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,65 @@
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.release>23</maven.compiler.release>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<junit.version>5.11.4</junit.version>
<assertj.version>3.27.3</assertj.version>
<mockito.version>5.15.2</mockito.version>
<maven.compiler.plugin.version>3.13.0</maven.compiler.plugin.version>
<surefire.version>3.5.2</surefire.version>
<failsafe.version>3.5.2</failsafe.version>
<jacoco.version>0.8.13</jacoco.version>
</properties>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.4</version>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.27.3</version>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.15.2</version>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<version>${maven.compiler.plugin.version}</version>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
<version>${surefire.version}</version>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.2</version>
<version>${failsafe.version}</version>
<executions>
<execution>
<goals>
Expand All @@ -57,23 +77,24 @@
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<version>${jacoco.version}</version>
<configuration>
<skip>true</skip>
<skip>false</skip>
</configuration>
<executions>
<execution>
<id>default-prepare-agent</id>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<phase>prepare-package</phase>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/example/payment/PaymentChargeResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.payment;

public interface PaymentChargeResponse {
boolean isSuccess();
}
5 changes: 5 additions & 0 deletions src/main/java/com/example/payment/PaymentEmailSender.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.payment;

public interface PaymentEmailSender {
void sendPaymentConfirmation(String toEmail, double amount);
}
5 changes: 5 additions & 0 deletions src/main/java/com/example/payment/PaymentGateway.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.payment;

public interface PaymentGateway {
PaymentChargeResponse charge(String apiKey, double amount);
}
58 changes: 37 additions & 21 deletions src/main/java/com/example/payment/PaymentProcessor.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
package com.example.payment;

//public class PaymentProcessor {
// private static final String API_KEY = "sk_test_123456";
//
// public boolean processPayment(double amount) {
// // Anropar extern betaltjänst direkt med statisk API-nyckel
// PaymentApiResponse response = PaymentApi.charge(API_KEY, amount);
//
// // Skriver till databas direkt
// if (response.isSuccess()) {
// DatabaseConnection.getInstance()
// .executeUpdate("INSERT INTO payments (amount, status) VALUES (" + amount + ", 'SUCCESS')");
// }
//
// // Skickar e-post direkt
// if (response.isSuccess()) {
// EmailService.sendPaymentConfirmation("user@example.com", amount);
// }
//
// return response.isSuccess();
// }
//}
import java.util.Objects;

public class PaymentProcessor {
private final PaymentGateway paymentGateway;
private final PaymentRepository paymentRepository;
private final PaymentEmailSender emailSender;
private final String apiKey;
private final String confirmationEmailTo;

public PaymentProcessor(PaymentGateway paymentGateway,
PaymentRepository paymentRepository,
PaymentEmailSender emailSender,
String apiKey,
String confirmationEmailTo) {
this.paymentGateway = Objects.requireNonNull(paymentGateway, "paymentGateway");
this.paymentRepository = Objects.requireNonNull(paymentRepository, "paymentRepository");
this.emailSender = Objects.requireNonNull(emailSender, "emailSender");
this.apiKey = Objects.requireNonNull(apiKey, "apiKey");
this.confirmationEmailTo = Objects.requireNonNull(confirmationEmailTo, "confirmationEmailTo");
}

public boolean processPayment(double amount) {
if (amount <= 0.0) {
throw new IllegalArgumentException("amount måste vara > 0");
}

PaymentChargeResponse response = paymentGateway.charge(apiKey, amount);

if (response.isSuccess()) {
paymentRepository.saveSuccessfulPayment(amount);
emailSender.sendPaymentConfirmation(confirmationEmailTo, amount);
return true;
}

return false;
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/example/payment/PaymentRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.payment;

public interface PaymentRepository {
void saveSuccessfulPayment(double amount);
}
7 changes: 7 additions & 0 deletions src/main/java/shop/Discount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package shop;

import java.math.BigDecimal;

public interface Discount {
BigDecimal apply(BigDecimal currentTotal);
}
19 changes: 19 additions & 0 deletions src/main/java/shop/FixedAmountDiscount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package shop;

import java.math.BigDecimal;
import java.util.Objects;

public final class FixedAmountDiscount implements Discount {
private final BigDecimal amount;

public FixedAmountDiscount(BigDecimal amount) {
this.amount = Objects.requireNonNull(amount, "amount");
if (amount.signum() < 0) throw new IllegalArgumentException("amount måste vara >= 0");
}

@Override
public BigDecimal apply(BigDecimal currentTotal) {
Objects.requireNonNull(currentTotal, "currentTotal");
return currentTotal.subtract(amount);
}
}
21 changes: 21 additions & 0 deletions src/main/java/shop/PercentageDiscount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package shop;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;

public final class PercentageDiscount implements Discount {
private final BigDecimal percent;

public PercentageDiscount(BigDecimal percent) {
this.percent = Objects.requireNonNull(percent, "percent");
if (percent.signum() < 0) throw new IllegalArgumentException("percent måste vara >= 0");
}

@Override
public BigDecimal apply(BigDecimal currentTotal) {
Objects.requireNonNull(currentTotal, "currentTotal");
BigDecimal factor = BigDecimal.ONE.subtract(percent.divide(new BigDecimal("100"), 10, RoundingMode.HALF_UP));
return currentTotal.multiply(factor);
}
}
31 changes: 31 additions & 0 deletions src/main/java/shop/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package shop;

import java.math.BigDecimal;
import java.util.Objects;

public final class Product {
private final String id;
private final String name;
private final BigDecimal unitPrice;

public Product(String id, String name, BigDecimal unitPrice) {
if (id == null || id.isBlank()) throw new IllegalArgumentException("id");
if (name == null || name.isBlank()) throw new IllegalArgumentException("name");
this.id = id;
this.name = name;
this.unitPrice = Objects.requireNonNull(unitPrice, "unitPrice");
if (unitPrice.signum() < 0) throw new IllegalArgumentException("unitPrice måste vara >= 0");
}

public String id() {
return id;
}

public String name() {
return name;
}

public BigDecimal unitPrice() {
return unitPrice;
}
}
82 changes: 82 additions & 0 deletions src/main/java/shop/ShoppingCart.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package shop;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

public final class ShoppingCart {

private final Map<String, CartLine> lines = new HashMap<>();
private final List<Discount> discounts = new ArrayList<>();

public void addItem(Product product, int quantity) {
if (product == null) throw new IllegalArgumentException("product");
if (quantity <= 0) throw new IllegalArgumentException("quantity måste vara > 0");

lines.merge(
product.id(),
new CartLine(product, quantity),
(existing, added) -> new CartLine(existing.product(), existing.quantity() + added.quantity())
);
}

public boolean removeItem(String productId) {
if (productId == null) throw new IllegalArgumentException("productId");
return lines.remove(productId) != null;
}

public void updateQuantity(String productId, int newQuantity) {
if (productId == null) throw new IllegalArgumentException("productId");
if (newQuantity < 0) throw new IllegalArgumentException("newQuantity måste vara >= 0");

CartLine existing = lines.get(productId);
if (existing == null) {
throw new IllegalArgumentException("Produkt finns inte i kundvagnen: " + productId);
}

if (newQuantity == 0) {
lines.remove(productId);
return;
}

lines.put(productId, new CartLine(existing.product(), newQuantity));
}

public int getQuantity(String productId) {
CartLine line = lines.get(productId);
return line == null ? 0 : line.quantity();
}

public BigDecimal totalBeforeDiscount() {
BigDecimal total = BigDecimal.ZERO;
for (CartLine line : lines.values()) {
total = total.add(line.product().unitPrice().multiply(BigDecimal.valueOf(line.quantity())));
}
return money(total);
}

public void applyDiscount(Discount discount) {
if (discount == null) throw new IllegalArgumentException("discount");
discounts.add(discount);
}

public BigDecimal total() {
BigDecimal total = totalBeforeDiscount();
for (Discount discount : discounts) {
total = discount.apply(total);
}
if (total.signum() < 0) total = BigDecimal.ZERO;
return money(total);
}

private static BigDecimal money(BigDecimal value) {
return value.setScale(2, RoundingMode.HALF_UP);
}

private record CartLine(Product product, int quantity) {
private CartLine {
Objects.requireNonNull(product, "product");
if (quantity <= 0) throw new IllegalArgumentException("quantity måste vara > 0");
}
}
}
Loading