Skip to content

Commit

Permalink
Merge pull request #112 from flickmatch/phonepe-integration
Browse files Browse the repository at this point in the history
Complete logic to initiate payment
  • Loading branch information
abhimanyu-fm authored Nov 16, 2023
2 parents 90eadd5 + 3ac52c9 commit e702b29
Show file tree
Hide file tree
Showing 17 changed files with 309 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.flickmatch.platform.bean.confugration;

import com.phonepe.sdk.pg.Env;
import com.phonepe.sdk.pg.payments.v1.PhonePePaymentClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ProxyConfiguration {

@Value("${phonepe.merchant.id}")
private String merchantId;
@Value("${phonepe.saltkey}")
private String saltKey;
@Value("${phonepe.saltkey.index}")
private Integer saltIndex;

@Bean
PhonePePaymentClient phonePePaymentClient() {
return new PhonePePaymentClient(merchantId, saltKey, saltIndex, Env.PROD, true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.flickmatch.platform.dynamodb.model;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import lombok.AllArgsConstructor;
import lombok.Builder;

import java.util.List;

@DynamoDBTable(tableName="PaymentRequest")
@Builder
@AllArgsConstructor
public class PaymentRequest {

private String merchantTransactionId;
private String uniqueEventId;
private List<Event.PlayerDetails> playerDetailsList;
/*
status values can be INITIATED, PAID, FAILED
*/
private String status;

@DynamoDBHashKey(attributeName="merchantTransactionId")
public String getMerchantTransactionId() {
return merchantTransactionId;
}

public void setMerchantTransactionId(String merchantTransactionId) {
this.merchantTransactionId = merchantTransactionId;
}

@DynamoDBAttribute(attributeName="uniqueEventId")
public String getUniqueEventId() {
return uniqueEventId;
}

public void setUniqueEventId(String uniqueEventId) {
this.uniqueEventId = uniqueEventId;
}

@DynamoDBAttribute(attributeName="playerDetailsList")
public List<Event.PlayerDetails> getPlayerDetailsList() {
return playerDetailsList;
}

public void setPlayerDetailsList(List<Event.PlayerDetails> playerDetailsList) {
this.playerDetailsList = playerDetailsList;
}

@DynamoDBAttribute(attributeName="status")
public String getStatus() {
return status;
}

public void setStatus(String status) {
this.status = status;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.flickmatch.platform.dynamodb.model.City;
import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.flickmatch.platform.dynamodb.repository;

import com.flickmatch.platform.dynamodb.model.PaymentRequest;
import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.springframework.data.repository.CrudRepository;

@EnableScan
public interface PaymentRequestRepository extends CrudRepository<PaymentRequest, String> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
Expand Down Expand Up @@ -56,21 +56,34 @@ public Event createEvent(CreateEventInput input, boolean shouldValidateStartTime

public void joinEvent(JoinEventInput input) {
int index = 0;
//TODO: Remove hardcoded value once whatsApp is not used for joining event
String date = input.getEventId();
String[] parts = date.split("-");
try{
date = parts[0] + "-" + parts[1] + "-" + parts[2];
index = Integer.parseInt(parts[3]);
}
catch (NumberFormatException e)
{
System.out.println("Error: The given string cannot be converted to an integer.");
String date = null;
String cityId = null;
if (StringUtils.hasText(input.getUniqueEventId())) {
String[] parts = input.getUniqueEventId().split("-");
try{
cityId = parts[0];
date = parts[1] + "-" + parts[2] + "-" + parts[3];
index = Integer.parseInt(parts[4]);
} catch (NumberFormatException e) {
log.error(input.getUniqueEventId());
throw new IllegalArgumentException("Input is invalid");
}
} else {
String[] parts = input.getEventId().split("-");
try{
date = parts[0] + "-" + parts[1] + "-" + parts[2];
index = Integer.parseInt(parts[3]);
cityId = input.getCityId();
} catch (NumberFormatException e) {
log.error(input.getEventId());
throw new IllegalArgumentException("Input is invalid");
}
}
log.info(date);
log.info(index);
log.info(cityId);
Optional<Event> eventsInCity =
eventRepository.findById(new Event.EventId(input.getCityId(),date));
eventRepository.findById(new Event.EventId(cityId, date));
if (eventsInCity.isPresent()) {
final int finalIndex = index;
Optional<Event.EventDetails> selectedEvent = eventsInCity.get().getEventDetailsList()
Expand Down Expand Up @@ -111,7 +124,7 @@ public List<com.flickmatch.platform.graphql.type.Event> getEvents(String cityId,
List<com.flickmatch.platform.graphql.type.Event> dailyEventList =
eventData.get().getEventDetailsList().stream()
.filter(eventDetails -> eventDetails.getStartTime().after(currentTime))
.map(eventDetails -> mapEventToGQLType(eventDetails, formattedDate, localTimeZone))
.map(eventDetails -> mapEventToGQLType(eventDetails, formattedDate, localTimeZone, cityId))
.toList();
eventList.addAll(dailyEventList);
}
Expand All @@ -132,14 +145,56 @@ public List<com.flickmatch.platform.graphql.type.Event> getPastEvents(String cit
eventDetails.getStartTime().before(currentTime) &&
eventDetails.getStartTime().after(dateBeforeInDays)
).map(eventDetails ->
mapEventToGQLType(eventDetails, event.getDate(), localTimeZone)
mapEventToGQLType(eventDetails, event.getDate(), localTimeZone, cityId)
).toList();
pastEventList.addAll(pastEventsInCity);
}
});
return pastEventList;
}

/**
* Gets amount for event.
*
* @param uniqueEventId the unique event id
* @return the amount for event in paise
*/
public long getAmountForEvent(final String uniqueEventId) {
int index = 0;
String date = null;
String cityId = null;
if (StringUtils.hasText(uniqueEventId)) {
String[] parts = uniqueEventId.split("-");
try{
cityId = parts[0];
date = parts[1] + "-" + parts[2] + "-" + parts[3];
index = Integer.parseInt(parts[4]);
} catch (NumberFormatException e) {
log.error(uniqueEventId);
throw new IllegalArgumentException("Input is invalid");
}
} else {
log.error(uniqueEventId);
throw new IllegalArgumentException("Input is invalid");
}
Optional<Event> eventsInCity =
eventRepository.findById(new Event.EventId(cityId, date));
if (eventsInCity.isPresent()) {
final int finalIndex = index;
Optional<Event.EventDetails> selectedEvent = eventsInCity.get().getEventDetailsList()
.stream().filter(eventDetails -> eventDetails.getIndex().equals(finalIndex)).findFirst();
if (selectedEvent.isPresent()) {
BigDecimal amount = BigDecimal.valueOf(selectedEvent.get().getCharges());
amount = amount.multiply(BigDecimal.valueOf(100));
return amount.longValue();
} else {
throw new IllegalArgumentException("Invalid Event selected");
}
} else {
throw new IllegalArgumentException("Invalid Event selected");
}
}

private Event.EventDetails buildEventDetails(CreateEventInput input, int index) throws ParseException {
List<SportsVenue> sportsVenueList = sportsVenueBuilder.getSportsVenues(input.getCityId());
String currency = getCurrencyForCity(input.getCityId());
Expand Down Expand Up @@ -189,8 +244,9 @@ private String getPaymentUrlForEvent(SportsVenue sportsVenue, Double amount) {
return stripePaymentLinkForAmount.get().getUrl();
}

private com.flickmatch.platform.graphql.type.Event mapEventToGQLType(Event.EventDetails eventDetails, String date, String localTimeZone) {
private com.flickmatch.platform.graphql.type.Event mapEventToGQLType(Event.EventDetails eventDetails, String date, String localTimeZone, String cityId) {
String eventId = date + "-" + eventDetails.getIndex();
String uniqueEventId = cityId + "-" + date + "-" + eventDetails.getIndex();
int players = eventDetails.getReservedPlayersCount() / 2;
String eventType = players + "v" + players;
//Todo: clean up this field from schema
Expand All @@ -203,6 +259,7 @@ private com.flickmatch.platform.graphql.type.Event mapEventToGQLType(Event.Event
.currency(eventDetails.getCurrency())
.endTime(eventDetails.getEndTime())
.eventId(eventId)
.uniqueEventId(uniqueEventId)
.displayTitle(title)
.date(getFormattedEventDate(eventDetails.getStartTime(), localTimeZone))
.time(getFormattedEventTime(eventDetails.getStartTime(), eventDetails.getEndTime(), localTimeZone))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.flickmatch.platform.graphql.builder;

import com.flickmatch.platform.dynamodb.model.Event;
import com.flickmatch.platform.dynamodb.model.PaymentRequest;
import com.flickmatch.platform.dynamodb.repository.PaymentRequestRepository;
import com.flickmatch.platform.graphql.input.PlayerInput;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

@Service
public class PaymentRequestBuilder {

@Autowired
private PaymentRequestRepository paymentRequestRepository;

public PaymentRequest createPaymentRequest(final String merchantTransactionId,
final String uniqueEventId,
final List<PlayerInput> playerInputList) {
List<Event.PlayerDetails> playerDetailsList = playerInputList.stream()
.map(playerInput -> Event.PlayerDetails.builder()
.name(playerInput.getName())
.waNumber(playerInput.getWaNumber())
.build())
.collect(Collectors.toList());
PaymentRequest paymentRequest = PaymentRequest.builder()
.merchantTransactionId(merchantTransactionId)
.uniqueEventId(uniqueEventId)
.playerDetailsList(playerDetailsList)
.status("INITIATED")
.build();
return paymentRequestRepository.save(paymentRequest);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.flickmatch.platform.graphql.controller;

import com.flickmatch.platform.graphql.builder.EventBuilder;
import com.flickmatch.platform.graphql.builder.PaymentRequestBuilder;
import com.flickmatch.platform.graphql.input.InitiatePaymentInput;
import com.flickmatch.platform.graphql.type.InitiatePaymentOutput;
import com.phonepe.sdk.pg.Env;
import com.phonepe.sdk.pg.common.http.PhonePeResponse;
import com.phonepe.sdk.pg.payments.v1.PhonePePaymentClient;
import com.phonepe.sdk.pg.payments.v1.models.request.PgPayRequest;
import com.phonepe.sdk.pg.payments.v1.models.response.PayPageInstrumentResponse;
import com.phonepe.sdk.pg.payments.v1.models.response.PgPayResponse;
import com.flickmatch.platform.proxy.PhonePeProxy;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.stereotype.Controller;
Expand All @@ -19,50 +17,31 @@
@Log4j2
public class PaymentController {

@Autowired
PhonePeProxy phonePeProxy;
@Autowired
PaymentRequestBuilder paymentRequestBuilder;
@Autowired
EventBuilder eventBuilder;

@MutationMapping
public InitiatePaymentOutput initiatePayment(@Argument InitiatePaymentInput input) {
String paymentLink = "https://example.com/payment/link";
try {
paymentLink = initiate();
String merchantTransactionId = UUID.randomUUID().toString().substring(0,34);
long eventAmount = eventBuilder.getAmountForEvent(input.getUniqueEventId());
long amount = eventAmount * input.getPlayerInputList().size();
String paymentLink = phonePeProxy.initiatePayment(merchantTransactionId, amount);
paymentRequestBuilder.createPaymentRequest(merchantTransactionId,
input.getUniqueEventId(), input.getPlayerInputList());
return InitiatePaymentOutput.builder()
.paymentLink(paymentLink)
.isInitiated(true)
.build();
} catch (Exception exception) {
exception.printStackTrace();
log.error("Couldn't initiate payment", exception);
return InitiatePaymentOutput.builder()
.isInitiated(false)
.build();
}
InitiatePaymentOutput output = new InitiatePaymentOutput(paymentLink);
output.setPaymentLink(paymentLink);
return output;
}

/*
Calls PhonePe Pay API using sdk
https://developer.phonepe.com/v1/reference/pay-api-1
https://developer.phonepe.com/v1/reference/java-standard-pay-page
*/
private String initiate() {
String merchantId = "M1FPXNBFIGDG";
String saltKey = "e7a5693b-0ccf-47ac-a258-c5d77e701e2c";
Integer saltIndex = Integer.valueOf(2);
Env env = Env.PROD;
boolean shouldPublishEvents = true;
PhonePePaymentClient phonepeClient = new PhonePePaymentClient(merchantId, saltKey, saltIndex, env, shouldPublishEvents);

String merchantTransactionId = UUID.randomUUID().toString().substring(0,34);
long amount = 200;
String callbackUrl = "https://service.flickmatch.in:8443/platform-0.0.1-SNAPSHOT/payment";
String merchantUserId = "merchantUserId";

PgPayRequest pgPayRequest = PgPayRequest.PayPagePayRequestBuilder()
.amount(amount)
.merchantId(merchantId)
.merchantTransactionId(merchantTransactionId)
.callbackUrl(callbackUrl)
.callbackMode("REDIRECT")
.redirectUrl("https://play.flickmatch.in/")
.redirectMode("REDIRECT")
.merchantUserId(merchantUserId)
.build();

PhonePeResponse<PgPayResponse> payResponse = phonepeClient.pay(pgPayRequest);
PayPageInstrumentResponse payPageInstrumentResponse = (PayPageInstrumentResponse) payResponse.getData().getInstrumentResponse();
return payPageInstrumentResponse.getRedirectInfo().getUrl();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import lombok.Builder;
import lombok.Getter;

import java.util.List;

@Builder
@Getter
public class InitiatePaymentInput {
String eventId;
String cityId;
PlayerInput player;
String uniqueEventId;
List<PlayerInput> playerInputList;
}
Loading

0 comments on commit e702b29

Please sign in to comment.