Skip to content

Commit

Permalink
#1180 & #1237 intergration with front end - create payment and order …
Browse files Browse the repository at this point in the history
…in paypal
  • Loading branch information
tuannguyenh1 committed Nov 11, 2024
1 parent a37f777 commit cc8e102
Show file tree
Hide file tree
Showing 25 changed files with 337 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ public void listen(ConsumerRecord<?, ?> consumerRecord) {

private void processCheckoutEvent(JsonObject valueObject) {
Optional.ofNullable(valueObject)
.filter(value -> value.has("after"))
.filter(
value -> value.has("op") && "u".equals(value.get("op").getAsString())
)
.map(value -> value.getAsJsonObject("after"))
.ifPresent(this::handleAfterJson);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,20 +89,12 @@ private void updateCheckOut(String checkoutId, String paymentProviderCheckoutId)
checkout.setCheckoutState(CheckoutState.PAYMENT_PROCESSING);
checkout.setProgress(CheckoutProgress.PAYMENT_CREATED);

ObjectNode updatedAttributes = updateAttributesWithCheckout(checkout.getAttributes(),
paymentProviderCheckoutId);
checkout.setAttributes(convertObjectToString(objectMapper, updatedAttributes));

checkoutService.updateCheckout(checkout);
}

private ObjectNode updateAttributesWithCheckout(String attributes, String paymentProviderCheckoutId) {

ObjectNode attributesNode = getAttributesNode(objectMapper, attributes);
ObjectNode attributesNode = getAttributesNode(objectMapper, checkout.getAttributes());
attributesNode.put(Constants.Column.CHECKOUT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD,
paymentProviderCheckoutId);
checkout.setAttributes(convertObjectToString(objectMapper, attributesNode));

return attributesNode;
checkoutService.updateCheckout(checkout);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.yas.order.viewmodel.checkout;

import com.yas.order.model.Checkout;
import com.yas.order.model.enumeration.CheckoutProgress;
import com.yas.order.model.enumeration.CheckoutState;
import com.yas.order.model.enumeration.PaymentMethod;
import java.math.BigDecimal;
import java.util.Set;
import lombok.Builder;
Expand All @@ -11,6 +14,11 @@ public record CheckoutVm(
String email,
String note,
String couponCode,
CheckoutState checkoutState,
CheckoutProgress progress,
PaymentMethod paymentMethodId,
String attributes,
String lastError,
BigDecimal totalAmount,
BigDecimal totalDiscountAmount,
Set<CheckoutItemVm> checkoutItemVms
Expand All @@ -22,6 +30,11 @@ public static CheckoutVm fromModel(Checkout checkout, Set<CheckoutItemVm> checko
.email(checkout.getEmail())
.note(checkout.getNote())
.couponCode(checkout.getCouponCode())
.checkoutState(checkout.getCheckoutState())
.progress(checkout.getProgress())
.paymentMethodId(checkout.getPaymentMethodId())
.attributes(checkout.getAttributes())
.lastError(checkout.getLastError())
.checkoutItemVms(checkoutItemVms)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.yas.order.OrderApplication;
import com.yas.order.model.enumeration.CheckoutProgress;
import com.yas.order.model.enumeration.CheckoutState;
import com.yas.order.model.enumeration.PaymentMethod;
import com.yas.order.service.CheckoutService;
import com.yas.order.viewmodel.checkout.CheckoutItemPostVm;
import com.yas.order.viewmodel.checkout.CheckoutItemVm;
Expand Down Expand Up @@ -201,6 +204,11 @@ private CheckoutVm getCheckoutVm() {
"[email protected]",
"Please deliver after 5 PM",
"DISCOUNT20",
CheckoutState.PAYMENT_PROCESSING,
CheckoutProgress.PROMOTION_CODE_APPLIED,
PaymentMethod.PAYPAL,
null,
null,
BigDecimal.valueOf(900),
BigDecimal.valueOf(9),
checkoutItemVms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class OrderStatusConsumerTest {

private OrderStatusConsumer orderStatusConsumer;

private final String jsonRecord = "{\"after\": {"
private final String jsonRecord = "{\"op\": \"u\", \"after\": {"
+ " \"status\": \"PAYMENT_PROCESSING\","
+ " \"progress\": \"STOCK_LOCKED\","
+ " \"id\": 12345,"
Expand Down
22 changes: 22 additions & 0 deletions payment/src/main/java/com/yas/payment/config/JsonConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.yas.payment.config;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class JsonConfig {

@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}

@Bean
public Gson gson() {
return new Gson();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ public InitPaymentResponseVm initPayment(@Valid @RequestBody InitPaymentRequestV
}

@PostMapping(value = "/capture")
public CapturePaymentResponseVm capturePayment(@Valid @RequestBody CapturePaymentRequestVm capturePaymentRequestVM) {
return paymentService.capturePayment(capturePaymentRequestVM);
public CapturePaymentResponseVm capturePayment(
@Valid @RequestBody CapturePaymentRequestVm capturePaymentRequestVm
) {
return paymentService.capturePayment(capturePaymentRequestVm);
}

@GetMapping(value = "/cancel")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.yas.payment.kafka.consumer;

import static com.yas.payment.utils.JsonUtils.convertObjectToString;
import static com.yas.payment.utils.JsonUtils.getAttributesNode;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
Expand Down Expand Up @@ -27,6 +32,7 @@ public class PaymentCreateConsumer {

private static final Logger LOGGER = LoggerFactory.getLogger(PaymentCreateConsumer.class);
private final PaymentService paymentService;
private final ObjectMapper objectMapper;
private final Gson gson;

@KafkaListener(
Expand Down Expand Up @@ -92,6 +98,10 @@ private void createOrderOnPaypal(Payment payment) {
payment.setPaymentStatus(PaymentStatus.PROCESSING);
payment.setPaymentProviderCheckoutId(initPaymentResponseVm.paymentId());

ObjectNode attributesNode = getAttributesNode(objectMapper, payment.getAttributes());
attributesNode.put(Constants.Column.REDIRECT_URL_ID_COLUMN, initPaymentResponseVm.redirectUrl());
payment.setAttributes(convertObjectToString(objectMapper, attributesNode));

paymentService.updatePayment(payment);
} else {
LOGGER.warn(Constants.ErrorCode.ORDER_CREATION_FAILED, payment.getId());
Expand Down
7 changes: 7 additions & 0 deletions payment/src/main/java/com/yas/payment/model/Payment.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.yas.payment.model.enumeration.PaymentMethod;
import com.yas.payment.model.enumeration.PaymentStatus;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
Expand All @@ -15,6 +16,8 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

@Entity
@Table(name = "payment")
Expand Down Expand Up @@ -49,4 +52,8 @@ public class Payment extends AbstractAuditEntity {

private String paymentProviderCheckoutId;

@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "attributes", columnDefinition = "jsonb")
private String attributes;

}
2 changes: 1 addition & 1 deletion payment/src/main/java/com/yas/payment/utils/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private Column() {
public static final String ID_COLUMN = "id";

// Column name of Checkout table
public static final String REDIRECT_URL_ID_COLUMN = "redirect-url";
public static final String REDIRECT_URL_ID_COLUMN = "redirect_url";
}

public final class Message {
Expand Down
33 changes: 33 additions & 0 deletions payment/src/main/java/com/yas/payment/utils/JsonUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.yas.payment.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yas.commonlibrary.exception.BadRequestException;

public class JsonUtils {

private JsonUtils() {
throw new UnsupportedOperationException();
}

public static String convertObjectToString(ObjectMapper objectMapper, Object value) {
try {
return objectMapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new BadRequestException(Constants.ErrorCode.CANNOT_CONVERT_TO_STRING, value);
}
}

public static ObjectNode getAttributesNode(ObjectMapper objectMapper, String attributes) {
try {
if (attributes == null || attributes.isBlank()) {
return objectMapper.createObjectNode();
} else {
return (ObjectNode) objectMapper.readTree(attributes);
}
} catch (JsonProcessingException e) {
throw new BadRequestException("Invalid Json: {}", attributes);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ public record PaymentVm(
PaymentMethod paymentMethod,
PaymentStatus paymentStatus,
String gatewayTransactionId,
String failureMessage
String failureMessage,
String paymentProviderCheckoutId,
String attributes
) {

public static PaymentVm fromModel(Payment payment) {
Expand All @@ -28,7 +30,9 @@ public static PaymentVm fromModel(Payment payment) {
payment.getPaymentMethod(),
payment.getPaymentStatus(),
payment.getGatewayTransactionId(),
payment.getFailureMessage()
payment.getFailureMessage(),
payment.getPaymentProviderCheckoutId(),
payment.getAttributes()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE IF EXISTS "payment"
ADD COLUMN attributes JSONB;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.yas.commonlibrary.exception.BadRequestException;
import com.yas.payment.model.Payment;
Expand All @@ -31,8 +32,9 @@ class PaymentCreateConsumerTest {
@BeforeEach
void setUp() {
paymentService = mock(PaymentService.class);
ObjectMapper objectMapper = new ObjectMapper();
Gson gson = new Gson();
paymentCreateConsumer = new PaymentCreateConsumer(paymentService, gson);
paymentCreateConsumer = new PaymentCreateConsumer(paymentService, objectMapper, gson);
}

@Test
Expand Down
5 changes: 5 additions & 0 deletions storefront/modules/common/models/EPaymentMethod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum EPaymentMethod {
COD = 'COD',
BANKING = 'BANKING',
PAYPAL = 'PAYPAL',
}
14 changes: 14 additions & 0 deletions storefront/modules/order/components/CheckOutDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ const CheckOutDetail = ({ orderItems, disablePaymentProcess, setPaymentMethod }:
</p>

<button
id="processToPaymentButton"
type="submit"
className="site-btn"
disabled={disablePaymentProcess ? true : disableCheckout}
Expand All @@ -148,6 +149,19 @@ const CheckOutDetail = ({ orderItems, disablePaymentProcess, setPaymentMethod }:
>
Process to Payment
</button>
<button
id="newProcessToPaymentButton"
type="submit"
className="site-btn"
disabled={disablePaymentProcess ? true : disableCheckout}
style={
disablePaymentProcess || disableCheckout
? { cursor: 'not-allowed', backgroundColor: 'gray' }
: { cursor: 'pointer' }
}
>
New Process to Payment
</button>
</div>
</>
);
Expand Down
10 changes: 9 additions & 1 deletion storefront/modules/order/models/Checkout.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { EPaymentMethod } from '@/modules/common/models/EPaymentMethod';
import { CheckoutItem } from './CheckoutItem';
import { ECheckoutState } from './ECheckoutState';
import { ECheckoutProgress } from './ECheckoutProgress';

export type Checkout = {
id?: string;
email: string;
note?: string;
couponCode?: string;
checkoutState?: ECheckoutState;
progress?: ECheckoutProgress;
paymentMethodId?: EPaymentMethod;
attributes?: string;
lastError?: string;
totalAmount: number;
totalDiscountAmount: number;
checkoutItemPostVms: CheckoutItem[];
checkoutItemVms: CheckoutItem[];
};
9 changes: 9 additions & 0 deletions storefront/modules/order/models/ECheckoutProgress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export enum ECheckoutProgress {
INIT = 'INIT',
PROMOTION_CODE_APPLIED = 'PROMOTION_CODE_APPLIED',
PROMOTION_CODE_APPLIED_FAILED = 'PROMOTION_CODE_APPLIED_FAILED',
STOCK_LOCKED = 'STOCK_LOCKED',
STOCK_LOCKED_FAILED = 'STOCK_LOCKED_FAILED',
PAYMENT_CREATED = 'PAYMENT_CREATED',
PAYMENT_CREATED_FAILED = 'PAYMENT_CREATED_FAILED',
}
10 changes: 10 additions & 0 deletions storefront/modules/order/models/ECheckoutState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export enum ECheckoutState {
COMPLETED = 'COMPLETED',
PENDING = 'PENDING',
LOCK = 'LOCK',
CHECKED_OUT = 'CHECKED_OUT',
PAYMENT_PROCESSING = 'PAYMENT_PROCESSING',
PAYMENT_FAILED = 'PAYMENT_FAILED',
PAYMENT_CONFIRMED = 'PAYMENT_CONFIRMED',
FULFILLED = 'FULFILLED',
}
8 changes: 7 additions & 1 deletion storefront/modules/order/services/OrderService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ export async function createCheckout(checkout: Checkout): Promise<Checkout | nul
throw new Error(response.statusText);
}

export async function getCheckoutById(id: string) {
export async function getCheckoutById(id: string): Promise<Checkout> {
const response = await apiClientService.get(`${baseUrl}/checkouts/${id}`);
if (response.status >= 200 && response.status < 300) return response.json();
throw new Error(response.statusText);
}

export async function processPayment(id: string): Promise<void> {
const response = await apiClientService.post(`${baseUrl}/checkouts/${id}/process-payment`, {});
if (response.status >= 200 && response.status < 300) return;
throw new Error(response.statusText);
}
7 changes: 7 additions & 0 deletions storefront/modules/payment/models/EPaymentStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export enum EPaymentStatus {
NEW = 'NEW',
PROCESSING = 'PROCESSING',
PENDING = 'PENDING',
COMPLETED = 'COMPLETED',
CANCELLED = 'CANCELLED',
}
Loading

0 comments on commit cc8e102

Please sign in to comment.