Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Stripe Webhook logic to retrieve payment intent events, and add to Payment entity valid status and description #57

Merged
merged 11 commits into from
Jul 28, 2023
Merged
15 changes: 0 additions & 15 deletions src/main/java/com/zufar/onlinestore/payment/PaymentApi.java

This file was deleted.

49 changes: 49 additions & 0 deletions src/main/java/com/zufar/onlinestore/payment/api/PaymentApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.zufar.onlinestore.payment.api;

import com.stripe.exception.SignatureVerificationException;
import com.stripe.exception.StripeException;
import com.zufar.onlinestore.payment.dto.CreatePaymentDto;
import com.zufar.onlinestore.payment.dto.CreatePaymentMethodDto;
import com.zufar.onlinestore.payment.dto.PaymentDetailsDto;
import com.zufar.onlinestore.payment.dto.PaymentDetailsWithTokenDto;
import com.zufar.onlinestore.payment.exception.PaymentNotFoundException;

public interface PaymentApi {

/**
* This method allows to create a payment object
*
* @param createPaymentDto the request dto to create a payment object
* @return PaymentDetailsWithTokenDto combines payment details and a payment token for payment processing on the front end side
* @throws StripeException this is the parent of all Stripe API exceptions, in this method, exceptions descendants related to the payment creation and processing
* */
PaymentDetailsWithTokenDto createPayment(final CreatePaymentDto createPaymentDto) throws StripeException;

/**
* This method allows to create a payment method object
*
* @param createPaymentMethodDto the request dto to create a payment method object
* @return String payment method identifier, for secure method transfer using the Stripe API
* @throws StripeException this is the parent of all Stripe API exceptions, in this method, exceptions descendants related to the payment method creation and processing
* */
String createPaymentMethod(final CreatePaymentMethodDto createPaymentMethodDto) throws StripeException;

/**
* This method allows to create a payment method object
*
* @param paymentId the payment identifier to search payment details
* @return PaymentDetailsDto these are payment details
* @throws PaymentNotFoundException this exception throw then payment wasn't found
* */
PaymentDetailsDto getPaymentDetails(final Long paymentId) throws PaymentNotFoundException;

/**
* This method allows to create a payment method object
*
* @param paymentIntentPayload this param it is a string describing of the payment intent event type.
* @param stripeSignatureHeader this param it is a string describing of the stripe signature, which provide safe work with Stripe API webhooks mechanism
* @throws SignatureVerificationException this exception throw then stripe signature verification problem was occurred
* */
void processPaymentEvent(final String paymentIntentPayload, final String stripeSignatureHeader) throws SignatureVerificationException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.zufar.onlinestore.payment.api.impl;

import com.stripe.exception.SignatureVerificationException;
import com.stripe.exception.StripeException;
import com.zufar.onlinestore.payment.api.PaymentApi;
import com.zufar.onlinestore.payment.dto.*;
import com.zufar.onlinestore.payment.exception.PaymentNotFoundException;
import com.zufar.onlinestore.payment.service.PaymentCreator;
import com.zufar.onlinestore.payment.service.event.PaymentEventProcessor;
import com.zufar.onlinestore.payment.service.PaymentMethodCreator;
import com.zufar.onlinestore.payment.service.PaymentRetriever;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentApiImpl implements PaymentApi {

private final PaymentRetriever paymentRetriever;
private final PaymentCreator paymentCreator;
private final PaymentMethodCreator paymentMethodCreator;
private final PaymentEventProcessor paymentEventProcessor;

@Override
public PaymentDetailsWithTokenDto createPayment(final CreatePaymentDto createPaymentDto) throws StripeException {
return paymentCreator.createPayment(createPaymentDto);
}

@Override
public String createPaymentMethod(final CreatePaymentMethodDto createPaymentMethodDto) throws StripeException {
return paymentMethodCreator.createPaymentMethod(createPaymentMethodDto);
}

@Override
public PaymentDetailsDto getPaymentDetails(final Long paymentId) throws PaymentNotFoundException {
return paymentRetriever.getPaymentDetails(paymentId);
}

@Override
public void processPaymentEvent(final String paymentIntentPayload, final String stripeSignatureHeader) throws SignatureVerificationException {
paymentEventProcessor.processPaymentEvent(paymentIntentPayload, stripeSignatureHeader);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.zufar.onlinestore.payment.calculator;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

@Slf4j
@Component
public class PaymentPriceCalculator {

private static final Long COIN_TO_CURRENCY_CONVERSION_VALUE = 100L;

public BigDecimal calculatePriceForPayment(Long totalPrice) {
return BigDecimal.valueOf(totalPrice / COIN_TO_CURRENCY_CONVERSION_VALUE);
}

public Long calculatePriceForPaymentIntent(BigDecimal totalPrice) {
return totalPrice.longValue() * COIN_TO_CURRENCY_CONVERSION_VALUE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
/**
* This record responsible for displaying the Stripe Api configuration in Spring as a bean.
* Configuration stores special keys with which Stripe controls data security.
* @param secretKey used to security work with the Stripe Api from the backend side.
*
Sunagatov marked this conversation as resolved.
Show resolved Hide resolved
* @param secretKey used to work with the Stripe Api from the backend side.
* @param publishableKey used to work with the Stripe Api from the frontend side.
*/
* @param publishableKey used to security work with the Stripe Api from the frontend side.
*
Sunagatov marked this conversation as resolved.
Show resolved Hide resolved
* @param webHookSecretKey used to security work with the Stripe Api from the webhooks side.
* */

@ConfigurationProperties(prefix = "stripe")
public record StripeConfiguration(String secretKey,
String publishableKey) {
String publishableKey,
String webHookSecretKey) {

@PostConstruct
private void init() {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.zufar.onlinestore.payment.mapper;
package com.zufar.onlinestore.payment.converter;

import com.zufar.onlinestore.payment.dto.PaymentDetailsDto;
import com.zufar.onlinestore.payment.entity.Payment;
Expand All @@ -7,23 +7,26 @@
@Component
public class PaymentConverter {

public PaymentDetailsDto toDto(Payment entity) {
public PaymentDetailsDto toDto(final Payment entity) {
return PaymentDetailsDto.builder()
.paymentId(entity.getPaymentId())
.paymentIntentId(entity.getPaymentIntentId())
.totalPrice(entity.getItemsTotalPrice())
.currency(entity.getCurrency())
.description(entity.getDescription())
.status(entity.getStatus())
.build();
}

public Payment toEntity(PaymentDetailsDto dto) {
public Payment toEntity(final PaymentDetailsDto dto) {
return Payment.builder()
.paymentId(dto.paymentId())
.paymentIntentId(dto.paymentIntentId())
.itemsTotalPrice(dto.totalPrice())
.currency(dto.currency())
.description(dto.description())
.status(dto.status())
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.zufar.onlinestore.payment.converter;

import com.stripe.model.PaymentIntent;
import com.stripe.param.PaymentIntentCreateParams;
import com.zufar.onlinestore.payment.calculator.PaymentPriceCalculator;
import com.zufar.onlinestore.payment.dto.CreatePaymentDto;
import com.zufar.onlinestore.payment.entity.Payment;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class PaymentIntentConverter {

private final PaymentPriceCalculator paymentPriceCalculator;

public Payment toPayment(final PaymentIntent paymentIntent) {
return Payment.builder()
.itemsTotalPrice(paymentPriceCalculator.calculatePriceForPayment(paymentIntent.getAmount()))
.paymentIntentId(paymentIntent.getId())
.currency(paymentIntent.getCurrency())
.build();
}

public PaymentIntentCreateParams toPaymentIntentParams(final CreatePaymentDto createPaymentDto) {
return PaymentIntentCreateParams.builder()
.setAmount(paymentPriceCalculator.calculatePriceForPaymentIntent(
createPaymentDto.priceDetails().totalPrice()))
.setCurrency( createPaymentDto.priceDetails().currency())
.setPaymentMethod(createPaymentDto.paymentMethodId())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.zufar.onlinestore.payment.converter;

import com.stripe.param.PaymentMethodCreateParams;
import com.zufar.onlinestore.payment.dto.CreatePaymentMethodDto;
import org.springframework.stereotype.Component;

@Component
public class PaymentMethodConverter {

public PaymentMethodCreateParams toPaymentMethodParams(final CreatePaymentMethodDto createPaymentMethodDto) {
PaymentMethodCreateParams.CardDetails cardDetails = PaymentMethodCreateParams.CardDetails.builder()
.setNumber(createPaymentMethodDto.cardNumber())
.setExpMonth(createPaymentMethodDto.expMonth())
.setExpYear(createPaymentMethodDto.expYear())
.setCvc(createPaymentMethodDto.cvc())
.build();

return PaymentMethodCreateParams.builder()
.setCard(cardDetails)
.setType(PaymentMethodCreateParams.Type.CARD)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

public record CreatePaymentDto(

@NotBlank(message = "paymentMethodId is the mandatory attribute")
@NotBlank(message = "PaymentMethodId is the mandatory attribute")
String paymentMethodId,

@NotNull(message = "PriceDetails is the mandatory attribute")
PriceDetailsDto priceDetails) {
PriceDetailsDto priceDetails
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.zufar.onlinestore.payment.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record CreatePaymentMethodDto (

@NotBlank(message = "CardNumber is the mandatory attribute")
String cardNumber,

@NotNull(message = "ExpMonth is the mandatory attribute")
Long expMonth,

@NotNull(message = "ExpYear is the mandatory attribute")
Long expYear,

@NotNull(message = "Cvc is the mandatory attribute")
String cvc
) {
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.zufar.onlinestore.payment.dto;

import com.zufar.onlinestore.payment.enums.PaymentStatus;
import lombok.Builder;

import java.math.BigDecimal;

@Builder
public record PaymentDetailsDto(Long paymentId,
BigDecimal totalPrice,
String currency,
String status,
String description) {
public record PaymentDetailsDto(
Long paymentId,
BigDecimal totalPrice,
String paymentIntentId,
String currency,
PaymentStatus status,
String description
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import lombok.Builder;

@Builder
public record PaymentDetailsWithTokenDto(String paymentToken,
PaymentDetailsDto paymentDetailsDto) {
public record PaymentDetailsWithTokenDto(
String paymentToken,
PaymentDetailsDto paymentDetailsDto
) {
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ public record PriceDetailsDto(

@NotBlank(message = "Currency is mandatory attribute")
@Size(min = 3, max = 3, message = "Currency value must be only 3 characters long")
String currency) {
String currency
) {
}
Loading
Loading