diff --git a/payment-service-app/docker-compose.yml b/docker-compose.yml
similarity index 71%
rename from payment-service-app/docker-compose.yml
rename to docker-compose.yml
index 0b616b6..8484a3a 100644
--- a/payment-service-app/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,7 +3,7 @@ services:
payment-service-app:
image: payment-service-app
build:
- context: .
+ context: payment-service-app
dockerfile: Dockerfile
ports:
- "8082:8082"
@@ -17,6 +17,18 @@ services:
KEYCLOAK_HOST: keycloak:8080
restart: unless-stopped
+ xpayment-adapter-app:
+ image: xpayment-adapter-app
+ build:
+ context: xpayment-adapter-app
+ dockerfile: xpayment-adapter-app/Dockerfile
+ ports:
+ - "8088:8080"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ restart: unless-stopped
+
postgres:
image: postgres:16
container_name: postgres-db
@@ -57,11 +69,11 @@ services:
- --import-realm
environment:
KEYCLOAK_ADMIN: admin
- KEYCLOAK_ADMIN_PASSWORD: admin
+ KEYCLOAK_ADMIN_PASSWORD: password
ports:
- "8085:8080"
volumes:
- - ./src/main/resources/keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json
+ - ./payment-service-app/src/main/resources/keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json
restart: unless-stopped
kafka:
@@ -98,6 +110,25 @@ services:
depends_on:
- kafka
+ rabbitmq:
+ image: rabbitmq:4.2.3-management-alpine
+ container_name: rabbitmq
+ ports:
+ - "5672:5672" # для подключения клиентов к брокеру
+ - "15672:15672" # для доступа к админке
+ environment:
+ RABBITMQ_DEFAULT_USER: admin
+ RABBITMQ_DEFAULT_PASS: admin
+ volumes:
+ - rabbitmq_data:/var/lib/rabbitmq
+ # делаем доступным в контейнере файл плагина
+ - ./rabbitmq_delayed_message_exchange-4.2.0.ez:/opt/rabbitmq/plugins/rabbitmq_delayed_message_exchange-4.2.0.ez
+ command: >
+ bash -c "rabbitmq-plugins enable --offline rabbitmq_delayed_message_exchange && rabbitmq-server"
+ restart: unless-stopped
+
volumes:
+ postgres_data:
pgdata:
kafka_data:
+ rabbitmq_data:
\ No newline at end of file
diff --git a/k8s/k8s-nginx-values.yml b/k8s/k8s-nginx-values.yml
new file mode 100644
index 0000000..cfb085d
--- /dev/null
+++ b/k8s/k8s-nginx-values.yml
@@ -0,0 +1,38 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nginx-deployment # имя Deployment
+spec:
+ replicas: 3 # количество реплик (Pod’ов)
+ selector: # селектор для идентификации Pod’ов
+ matchLabels:
+ app: nginx # выбираем Pod’ы с меткой app=nginx
+ template: # шаблон для создания Pod’ов
+ metadata:
+ labels:
+ app: nginx # метка, чтобы Pod соответствовал селектору
+ spec: # спецификация Pod’а
+ containers: # список контейнеров внутри Pod’а
+ - name: nginx # имя контейнера
+ image: nginx:1.25 # образ контейнера (nginx версии 1.25)
+ ports:
+ - containerPort: 80 # порт, который будет слушать контейнер (внутри Pod)
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: nginx-service
+spec:
+ selector:
+ app: nginx # выбираем Pod’ы с меткой app=nginx
+ ports: # список портов
+ - protocol: TCP # протокол (TCP по умолчанию)
+ port: 80 # порт Service
+ targetPort: 80 # порт внутри Pod’а
+ type: ClusterIP # тип Service:
+
+ # ClusterIP (по умолчанию) -- доступ только изнутри кластера
+
+ # NodePort -- открывает порт на нодах для внешнего доступа
+
+ # LoadBalancer -- для облачных провайдеров, создаёт балансировщик
\ No newline at end of file
diff --git a/k8s/kafka-values.yml b/k8s/kafka-values.yml
new file mode 100644
index 0000000..77ec8e3
--- /dev/null
+++ b/k8s/kafka-values.yml
@@ -0,0 +1,12 @@
+kraft:
+ enabled: true # режим KRaft (по умолчанию true в новых релизах)
+
+controller:
+ replicaCount: 1 # один контроллер для стенда
+
+broker:
+ replicaCount: 1 # один брокер
+
+listeners:
+ client:
+ protocol: PLAINTEXT # для простоты: без TLS/SASL на учебном стенде
\ No newline at end of file
diff --git a/k8s/keycloak-values.yml b/k8s/keycloak-values.yml
new file mode 100644
index 0000000..db30dca
--- /dev/null
+++ b/k8s/keycloak-values.yml
@@ -0,0 +1,30 @@
+image:
+ registry: quay.io
+ repository: keycloak/keycloak
+ tag: 24.0.3
+
+extraEnvVars:
+ - name: KEYCLOAK_ADMIN
+ value: admin
+ - name: KEYCLOAK_ADMIN_PASSWORD
+ value: admin
+
+command:
+ - start-dev
+ - --http-port=8080
+ - --import-realm
+ - --log-level=DEBUG
+
+service:
+ ports:
+ http: 8085
+
+extraVolumes:
+ - name: realm-import
+ configMap:
+ name: keycloak-realm
+
+extraVolumeMounts:
+ - name: realm-import
+ mountPath: /opt/keycloak/data/import/realm-export.json
+ subPath: realm-export.json
\ No newline at end of file
diff --git a/k8s/payment-service-values.yml b/k8s/payment-service-values.yml
new file mode 100644
index 0000000..df8710e
--- /dev/null
+++ b/k8s/payment-service-values.yml
@@ -0,0 +1,48 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: payment-service-app
+ labels:
+ app: payment-service-app
+spec:
+ replicas: 1 # одно приложение (можно увеличить)
+ selector:
+ matchLabels:
+ app: payment-service-app
+ template:
+ metadata:
+ labels:
+ app: payment-service-app
+ spec:
+ containers:
+ - name: payment-service-app
+ image: payment-service:latest # докер образ
+ ports:
+ - containerPort: 8080 # порт внутри Pod’а
+ env:
+ - name: SPRING_DATASOURCE_URL
+ value: jdbc:postgresql://my-pg-postgresql.db.svc.cluster.local:5432/payment-db
+ - name: SPRING_DATASOURCE_USERNAME
+ value: postgres
+ - name: SPRING_DATASOURCE_PASSWORD
+ value: secret
+ - name: SPRING_KAFKA_BOOTSTRAP_SERVERS
+ value: my-kafka.kafka.svc.cluster.local:9092
+ - name: SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI
+ value: http://keycloak.keycloak.svc.cluster.local:8080/realms/iprody-lms
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: payment-service-app
+ labels:
+ app: payment-service-app
+spec:
+ selector:
+ app: payment-service-app
+ ports:
+ - protocol: TCP
+ port: 8080 # порт внутри кластера
+ targetPort: 8080 # порт контейнера
+ nodePort: 30080 # внешний порт (только если type=NodePort)
+ type: NodePort # вариант для доступа снаружи (Minikube, DockerDesktop)
diff --git a/k8s/xpayment-adapter-values.yml b/k8s/xpayment-adapter-values.yml
new file mode 100644
index 0000000..9b27872
--- /dev/null
+++ b/k8s/xpayment-adapter-values.yml
@@ -0,0 +1,43 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: xpayment-adapter-app
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: xpayment-adapter-app
+ template:
+ metadata:
+ labels:
+ app: xpayment-adapter-app
+ spec:
+ containers:
+ - name: xpayment-adapter-app
+ image: xpayment-adapter-app:latest
+ ports:
+ - containerPort: 8080
+ env:
+ - name: SPRING_KAFKA_BOOTSTRAP_SERVERS
+ value: "my-kafka.kafka.svc.cluster.local:9093"
+ - name: SPRING_KAFKA_CONSUMER_GROUP_ID
+ value: "xpayment-adapter-result-consumers"
+ - name: APP_KAFKA_TOPIC_REQUEST
+ value: "xpayment-adapter.requests"
+ - name: APP_KAFKA_TOPIC_RESPONSE
+ value: "xpayment-adapter.responses"
+ - name: RABBITMQ_URL
+ value: "amqp://rabbitmq.rabbitmq.svc.cluster.local:5672"
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: xpayment-adapter-app
+spec:
+ selector:
+ app: xpayment-adapter-app
+ ports:
+ - protocol: TCP
+ port: 8080
+ targetPort: 8080
+ type: ClusterIP
\ No newline at end of file
diff --git a/payment-service-api/pom.xml b/payment-service-api/pom.xml
index 6f44ea0..4224b92 100644
--- a/payment-service-api/pom.xml
+++ b/payment-service-api/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.iprody
- PaymentService
+ Payment-Service-Main
1.0-SNAPSHOT
../pom.xml
diff --git a/payment-service-app/pom.xml b/payment-service-app/pom.xml
index 392a548..effc67b 100644
--- a/payment-service-app/pom.xml
+++ b/payment-service-app/pom.xml
@@ -4,14 +4,14 @@
4.0.0
com.iprody
- PaymentService
+ Payment-Service-Main
1.0-SNAPSHOT
../pom.xml
payment-service-app
0.0.1-SNAPSHOT
- Payment Service App
+ payment-service-app
Payment Service application
jar
@@ -127,7 +127,6 @@
mapstruct-processor
${mapstruct.version}
-
diff --git a/payment-service-app/src/main/java/com/iprody/controller/PaymentController.java b/payment-service-app/src/main/java/com/iprody/controller/PaymentController.java
index e12fed7..bdf3168 100644
--- a/payment-service-app/src/main/java/com/iprody/controller/PaymentController.java
+++ b/payment-service-app/src/main/java/com/iprody/controller/PaymentController.java
@@ -13,9 +13,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
-import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
-import java.net.URI;
import java.util.List;
import java.util.UUID;
@@ -58,12 +56,8 @@ public ResponseEntity> fetchAll() {
public ResponseEntity addPayment(@RequestBody PaymentDto paymentDto) {
log.info("POST: save one payment: \n\n {} \n", paymentDto.toString());
final PaymentDto savedPayment = this.paymentService.createAsync(paymentDto);
- final URI location = ServletUriComponentsBuilder.fromCurrentRequest()
- .path("/{id}")
- .buildAndExpand(savedPayment.getGuid())
- .toUri();
- log.debug("Payment saved: {}", savedPayment);
- return ResponseEntity.created(location).body(savedPayment);
+ log.debug("Payment saved in status: {}", savedPayment.getStatus());
+ return ResponseEntity.status(HttpStatus.CREATED).body(savedPayment);
}
@GetMapping(path = "/{id}")
diff --git a/payment-service-app/src/main/resources/application-db.yaml b/payment-service-app/src/main/resources/application-db.yaml
index 32db3a1..e916081 100644
--- a/payment-service-app/src/main/resources/application-db.yaml
+++ b/payment-service-app/src/main/resources/application-db.yaml
@@ -1,8 +1,8 @@
spring:
datasource:
- url: jdbc:postgresql://localhost:5432/payment-db # URL подключения к БД
- #url: jdbc:postgresql://postgres-db:5432/payment-db
+ #url: jdbc:postgresql://localhost:5432/payment-db # URL подключения к БД
+ url: jdbc:postgresql://postgres-db:5432/payment-db
username: user # Имя пользователя
password: secret # Пароль
driver-class-name: org.postgresql.Driver # класс jdbc драйвера
diff --git a/payment-service-app/src/main/resources/application.yaml b/payment-service-app/src/main/resources/application.yaml
index 11ad695..9f987ce 100644
--- a/payment-service-app/src/main/resources/application.yaml
+++ b/payment-service-app/src/main/resources/application.yaml
@@ -1,5 +1,7 @@
server:
port: 8082
+ error:
+ include-message: always
spring:
@@ -13,14 +15,14 @@ spring:
jwt:
issuer-uri: http://localhost:8085/realms/iprody-lms
-# logging:
-# level:
-# root: INFO
-# org.hibernate: ERROR
-# # com.iprody.payment: ERROR
-# org.springframework.web: WARN
-# org.springframework.data: WARN
-# org.springframework.security: WARN
+ logging:
+ level:
+ root: INFO
+ org.hibernate: ERROR
+ com.iprody: INFO
+ org.springframework.web: WARN
+ org.springframework.data: WARN
+ org.springframework.security: WARN
management:
endpoints:
diff --git a/pom.xml b/pom.xml
index 5847f9c..600bd85 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
com.iprody
- PaymentService
+ Payment-Service-Main
1.0-SNAPSHOT
pom
@@ -36,6 +36,7 @@
3.6.0
1.6.3
1.21.4
+ 3.5.8
0.0.1-SNAPSHOT
diff --git a/rabbitmq_delayed_message_exchange-4.2.0.ez b/rabbitmq_delayed_message_exchange-4.2.0.ez
new file mode 100644
index 0000000..8ba5d78
Binary files /dev/null and b/rabbitmq_delayed_message_exchange-4.2.0.ez differ
diff --git a/simple-http-server/pom.xml b/simple-http-server/pom.xml
index dcc36f7..cbf53b2 100644
--- a/simple-http-server/pom.xml
+++ b/simple-http-server/pom.xml
@@ -5,7 +5,7 @@
4.0.0
com.iprody
- PaymentService
+ Payment-Service-Main
1.0-SNAPSHOT
diff --git a/xpayment-adapter-app/pom.xml b/xpayment-adapter-app/pom.xml
index 1e05034..24d39ed 100644
--- a/xpayment-adapter-app/pom.xml
+++ b/xpayment-adapter-app/pom.xml
@@ -4,15 +4,17 @@
4.0.0
com.iprody
- PaymentService
+ Payment-Service-Main
1.0-SNAPSHOT
../pom.xml
- xpayment-service-adapter-app
+ xpayment-adapter-app
0.0.1-SNAPSHOT
- payment-service-adapter-app
- xpayment-adapter-app
+ payment-adapter-app
+ XPayment Service Adapter App
+
+ jar
@@ -44,6 +46,11 @@
org.springframework.kafka
spring-kafka
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+ 3.5.8
+
com.iprody
payment-service-api
@@ -55,6 +62,12 @@
${lombok.version}
true
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ 2.21.0
+ compile
+
org.openapitools
jackson-databind-nullable
@@ -71,6 +84,26 @@
${mapstruct.version}
provided
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.10.1
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.10.1
+ test
+
+
+ junit
+ junit
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
@@ -94,6 +127,20 @@
+
+ maven-assembly-plugin
+
+
+
+ com.iprody.adapter.XPaymentAdapterAppApplication
+
+
+
+ jar-with-dependencies
+
+ false
+
+
org.springframework.boot
spring-boot-maven-plugin
@@ -106,6 +153,13 @@
+
+
+
+ repackage
+
+
+
org.openapitools
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/api/XPaymentProviderGatewayImpl.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/api/XPaymentProviderGatewayImpl.java
index 652fa18..c51c506 100644
--- a/xpayment-adapter-app/src/main/java/com/iprody/adapter/api/XPaymentProviderGatewayImpl.java
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/api/XPaymentProviderGatewayImpl.java
@@ -2,8 +2,10 @@
import com.iprody.adapter.dto.CreateChargeRequestDto;
import com.iprody.adapter.dto.CreateChargeResponseDto;
+import com.iprody.adapter.mapper.XPaymentConverter;
import com.iprody.adapter.mapper.XPaymentMapper;
import com.iprody.xpayment.app.api.client.DefaultApi;
+import com.iprody.xpayment.app.api.model.ChargeResponse;
import com.iprody.xpayment.app.api.model.CreateChargeRequest;
import org.springframework.stereotype.Service;
@@ -16,19 +18,21 @@
class XPaymentProviderGatewayImpl implements XPaymentProviderGateway {
private final DefaultApi defaultApi;
- private final XPaymentMapper mapper;
+ private final XPaymentConverter converter;
- public XPaymentProviderGatewayImpl(DefaultApi defaultApi, XPaymentMapper mapper) {
+ public XPaymentProviderGatewayImpl(DefaultApi defaultApi,
+ XPaymentConverter converter) {
this.defaultApi = defaultApi;
- this.mapper = mapper;
+ this.converter = converter;
}
@Override
public CreateChargeResponseDto createCharge(CreateChargeRequestDto dto)
throws RestClientException {
try {
- CreateChargeRequest chargeRequest = mapper.toCreateChargeRequest(dto);
- return mapper.toCreateChargeResponseDto(defaultApi.createCharge(chargeRequest));
+ CreateChargeRequest chargeRequest = converter.toCreateChargeRequest(dto);
+ ChargeResponse response = defaultApi.createCharge(chargeRequest);
+ return converter.toCreateChargeResponseDto(response);
} catch (Exception e) {
throw toRestClientException("POST /charges failed", e);
}
@@ -37,7 +41,7 @@ public CreateChargeResponseDto createCharge(CreateChargeRequestDto dto)
@Override
public CreateChargeResponseDto retrieveCharge(UUID id) throws RestClientException {
try {
- return mapper.toCreateChargeResponseDto(defaultApi.retrieveCharge(id));
+ return converter.toCreateChargeResponseDto(defaultApi.retrieveCharge(id));
} catch (Exception e) {
throw toRestClientException("GET /charges/{id} failed (id=" + id + ")", e);
}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/async/handler/RequestMessageHandler.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/async/handler/RequestMessageHandler.java
index 43be2a4..474796c 100644
--- a/xpayment-adapter-app/src/main/java/com/iprody/adapter/async/handler/RequestMessageHandler.java
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/async/handler/RequestMessageHandler.java
@@ -1,13 +1,15 @@
package com.iprody.adapter.async.handler;
-import com.iprody.adapter.api.XPaymentProviderGateway;
+import com.iprody.adapter.dto.CreateChargeRequestDto;
+import com.iprody.adapter.dto.CreateChargeResponseDto;
+import com.iprody.adapter.mapper.XPaymentMapper;
import com.iprody.api.AsyncSender;
import com.iprody.api.XPaymentAdapterStatus;
import com.iprody.api.dto.XPaymentAdapterRequestMessage;
import com.iprody.api.dto.XPaymentAdapterResponseMessage;
+import com.iprody.adapter.api.XPaymentProviderGateway;
+import com.iprody.adapter.checkstate.PaymentStateCheckRegistrar;
-import com.iprody.xpayment.app.api.model.ChargeResponse;
-import com.iprody.xpayment.app.api.model.CreateChargeRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -16,39 +18,46 @@
import java.time.OffsetDateTime;
-@Slf4j
@Component
-public class RequestMessageHandler implements MessageHandler {
+@Slf4j
+public class RequestMessageHandler implements
+ MessageHandler {
private final XPaymentProviderGateway xPaymentProviderGateway;
private final AsyncSender asyncSender;
+ private final PaymentStateCheckRegistrar paymentStateCheckRegistrar;
+ private final XPaymentMapper mapper;
@Autowired
public RequestMessageHandler(
XPaymentProviderGateway xPaymentProviderGateway,
- AsyncSender asyncSender) {
+ AsyncSender asyncSender,
+ PaymentStateCheckRegistrar paymentStateCheckRegistrar,
+ XPaymentMapper mapper) {
this.xPaymentProviderGateway = xPaymentProviderGateway;
this.asyncSender = asyncSender;
+ this.paymentStateCheckRegistrar = paymentStateCheckRegistrar;
+ this.mapper = mapper;
}
@Override
public void handle(XPaymentAdapterRequestMessage message) {
-
log.info("Payment request received paymentGuid - {}, amount - {}, currency - {}",
+ message.getPaymentGuid(),
+ message.getAmount(),
+ message.getCurrency());
- message.getPaymentGuid(), message.getAmount(), message.getCurrency());
-
- CreateChargeRequest createChargeRequest = new CreateChargeRequest();
- createChargeRequest.setAmount(message.getAmount());
- createChargeRequest.setCurrency(message.getCurrency());
- createChargeRequest.setOrder(message.getPaymentGuid());
+ CreateChargeRequestDto dto = new CreateChargeRequestDto();
+ dto.setAmount(message.getAmount());
+ dto.setCurrency(message.getCurrency());
+ dto.setOrder(message.getPaymentGuid());
try {
- ChargeResponse chargeResponse =
- xPaymentProviderGateway.createCharge(createChargeRequest);
+ CreateChargeResponseDto chargeResponse =
+ xPaymentProviderGateway.createCharge(dto);
log.info("Payment request with paymentGuid - {} is sent for payment processing. " +
- "Current status - ", chargeResponse.getStatus());
+ "Current status {}", message.getPaymentGuid(), chargeResponse.getStatus());
XPaymentAdapterResponseMessage responseMessage = new XPaymentAdapterResponseMessage();
responseMessage.setPaymentGuid(chargeResponse.getOrder());
@@ -56,15 +65,18 @@ public void handle(XPaymentAdapterRequestMessage message) {
responseMessage.setAmount(chargeResponse.getAmount());
responseMessage.setCurrency(chargeResponse.getCurrency());
responseMessage.setStatus(XPaymentAdapterStatus.valueOf(chargeResponse.getStatus()));
-
responseMessage.setOccurredAt(OffsetDateTime.now());
- asyncSender.send(responseMessage);
+ asyncSender.send(responseMessage);
+ paymentStateCheckRegistrar.register(
+ chargeResponse.getId(),
+ chargeResponse.getOrder(),
+ chargeResponse.getAmount(),
+ chargeResponse.getCurrency()
+ );
} catch (RestClientException ex) {
-
log.error("Error in time of sending payment request with paymentGuid - {}",
message.getPaymentGuid(), ex);
-
XPaymentAdapterResponseMessage responseMessage = new XPaymentAdapterResponseMessage();
responseMessage.setPaymentGuid(message.getPaymentGuid());
responseMessage.setAmount(message.getAmount());
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/async/kafka/KafkaXPaymentAdapterMessageProducer.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/async/kafka/KafkaXPaymentAdapterMessageProducer.java
index 2258daa..c7addde 100644
--- a/xpayment-adapter-app/src/main/java/com/iprody/adapter/async/kafka/KafkaXPaymentAdapterMessageProducer.java
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/async/kafka/KafkaXPaymentAdapterMessageProducer.java
@@ -20,9 +20,10 @@ public class KafkaXPaymentAdapterMessageProducer implements AsyncSender topic={}",
+ log.info("Sending XPayment Adapter response: guid={}, amount={}, status={}, currency={} -> topic={}",
msg.getPaymentGuid(),
msg.getAmount(),
+ msg.getStatus(),
msg.getCurrency(),
kafkaProperties.getResponseTopic());
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentCheckStateMessage.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentCheckStateMessage.java
new file mode 100644
index 0000000..1de7a4a
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentCheckStateMessage.java
@@ -0,0 +1,58 @@
+package com.iprody.adapter.checkstate;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+
+public class PaymentCheckStateMessage {
+
+ private UUID chargeGuid;
+ private UUID paymentGuid;
+
+ private BigDecimal amount;
+ private String currency;
+
+ public PaymentCheckStateMessage(
+ UUID chargeGuid,
+ UUID paymentGuid,
+ BigDecimal amount,
+ String currency) {
+ this.chargeGuid = chargeGuid;
+ this.paymentGuid = paymentGuid;
+ this.amount = amount;
+ this.currency = currency;
+ }
+
+ public UUID getChargeGuid() {
+ return chargeGuid;
+ }
+
+ public void setChargeGuid(UUID chargeGuid) {
+ this.chargeGuid = chargeGuid;
+ }
+
+ public UUID getPaymentGuid() {
+ return paymentGuid;
+ }
+
+ public void setPaymentGuid(UUID paymentGuid) {
+ this.paymentGuid = paymentGuid;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public String getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(String currency) {
+ this.currency = currency;
+ }
+
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckListener.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckListener.java
new file mode 100644
index 0000000..b535394
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckListener.java
@@ -0,0 +1,87 @@
+package com.iprody.adapter.checkstate;
+
+import com.iprody.adapter.checkstate.handler.PaymentStatusCheckHandler;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.core.MessageProperties;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+
+@Component
+public class PaymentStateCheckListener {
+
+ private final RabbitTemplate rabbitTemplate;
+ private final String exchangeName;
+ private final String routingKey;
+ private final String dlxExchangeName;
+ private final String dlxRoutingKey;
+ private final PaymentStatusCheckHandler paymentStatusCheckHandler;
+
+ @Value("${spring.app.rabbitmq.max-retries:60}")
+ private int maxRetries;
+
+ @Value("${spring.app.rabbitmq.interval-ms:60000}")
+ private long intervalMs;
+
+ @Autowired
+ public PaymentStateCheckListener(
+ RabbitTemplate rabbitTemplate,
+ @Value("${spring.app.rabbitmq.delayed-exchange-name}") String exchangeName,
+ @Value("${spring.app.rabbitmq.queue-name}") String routingKey,
+ @Value("${spring.app.rabbitmq.dlx-exchange-name}") String dlxExchangeName,
+ @Value("${spring.app.rabbitmq.dlx-routing-key}") String dlxRoutingKey,
+ PaymentStatusCheckHandler paymentStatusCheckHandler) {
+ this.rabbitTemplate = rabbitTemplate;
+ this.exchangeName = exchangeName;
+ this.routingKey = routingKey;
+ this.dlxExchangeName = dlxExchangeName;
+ this.dlxRoutingKey = dlxRoutingKey;
+ this.paymentStatusCheckHandler = paymentStatusCheckHandler;
+ }
+
+ @RabbitListener(queues = "${spring.app.rabbitmq.queue-name}")
+ public void handle(PaymentCheckStateMessage message, Message raw) {
+ MessageProperties props = raw.getMessageProperties();
+ int retryCount = (int)props.getHeaders().getOrDefault("x-retry-count", 0);
+ boolean paid = paymentStatusCheckHandler.handle(message.getChargeGuid());
+ if (paid) {
+ return;
+ }
+ if (retryCount < maxRetries) {
+ // Планируем следующую проверку
+ PaymentCheckStateMessage newMessage = new PaymentCheckStateMessage(
+ message.getChargeGuid(),
+ message.getPaymentGuid(),
+ message.getAmount(),
+ message.getCurrency()
+ );
+ rabbitTemplate.convertAndSend(
+ exchangeName,
+ routingKey,
+ newMessage,
+ m -> {
+ m.getMessageProperties().setHeader("x-delay", intervalMs);
+ m.getMessageProperties().setHeader("x-retry-count", retryCount + 1);
+ return m;
+ }
+ );
+ } else {
+ // Исчерпали попытки -- кладём сообщение в DLX
+ rabbitTemplate.convertAndSend(
+ dlxExchangeName,
+ dlxRoutingKey,
+ message,
+ m -> {
+ m.getMessageProperties().setHeader("x-retry-count", retryCount);
+ m.getMessageProperties().setHeader("x-final-status", "TIMEOUT");
+ m.getMessageProperties().setHeader("x-original-queue", props.getConsumerQueue());
+ return m;
+ }
+ );
+ }
+ }
+
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckRegistrar.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckRegistrar.java
new file mode 100644
index 0000000..148ef5e
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckRegistrar.java
@@ -0,0 +1,12 @@
+package com.iprody.adapter.checkstate;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+public interface PaymentStateCheckRegistrar {
+ void register(
+ UUID chargeGuid,
+ UUID paymentGuid,
+ BigDecimal amount,
+ String currency);
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckRegistrarImpl.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckRegistrarImpl.java
new file mode 100644
index 0000000..432813e
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/PaymentStateCheckRegistrarImpl.java
@@ -0,0 +1,64 @@
+package com.iprody.adapter.checkstate;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import java.math.BigDecimal;
+import java.util.UUID;
+
+
+@Service
+@Slf4j
+public class PaymentStateCheckRegistrarImpl implements PaymentStateCheckRegistrar {
+
+ private final RabbitTemplate rabbitTemplate;
+ private final String exchangeName;
+ private final String routingKey;
+
+ @Value("${spring.app.rabbitmq.max-retries:60}")
+ private int maxRetries;
+
+ @Value("${spring.app.rabbitmq.interval-ms:60000}")
+ private long intervalMs;
+
+ @Autowired
+ public PaymentStateCheckRegistrarImpl(
+ RabbitTemplate rabbitTemplate,
+ @Value("${spring.app.rabbitmq.delayed-exchange-name}") String exchangeName,
+ @Value("${spring.app.rabbitmq.queue-name}") String routingKey) {
+ this.rabbitTemplate = rabbitTemplate;
+ this.exchangeName = exchangeName;
+ this.routingKey = routingKey;
+ }
+
+ @Override
+ public void register(
+ UUID chargeGuid,
+ UUID paymentGuid,
+ BigDecimal amount,
+ String currency) {
+
+ log.info("Registering message with RabbitMQ, id={}", paymentGuid);
+
+ PaymentCheckStateMessage message = new PaymentCheckStateMessage(
+ chargeGuid,
+ paymentGuid,
+ amount,
+ currency
+ );
+
+ rabbitTemplate.convertAndSend(
+ exchangeName,
+ routingKey,
+ message,
+ m -> {
+ m.getMessageProperties().setHeader("x-delay", intervalMs);
+ m.getMessageProperties().setHeader("x-retry-count", 1);
+ return m;
+ }
+ );
+ }
+
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/RabbitMqDlxConfig.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/RabbitMqDlxConfig.java
new file mode 100644
index 0000000..8fbbe29
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/RabbitMqDlxConfig.java
@@ -0,0 +1,27 @@
+package com.iprody.adapter.checkstate;
+
+import org.springframework.amqp.core.*;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RabbitMqDlxConfig {
+
+ @Bean
+ DirectExchange deadLetterExchange() {
+ return new DirectExchange("payments.dlx");
+ }
+
+ @Bean
+ Queue deadLetterQueue() {
+ return QueueBuilder.durable("payments.dead.queue").build();
+ }
+
+ @Bean
+ Binding dlxBinding() {
+ return BindingBuilder.bind(deadLetterQueue())
+ .to(deadLetterExchange())
+ .with("payments.dead");
+ }
+
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/RabbitMqPaymentRetryConfig.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/RabbitMqPaymentRetryConfig.java
new file mode 100644
index 0000000..59bebd8
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/RabbitMqPaymentRetryConfig.java
@@ -0,0 +1,54 @@
+package com.iprody.adapter.checkstate;
+
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.CustomExchange;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.core.QueueBuilder;
+
+import java.util.Map;
+
+
+@Configuration
+public class RabbitMqPaymentRetryConfig {
+
+ @Value("${spring.app.rabbitmq.queue-name}")
+ private String queueName;
+
+ @Value("${spring.app.rabbitmq.exchange-name}")
+ private String delayedExchangeName;
+
+ @Bean
+ public Queue xpaymentQueue() {
+
+ return QueueBuilder.durable(queueName)
+ .withArgument("x-dead-letter-exchange", "payments.dlx")
+ .withArgument("x-dead-letter-routing-key", "payments.dead")
+ .build();
+ }
+
+ @Bean
+ public CustomExchange delayedExchange() {
+ return new CustomExchange(delayedExchangeName,
+ "x-delayed-message",
+ true,
+ false,
+ Map.of("x-delayed-type", "direct"));
+ }
+
+ @Bean
+ public Binding queueBinding(Queue xpaymentQueue, CustomExchange delayedExchange) {
+ return BindingBuilder.bind(xpaymentQueue).to(delayedExchange).with(queueName).noargs();
+ }
+
+ @Bean
+ public MessageConverter messageConverter() {
+ return new Jackson2JsonMessageConverter();
+ }
+
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/handler/PaymentStatusCheckHandler.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/handler/PaymentStatusCheckHandler.java
new file mode 100644
index 0000000..848859b
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/handler/PaymentStatusCheckHandler.java
@@ -0,0 +1,17 @@
+package com.iprody.adapter.checkstate.handler;
+
+import java.util.UUID;
+
+public interface PaymentStatusCheckHandler {
+ /**
+ * Проверяет статус платежа в X Payment Provider по заданному
+ идентификатору. Если статус нетерминальный, то метод возвращает
+ false. В противном случае, отправляет асинхронное уведомление
+ Payment Service o измененном статус платежа и возвращает true.
+ *
+ * @param paymentGuid UUID платежа для проверки
+ * @return true, если платеж завершен и новые проверки статуса
+ не требуются, иначе false
+ */
+ boolean handle(UUID paymentGuid);
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/handler/PaymentStatusCheckerHandlerImpl.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/handler/PaymentStatusCheckerHandlerImpl.java
new file mode 100644
index 0000000..e731363
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/checkstate/handler/PaymentStatusCheckerHandlerImpl.java
@@ -0,0 +1,37 @@
+package com.iprody.adapter.checkstate.handler;
+
+import com.iprody.adapter.api.XPaymentProviderGateway;
+import com.iprody.adapter.dto.CreateChargeResponseDto;
+import com.iprody.adapter.mapper.XPaymentMapper;
+import com.iprody.api.AsyncSender;
+import com.iprody.api.dto.XPaymentAdapterResponseMessage;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+import static com.iprody.api.XPaymentAdapterStatus.CANCELED;
+import static com.iprody.api.XPaymentAdapterStatus.SUCCEEDED;
+
+
+@Service
+@RequiredArgsConstructor
+public class PaymentStatusCheckerHandlerImpl implements PaymentStatusCheckHandler {
+
+ private final AsyncSender asyncSender;
+ private final XPaymentProviderGateway gateway;
+ private final XPaymentMapper mapper;
+
+ @Override
+ public boolean handle(UUID id) {
+ CreateChargeResponseDto dto = gateway.retrieveCharge(id);
+
+ if (dto != null && (dto.getStatus().equals(SUCCEEDED.name()) || dto.getStatus().equals(CANCELED.name()))) {
+ asyncSender.send(mapper.responseDtoToKafkaMessage(dto));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/config/KafkaProperties.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/config/KafkaProperties.java
index f2c6aa0..c621bd3 100644
--- a/xpayment-adapter-app/src/main/java/com/iprody/adapter/config/KafkaProperties.java
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/config/KafkaProperties.java
@@ -2,16 +2,26 @@
import lombok.Data;
import lombok.ToString;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
+@Component
@Data
@ToString
-@Component
+@PropertySource("classpath:application-kafka.yaml")
@ConfigurationProperties(prefix = "app.kafka.topics.xpayment-adapter")
public class KafkaProperties {
private String requestTopic;
private String responseTopic;
-}
+
+ public KafkaProperties(
+ @Value("${request-topic}") String requestTopic,
+ @Value("${response-topic}") String responseTopic) {
+ this.requestTopic = requestTopic;
+ this.responseTopic = responseTopic;
+ }
+}
\ No newline at end of file
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/config/XPaymentRestClientConfig.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/config/XPaymentRestClientConfig.java
index 53f5734..e424d17 100644
--- a/xpayment-adapter-app/src/main/java/com/iprody/adapter/config/XPaymentRestClientConfig.java
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/config/XPaymentRestClientConfig.java
@@ -13,9 +13,9 @@ class XPaymentRestClientConfig {
@Bean
RestTemplate xpaymentRestTemplate(
- @Value("${spring.app.xpayment-api.client.username}") String username,
- @Value("${spring.app.xpayment-api.client.password}") String password,
- @Value("${spring.app.xpayment-api.client.account}") String xPayAccount) {
+ @Value("${xpayment-api.client.username}") String username,
+ @Value("${xpayment-api.client.password}") String password,
+ @Value("${xpayment-api.client.account}") String xPayAccount) {
RestTemplate rt = new RestTemplate();
@@ -29,7 +29,7 @@ RestTemplate xpaymentRestTemplate(
@Bean
ApiClient xpaymentApiClient(
- @Value("app.xpayment.client.url") String xPaymentUrl,
+ @Value("${xpayment-api.client.url}") String xPaymentUrl,
RestTemplate xpaymentRestTemplate) {
ApiClient apiClient = new ApiClient(xpaymentRestTemplate);
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/dto/CreateChargeRequestDto.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/dto/CreateChargeRequestDto.java
index 2aaedff..59f5b46 100644
--- a/xpayment-adapter-app/src/main/java/com/iprody/adapter/dto/CreateChargeRequestDto.java
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/dto/CreateChargeRequestDto.java
@@ -1,10 +1,15 @@
package com.iprody.adapter.dto;
+import lombok.Getter;
+import lombok.Setter;
+
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+@Getter
+@Setter
public class CreateChargeRequestDto {
private BigDecimal amount;
private String currency;
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/dto/CreateChargeResponseDto.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/dto/CreateChargeResponseDto.java
index 9578778..0c81c3d 100644
--- a/xpayment-adapter-app/src/main/java/com/iprody/adapter/dto/CreateChargeResponseDto.java
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/dto/CreateChargeResponseDto.java
@@ -1,10 +1,15 @@
package com.iprody.adapter.dto;
+import lombok.Builder;
+import lombok.Getter;
+
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+@Getter
+@Builder
public class CreateChargeResponseDto {
private UUID id;
private BigDecimal amount;
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/mapper/XPaymentConverter.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/mapper/XPaymentConverter.java
new file mode 100644
index 0000000..1b0224a
--- /dev/null
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/mapper/XPaymentConverter.java
@@ -0,0 +1,41 @@
+package com.iprody.adapter.mapper;
+
+import com.iprody.adapter.dto.CreateChargeRequestDto;
+import com.iprody.adapter.dto.CreateChargeResponseDto;
+import com.iprody.xpayment.app.api.model.ChargeResponse;
+import com.iprody.xpayment.app.api.model.CreateChargeRequest;
+import org.springframework.stereotype.Component;
+
+
+@Component
+public class XPaymentConverter {
+
+ public CreateChargeResponseDto toCreateChargeResponseDto(ChargeResponse chargeResponse) {
+ return CreateChargeResponseDto.builder()
+ .id(chargeResponse.getId())
+ .amount(chargeResponse.getAmount())
+ .currency(chargeResponse.getCurrency())
+ .amountReceived(chargeResponse.getAmountReceived())
+ .createdAt(chargeResponse.getCreatedAt())
+ .chargedAt(chargeResponse.getChargedAt())
+ .customer("test customer")
+ .order(chargeResponse.getOrder())
+ .receiptEmail("abc@test.com")
+ .status(chargeResponse.getStatus())
+ .metadata(chargeResponse.getMetadata())
+ .build();
+ }
+
+ public CreateChargeRequest toCreateChargeRequest(CreateChargeRequestDto dto) {
+ CreateChargeRequest result = new CreateChargeRequest();
+ result.setOrder(dto.getOrder());
+ result.setAmount(dto.getAmount());
+ result.setCurrency(dto.getCurrency());
+ result.setCustomer("test customer");
+ result.setReceiptEmail("abc@test.com");
+ result.setMetadata(dto.getMetadata());
+
+ return result;
+ }
+
+}
diff --git a/xpayment-adapter-app/src/main/java/com/iprody/adapter/mapper/XPaymentMapper.java b/xpayment-adapter-app/src/main/java/com/iprody/adapter/mapper/XPaymentMapper.java
index 5b5226a..9530282 100644
--- a/xpayment-adapter-app/src/main/java/com/iprody/adapter/mapper/XPaymentMapper.java
+++ b/xpayment-adapter-app/src/main/java/com/iprody/adapter/mapper/XPaymentMapper.java
@@ -2,18 +2,26 @@
import com.iprody.adapter.dto.CreateChargeRequestDto;
import com.iprody.adapter.dto.CreateChargeResponseDto;
+import com.iprody.api.dto.XPaymentAdapterRequestMessage;
+import com.iprody.api.dto.XPaymentAdapterResponseMessage;
import com.iprody.xpayment.app.api.model.ChargeResponse;
import com.iprody.xpayment.app.api.model.CreateChargeRequest;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
@Mapper(componentModel = "spring")
public interface XPaymentMapper {
- CreateChargeRequestDto toChargeRequestDto(CreateChargeRequest chargeRequest);
-
CreateChargeResponseDto toCreateChargeResponseDto(ChargeResponse chargeResponse);
CreateChargeRequest toCreateChargeRequest(CreateChargeRequestDto dto);
- ChargeResponse toChargeResponse(CreateChargeResponseDto dto);
+ @Mapping(target = "paymentGuid", source = "order")
+ @Mapping(target = "transactionRefId", source = "id")
+ @Mapping(target = "occurredAt", expression = "java(java.time.OffsetDateTime.now())")
+ XPaymentAdapterResponseMessage responseDtoToKafkaMessage(CreateChargeResponseDto response);
+
+ @Mapping(target = "order", source = "paymentGuid")
+ CreateChargeRequestDto kafkaMessageToRequestDto(XPaymentAdapterRequestMessage request);
}
diff --git a/xpayment-adapter-app/src/main/resources/README.txt b/xpayment-adapter-app/src/main/resources/README.txt
new file mode 100644
index 0000000..4b1d118
--- /dev/null
+++ b/xpayment-adapter-app/src/main/resources/README.txt
@@ -0,0 +1,9 @@
+Compile jar file with
+"mvn clean compile assembly:single" to avoid the error "no main manifest attribute in app.jar"
+
+Build docker image:
+ docker build --build-arg JAR_FILE=.//target//xpayment-adapter-app-0.0.1-SNAPSHOT.jar -t xpayment-adapter-app .
+
+K8S:
+
+ helm install keycloak bitnami/keycloak -f keycloak-values.yml --set global.security.allowInsecureImages=true
\ No newline at end of file
diff --git a/xpayment-adapter-app/src/main/resources/application-kafka.yaml b/xpayment-adapter-app/src/main/resources/application-kafka.yaml
index 14a809b..7080727 100644
--- a/xpayment-adapter-app/src/main/resources/application-kafka.yaml
+++ b/xpayment-adapter-app/src/main/resources/application-kafka.yaml
@@ -1,7 +1,7 @@
spring:
kafka:
- bootstrap-servers: localhost:9093
+ bootstrap-servers: kafka:9092
producer:
# Acknowledgment level:
# "0": Producer does not wait for any acknowledgment from the broker (highest throughput, lowest durability).
@@ -32,10 +32,3 @@ spring:
xpayment-adapter:
request-topic: xpayment-adapter.requests
response-topic: xpayment-adapter.responses
-
- xpayment-api:
- client:
- url: http://localhost:9999
- username: paymentAgentIprody
- password: iprodyTestPassword0123
- account: paymentAgentIprodyApiToken
\ No newline at end of file
diff --git a/xpayment-adapter-app/src/main/resources/application-rabbitmq.yaml b/xpayment-adapter-app/src/main/resources/application-rabbitmq.yaml
new file mode 100644
index 0000000..93dd106
--- /dev/null
+++ b/xpayment-adapter-app/src/main/resources/application-rabbitmq.yaml
@@ -0,0 +1,17 @@
+spring:
+
+ rabbitmq:
+ host: rabbitmq
+ port: 5672
+ username: admin
+ password: admin
+
+ app:
+ rabbitmq:
+ exchange-name: payment-state-check-exchange
+ delayed-exchange-name: payment-state-check-exchange
+ dlx-exchange-name: payment.dlx
+ dlx-routing-key: payment.dead
+ queue-name: payment-state-check-queue
+ max-retries: 60
+ interval-ms: 60000
diff --git a/xpayment-adapter-app/src/main/resources/application.yaml b/xpayment-adapter-app/src/main/resources/application.yaml
index 303d059..f32464e 100644
--- a/xpayment-adapter-app/src/main/resources/application.yaml
+++ b/xpayment-adapter-app/src/main/resources/application.yaml
@@ -1,12 +1,14 @@
server:
port: 8088
+ error:
+ include-message: always
spring:
application:
name: xpayment-adapter-app
profiles:
- active: kafka
+ active: kafka,rabbitmq
security:
oauth2:
resourceserver:
@@ -40,4 +42,11 @@ spring:
include: health,info,liquibase,beans,metrics,env,loggers
endpoint:
health:
- show-details: always
\ No newline at end of file
+ show-details: always
+
+xpayment-api:
+ client:
+ url: http://host.docker.internal:9999
+ username: paymentAgentIprody
+ password: iprodyTestPassword0123
+ account: paymentAgentIprodyApiToken
diff --git a/xpayment-api.openapi.yml b/xpayment-api.openapi.yml
deleted file mode 100644
index b327aad..0000000
--- a/xpayment-api.openapi.yml
+++ /dev/null
@@ -1,187 +0,0 @@
-openapi: 3.0.3
-info:
- title: X Payment API
- description: API for creating and retrieving charges
- version: 1.0.0
-servers:
- - url: https://api.xpayment.com/v1
- description: X Payment API server (Production)
- - url: http://localhost:8080
- description: X Payment API server (Development)
-paths:
- /charges:
- post:
- summary: Create a charge
- description: Create a new charge
- operationId: createCharge
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/CreateChargeRequest'
- responses:
- '201':
- description: Charge created successfully
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ChargeResponse'
- '401':
- description: Unauthorized. Either X-Pay-Account or Authorization or both headers were not provided or invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '402':
- description: Request Failed. The parameters were valid but the request failed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- /charges/{id}:
- get:
- summary: Retrieve a charge
- description: Retrieve a charge by its ID
- operationId: retrieveCharge
- parameters:
- - name: id
- in: path
- required: true
- schema:
- type: string
- description: Unique identifier for the object. Must conform to UUID format.
- format: uuid
- responses:
- '200':
- description: Charge retrieved successfully
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ChargeResponse'
- '401':
- description: Unauthorized. Either X-Pay-Account or Authorization or both headers were not provided or invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: Not Found. The requested resource doesn’t exist.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
-components:
- schemas:
- CreateChargeRequest:
- type: object
- properties:
- amount:
- type: string
- format: decimal
- description: The amount to be charged by this payment, in decimal format (e.g., 100.50 for $100.50).
- example: "1090.50"
- currency:
- type: string
- description: Three-letter ISO currency code, in uppercase. Must conform with ISO 4217.
- example: USD
- customer:
- type: string
- description: Full name of the customer this payment belongs to.
- example: Henry Ford
- order:
- type: string
- description: Unique identifier of the order this payment belongs to. Also it will be used as idempotency-key.
- format: uuid
- receiptEmail:
- type: string
- description: Email address to send the receipt to.
- example: test@email.com
- metadata:
- type: object
- description: Set of key-value pairs that you can attach to an object.
- additionalProperties: true
- required:
- - id
- - amount
- - currency
- - customer
- - order
- - receiptEmail
- ChargeResponse:
- type: object
- properties:
- id:
- type: string
- description: Unique identifier for the charge a the inquired payment belongs to. Must conform to UUID format.
- format: uuid
- amount:
- type: string
- format: decimal
- description: The amount to be charged by this payment, in decimal format (e.g., 100.50 for $100.50).
- example: "2000.00"
- currency:
- type: string
- description: Three-letter ISO currency code, in uppercase. Must conform with ISO 4217.
- example: USD
- amountReceived:
- type: string
- format: decimal
- description: Amount eventually charged by this payment.
- example: "0.00"
- createdAt:
- type: string
- description: Time at which the payment was created but not yet charged. Must conform ISO 8601.
- example: 2011-12-03T10:15:30Z
- chargedAt:
- type: string
- description: Time at which the payment was created. Must conform ISO 8601.
- example: 2011-12-03T10:15:30Z
- customer:
- type: string
- description: Full name of the customer this payment belongs to.
- example: Henry Ford
- order:
- type: string
- description: Unique identifier of the order this payment belongs to.
- format: uuid
- receiptEmail:
- type: string
- description: Email address to send the receipt to.
- example: henry.ford@email.com
- status:
- type: string
- description: Status of this payment, one of processing, canceled, or succeeded.
- example: processing
- metadata:
- type: object
- description: Set of key-value pairs that you can attach to a payment.
- additionalProperties: true
- ErrorResponse:
- type: object
- properties:
- statusCode:
- type: integer
- description: HTTP status code of the error.
- example: 401
- message:
- type: string
- description: Error message describing the error.
- example: Unauthorized. Either X-Pay-Account or Authorization or both headers were not provided or invalid.
- chargeId:
- type: string
- description: Unique identifier of the order related to the error.
- format: uuid
- securitySchemes:
- X-Pay-Account:
- type: apiKey
- in: header
- name: X-Pay-Account
- description: To act as connected accounts, clients can issue requests using the X-Pay-Account special header. Make sure that this header contains a pre-generated XPAY Account ID.
- BasicAuth:
- type: http
- scheme: basic
- description: Basic authentication using username and password.
-security:
- - X-Pay-Account: []
- - BasicAuth: []
\ No newline at end of file