package dev.lions.unionflow.server.resource; import dev.lions.unionflow.server.api.dto.paiement.request.CreatePaiementRequest; import dev.lions.unionflow.server.api.dto.paiement.request.DeclarerPaiementManuelRequest; import dev.lions.unionflow.server.api.dto.paiement.request.InitierPaiementEnLigneRequest; import dev.lions.unionflow.server.api.dto.paiement.response.IntentionStatutResponse; import dev.lions.unionflow.server.api.dto.paiement.response.PaiementGatewayResponse; import dev.lions.unionflow.server.api.dto.paiement.response.PaiementResponse; import dev.lions.unionflow.server.api.dto.paiement.response.PaiementSummaryResponse; import dev.lions.unionflow.server.service.PaiementService; import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; import jakarta.validation.Valid; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.List; import java.util.UUID; import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.jboss.logging.Logger; /** * Resource REST pour la gestion des paiements (Wave Checkout et paiements manuels). * *

Endpoints principaux : *

* * @author UnionFlow Team * @version 3.0 * @since 2026-04-13 */ @Path("/api/paiements") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE", "USER"}) @Tag(name = "Paiements", description = "Paiements de cotisations — Wave Checkout et manuel") public class PaiementResource { private static final Logger LOG = Logger.getLogger(PaiementResource.class); @Inject PaiementService paiementService; // ── Lecture ─────────────────────────────────────────────────────────────── @GET @Path("/{id}") public Response trouverParId(@PathParam("id") UUID id) { LOG.infof("GET /api/paiements/%s", id); PaiementResponse result = paiementService.trouverParId(id); return Response.ok(result).build(); } @GET @Path("/reference/{numeroReference}") public Response trouverParNumeroReference( @PathParam("numeroReference") String numeroReference) { LOG.infof("GET /api/paiements/reference/%s", numeroReference); PaiementResponse result = paiementService.trouverParNumeroReference(numeroReference); return Response.ok(result).build(); } @GET @Path("/membre/{membreId}") @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION"}) public Response listerParMembre(@PathParam("membreId") UUID membreId) { LOG.infof("GET /api/paiements/membre/%s", membreId); List result = paiementService.listerParMembre(membreId); return Response.ok(result).build(); } @GET @Path("/mon-historique") @RolesAllowed({"MEMBRE", "ADMIN", "ADMIN_ORGANISATION"}) public Response getMonHistoriquePaiements( @QueryParam("limit") @DefaultValue("20") int limit) { LOG.infof("GET /api/paiements/mon-historique?limit=%d", limit); List result = paiementService.getMonHistoriquePaiements(limit); return Response.ok(result).build(); } // ── Administration ──────────────────────────────────────────────────────── @POST @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION"}) public Response creerPaiement(@Valid CreatePaiementRequest request) { LOG.infof("POST /api/paiements — référence: %s", request.numeroReference()); PaiementResponse result = paiementService.creerPaiement(request); return Response.status(Response.Status.CREATED).entity(result).build(); } @POST @Path("/{id}/valider") @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION"}) public Response validerPaiement(@PathParam("id") UUID id) { LOG.infof("POST /api/paiements/%s/valider", id); PaiementResponse result = paiementService.validerPaiement(id); return Response.ok(result).build(); } @POST @Path("/{id}/annuler") @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"}) public Response annulerPaiement(@PathParam("id") UUID id) { LOG.infof("POST /api/paiements/%s/annuler", id); PaiementResponse result = paiementService.annulerPaiement(id); return Response.ok(result).build(); } // ── Flux Wave Checkout (QR code web) ────────────────────────────────────── /** * Initie un paiement Wave via Checkout QR code. * Le web encode le {@code waveLaunchUrl} en QR code, l'utilisateur le scanne * depuis l'app Wave. Après confirmation, Wave redirige vers la success URL. */ @POST @Path("/initier-paiement-en-ligne") @RolesAllowed({"MEMBRE", "ADMIN", "ADMIN_ORGANISATION", "USER"}) public Response initierPaiementEnLigne(@Valid InitierPaiementEnLigneRequest request) { LOG.infof("POST /api/paiements/initier-paiement-en-ligne — cotisation: %s, méthode: %s", request.cotisationId(), request.methodePaiement()); PaiementGatewayResponse result = paiementService.initierPaiementEnLigne(request); return Response.status(Response.Status.CREATED).entity(result).build(); } /** * Polling du statut d'une intention de paiement Wave. * Appelé toutes les 3 secondes par le web pendant que l'utilisateur scanne le QR code. * Retourne {@code confirme=true} dès que Wave confirme le paiement. */ @GET @Path("/statut-intention/{intentionId}") @RolesAllowed({"MEMBRE", "ADMIN", "ADMIN_ORGANISATION", "USER"}) public Response getStatutIntention(@PathParam("intentionId") UUID intentionId) { LOG.infof("GET /api/paiements/statut-intention/%s", intentionId); IntentionStatutResponse result = paiementService.getStatutIntention(intentionId); return Response.ok(result).build(); } // ── Flux manuel ─────────────────────────────────────────────────────────── @POST @Path("/declarer-manuel") @RolesAllowed({"MEMBRE", "ADMIN", "ADMIN_ORGANISATION"}) public Response declarerPaiementManuel(@Valid DeclarerPaiementManuelRequest request) { LOG.infof("POST /api/paiements/declarer-manuel — cotisation: %s, méthode: %s", request.cotisationId(), request.methodePaiement()); PaiementResponse result = paiementService.declarerPaiementManuel(request); return Response.status(Response.Status.CREATED).entity(result).build(); } }