Skip to content

Commit 7ea6969

Browse files
committed
Finish payment workflow
1 parent 7644f5b commit 7ea6969

File tree

25 files changed

+627
-168
lines changed

25 files changed

+627
-168
lines changed

account/account-web/src/main/java/demo/account/controller/AccountController.java

+20-28
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public ResponseEntity updateAccount(@RequestBody Account account, @PathVariable
4949

5050
@RequestMapping(path = "/accounts/{id}")
5151
public ResponseEntity getAccount(@PathVariable Long id) {
52-
return Optional.ofNullable(getAccountResource(id))
52+
return Optional.ofNullable(accountService.get(id))
53+
.map(this::getAccountResource)
5354
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
5455
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
5556
}
@@ -91,57 +92,48 @@ public ResponseEntity getCommands(@PathVariable Long id) {
9192

9293
@RequestMapping(path = "/accounts/{id}/commands/confirm")
9394
public ResponseEntity confirm(@PathVariable Long id) {
94-
return Optional.ofNullable(getAccountResource(accountService.get(id)
95-
.confirm()))
95+
return Optional.ofNullable(accountService.get(id))
96+
.map(Account::confirm)
97+
.map(this::getAccountResource)
9698
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
9799
.orElseThrow(() -> new RuntimeException("The command could not be applied"));
98100
}
99101

100102
@RequestMapping(path = "/accounts/{id}/commands/activate")
101103
public ResponseEntity activate(@PathVariable Long id) {
102-
return Optional.ofNullable(getAccountResource(accountService.get(id)
103-
.activate()))
104+
return Optional.ofNullable(accountService.get(id))
105+
.map(Account::activate)
106+
.map(this::getAccountResource)
104107
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
105108
.orElseThrow(() -> new RuntimeException("The command could not be applied"));
106109
}
107110

108111
@RequestMapping(path = "/accounts/{id}/commands/suspend")
109112
public ResponseEntity suspend(@PathVariable Long id) {
110-
return Optional.ofNullable(getAccountResource(accountService.get(id)
111-
.suspend()))
113+
return Optional.ofNullable(accountService.get(id))
114+
.map(Account::suspend)
115+
.map(this::getAccountResource)
112116
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
113117
.orElseThrow(() -> new RuntimeException("The command could not be applied"));
114118
}
115119

116120
@RequestMapping(path = "/accounts/{id}/commands/archive")
117121
public ResponseEntity archive(@PathVariable Long id) {
118-
return Optional.ofNullable(getAccountResource(accountService.get(id)
119-
.archive()))
122+
return Optional.ofNullable(accountService.get(id))
123+
.map(Account::archive)
124+
.map(this::getAccountResource)
120125
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
121126
.orElseThrow(() -> new RuntimeException("The command could not be applied"));
122127
}
123128

124129
@RequestMapping(path = "/accounts/{id}/commands/postOrder", method = RequestMethod.POST)
125130
public ResponseEntity postOrder(@PathVariable Long id, @RequestBody Order order) {
126-
return Optional.ofNullable(accountService.get(id)
127-
.postOrder(order))
128-
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
131+
return Optional.ofNullable(accountService.get(id))
132+
.map(a -> a.postOrder(order))
133+
.map(o -> new ResponseEntity<>(o, HttpStatus.CREATED))
129134
.orElseThrow(() -> new RuntimeException("The command could not be applied"));
130135
}
131136

132-
/**
133-
* Retrieves a hypermedia resource for {@link Account} with the specified identifier.
134-
*
135-
* @param id is the unique identifier for looking up the {@link Account} entity
136-
* @return a hypermedia resource for the fetched {@link Account}
137-
*/
138-
private Resource<Account> getAccountResource(Long id) {
139-
// Get the account for the provided id
140-
Account account = accountService.get(id);
141-
142-
return getAccountResource(account);
143-
}
144-
145137
/**
146138
* Creates a new {@link Account} entity and persists the result to the repository.
147139
*
@@ -247,17 +239,17 @@ private LinkBuilder linkBuilder(String name, Long id) {
247239
private Resource<Account> getAccountResource(Account account) {
248240
Assert.notNull(account, "Account must not be null");
249241

250-
if(account.getLink("commands") == null) {
242+
if (!account.hasLink("commands")) {
251243
// Add command link
252244
account.add(linkBuilder("getCommands", account.getIdentity()).withRel("commands"));
253245
}
254246

255-
if(account.getLink("events") == null) {
247+
if (!account.hasLink("events")) {
256248
// Add get events link
257249
account.add(linkBuilder("getAccountEvents", account.getIdentity()).withRel("events"));
258250
}
259251

260-
if(account.getLink("orders") == null) {
252+
if (!account.hasLink("orders")) {
261253
// Add orders link
262254
account.add(linkBuilder("getAccountOrders", account.getIdentity()).withRel("orders"));
263255
}

order/order-web/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@
6161
<version>1.0-SNAPSHOT</version>
6262
</dependency>
6363

64+
<dependency>
65+
<groupId>com.jayway.jsonpath</groupId>
66+
<artifactId>json-path</artifactId>
67+
<version>${json-path.version}</version>
68+
</dependency>
6469
<dependency>
6570
<groupId>com.h2database</groupId>
6671
<artifactId>h2</artifactId>
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package demo.order.action;
22

33
import demo.domain.Action;
4-
import demo.order.event.OrderEvent;
5-
import demo.order.event.OrderEventType;
64
import demo.order.domain.Order;
75
import demo.order.domain.OrderModule;
86
import demo.order.domain.OrderService;
97
import demo.order.domain.OrderStatus;
8+
import demo.order.event.OrderEvent;
9+
import demo.order.event.OrderEventType;
10+
import org.apache.log4j.Logger;
1011
import org.springframework.stereotype.Service;
12+
import org.springframework.util.Assert;
1113

12-
import java.util.function.BiConsumer;
14+
import java.util.function.BiFunction;
1315

1416
/**
1517
* Connects an {@link Order} to an Account.
@@ -18,19 +20,33 @@
1820
*/
1921
@Service
2022
public class ConnectAccount extends Action<Order> {
23+
private final Logger log = Logger.getLogger(this.getClass());
2124

22-
public BiConsumer<Order, Long> getConsumer() {
25+
public BiFunction<Order, Long, Order> getFunction() {
2326
return (order, accountId) -> {
24-
OrderService orderService = order.getModule(OrderModule.class)
25-
.getDefaultService();
27+
Assert.isTrue(order.getStatus() == OrderStatus.ORDER_CREATED, "Order must be in a created state");
28+
29+
Order result;
30+
OrderService orderService = order.getModule(OrderModule.class).getDefaultService();
2631

2732
// Connect the account
2833
order.setAccountId(accountId);
2934
order.setStatus(OrderStatus.ACCOUNT_CONNECTED);
3035
order = orderService.update(order);
3136

32-
// Trigger the account connected event
33-
order.sendAsyncEvent(new OrderEvent(OrderEventType.ACCOUNT_CONNECTED, order));
37+
try {
38+
// Trigger the account connected event
39+
result = order.sendEvent(new OrderEvent(OrderEventType.ACCOUNT_CONNECTED, order)).getEntity();
40+
} catch (Exception ex) {
41+
log.error("Could not connect order to account", ex);
42+
order.setAccountId(null);
43+
order.setStatus(OrderStatus.ORDER_CREATED);
44+
orderService.update(order);
45+
throw new IllegalStateException("Could not connect order to account", ex);
46+
}
47+
48+
return result;
3449
};
3550
}
51+
3652
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package demo.order.action;
22

33
import demo.domain.Action;
4-
import demo.order.event.OrderEvent;
5-
import demo.order.event.OrderEventType;
64
import demo.order.domain.Order;
75
import demo.order.domain.OrderModule;
86
import demo.order.domain.OrderService;
97
import demo.order.domain.OrderStatus;
8+
import demo.order.event.OrderEvent;
9+
import demo.order.event.OrderEventType;
1010
import demo.payment.domain.Payment;
11+
import org.apache.log4j.Logger;
1112
import org.springframework.stereotype.Service;
13+
import org.springframework.util.Assert;
1214

13-
import java.util.function.BiConsumer;
15+
import java.util.function.BiFunction;
1416

1517
/**
1618
* Connects a {@link Payment} to an {@link Order}.
@@ -19,19 +21,33 @@
1921
*/
2022
@Service
2123
public class ConnectPayment extends Action<Order> {
22-
public BiConsumer<Order, Long> getConsumer() {
24+
private final Logger log = Logger.getLogger(this.getClass());
25+
26+
public BiFunction<Order, Long, Order> getFunction() {
2327
return (order, paymentId) -> {
28+
Assert.isTrue(order
29+
.getStatus() == OrderStatus.PAYMENT_CREATED, "Order must be in a payment created state");
2430

25-
OrderService orderService = order.getModule(OrderModule.class)
26-
.getDefaultService();
31+
Order result;
32+
OrderService orderService = order.getModule(OrderModule.class).getDefaultService();
2733

28-
// Connect the account
34+
// Connect the payment
2935
order.setPaymentId(paymentId);
3036
order.setStatus(OrderStatus.PAYMENT_CONNECTED);
3137
order = orderService.update(order);
3238

33-
// Trigger the account connected event
34-
order.sendAsyncEvent(new OrderEvent(OrderEventType.PAYMENT_CONNECTED, order));
39+
try {
40+
// Trigger the payment connected event
41+
result = order.sendEvent(new OrderEvent(OrderEventType.PAYMENT_CONNECTED, order)).getEntity();
42+
} catch (Exception ex) {
43+
log.error("Could not connect payment to order", ex);
44+
order.setPaymentId(null);
45+
order.setStatus(OrderStatus.ORDER_CREATED);
46+
orderService.update(order);
47+
throw new IllegalStateException("Could not connect payment to order", ex);
48+
}
49+
50+
return result;
3551
};
3652
}
3753
}
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
11
package demo.order.action;
22

33
import demo.domain.Action;
4-
import demo.order.event.OrderEvent;
5-
import demo.order.event.OrderEventType;
64
import demo.order.domain.Order;
75
import demo.order.domain.OrderModule;
86
import demo.order.domain.OrderService;
97
import demo.order.domain.OrderStatus;
8+
import demo.order.event.OrderEvent;
9+
import demo.order.event.OrderEventType;
1010
import demo.payment.domain.Payment;
1111
import demo.payment.domain.PaymentMethod;
12+
import demo.payment.domain.PaymentService;
1213
import org.apache.log4j.Logger;
13-
import org.springframework.hateoas.MediaTypes;
14-
import org.springframework.hateoas.Resource;
15-
import org.springframework.http.MediaType;
16-
import org.springframework.http.RequestEntity;
1714
import org.springframework.stereotype.Service;
1815
import org.springframework.util.Assert;
19-
import org.springframework.web.client.RestTemplate;
2016

21-
import java.net.URI;
22-
import java.util.function.Consumer;
17+
import java.util.function.Function;
2318

2419
/**
2520
* Creates a {@link Payment} for an {@link Order}.
@@ -29,54 +24,54 @@
2924
@Service
3025
public class CreatePayment extends Action<Order> {
3126

32-
private final Logger log = Logger.getLogger(CreatePayment.class);
27+
private final Logger log = Logger.getLogger(this.getClass());
28+
private final PaymentService paymentService;
3329

34-
private RestTemplate restTemplate;
35-
36-
public CreatePayment(RestTemplate restTemplate) {
37-
this.restTemplate = restTemplate;
30+
public CreatePayment(PaymentService paymentService) {
31+
this.paymentService = paymentService;
3832
}
3933

40-
public Consumer<Order> getConsumer() {
34+
public Function<Order, Order> getFunction() {
4135
return order -> {
4236
Assert.isTrue(order.getPaymentId() == null, "Payment has already been created");
4337
Assert.isTrue(order.getStatus() == OrderStatus.ACCOUNT_CONNECTED, "Account must be connected first");
4438

45-
OrderService orderService = order.getModule(OrderModule.class)
46-
.getDefaultService();
39+
// Get entity services
40+
OrderService orderService = order.getModule(OrderModule.class).getDefaultService();
41+
Order result;
4742

4843
Payment payment = new Payment();
49-
50-
// Calculate payment amount
5144
payment.setAmount(order.calculateTotal());
52-
53-
// Set payment method
5445
payment.setPaymentMethod(PaymentMethod.CREDIT_CARD);
46+
payment = paymentService.create(payment);
5547

56-
// Create a new request entity
57-
RequestEntity<Resource<Payment>> requestEntity = RequestEntity.post(
58-
URI.create("http://payment-web/v1/payments"))
59-
.contentType(MediaType.APPLICATION_JSON)
60-
.accept(MediaTypes.HAL_JSON)
61-
.body(new Resource<>(payment), Resource.class);
48+
log.info(payment);
6249

63-
// Update the order entity's status
64-
Resource paymentResource = restTemplate
65-
.exchange(requestEntity, Resource.class)
66-
.getBody();
67-
68-
log.info(paymentResource);
69-
70-
// Update the status
50+
// Update the order status
7151
order.setStatus(OrderStatus.PAYMENT_CREATED);
7252
order = orderService.update(order);
7353

74-
OrderEvent event = new OrderEvent(OrderEventType.PAYMENT_CREATED, order);
75-
event.add(paymentResource.getLink("self")
76-
.withRel("payment"));
54+
try {
55+
OrderEvent event = new OrderEvent(OrderEventType.PAYMENT_CREATED, order);
56+
event.add(payment.getLink("self").withRel("payment"));
57+
58+
// Trigger payment created event
59+
result = order.sendEvent(event).getEntity();
60+
} catch (Exception ex) {
61+
log.error("The order's payment could not be created", ex);
62+
63+
// Rollback the payment creation
64+
if (payment.getIdentity() != null)
65+
paymentService.delete(payment.getIdentity());
66+
67+
order.setPaymentId(null);
68+
order.setStatus(OrderStatus.ACCOUNT_CONNECTED);
69+
orderService.update(order);
70+
71+
throw new IllegalStateException("Payment creation failed", ex);
72+
}
7773

78-
// Trigger the payment created
79-
order.sendAsyncEvent(event);
74+
return result;
8075
};
8176
}
8277
}

order/order-web/src/main/java/demo/order/action/DeleteOrder.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
import demo.order.domain.Order;
55
import demo.order.domain.OrderModule;
66
import demo.payment.domain.Payment;
7+
import demo.payment.domain.PaymentService;
8+
import org.apache.log4j.Logger;
79
import org.springframework.stereotype.Service;
8-
import org.springframework.web.client.RestTemplate;
910

1011
import java.util.function.Consumer;
1112

@@ -17,19 +18,18 @@
1718
@Service
1819
public class DeleteOrder extends Action<Order> {
1920

20-
private RestTemplate restTemplate;
21+
private final Logger log = Logger.getLogger(this.getClass());
22+
private final PaymentService paymentService;
2123

22-
public DeleteOrder(RestTemplate restTemplate) {
23-
this.restTemplate = restTemplate;
24+
public DeleteOrder(PaymentService paymentService) {
25+
this.paymentService = paymentService;
2426
}
2527

2628
public Consumer<Order> getConsumer() {
2729
return (order) -> {
2830
// Delete payment
29-
if (order.getPaymentId() != null) {
30-
String href = "http://payment-web/v1/payments/" + order.getPaymentId();
31-
restTemplate.delete(href);
32-
}
31+
if (order.getPaymentId() != null)
32+
paymentService.delete(order.getPaymentId());
3333

3434
// Delete order
3535
order.getModule(OrderModule.class)

0 commit comments

Comments
 (0)