176 lines
8.6 KiB
Java

package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CheckoutModel;
import fr.titionfire.ffsaf.data.model.LogModel;
import fr.titionfire.ffsaf.data.repository.CheckoutRepository;
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
import fr.titionfire.ffsaf.rest.client.HelloAssoService;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsRequest;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsResponse;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutMetadata;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.scheduler.Scheduled;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@WithSession
@ApplicationScoped
public class CheckoutService {
@Inject
CheckoutRepository repository;
@Inject
LicenceRepository licenceRepository;
@Inject
MembreService membreService;
@Inject
LicenceService licenceService;
@Inject
LoggerService ls;
@RestClient
HelloAssoService helloAssoService;
@ConfigProperty(name = "frontRootUrl")
String frontRootUrl;
@ConfigProperty(name = "unitLicencePrice")
int unitLicencePrice;
@ConfigProperty(name = "helloasso.organizationSlug")
String organizationSlug;
public Uni<Boolean> canDeleteLicence(long id) {
return repository.find("?1 IN licenseIds", id).count().map(count -> count == 0);
}
public Uni<String> create(List<Long> ids, SecurityCtx securityCtx) {
return membreService.getByAccountId(securityCtx.getSubject())
.call(membreModel -> Mutiny.fetch(membreModel.getClub()))
.chain(membreModel -> {
CheckoutModel model = new CheckoutModel();
model.setMembre(membreModel);
model.setLicenseIds(ids);
model.setPaymentStatus(CheckoutModel.PaymentStatus.UNKNOW);
return Panache.withTransaction(() -> repository.persist(model));
})
.chain(checkoutModel -> {
CheckoutIntentsRequest request = new CheckoutIntentsRequest();
request.setTotalAmount(unitLicencePrice * checkoutModel.getLicenseIds().size());
request.setInitialAmount(unitLicencePrice * checkoutModel.getLicenseIds().size());
request.setItemName("%d licences %d-%d pour %s".formatted(checkoutModel.getLicenseIds().size(),
Utils.getSaison(), Utils.getSaison() + 1, checkoutModel.getMembre().getClub().getName()));
request.setBackUrl(frontRootUrl + "/club/member/pay");
request.setErrorUrl(frontRootUrl + "/club/member/pay/error");
request.setReturnUrl(frontRootUrl + "/club/member/pay/return");
request.setContainsDonation(false);
request.setPayer(new CheckoutIntentsRequest.Payer(checkoutModel.getMembre().getFname(),
checkoutModel.getMembre().getLname(), checkoutModel.getMembre().getEmail()));
request.setMetadata(new CheckoutMetadata(checkoutModel.getId()));
return helloAssoService.checkout(organizationSlug, request)
.call(response -> {
checkoutModel.setCheckoutId(response.getId());
return Panache.withTransaction(() -> repository.persist(checkoutModel));
});
})
.onFailure().transform(t -> new DInternalError(t.getMessage()))
.map(CheckoutIntentsResponse::getRedirectUrl);
}
public Uni<Response> paymentStatusChange(String state, CheckoutMetadata metadata) {
return repository.findById(metadata.getCheckoutDBId())
.chain(checkoutModel -> {
CheckoutModel.PaymentStatus newStatus = CheckoutModel.PaymentStatus.valueOf(state.toUpperCase());
Uni<?> uni = Uni.createFrom().nullItem();
if (checkoutModel.getPaymentStatus().equals(newStatus))
return uni;
if (newStatus.equals(CheckoutModel.PaymentStatus.AUTHORIZED)) {
for (Long id : checkoutModel.getLicenseIds()) {
uni = uni.chain(__ -> licenceRepository.findById(id)
.onFailure().recoverWithNull()
.call(licenceModel -> {
if (licenceModel == null) {
ls.logAnonymous(LogModel.ActionType.UPDATE, LogModel.ObjectType.Licence,
"Fail to save payment for licence (checkout n°" + checkoutModel.getCheckoutId() + ")",
"", id);
return Uni.createFrom().nullItem();
}
ls.logUpdateAnonymous("Paiement de la licence", licenceModel);
licenceModel.setPay(true);
if (licenceModel.getCertificate() != null && licenceModel.getCertificate()
.length() > 3) {
if (!licenceModel.isValidate())
ls.logUpdateAnonymous("Validation automatique de la licence",
licenceModel);
return licenceService.validateLicences(licenceModel);
} else {
return Panache.withTransaction(
() -> licenceRepository.persist(licenceModel));
}
}));
}
} else if (checkoutModel.getPaymentStatus().equals(CheckoutModel.PaymentStatus.AUTHORIZED)) {
for (Long id : checkoutModel.getLicenseIds()) {
uni = uni.chain(__ -> licenceRepository.findById(id)
.onFailure().recoverWithNull()
.call(licenceModel -> {
if (licenceModel == null)
return Uni.createFrom().nullItem();
ls.logUpdateAnonymous("Annulation automatique du paiement de la licence",
licenceModel);
licenceModel.setPay(false);
if (licenceModel.isValidate())
ls.logUpdateAnonymous(
"Annulation automatique de la validation de la licence",
licenceModel);
licenceModel.setValidate(false);
return Panache.withTransaction(() -> licenceRepository.persist(licenceModel));
}));
}
}
uni = uni.call(__ -> ls.append());
checkoutModel.setPaymentStatus(newStatus);
return uni.chain(__ -> Panache.withTransaction(() -> repository.persist(checkoutModel)));
})
.onFailure().invoke(Throwable::printStackTrace)
.map(__ -> Response.ok().build());
}
@Scheduled(cron = "0 0 * * * ?")
Uni<Void> everyHours() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR, -1);
Date dateLimit = calendar.getTime();
return repository.delete("creationDate < ?1 AND (checkoutId IS NULL OR paymentStatus = ?2)", dateLimit,
CheckoutModel.PaymentStatus.UNKNOW)
.map(__ -> null);
}
}