Strategy pattern in Spring boot application for payment gateway and methods - spring-boot

I have a controller where I am being passed a gatewayid for different providers. I have a BasePaymentService interface which is implemented by Payu service and Razorpay service for now.
I want to avoid using if condition for and have the strategy added without changing code and have the container inject correct strategy.
How do I add a strategy pattern here?
Would callback method be also a part of strategy here?
How do I account for different payment methods here? (cards, wallets)
BasePaymentService
public interface BasePaymentService {
PaymentDetail makePayment(PaymentDetail detail);
Payment callbackPayment(PaymentCallback detail);
}
Concrete PaymentService
#Service
#Slf4j
public class PayuPaymentService implements BasePaymentService {
#Autowired
PaymentRepository paymentRepository;
public PaymentDetail makePayment(PaymentDetail paymentDetail) {
PaymentUtil paymentUtil = new PaymentUtil();
paymentDetail = paymentUtil.populatePaymentDetail(paymentDetail);
savePaymentDetail(paymentDetail);
return paymentDetail;
}
#Override
public Payment callbackPayment(PaymentCallback paymentResponse) {
log.info("inside callback service >>>>>");
String msg = "Transaction failed.";
Payment payment = paymentRepository.findByTxnId(paymentResponse.getTxnid());
if(payment != null) {
log.info("in condition callback service");
//TODO validate the hash
PaymentStatus paymentStatus = null;
if(paymentResponse.getStatus().equals("failure")){
paymentStatus = PaymentStatus.Failed;
}else if(paymentResponse.getStatus().equals("success")) {
paymentStatus = PaymentStatus.Success;
msg = "Transaction success";
}
payment.setPaymentStatus(paymentStatus);
payment.setMihpayId(paymentResponse.getMihpayid());
payment.setMode(paymentResponse.getMode());
paymentRepository.save(payment);
}
return payment;
}
private void savePaymentDetail(PaymentDetail paymentDetail) {
log.info("in proceedPayment save");
Payment payment = new Payment();
payment.setAmount(Double.parseDouble(paymentDetail.getAmount()));
payment.setEmail(paymentDetail.getEmail());
payment.setName(paymentDetail.getName());
payment.setPaymentDate(new Date());
payment.setPaymentStatus(PaymentStatus.Pending);
payment.setPhone(paymentDetail.getPhone());
payment.setProductInfo(paymentDetail.getProductInfo());
payment.setTxnId(paymentDetail.getTxnId());
paymentRepository.save(payment);
}
}
Controller
#Api(value = "swipe: payment Service", tags = "Example")
#Validated
#RestController
#Slf4j
#RequestMapping(value = CommonConstants.BASE_CONTEXT_PATH)
public class CommonController {
#Autowired
private BasePaymentService paymentService;
#CrossOrigin(origins = "*")
#PostMapping(path = "/payment-details")
public #ResponseBody
PaymentDetail proceedPayment(#RequestBody PaymentDetail paymentDetail){
if(paymentDetail.getGatewayId().equalsIgnoreCase("payu") ){
paymentService.makePayment(paymentDetail);
}
else if(paymentDetail.getGatewayId().equalsIgnoreCase("rp")){
paymentService.makePayment(paymentDetail);
}
return paymentDetail;
}
#CrossOrigin(origins = "*" )
#RequestMapping(path = "/payment-response", method = RequestMethod.POST)
public #ResponseBody
Payment payuCallback(#RequestParam String mihpayid, #RequestParam String status, #RequestParam PaymentMode mode, #RequestParam String txnid, #RequestParam String hash, #RequestParam String amount, #RequestParam String productinfo, #RequestParam String firstname, #RequestParam String lastname, #RequestParam String email, #RequestParam String phone, #RequestParam String error, #RequestParam String bankcode, #RequestParam String PG_TYPE, #RequestParam String bank_ref_num, #RequestParam String unmappedstatus){
log.info("inside callback");
PaymentCallback paymentCallback = new PaymentCallback();
paymentCallback.setMihpayid(mihpayid);
paymentCallback.setTxnid(txnid);
paymentCallback.setMode(mode);
paymentCallback.setHash(hash);
paymentCallback.setStatus(status);
return paymentService.callbackPayment(paymentCallback);
}
}

Related

Can spring map POST parameters by a way other than #RequestBody

I am using #RestControllers with an application where all requests are POST requests ... As I learned from this post , you can't map individual post parameters to individual method arguments, rather you need to wrap all the parameters in an object and then use this object as a method parameter annotated with #RequestBody thus
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestParam(value="idNumber") String idNumber , #RequestParam(value="applicationId") String applicationId) {
return customerService.requestOTP(idNumber, applicationId);
will not work with a POST request of body {"idNumber":"345","applicationId":"64536"}
MY issue is that I have A LOT of POST requests , each with only one or two parameters, It will be tedious to create all these objects just to receive the requests inside ... so is there any other way similar to the way where get request parameters (URL parameters) are handled ?
Yes there are two ways -
first - the way you are doing just you need to do is append these parameter with url, no need to give them in body.
url will be like - baseurl+/requestotp?idNumber=123&applicationId=123
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestParam(value="idNumber") String idNumber , #RequestParam(value="applicationId") String applicationId) {
return customerService.requestOTP(idNumber, applicationId);
second- you can use map as follows
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody Map<String,Object> body) {
return customerService.requestOTP(body.get("idNumber").toString(), body.get("applicationId").toString());
I have change your code please check it
DTO Class
public class DTO1 {
private String idNumber;
private String applicationId;
public String getIdNumber() {
return idNumber;
}
public void setIdNumber(String idNumber) {
this.idNumber = idNumber;
}
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
}
Rest Controller Method
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody DTO1 dto){
System.out.println(dto.getApplicationId()+" (------) "+dto.getIdNumber());
return "";
}
Request Type -- application/json
{"idNumber":"345","applicationId":"64536"}
OR
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody String dto){
System.out.println(dto);
return "";
}

RestController return CompletableFuture<List>

I try to create rest controller which return all products. I want to use CompletableFuture to return list with product.
I have async request to spring data
#Async
#Query("select product from Product product")
CompletableFuture<List<Product>> findAllAsync();
and controller
#Async
#RequestMapping(path = "/products", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
CompletableFuture<List<ProductData>> loadAllProducts2(){
return this.products.findAllAsync()
.thenApplyAsync(Collection::stream)
.thenApplyAsync(s -> s.map(Product::data))
.thenApplyAsync(s -> s.collect(Collectors.toList()));
}
ProgramData is simple DTO:
public final class ProductData {
private final String name;
private final String label;
public ProductData(String name, String label) {
this.name = name;
this.label = label;
}
public String getName() {
return this.name;
}
public String getLabel() {
return this.label;
}
}
Spring return nothing, in log output is :
o.s.b.a.e.mvc.EndpointHandlerMapping : Did not find handler method for [/products]
Any ideas what's wrong ?
I removed target directory and It did work.

Spring REST Service Controller not being validate by #PathVariable and #Valid

#Controller
#EnableWebMvc
#Validated
public class ChildController extends ParentController<InterfaceController> implements InterfaceController{
#Override
#RequestMapping(value = "/map/{name}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public List<Friends> getAllFriendsByName(
#Valid
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable("name") String name,
#RequestParam(value="pageSize", required=false) String pageSize,
#RequestParam(value="pageNumber", required=false) String pageNumber,
HttpServletRequest request) throws BasicException {
//Some logic over here;
return results;
}
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
Hi, I am trying to do pretty basic validation for a spring request parameter but it just doesn't seem to call the Exception handler, could someone point me into the right direction
P.S. I keep getting NoHandlerFoundException
Spring doesn't support #PathVariable to be validated using #Valid. However, you can do custom validation in your handler method or if you insist on using #Valid then write a custom editor, convert your path variable value to an object, use JSR 303 bean validation and then use #Valid on that object. That might actually work.
Edit:
Here's a third approach. You can actually trick spring to treat your path variable as a model attribute and then validate it.
1. Write a custom validator for your path variable
2. Construct a #ModelAttribute for your path variable and then use #Validator (yes not #Valid as it doesn't let you specify a validator) on that model attribute.
#Component
public class NameValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return String.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
String name = (String) target;
if(!StringUtils.isValidName(name)) {
errors.reject("name.invalid.format");
}
}
}
#RequestMapping(value = "/path/{name}", method = RequestMethod.GET)
public List<Friend> getAllFriendsByName(#ModelAttribute("name") #Validated(NameValidator.class) String name) {
// your code
return friends;
}
#ModelAttribute("name")
private String nameAsModelAttribute(#PathVariable String name) {
return name;
}

#PathVariable Validation in Spring 4

How can i validate my path variable in spring. I want to validate id field, since its only single field i do not want to move to a Pojo
#RestController
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(#PathVariable String id) {
/// Some code
}
}
I tried doing adding validation to the path variable but its still not working
#RestController
#Validated
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(
#Valid
#Nonnull
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable String id) {
/// Some code
}
}
You need to create a bean in your Spring configuration:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
You should leave the #Validated annotation on your controller.
And you need an Exceptionhandler in your MyController class to handle theConstraintViolationException :
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
After those changes you should see your message when the validation hits.
P.S.: I just tried it with your #Size validation.
To archive this goal I have apply this workaround for getting a response message equals to a real Validator:
#GetMapping("/check/email/{email:" + Constants.LOGIN_REGEX + "}")
#Timed
public ResponseEntity isValidEmail(#Email #PathVariable(value = "email") String email) {
return userService.getUserByEmail(email).map(user -> {
Problem problem = Problem.builder()
.withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
.withTitle("Method argument not valid")
.withStatus(Status.BAD_REQUEST)
.with("message", ErrorConstants.ERR_VALIDATION)
.with("fieldErrors", Arrays.asList(new FieldErrorVM("", "isValidEmail.email", "not unique")))
.build();
return new ResponseEntity(problem, HttpStatus.BAD_REQUEST);
}).orElse(
new ResponseEntity(new UtilsValidatorResponse(EMAIL_VALIDA), HttpStatus.OK)
);
}

Assitance regarding JUnit Testing for Spring Controller Dao

I am new to Junit.Please help me to test Spring hibernate Controller with ContentType is application/json
Below is my Controller
#Controller
#RequestMapping(value="/users")
public class UserServiceImpl implements UserService{
private static Logger logger = Logger.getLogger(UserService.class);
private UserDao userDao;
#Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
#RequestMapping(method = RequestMethod.POST,headers = "content-type=application/json")
#ResponseBody
public long addUser(#RequestBody UserForm user) {
logger.info("Creating new user {}"+ user);
return userDao.create(user);
}
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
public User findUser(#PathVariable(value = "userId") String userId) {
logger.info("Reading user with id {}"+ userId);
User user = userDao.find(userId);
Validate.isTrue(user != null, "Unable to find user with id: " + userId);
return user;
}
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT,headers = "content-type=application/json")
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable(value = "userId") String userId, #RequestBody UserForm user) {
logger.info("Updating user with id {} with {}"+ userId +"->"+ user);
Validate.isTrue(userId.equals(user.getUserId()), "userId doesn't match URL userId: " + user.getUserId());
userDao.update(user);
}
#RequestMapping(value = "/{userId}", method = RequestMethod.DELETE)
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public void deleteUser(#PathVariable(value = "userId") String userId) {
logger.info("Deleting user with id {}"+ userId);
userDao.delete(userId);
}
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public List<User> list() {
logger.info("Listing users");
return new ArrayList<User>(userDao.getUsers());
}
}
Can any one Send me the Junit Test case for Any one of the CRUD operations.
Thanks in Advance
Srikanth
If you just want to test your controller, then I would say that mock the DAO. You don't have to care about content types and such because Spring takes care of them. You are interested what the controller method is returning. If you want to test your DAO that User actually is saved to database, that's another story.
But just for testing that controller does what it is supposed to, something like this for example. Example uses EasyMock. I haven't compiled this example so it might have typos.
import static org.easymock.EasyMock.createNiceMock;
public class ControllerTest {
private UserServiceImpl userService;
private UserDao userDaoMock;
#Before
public void setup() {
userDaoMock = createNiceMock(UserDao.class);
userService = new UserServiceImpl();
userSerivce.setUserDao(userDaoMock);
}
#Test
public void testAddUser() {
UserForm userForm = new UserForm();
long expectedResult = 5L;
expect(userDaoMock.create(userForm)).andReturn(expectedResult);
replay(userDaoMock);
long actualResult = userService.addUser(userForm);
verify(userDaoMock);
assertEquals(expectedResult, actualResult);
}
}

Resources