feat: HelloAsso payment
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package fr.titionfire.ffsaf.rest.client;
|
||||
|
||||
import fr.titionfire.ffsaf.rest.client.dto.TokenResponse;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@Path("/")
|
||||
@RegisterRestClient(configKey = "helloasso-auth")
|
||||
public interface HelloAssoAuthClient {
|
||||
|
||||
@POST
|
||||
@Path("/token")
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
Uni<TokenResponse> getToken(
|
||||
@FormParam("grant_type") String grantType,
|
||||
@FormParam("client_id") String clientId,
|
||||
@FormParam("client_secret") String clientSecret
|
||||
);
|
||||
|
||||
@POST
|
||||
@Path("/token")
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
Uni<TokenResponse> refreshToken(
|
||||
@FormParam("grant_type") String grantType,
|
||||
@FormParam("client_id") String clientId,
|
||||
@FormParam("refresh_token") String refreshToken
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package fr.titionfire.ffsaf.rest.client;
|
||||
|
||||
import fr.titionfire.ffsaf.domain.service.HelloAssoTokenService;
|
||||
import io.quarkus.rest.client.reactive.ReactiveClientHeadersFactory;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import org.jboss.resteasy.reactive.common.util.MultivaluedTreeMap;
|
||||
|
||||
@ApplicationScoped
|
||||
public class HelloAssoHeadersFactory extends ReactiveClientHeadersFactory {
|
||||
|
||||
@Inject
|
||||
HelloAssoTokenService helloAssoTokenService;
|
||||
|
||||
@Override
|
||||
public Uni<MultivaluedMap<String, String>> getHeaders(MultivaluedMap<String, String> incomingHeaders,
|
||||
MultivaluedMap<String, String> clientOutgoingHeaders) {
|
||||
MultivaluedMap<String, String> map = new MultivaluedTreeMap<>();
|
||||
return helloAssoTokenService.getValidAccessToken()
|
||||
.invoke(token -> map.putSingle("Authorization", "Bearer " + token)).map(__ -> map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package fr.titionfire.ffsaf.rest.client;
|
||||
|
||||
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsRequest;
|
||||
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsResponse;
|
||||
import io.quarkus.rest.client.reactive.ClientExceptionMapper;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Path("/")
|
||||
@RegisterRestClient(configKey = "helloasso-api")
|
||||
@RegisterClientHeaders(HelloAssoHeadersFactory.class)
|
||||
public interface HelloAssoService {
|
||||
|
||||
@GET
|
||||
@Path("/users/me/organizations")
|
||||
@Produces("text/plain")
|
||||
Uni<String> test();
|
||||
|
||||
@POST
|
||||
@Path("organizations/{organizationSlug}/checkout-intents")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
Uni<CheckoutIntentsResponse> checkout(@PathParam("organizationSlug") String organizationSlug,
|
||||
CheckoutIntentsRequest data);
|
||||
|
||||
@ClientExceptionMapper
|
||||
static RuntimeException toException(Response response, Method method) {
|
||||
if (!method.getDeclaringClass().getName().equals("fr.titionfire.ffsaf.rest.client.HelloAssoService"))
|
||||
return null;
|
||||
|
||||
if (method.getName().equals("checkout")) {
|
||||
if (response.getStatus() == 400) {
|
||||
if (response.getEntity() instanceof ByteArrayInputStream) {
|
||||
ByteArrayInputStream error = response.readEntity(ByteArrayInputStream.class);
|
||||
return new RuntimeException(new String(error.readAllBytes(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
return new RuntimeException("The remote service responded with HTTP 400");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package fr.titionfire.ffsaf.rest.client.dto;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
@RegisterForReflection
|
||||
public class ApiError {
|
||||
public String error;
|
||||
public String error_description;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return error + ": " + error_description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package fr.titionfire.ffsaf.rest.client.dto;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class CheckoutIntentsRequest {
|
||||
public int totalAmount;
|
||||
public int initialAmount;
|
||||
public String itemName;
|
||||
public String backUrl;
|
||||
public String errorUrl;
|
||||
public String returnUrl;
|
||||
public boolean containsDonation;
|
||||
public Payer payer;
|
||||
public CheckoutMetadata metadata;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public static class Payer {
|
||||
public String firstName;
|
||||
public String lastName;
|
||||
public String email;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package fr.titionfire.ffsaf.rest.client.dto;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@RegisterForReflection
|
||||
public class CheckoutIntentsResponse {
|
||||
public int id;
|
||||
public String redirectUrl;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package fr.titionfire.ffsaf.rest.client.dto;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class CheckoutMetadata {
|
||||
public long checkoutDBId;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package fr.titionfire.ffsaf.rest.client.dto;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class HelloassoNotification {
|
||||
private NotificationData data;
|
||||
private String eventType;
|
||||
private CheckoutMetadata metadata;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package fr.titionfire.ffsaf.rest.client.dto;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class NotificationData {
|
||||
private Order order;
|
||||
private Integer id;
|
||||
private String organizationSlug;
|
||||
private String checkoutIntentId;
|
||||
private String oldSlugOrganization; // Pour les changements de nom d'association
|
||||
private String newSlugOrganization;
|
||||
private String state; // Pour les formulaires
|
||||
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public static class Order {
|
||||
private Integer id;
|
||||
private String organizationSlug;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package fr.titionfire.ffsaf.rest.client.dto;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
@RegisterForReflection
|
||||
public class TokenResponse {
|
||||
@JsonProperty("access_token")
|
||||
public String accessToken;
|
||||
|
||||
@JsonProperty("refresh_token")
|
||||
public String refreshToken;
|
||||
|
||||
@JsonProperty("token_type")
|
||||
public String tokenType; // Toujours "bearer"
|
||||
|
||||
@JsonProperty("expires_in")
|
||||
public long expiresIn; // Durée de validité en secondes (1800s = 30min)
|
||||
|
||||
// Pour stocker l'heure d'obtention du token
|
||||
private long timestamp;
|
||||
|
||||
public TokenResponse() {
|
||||
this.timestamp = System.currentTimeMillis() / 1000; // Timestamp en secondes
|
||||
}
|
||||
|
||||
// Vérifie si le token est expiré
|
||||
public boolean isExpired() {
|
||||
return (System.currentTimeMillis() / 1000) - timestamp >= expiresIn;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user