poc completed
This commit is contained in:
17
src/main/java/zw/qantra/tm/configs/SpecificationConfig.java
Normal file
17
src/main/java/zw/qantra/tm/configs/SpecificationConfig.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package zw.qantra.tm.configs;
|
||||
|
||||
import net.kaczmarzyk.spring.data.jpa.web.SpecificationArgumentResolver;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class SpecificationConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
argumentResolvers.add(new SpecificationArgumentResolver());
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,12 @@ import zw.qantra.tm.domain.enums.CurrencyType;
|
||||
import zw.qantra.tm.domain.models.Category;
|
||||
import zw.qantra.tm.domain.models.Charge;
|
||||
import zw.qantra.tm.domain.models.ChargeCondition;
|
||||
import zw.qantra.tm.domain.models.IntegrationProcessor;
|
||||
import zw.qantra.tm.domain.models.PaymentProcessor;
|
||||
import zw.qantra.tm.domain.models.Provider;
|
||||
import zw.qantra.tm.domain.services.ChargeConditionService;
|
||||
import zw.qantra.tm.domain.services.ChargeService;
|
||||
import zw.qantra.tm.domain.services.IntegrationProcessorService;
|
||||
import zw.qantra.tm.domain.services.CategoryService;
|
||||
import zw.qantra.tm.domain.services.PaymentProcessorService;
|
||||
import zw.qantra.tm.domain.services.ProviderService;
|
||||
@@ -33,9 +35,20 @@ public class ConfigController {
|
||||
private final PaymentProcessorService paymentProcessorService;
|
||||
private final ProviderService providerService;
|
||||
private final CategoryService categoryService;
|
||||
private final IntegrationProcessorService integrationProcessorService;
|
||||
|
||||
@GetMapping("/seed")
|
||||
public ResponseEntity charges(){
|
||||
|
||||
integrationProcessorService.getIntegrationProcessorRepository().deleteAll();
|
||||
integrationProcessorService.getIntegrationProcessorRepository().saveAll(Arrays.asList(
|
||||
IntegrationProcessor.builder()
|
||||
.name("STEWARD")
|
||||
.label("STEWARD")
|
||||
.description("Steward Integration Processor")
|
||||
.build()
|
||||
));
|
||||
|
||||
// clear existing categories before seeding
|
||||
categoryService.getCategoryRepository().deleteAll();
|
||||
// seed categories
|
||||
@@ -65,6 +78,7 @@ public class ConfigController {
|
||||
.category("AIRTIME")
|
||||
.image("econet.png")
|
||||
.externalId("78f1f497-c9eb-401c-b22c-884756e68e40")
|
||||
.integrationProcessorLabel("STEWARD")
|
||||
.build(),
|
||||
Provider.builder()
|
||||
.description("NetOne Airtime")
|
||||
@@ -73,6 +87,7 @@ public class ConfigController {
|
||||
.category("AIRTIME")
|
||||
.image("netone.png")
|
||||
.externalId("95d485a7-39c9-4559-8a27-6521969abe8f")
|
||||
.integrationProcessorLabel("STEWARD")
|
||||
.build(),
|
||||
Provider.builder()
|
||||
.description("Prepaid Zesa")
|
||||
@@ -81,6 +96,7 @@ public class ConfigController {
|
||||
.category("UTILITIES")
|
||||
.image("zesa.png")
|
||||
.externalId("0f67f7cc-b62b-4fb5-915f-6740b2afdba6")
|
||||
.integrationProcessorLabel("STEWARD")
|
||||
.build(),
|
||||
Provider.builder()
|
||||
.description("Econet Broadband")
|
||||
@@ -89,6 +105,7 @@ public class ConfigController {
|
||||
.category("AIRTIME")
|
||||
.image("econet.png")
|
||||
.externalId("287863e2-a791-4106-b629-79dadc9a6779")
|
||||
.integrationProcessorLabel("STEWARD")
|
||||
.build()
|
||||
);
|
||||
providerService.getProviderRepository().saveAll(providers);
|
||||
|
||||
@@ -17,7 +17,7 @@ public class ProviderController {
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity getProviders(@RequestParam(required = false) String category) {
|
||||
return ResponseEntity.ok(providerService.getProviders(category));
|
||||
return ResponseEntity.ok(providerService.getProvidersByCategory(category));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/products")
|
||||
@@ -25,6 +25,16 @@ public class ProviderController {
|
||||
return ResponseEntity.ok(providerService.getProviderProducts(id));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/products/{productId}")
|
||||
public ResponseEntity getProducts(@PathVariable UUID id, @PathVariable UUID productId) {
|
||||
return ResponseEntity.ok(providerService.getProviderProduct(id, productId));
|
||||
}
|
||||
|
||||
@GetMapping("/client/{clientId}")
|
||||
public ResponseEntity getProviderByClientId(@PathVariable String clientId) {
|
||||
return ResponseEntity.ok(providerService.getProviderByClientId(clientId));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity getProvider(@PathVariable UUID id) {
|
||||
return providerService.getProvider(id)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package zw.qantra.tm.domain.controllers;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kaczmarzyk.spring.data.jpa.domain.Equal;
|
||||
import net.kaczmarzyk.spring.data.jpa.domain.Like;
|
||||
import net.kaczmarzyk.spring.data.jpa.web.annotation.Spec;
|
||||
import net.kaczmarzyk.spring.data.jpa.web.annotation.Or;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import zw.qantra.tm.domain.models.Recipient;
|
||||
import zw.qantra.tm.domain.services.RecipientService;
|
||||
import net.kaczmarzyk.spring.data.jpa.web.annotation.And;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/recipients")
|
||||
@RequiredArgsConstructor
|
||||
public class RecipientController {
|
||||
|
||||
private final RecipientService recipientService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<List<Recipient>> getAllRecipients() {
|
||||
return ResponseEntity.ok(recipientService.getAllRecipients());
|
||||
}
|
||||
|
||||
@GetMapping("/{account}/user/{userId}")
|
||||
public ResponseEntity<Recipient> getRecipient(@PathVariable String account, @PathVariable String userId) {
|
||||
List<Recipient> recipients = recipientService.getRecipient(account, userId);
|
||||
if(recipients.size() > 0){
|
||||
return ResponseEntity.ok(recipients.get(0));
|
||||
}
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Recipient> createRecipient(@RequestBody Recipient recipient) {
|
||||
return ResponseEntity.ok(recipientService.save(recipient));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<Recipient> updateRecipient(@PathVariable UUID id, @RequestBody Recipient recipient) {
|
||||
Recipient updatedRecipient = recipientService.updateRecipient(id, recipient);
|
||||
if (updatedRecipient != null) {
|
||||
return ResponseEntity.ok(updatedRecipient);
|
||||
}
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<String> deleteRecipient(@PathVariable UUID id) {
|
||||
if (recipientService.deleteRecipient(id)) {
|
||||
return ResponseEntity.ok("Recipient deleted successfully");
|
||||
}
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@GetMapping("/search")
|
||||
public ResponseEntity<List<Recipient>> searchRecipients(
|
||||
@And({
|
||||
@Spec(path = "name", spec = Equal.class),
|
||||
@Spec(path = "email", spec = Equal.class),
|
||||
@Spec(path = "phoneNumber", spec = Equal.class),
|
||||
@Spec(path = "account", spec = Equal.class),
|
||||
@Spec(path = "latestProviderLabel", spec = Equal.class),
|
||||
@Spec(path = "userId", spec = Equal.class)
|
||||
}) Specification<Recipient> specification) {
|
||||
return ResponseEntity.ok(recipientService.searchRecipients(specification));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
package zw.qantra.tm.domain.controllers;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kaczmarzyk.spring.data.jpa.domain.Equal;
|
||||
import net.kaczmarzyk.spring.data.jpa.web.annotation.And;
|
||||
import net.kaczmarzyk.spring.data.jpa.web.annotation.Or;
|
||||
import net.kaczmarzyk.spring.data.jpa.web.annotation.Spec;
|
||||
import net.kaczmarzyk.spring.data.jpa.web.annotation.Conjunction;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import zw.qantra.tm.domain.services.TransactionService;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
|
||||
@@ -8,6 +14,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import zw.qantra.tm.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -21,15 +28,40 @@ public class TransactonController {
|
||||
private final TransactionService transactionService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<List<Transaction>> getAll() {
|
||||
return ResponseEntity.ok(transactionService.getTransactionRepository().findAll());
|
||||
public ResponseEntity<List<Transaction>> getAll(
|
||||
@Conjunction(value = {
|
||||
@Or({
|
||||
@Spec(path = "status", spec = Equal.class),
|
||||
@Spec(path = "createdAt", spec = Equal.class),
|
||||
@Spec(path = "providerLabel", spec = Equal.class),
|
||||
@Spec(path = "billClientId", spec = Equal.class),
|
||||
@Spec(path = "productUid", spec = Equal.class),
|
||||
@Spec(path = "productName", spec = Equal.class),
|
||||
@Spec(path = "debitName", spec = Equal.class),
|
||||
@Spec(path = "debitAccount", spec = Equal.class),
|
||||
@Spec(path = "debitCurrency", spec = Equal.class),
|
||||
@Spec(path = "debitPhone", spec = Equal.class),
|
||||
@Spec(path = "creditName", spec = Equal.class),
|
||||
@Spec(path = "creditAccount", spec = Equal.class),
|
||||
@Spec(path = "creditCurrency", spec = Equal.class),
|
||||
@Spec(path = "creditPhone", spec = Equal.class),
|
||||
@Spec(path = "amount", spec = Equal.class),
|
||||
@Spec(path = "reference", spec = Equal.class),
|
||||
@Spec(path = "totalAmount", spec = Equal.class),
|
||||
@Spec(path = "authType", spec = Equal.class),
|
||||
@Spec(path = "sdkActionId", spec = Equal.class),
|
||||
@Spec(path = "paymentProcessorLabel", spec = Equal.class),
|
||||
})
|
||||
}, and = {
|
||||
@Spec(path = "type", spec = Equal.class),
|
||||
@Spec(path = "userId", spec = Equal.class),
|
||||
}) Specification<Transaction> specification) {
|
||||
return ResponseEntity.ok(transactionService.findAll(specification));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Transaction> getById(@PathVariable UUID id) {
|
||||
return transactionService.getTransactionRepository().findById(id)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
return ResponseEntity.ok(transactionService.findById(id));
|
||||
}
|
||||
|
||||
@GetMapping("/poll/{id}")
|
||||
@@ -39,9 +71,9 @@ public class TransactonController {
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Transaction> create(@RequestBody Transaction transaction) {
|
||||
logger.info("incoming transaction: {}", transaction);
|
||||
logger.info("incoming transaction: {}", Utils.toJson(transaction));
|
||||
Transaction createdTransaction = transactionService.execute(transaction);
|
||||
logger.info("outgoing transaction: {}", createdTransaction);
|
||||
logger.info("outgoing transaction: {}", Utils.toJson(createdTransaction));
|
||||
return ResponseEntity.ok(createdTransaction);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import zw.qantra.tm.domain.enums.AuthType;
|
||||
import zw.qantra.tm.domain.enums.CurrencyType;
|
||||
import zw.qantra.tm.domain.enums.RequestType;
|
||||
|
||||
@@ -17,6 +18,7 @@ import java.math.BigDecimal;
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class BillPaymentDto {
|
||||
private RequestType type;
|
||||
private AuthType authType;
|
||||
private String billClientId;
|
||||
private String debitRef;
|
||||
private CurrencyType debitCurrency;
|
||||
@@ -28,4 +30,5 @@ public class BillPaymentDto {
|
||||
private CurrencyType creditCurrency;
|
||||
private String creditPhone;
|
||||
private String billName;
|
||||
private String trace;
|
||||
}
|
||||
@@ -6,10 +6,13 @@ import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@@ -25,4 +28,13 @@ public class BaseEntity {
|
||||
@JdbcTypeCode(SqlTypes.UUID)
|
||||
@Column(name = "id", updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at", nullable = true, updatable = false)
|
||||
private LocalDateTime createdAt; // Or Date, Timestamp
|
||||
|
||||
@UpdateTimestamp
|
||||
@Column(name = "updated_at", nullable = true)
|
||||
private LocalDateTime updatedAt; // Or Date, Timestamp
|
||||
|
||||
}
|
||||
|
||||
@@ -17,4 +17,6 @@ public class Provider extends BaseEntity {
|
||||
private String externalId;
|
||||
private String image;
|
||||
private String category;
|
||||
private String accountFieldName;
|
||||
private String integrationProcessorLabel;
|
||||
}
|
||||
|
||||
21
src/main/java/zw/qantra/tm/domain/models/Recipient.java
Normal file
21
src/main/java/zw/qantra/tm/domain/models/Recipient.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package zw.qantra.tm.domain.models;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Recipient extends BaseEntity {
|
||||
private String name;
|
||||
private String email;
|
||||
private String phoneNumber;
|
||||
private String address;
|
||||
private String account;
|
||||
private String initials;
|
||||
private String latestProviderLabel;
|
||||
private String userId;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import java.math.BigDecimal;
|
||||
@NoArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Transaction extends BaseEntity {
|
||||
private String userId;
|
||||
private String trace;
|
||||
private BigDecimal amount;
|
||||
private BigDecimal charge;
|
||||
@@ -40,6 +41,7 @@ public class Transaction extends BaseEntity {
|
||||
private String debitName;
|
||||
private String debitCard;
|
||||
private String debitRef;
|
||||
private String debitEmail;
|
||||
private String creditPhone;
|
||||
private String creditAccount;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@@ -47,11 +49,15 @@ public class Transaction extends BaseEntity {
|
||||
private String creditName;
|
||||
private String creditCard;
|
||||
private String creditRef;
|
||||
private String creditEmail;
|
||||
private String billClientId;
|
||||
private String clientSecret;
|
||||
private String aggregatorId;
|
||||
private String billName;
|
||||
private String billProductName;
|
||||
private String paymentProcessorName;
|
||||
private String providerImage;
|
||||
private String paymentProcessorImage;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private RequestType type;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@@ -60,6 +66,7 @@ public class Transaction extends BaseEntity {
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String errorMessage;
|
||||
private String responseCode;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Status status;
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String targetUrl;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package zw.qantra.tm.domain.repositories;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import zw.qantra.tm.domain.models.Recipient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface RecipientRepository extends JpaRepository<Recipient, UUID>, JpaSpecificationExecutor<Recipient> {
|
||||
List<Recipient> findByNameContainingIgnoreCase(String name);
|
||||
List<Recipient> findByEmailContainingIgnoreCase(String email);
|
||||
List<Recipient> findByPhoneNumberContaining(String phoneNumber);
|
||||
List<Recipient> findByAccountAndUserId(String account, String userId);
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
package zw.qantra.tm.domain.repositories;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import zw.qantra.tm.domain.enums.RequestType;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface TransactionRepository extends JpaRepository<Transaction, UUID> {
|
||||
public interface TransactionRepository extends JpaRepository<Transaction, UUID>, JpaSpecificationExecutor<Transaction> {
|
||||
Transaction findByTraceAndType(String trace, RequestType type);
|
||||
}
|
||||
@@ -30,6 +30,13 @@ public class ProviderService {
|
||||
@Value("${sbz.aggregator.url}")
|
||||
private String aggregatorUrl;
|
||||
|
||||
public SBZProductDto getProviderProduct(UUID providerId, UUID productId) {
|
||||
return getProviderProducts(providerId).stream()
|
||||
.filter(product -> product.getUid().equals(productId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public List<SBZProductDto> getProviderProducts(UUID providerId) {
|
||||
String token = restService.getMerchantToken();
|
||||
|
||||
@@ -57,13 +64,15 @@ public class ProviderService {
|
||||
return products;
|
||||
}
|
||||
|
||||
public Object getProviders(String category) {
|
||||
String token = restService.getMerchantToken();
|
||||
public Object getProviderByClientId(String clientId) {
|
||||
Provider provider = providerRepository.findByClientId(clientId);
|
||||
if (provider != null) {
|
||||
return ((List)getProviders(Collections.singletonList(provider))).stream().findFirst().orElse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
headers.setBearerAuth(token);
|
||||
public Object getProvidersByCategory(String category) {
|
||||
|
||||
List<Provider> existingProviders;
|
||||
if (category != null && !category.isEmpty()) {
|
||||
@@ -72,6 +81,17 @@ public class ProviderService {
|
||||
existingProviders = getProviderRepository().findAll();
|
||||
}
|
||||
|
||||
return getProviders(existingProviders);
|
||||
}
|
||||
|
||||
public Object getProviders(List<Provider> existingProviders) {
|
||||
String token = restService.getMerchantToken();
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
headers.setBearerAuth(token);
|
||||
|
||||
List<String> existingProviderIds = existingProviders.stream()
|
||||
.map(Provider::getClientId)
|
||||
.collect(Collectors.toList());
|
||||
@@ -100,6 +120,7 @@ public class ProviderService {
|
||||
provider.put("image", existingProvider.getImage());
|
||||
provider.put("label", existingProvider.getLabel());
|
||||
provider.put("category", existingProvider.getCategory());
|
||||
provider.put("accountFieldName", existingProvider.getAccountFieldName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package zw.qantra.tm.domain.services;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import zw.qantra.tm.domain.models.Recipient;
|
||||
import zw.qantra.tm.domain.repositories.RecipientRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RecipientService {
|
||||
@Getter
|
||||
private final RecipientRepository recipientRepository;
|
||||
|
||||
public List<Recipient> getAllRecipients() {
|
||||
return recipientRepository.findAll();
|
||||
}
|
||||
|
||||
public List<Recipient> getRecipient(String account, String userId) {
|
||||
return recipientRepository.findByAccountAndUserId(account, userId);
|
||||
}
|
||||
|
||||
public Recipient save(Recipient recipient) {
|
||||
return recipientRepository.save(recipient);
|
||||
}
|
||||
|
||||
public Recipient updateRecipient(UUID id, Recipient recipient) {
|
||||
if (recipientRepository.existsById(id)) {
|
||||
recipient.setId(id);
|
||||
return recipientRepository.save(recipient);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean deleteRecipient(UUID id) {
|
||||
if (recipientRepository.existsById(id)) {
|
||||
recipientRepository.deleteById(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Recipient> searchRecipients(Specification<Recipient> specification) {
|
||||
return recipientRepository.findAll(specification);
|
||||
}
|
||||
}
|
||||
@@ -3,23 +3,34 @@ package zw.qantra.tm.domain.services;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import zw.qantra.tm.domain.enums.RequestType;
|
||||
import zw.qantra.tm.domain.enums.Status;
|
||||
import zw.qantra.tm.domain.models.AdditionalData;
|
||||
import zw.qantra.tm.domain.models.Provider;
|
||||
import zw.qantra.tm.domain.models.Recipient;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
import zw.qantra.tm.domain.repositories.TransactionRepository;
|
||||
import zw.qantra.tm.domain.services.factories.PaymentProcessorFactory;
|
||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
||||
import zw.qantra.tm.domain.services.processors.handlers.CalculateChargesHandler;
|
||||
import zw.qantra.tm.domain.services.processors.handlers.ConfirmationLogicHandler;
|
||||
import zw.qantra.tm.domain.services.processors.handlers.FormattingHandler;
|
||||
import zw.qantra.tm.domain.services.processors.handlers.LogicPipeline;
|
||||
import zw.qantra.tm.domain.services.processors.handlers.RecipientsUpdateHandler;
|
||||
import zw.qantra.tm.domain.services.processors.handlers.ValidationHandler;
|
||||
import zw.qantra.tm.exceptions.ApiException;
|
||||
import zw.qantra.tm.utils.Utils;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -29,42 +40,25 @@ public class TransactionService {
|
||||
@Getter
|
||||
private final TransactionRepository transactionRepository;
|
||||
private final PaymentProcessorFactory paymentProcessorFactory;
|
||||
private final CalculateChargesHandler calculateChargesHandler;
|
||||
private final ProviderService providerService;
|
||||
private final ChargeService chargeService;
|
||||
private final AdditionalDataService additionalDataService;
|
||||
private final RecipientService recipientService;
|
||||
|
||||
public Transaction poll(UUID uuid){
|
||||
if(!transactionRepository.existsById(uuid)){
|
||||
throw new ApiException("Transaction not found for ID: " + uuid);
|
||||
}
|
||||
|
||||
Transaction transaction = transactionRepository.findById(uuid).get();
|
||||
|
||||
// no persistence happens during polling unless the transaction status changes
|
||||
String label = getLabel(transaction);
|
||||
logger.info("label: {}", label);
|
||||
|
||||
TransactionProcessorInterface transactionProcessorInterface =
|
||||
(TransactionProcessorInterface) paymentProcessorFactory.getPaymentProcessor(label);
|
||||
try {
|
||||
transactionProcessorInterface.poll(transaction);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setResponseCode("01");
|
||||
transaction.setErrorMessage(e.getMessage());
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public Transaction execute(Transaction transaction) {
|
||||
transaction.setReference(UUID.randomUUID().toString());
|
||||
public Transaction integration(Transaction transaction) {
|
||||
transaction.setType(RequestType.INTEGRATION);
|
||||
|
||||
LogicPipeline<Transaction, Transaction> transactionLogic =
|
||||
new LogicPipeline<>(calculateChargesHandler)
|
||||
.addHandler(new ValidationHandler());
|
||||
new LogicPipeline<>(new ValidationHandler());
|
||||
transaction = transactionLogic.execute(transaction);
|
||||
|
||||
String label = getLabel(transaction);
|
||||
Provider provider = providerService.getProviderRepository()
|
||||
.findByClientId(transaction.getBillClientId());
|
||||
if(provider == null){
|
||||
throw new ApiException("Provider not found for billClientId: " + transaction.getBillClientId());
|
||||
}
|
||||
String label = transaction.getType() + "_" +
|
||||
provider.getIntegrationProcessorLabel();
|
||||
logger.info("label: {}", label);
|
||||
|
||||
TransactionProcessorInterface transactionProcessorInterface =
|
||||
@@ -83,9 +77,80 @@ public class TransactionService {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public Transaction poll(UUID uuid){
|
||||
if(!transactionRepository.existsById(uuid)){
|
||||
throw new ApiException("Transaction not found for ID: " + uuid);
|
||||
}
|
||||
|
||||
Transaction transaction = transactionRepository.findById(uuid).get();
|
||||
|
||||
// no persistence happens during polling unless the transaction status changes
|
||||
String label = getLabel(transaction);
|
||||
logger.info("label: {}", label);
|
||||
|
||||
TransactionProcessorInterface transactionProcessorInterface =
|
||||
(TransactionProcessorInterface) paymentProcessorFactory.getPaymentProcessor(label);
|
||||
try {
|
||||
// find out if the gateway tran was successful
|
||||
transactionProcessorInterface.poll(transaction);
|
||||
|
||||
// if tran was successful deep copy transaction & push integration to the aggregator
|
||||
Transaction transactionCopy = Utils.deepCopy(transaction, Transaction.class);
|
||||
transactionCopy.setType(RequestType.INTEGRATION);
|
||||
transactionCopy.setId(null);
|
||||
integration(transactionCopy);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setResponseCode("01");
|
||||
transaction.setErrorMessage(e.getMessage());
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public Transaction execute(Transaction transaction) {
|
||||
transaction.setReference(UUID.randomUUID().toString());
|
||||
|
||||
// run pre transaction logic
|
||||
LogicPipeline<Transaction, Transaction> preTransactionLogic =
|
||||
new LogicPipeline<>(new CalculateChargesHandler(chargeService))
|
||||
.addHandler(new ConfirmationLogicHandler())
|
||||
.addHandler(new FormattingHandler())
|
||||
.addHandler(new ValidationHandler());
|
||||
transaction = preTransactionLogic.execute(transaction);
|
||||
|
||||
String label = getLabel(transaction);
|
||||
logger.info("label: {}", label);
|
||||
|
||||
TransactionProcessorInterface transactionProcessorInterface =
|
||||
(TransactionProcessorInterface) paymentProcessorFactory.getPaymentProcessor(label);
|
||||
|
||||
try {
|
||||
// MAIN TRANSACTION LOGIC!
|
||||
transactionProcessorInterface.process(transaction);
|
||||
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setResponseCode("01");
|
||||
transaction.setErrorMessage(e.getMessage());
|
||||
transactionRepository.save(transaction);
|
||||
return transaction;
|
||||
}
|
||||
|
||||
// run post transaction logic
|
||||
LogicPipeline<Transaction, Transaction> postTransactionLogic =
|
||||
new LogicPipeline<>(new RecipientsUpdateHandler(recipientService));
|
||||
transaction = postTransactionLogic.execute(transaction);
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
private String getLabel(Transaction transaction){
|
||||
String label = "";
|
||||
if(transaction.getType() == RequestType.CONFIRM){
|
||||
List<RequestType> list = Arrays.asList(RequestType.CONFIRM);
|
||||
if(list.contains(transaction.getType())){
|
||||
// get processor label from billClientId
|
||||
Provider provider = providerService.getProviderRepository()
|
||||
.findByClientId(transaction.getBillClientId());
|
||||
@@ -102,6 +167,20 @@ public class TransactionService {
|
||||
return label;
|
||||
}
|
||||
|
||||
public Transaction findById(UUID id) {
|
||||
Transaction transaction = transactionRepository.findById(id).orElseThrow(() -> new ApiException("Transaction not found for ID: " + id));
|
||||
|
||||
AdditionalData additionalData = additionalDataService.getAdditionalDataRepository()
|
||||
.findByTransactionId(id).orElseThrow(() -> new ApiException("Additional data not found for transaction ID: " + id));
|
||||
transaction.setAdditionalData(Utils.fromJson(additionalData.getJsonString(), List.class));
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public List<Transaction> findAll(Specification<Transaction> spec) {
|
||||
return transactionRepository.findAll(spec, Sort.by(Sort.Direction.ASC, "createdAt"));
|
||||
}
|
||||
|
||||
public Transaction save(Transaction transaction){
|
||||
return transactionRepository.save(transaction);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package zw.qantra.tm.domain.services.processors.confirmations;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import zw.qantra.tm.domain.dtos.BillPaymentDto;
|
||||
import zw.qantra.tm.domain.enums.RequestType;
|
||||
import zw.qantra.tm.domain.enums.Status;
|
||||
import zw.qantra.tm.domain.models.AdditionalData;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
import zw.qantra.tm.domain.services.AdditionalDataService;
|
||||
import zw.qantra.tm.domain.services.TransactionService;
|
||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
||||
import zw.qantra.tm.rest.RestService;
|
||||
import zw.qantra.tm.utils.Utils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
@Service("CONFIRM_ECONET")
|
||||
@RequiredArgsConstructor
|
||||
public class EconetConfirmationProcessor implements TransactionProcessorInterface {
|
||||
Logger logger = LoggerFactory.getLogger(EconetConfirmationProcessor.class);
|
||||
|
||||
private final RestService restService;
|
||||
private final TransactionService transactionService;
|
||||
private final AdditionalDataService additionalDataService;
|
||||
|
||||
@Value("${sbz.aggregator.url}")
|
||||
private String aggregatorUrl;
|
||||
@Value("${sbz.aggregator.client-id}")
|
||||
private String aggregatorClientId;
|
||||
@Value("${sbz.aggregator.phone}")
|
||||
private String aggregatorPhone;
|
||||
@Value("${sbz.aggregator.account}")
|
||||
private String aggregatorAccount;
|
||||
|
||||
@Override
|
||||
public Object process(Transaction transaction) throws Exception {
|
||||
logger.info("processing transaction");
|
||||
String token = restService.getMerchantToken();
|
||||
|
||||
BillPaymentDto request = BillPaymentDto.builder()
|
||||
.type(RequestType.CONFIRM)
|
||||
.billClientId(transaction.getBillClientId())
|
||||
.debitRef(transaction.getDebitRef())
|
||||
.debitCurrency(transaction.getDebitCurrency())
|
||||
.amount(transaction.getAmount())
|
||||
.aggregatorId(aggregatorClientId)
|
||||
.debitPhone(aggregatorPhone) // qantra mobile number
|
||||
.debitAccount(aggregatorAccount) // qantra corporate account
|
||||
.creditAccount(transaction.getCreditAccount()) // customer's phone number
|
||||
.creditPhone(transaction.getCreditPhone()) // customer's contact phone number
|
||||
.billName(transaction.getBillName())
|
||||
.build();
|
||||
|
||||
String url = aggregatorUrl + "/merchant/aggregation/transaction";
|
||||
|
||||
LinkedHashMap response = restService.postWithToken(token, url, request, LinkedHashMap.class);
|
||||
|
||||
logger.info("response: {}", response.toString());
|
||||
|
||||
LinkedHashMap body = (LinkedHashMap) response.get("body");
|
||||
|
||||
if (body.get("status").equals("SUCCESS")) {
|
||||
transaction.setStatus(Status.SUCCESS);
|
||||
transaction.setResponseCode((String) body.get("responseCode"));
|
||||
transaction.setAdditionalData(body.get("additionalData"));
|
||||
transactionService.save(transaction);
|
||||
|
||||
AdditionalData additionalData = AdditionalData.builder()
|
||||
.transaction(transaction)
|
||||
.jsonString(Utils.toJson(body.get("additionalData")))
|
||||
.build();
|
||||
additionalDataService.getAdditionalDataRepository().save(additionalData);
|
||||
|
||||
} else {
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setErrorMessage((String) body.get("message"));
|
||||
transaction.setResponseCode((String) body.get("responseCode"));
|
||||
transaction.setErrorMessage((String) body.get("errorMessage"));
|
||||
}
|
||||
|
||||
return transactionService.save(transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object reverse(Transaction transaction) {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object poll(Transaction transaction) {
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,7 @@ public class ZesaConfirmationProcessor implements TransactionProcessorInterface
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setErrorMessage((String) body.get("message"));
|
||||
transaction.setResponseCode((String) body.get("responseCode"));
|
||||
transaction.setErrorMessage((String) body.get("errorMessage"));
|
||||
}
|
||||
|
||||
return transactionService.save(transaction);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package zw.qantra.tm.domain.services.processors.handlers;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -17,11 +18,11 @@ import java.util.List;
|
||||
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CalculateChargesHandler implements LogicHandler<Transaction, Transaction> {
|
||||
Logger logger = LoggerFactory.getLogger(CalculateChargesHandler.class);
|
||||
|
||||
@Autowired
|
||||
private ChargeService chargeService;
|
||||
private final ChargeService chargeService;
|
||||
|
||||
@Override
|
||||
public Transaction process(Transaction transaction) {
|
||||
@@ -126,14 +127,17 @@ public class CalculateChargesHandler implements LogicHandler<Transaction, Transa
|
||||
|
||||
// calculate total amount for ease of use later
|
||||
transaction.setTotalAmount(
|
||||
transaction.getAmount().add(
|
||||
transaction.getCharge().add(
|
||||
transaction.getTax().add(
|
||||
transaction.getGatewayCharge())));
|
||||
transaction.getGatewayCharge()))));
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
void applyCharge(Transaction transaction, Charge charge, BigDecimal chargeAmount) {
|
||||
if(chargeAmount == null) return;
|
||||
|
||||
switch (charge.getChargeLabel()) {
|
||||
case FEE -> transaction.setCharge(chargeAmount);
|
||||
case TAX -> transaction.setTax(chargeAmount);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package zw.qantra.tm.domain.services.processors.handlers;
|
||||
|
||||
import zw.qantra.tm.domain.enums.RequestType;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
import zw.qantra.tm.utils.Utils;
|
||||
|
||||
public class ConfirmationLogicHandler implements LogicHandler<Transaction, Transaction> {
|
||||
@Override
|
||||
public Transaction process(Transaction transaction) {
|
||||
if(transaction.getType().equals(RequestType.CONFIRM)) {
|
||||
transaction.setTrace(Utils.getUid());
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package zw.qantra.tm.domain.services.processors.handlers;
|
||||
|
||||
import com.google.i18n.phonenumbers.NumberParseException;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
|
||||
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
|
||||
public class FormattingHandler implements LogicHandler<Transaction, Transaction> {
|
||||
@Override
|
||||
public Transaction process(Transaction transaction) {
|
||||
System.out.println("processing formatting");
|
||||
|
||||
// strip credit account and make sure there's nothing funny
|
||||
String creditAccount = transaction.getCreditAccount();
|
||||
if (creditAccount != null && !creditAccount.isEmpty()) {
|
||||
// Remove all non-hexadecimal characters (keep only 0-9, a-f, A-F)
|
||||
String strippedAccount = creditAccount.replaceAll("[^0-9a-fA-F]", "");
|
||||
|
||||
// Only set if the stripped result is not empty
|
||||
if (!strippedAccount.isEmpty()) {
|
||||
transaction.setCreditAccount(strippedAccount);
|
||||
} else {
|
||||
// If stripping results in empty string, set to null
|
||||
transaction.setCreditAccount(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sanitize phone number formatting
|
||||
String debitPhoneNumber = transaction.getDebitPhone();
|
||||
String creditPhoneNumber = transaction.getCreditPhone();
|
||||
|
||||
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
||||
|
||||
if(debitPhoneNumber != null && !debitPhoneNumber.isEmpty()){
|
||||
try {
|
||||
// phone must begin with '+'
|
||||
PhoneNumber zimPhoneNumber = phoneUtil.parse(debitPhoneNumber, "");
|
||||
String formattedPhoneNumber = phoneUtil.format(zimPhoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
|
||||
transaction.setDebitPhone(formattedPhoneNumber);
|
||||
} catch (NumberParseException e) {
|
||||
System.err.println("NumberParseException was thrown: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if(creditPhoneNumber != null && !creditPhoneNumber.isEmpty()){
|
||||
try {
|
||||
PhoneNumber zimPhoneNumber = phoneUtil.parse(creditPhoneNumber, "");
|
||||
String formattedPhoneNumber = phoneUtil.format(zimPhoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
|
||||
transaction.setCreditPhone(formattedPhoneNumber);
|
||||
} catch (NumberParseException e) {
|
||||
System.err.println("NumberParseException was thrown: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package zw.qantra.tm.domain.services.processors.handlers;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
import zw.qantra.tm.domain.enums.Status;
|
||||
import zw.qantra.tm.domain.models.Recipient;
|
||||
import zw.qantra.tm.domain.services.RecipientService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RecipientsUpdateHandler implements LogicHandler<Transaction, Transaction> {
|
||||
|
||||
private final RecipientService recipientService;
|
||||
|
||||
@Override
|
||||
public Transaction process(Transaction transaction) {
|
||||
System.out.println("processing recipients");
|
||||
|
||||
if(transaction.getStatus() == Status.SUCCESS){
|
||||
List<Recipient> recipients = recipientService.getRecipient(transaction.getCreditAccount(), transaction.getUserId());
|
||||
|
||||
Recipient recipient = null;
|
||||
if(recipients.size() > 0){
|
||||
recipient = recipients.get(0);
|
||||
}else{
|
||||
recipient = new Recipient();
|
||||
recipient.setAccount(transaction.getCreditAccount());
|
||||
recipient.setUserId(transaction.getUserId());
|
||||
}
|
||||
|
||||
recipient.setLatestProviderLabel(transaction.getProviderLabel());
|
||||
if(transaction.getCreditName() != null && !transaction.getCreditName().isEmpty()){
|
||||
recipient.setName(transaction.getCreditName());
|
||||
}
|
||||
if(transaction.getCreditEmail() != null && !transaction.getCreditEmail().isEmpty()){
|
||||
recipient.setEmail(transaction.getCreditEmail());
|
||||
}
|
||||
if(transaction.getCreditPhone() != null && !transaction.getCreditPhone().isEmpty()){
|
||||
recipient.setPhoneNumber(transaction.getCreditPhone());
|
||||
}
|
||||
|
||||
String stringToInitial = transaction.getCreditName() != null && !transaction.getCreditName().isEmpty()
|
||||
? transaction.getCreditName() : transaction.getBillName();
|
||||
|
||||
recipient.setInitials(stringToInitial.substring(0, 2).toUpperCase());
|
||||
recipientService.save(recipient);
|
||||
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package zw.qantra.tm.domain.services.processors.integrations;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import zw.qantra.tm.domain.dtos.BillPaymentDto;
|
||||
import zw.qantra.tm.domain.enums.RequestType;
|
||||
import zw.qantra.tm.domain.enums.Status;
|
||||
import zw.qantra.tm.domain.models.AdditionalData;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
import zw.qantra.tm.domain.services.AdditionalDataService;
|
||||
import zw.qantra.tm.domain.services.TransactionService;
|
||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
||||
import zw.qantra.tm.rest.RestService;
|
||||
import zw.qantra.tm.utils.Utils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
@Service("INTEGRATION_STEWARD")
|
||||
@RequiredArgsConstructor
|
||||
public class StewardIntegrationProcessor implements TransactionProcessorInterface {
|
||||
Logger logger = LoggerFactory.getLogger(TransactionService.class);
|
||||
private final RestService restService;
|
||||
private final TransactionService transactionService;
|
||||
private final AdditionalDataService additionalDataService;
|
||||
|
||||
@Value("${sbz.aggregator.url}")
|
||||
private String aggregatorUrl;
|
||||
@Value("${sbz.aggregator.client-id}")
|
||||
private String aggregatorClientId;
|
||||
@Value("${sbz.aggregator.phone}")
|
||||
private String aggregatorPhone;
|
||||
@Value("${sbz.aggregator.account}")
|
||||
private String aggregatorAccount;
|
||||
|
||||
@Override
|
||||
public Object process(Transaction transaction) throws Exception {
|
||||
logger.info("processing integration");
|
||||
String token = restService.getMerchantToken();
|
||||
|
||||
BillPaymentDto request = BillPaymentDto.builder()
|
||||
.type(RequestType.REQUEST)
|
||||
.billClientId(transaction.getBillClientId())
|
||||
.debitRef(transaction.getDebitRef())
|
||||
.debitCurrency(transaction.getDebitCurrency())
|
||||
.amount(transaction.getAmount())
|
||||
.aggregatorId(aggregatorClientId)
|
||||
.debitPhone(aggregatorPhone) // qantra mobile number
|
||||
.debitAccount(aggregatorAccount) // qantra corporate account
|
||||
.creditAccount(transaction.getCreditAccount()) // customer's zesa meter number
|
||||
.creditPhone(transaction.getCreditPhone()) // customer's contact phone number
|
||||
.billName(transaction.getBillName())
|
||||
.authType(transaction.getAuthType())
|
||||
.trace(transaction.getTrace())
|
||||
.build();
|
||||
|
||||
String url = aggregatorUrl + "/merchant/aggregation/transaction";
|
||||
|
||||
LinkedHashMap response = restService.postWithToken(token, url, request, LinkedHashMap.class);
|
||||
|
||||
logger.info("response: {}", response.toString());
|
||||
|
||||
LinkedHashMap body = (LinkedHashMap) response.get("body");
|
||||
|
||||
if (body.get("status").equals("SUCCESS")) {
|
||||
transaction.setStatus(Status.SUCCESS);
|
||||
transaction.setResponseCode((String) body.get("responseCode"));
|
||||
transaction.setAdditionalData(body.get("additionalData"));
|
||||
transactionService.save(transaction);
|
||||
|
||||
AdditionalData additionalData = AdditionalData.builder()
|
||||
.transaction(transaction)
|
||||
.jsonString(Utils.toJson(body.get("additionalData")))
|
||||
.build();
|
||||
additionalDataService.getAdditionalDataRepository().save(additionalData);
|
||||
|
||||
// fetch the corresponding request transaction if it exists
|
||||
Transaction requestTransaction = transactionService.getTransactionRepository()
|
||||
.findByTraceAndType(transaction.getTrace(), RequestType.REQUEST);
|
||||
if (requestTransaction != null) {
|
||||
// create a new additional data for the request transaction
|
||||
AdditionalData requestAdditionalData = AdditionalData.builder()
|
||||
.transaction(requestTransaction)
|
||||
.jsonString(Utils.toJson(body.get("additionalData")))
|
||||
.build();
|
||||
additionalDataService.getAdditionalDataRepository().save(requestAdditionalData);
|
||||
}
|
||||
|
||||
} else {
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setErrorMessage((String) body.get("message"));
|
||||
transaction.setResponseCode((String) body.get("responseCode"));
|
||||
transaction.setErrorMessage((String) body.get("errorMessage"));
|
||||
}
|
||||
|
||||
return transactionService.save(transaction);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object reverse(Transaction transaction) {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object poll(Transaction transaction) {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package zw.qantra.tm.domain.services.processors.integrations;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
||||
import zw.qantra.tm.rest.RestService;
|
||||
|
||||
@Service("REQUEST_ZESA")
|
||||
@RequiredArgsConstructor
|
||||
public class ZesaIntegrationProcessor implements TransactionProcessorInterface {
|
||||
private final RestService restService;
|
||||
|
||||
@Value("${sbz.aggregator.url}")
|
||||
private String aggregatorUrl;
|
||||
|
||||
@Override
|
||||
public Object process(Transaction transaction) throws Exception {
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object reverse(Transaction transaction) {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object poll(Transaction transaction) {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,8 @@ import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
||||
import zw.qantra.tm.rest.RestService;
|
||||
import zw.qantra.tm.utils.Utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -40,10 +42,13 @@ public class MPGSWebPaymentProcessor implements TransactionProcessorInterface {
|
||||
logger.info("processing transaction");
|
||||
String token = restService.getMerchantToken();
|
||||
|
||||
// send total less gateway charges
|
||||
BigDecimal totalAmount = transaction.getAmount().add(transaction.getTax()).add(transaction.getCharge());
|
||||
|
||||
String concat = clientId+
|
||||
encryptionKey+
|
||||
transaction.getCreditPhone()+
|
||||
transaction.getAmount();
|
||||
totalAmount;
|
||||
|
||||
String hex = getHash(concat);
|
||||
|
||||
@@ -51,7 +56,7 @@ public class MPGSWebPaymentProcessor implements TransactionProcessorInterface {
|
||||
.clientId(clientId)
|
||||
.clientSecret(clientSecret)
|
||||
.phone(transaction.getCreditPhone())
|
||||
.amount(transaction.getAmount().toString())
|
||||
.amount(totalAmount.toString())
|
||||
.currency(transaction.getDebitCurrency().toString())
|
||||
.hash(hex)
|
||||
.authType("WEB")
|
||||
@@ -59,7 +64,7 @@ public class MPGSWebPaymentProcessor implements TransactionProcessorInterface {
|
||||
.action("PAYMENT")
|
||||
.sandbox(true)
|
||||
.paymentProcessorLabel("MPGS")
|
||||
.responseUrl("https://www.google.com")
|
||||
.responseUrl("https://stewardbank.co.zw")
|
||||
.build();
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
@@ -12,6 +12,11 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Utils {
|
||||
public static <T> T deepCopy(Object object, Class<T> clazz) {
|
||||
String json = toJson(object);
|
||||
return fromJson(json, clazz);
|
||||
}
|
||||
|
||||
public static String hashPassword(String password) {
|
||||
try {
|
||||
// Create a MessageDigest instance for SHA-256
|
||||
|
||||
Reference in New Issue
Block a user