Skip to content

Commit

Permalink
[NT] PayPal integration, Redis/RabbitMQ
Browse files Browse the repository at this point in the history
  • Loading branch information
overpathz committed Apr 14, 2024
1 parent 6b69c66 commit ff0115e
Show file tree
Hide file tree
Showing 31 changed files with 380 additions and 34 deletions.
3 changes: 2 additions & 1 deletion src/main/java/com/archivision/community/bot/State.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public enum State {
DESCRIPTION("description"),
APPROVE("approve"),
MATCH("match"),
PHOTO("photo");
PHOTO("photo"),
SETTINGS("settings");

private final String value;
State(String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ public class RabbitConfig {
public Queue likesQueue() {
return new Queue("like-events", true);
}

@Bean
public Queue paymentQueue() {
return new Queue("payment-events", true);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
package com.archivision.community.controller;

import com.archivision.community.dto.PaymentRequestDto;
import com.archivision.community.event.PaymentEvent;
import com.archivision.community.service.PayPalService;
import com.paypal.api.payments.Links;
import com.paypal.api.payments.Payment;
import com.paypal.base.rest.PayPalRESTException;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;

@RestController
@RequestMapping("/paypal")
@RequiredArgsConstructor
@Slf4j
public class PayPalController {
private final PayPalService payPalService;
private final RabbitTemplate rabbitTemplate;
@Value("${community.payment-queue}")
private String paymentEventQueue;

@PostMapping("/pay")
public ResponseEntity<?> payment(@RequestBody PaymentRequestDto paymentRequest) {
Expand All @@ -26,8 +35,8 @@ public ResponseEntity<?> payment(@RequestBody PaymentRequestDto paymentRequest)
"paypal",
"sale",
"Payment description",
"http://localhost:8080/paypal/cancel",
"http://localhost:8080/paypal/success");
"https://2bf3-194-44-71-193.ngrok-free.app/paypal/cancel",
"https://2bf3-194-44-71-193.ngrok-free.app/paypal/success");

for (Links link : payment.getLinks()) {
if (link.getRel().equals("approval_url")) {
Expand All @@ -47,15 +56,20 @@ public void cancelPay() {
}

@GetMapping("/success")
public void successPay(@RequestParam("paymentId") String paymentId, @RequestParam("PayerID") String payerId) {
public void successPay(@RequestParam("paymentId") String paymentId,
@RequestParam("PayerID") String payerId,
HttpServletResponse response) {
try {
Payment payment = payPalService.executePayment(paymentId, payerId);
if (payment.getState().equals("approved")) {
log.info("Success payment: {}", payment);
// do smth
rabbitTemplate.convertAndSend(paymentEventQueue, new PaymentEvent(paymentId, "success", payment.getTransactions().get(0).getAmount().getTotal()));
response.sendRedirect("https://t.me/CommuLinkBot");
}
} catch (PayPalRESTException e) {
log.error("Payment error: {}", e.getMessage(), e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.archivision.community.dto;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class PaymentRequestDto {
private double total;
private String currency;
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/com/archivision/community/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import static jakarta.persistence.CascadeType.MERGE;
import static jakarta.persistence.CascadeType.PERSIST;
import static jakarta.persistence.FetchType.*;
import static jakarta.persistence.FetchType.LAZY;

@Entity
@Table(name = "users")
Expand All @@ -36,9 +36,10 @@ public class User {
private Gender lookingFor = Gender.ANYONE;
private String description;
private String photoId;

@Enumerated(EnumType.STRING)
private State state = State.START;
@Enumerated(EnumType.STRING)
private Subscription subscription = Subscription.NONE;

@ManyToMany(cascade = {PERSIST, MERGE}, fetch = LAZY)
@JoinTable(
Expand All @@ -48,4 +49,10 @@ public class User {
)
@ToString.Exclude
private Set<Topic> topics = new HashSet<>();

public enum Subscription {
VIP,
PREMIUM,
NONE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.archivision.community.event;

import java.io.Serializable;

public record PaymentEvent(String paymentId, String status, String amount) implements Serializable {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.archivision.community.listener;

import com.archivision.community.entity.User;
import com.archivision.community.event.PaymentEvent;
import com.archivision.community.service.NotificationService;
import com.archivision.community.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
@Slf4j
public class PaymentEventListener {
private final NotificationService notificationService;
private final RedisTemplate<String, String> redisTemplate;
private final UserService userService;

@RabbitListener(queues = "payment-events")
public void handleSuccessPayment(PaymentEvent event) {
String chatId = redisTemplate.opsForValue().getAndDelete(event.paymentId());
userService.changeSubscription(chatId, User.Subscription.VIP);
notificationService.notifyUserAboutSuccessfulPayment(chatId, "Успішний платіж!");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import com.archivision.community.state.OptionalState;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;

public interface MessageSender {
void sendTextMessage(Long userId, String message);
void sendMessage(SendMessage message);
void sendMsgWithMarkup(Long chatId, String userResponseText, ReplyKeyboardMarkup markup);
void sendNextStateData(Long chatId, OptionalState.NextStateData nextState);
void sendMsgWithInline(Long chatId, InlineKeyboardMarkup markup);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

Expand Down Expand Up @@ -56,6 +57,16 @@ public void sendNextStateData(Long chatId, OptionalState.NextStateData nextState
sendMsgWithMarkup(chatId, nextState.responseText(), nextState.markup());
}

@Override
public void sendMsgWithInline(Long chatId, InlineKeyboardMarkup markup) {
SendMessage msg = SendMessage.builder()
.replyMarkup(markup)
.chatId(chatId)
.text("Посилання на оплату")
.build();
sendMessage(msg);
}

@Autowired
public void setCommunityBot(CommunityBot communityBot) {
this.communityBot = communityBot;
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/archivision/community/model/FilterResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.archivision.community.model;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class FilterResult {
private String message;
private boolean processNext;
}
3 changes: 2 additions & 1 deletion src/main/java/com/archivision/community/model/Reply.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ public enum Reply {
GIRL("Дівчина"),
OTHER("Інше"),
LIKE("+"),
DISLIKE("-");
DISLIKE("-"),
SETTINGS("settings");

private final String replyOption;
Reply(String replyOption) {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/archivision/community/model/Subscription.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.archivision.community.model;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Subscription {
private String name;
private int price;
private String description;
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
package com.archivision.community.service;

import com.archivision.community.model.Subscription;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static com.archivision.community.model.Reply.*;
import static com.archivision.community.model.Reply.CHANGE;
import static com.archivision.community.model.Reply.GIRL;
import static com.archivision.community.model.Reply.MAN;
import static com.archivision.community.model.Reply.OTHER;
import static com.archivision.community.model.Reply.SKIP;
import static com.archivision.community.model.Reply.YES;

@Component
@RequiredArgsConstructor
public class KeyboardBuilderService {
private final SubscriptionService subscriptionService;

public ReplyKeyboardMarkup skipButton() {
return ReplyKeyboardMarkup.builder()
Expand Down Expand Up @@ -62,6 +74,16 @@ public ReplyKeyboardMarkup multiButtons(boolean oneTime, String ... buttonTexts)
}

public ReplyKeyboardMarkup matchButtons() {
return multiButtons(false, "+", "-");
return multiButtons(false, "+", "-", "settings");
}

public ReplyKeyboardMarkup subscriptions() {
List<String> strings = subscriptionService.getAvailableSubscriptionTypes().stream().map(Subscription::getName).toList();
return multiButtons(false, strings.toArray(new String[0]));
}

public InlineKeyboardMarkup inlineBtn(String subscription, String paymentUrl) {
InlineKeyboardButton build = InlineKeyboardButton.builder().text(subscription).url(paymentUrl).build();
return new InlineKeyboardMarkup(List.of(List.of(build)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ public void notifyUsersAboutMatch(Long likerId, Long likedId) {
messageSender.sendTextMessage(likerId, "У вас симпатія! @"+likedUser.getUsername());
profileSender.showUserProfileTo(likedId, likerId);
}

public void notifyUserAboutSuccessfulPayment(String chatId, String message) {
messageSender.sendTextMessage(Long.valueOf(chatId), message);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package com.archivision.community.service;

import com.paypal.api.payments.*;
import com.archivision.community.dto.PaymentRequestDto;
import com.paypal.api.payments.Amount;
import com.paypal.api.payments.Links;
import com.paypal.api.payments.Payer;
import com.paypal.api.payments.Payment;
import com.paypal.api.payments.PaymentExecution;
import com.paypal.api.payments.RedirectUrls;
import com.paypal.api.payments.Transaction;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
Expand Down Expand Up @@ -60,5 +68,16 @@ public Payment executePayment(String paymentId, String payerId) throws PayPalRES

return payment.execute(apiContext, paymentExecution);
}

public Payment createPaymentUrl(PaymentRequestDto paymentRequest) throws PayPalRESTException {
return createPayment(
paymentRequest.getTotal(),
paymentRequest.getCurrency(),
"paypal",
"sale",
"Payment description",
"http://localhost:8080/paypal/cancel",
"http://localhost:8080/paypal/success");
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.archivision.community.service;

import com.archivision.community.messagesender.MessageSender;
import com.archivision.community.model.FilterResult;
import com.archivision.community.model.Subscription;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.telegram.telegrambots.meta.api.objects.Message;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ServiceCommandChecker {
private final Set<String> commands = new HashSet<>();
private final MessageSender messageSender;
private final SubscriptionService subscriptionService;

@PostConstruct
public void init() {
commands.add("/subscriptions");
}

public FilterResult filter(Message message) {
Long chatId = message.getChatId();
String text = message.getText();
if (commands.contains(text)) {
List<Subscription> subscriptionTypes = subscriptionService.getAvailableSubscriptionTypes();
messageSender.sendTextMessage(chatId, formMessage(subscriptionTypes));
}
return FilterResult.builder().processNext(true).message("Success").build();
}

private String formMessage(List<Subscription> subscriptionTypes) {
return subscriptionTypes.stream()
.map(Subscription::getName)
.collect(Collectors.joining(", "));
}
}
Loading

0 comments on commit ff0115e

Please sign in to comment.