Define an endpoint to set the default payment method for iDEAL subscriptions
This commit is contained in:
parent
5990a100db
commit
8ec062fbef
|
@ -435,16 +435,7 @@ public class SubscriptionController {
|
|||
|
||||
final SubscriptionProcessorManager manager = getManagerForProcessor(processor);
|
||||
|
||||
return subscriptionManager.get(requestData.subscriberUser, requestData.hmac)
|
||||
.thenApply(this::requireRecordFromGetResult)
|
||||
.thenCompose(record -> record.getProcessorCustomer()
|
||||
.map(processorCustomer -> manager.setDefaultPaymentMethodForCustomer(processorCustomer.customerId(),
|
||||
paymentMethodToken, record.subscriptionId))
|
||||
.orElseThrow(() ->
|
||||
// a missing customer ID indicates the client made requests out of order,
|
||||
// and needs to call create_payment_method to create a customer for the given payment method
|
||||
new ClientErrorException(Status.CONFLICT)))
|
||||
.thenApply(customer -> Response.ok().build());
|
||||
return setDefaultPaymentMethod(manager, paymentMethodToken, requestData);
|
||||
}
|
||||
|
||||
public record SetSubscriptionLevelSuccessResponse(long level) {
|
||||
|
@ -987,6 +978,33 @@ public class SubscriptionController {
|
|||
});
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{subscriberId}/default_payment_method_for_ideal/{setupIntentId}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public CompletableFuture<Response> setDefaultPaymentMethodForIdeal(
|
||||
@Auth Optional<AuthenticatedAccount> authenticatedAccount,
|
||||
@PathParam("subscriberId") String subscriberId,
|
||||
@PathParam("setupIntentId") @NotEmpty String setupIntentId) {
|
||||
RequestData requestData = RequestData.process(authenticatedAccount, subscriberId, clock);
|
||||
|
||||
return stripeManager.getGeneratedSepaIdFromSetupIntent(setupIntentId)
|
||||
.thenCompose(generatedSepaId -> setDefaultPaymentMethod(stripeManager, generatedSepaId, requestData));
|
||||
}
|
||||
|
||||
private CompletableFuture<Response> setDefaultPaymentMethod(final SubscriptionProcessorManager manager,
|
||||
final String paymentMethodId,
|
||||
final RequestData requestData) {
|
||||
return subscriptionManager.get(requestData.subscriberUser, requestData.hmac)
|
||||
.thenApply(this::requireRecordFromGetResult)
|
||||
.thenCompose(record -> record.getProcessorCustomer()
|
||||
.map(processorCustomer -> manager.setDefaultPaymentMethodForCustomer(processorCustomer.customerId(),
|
||||
paymentMethodId, record.subscriptionId))
|
||||
.orElseThrow(() ->
|
||||
// a missing customer ID indicates the client made requests out of order,
|
||||
// and needs to call create_payment_method to create a customer for the given payment method
|
||||
new ClientErrorException(Status.CONFLICT)))
|
||||
.thenApply(customer -> Response.ok().build());
|
||||
}
|
||||
private Instant receiptExpirationWithGracePeriod(Instant itemExpiration) {
|
||||
return itemExpiration.plus(subscriptionConfiguration.getBadgeGracePeriod())
|
||||
.truncatedTo(ChronoUnit.DAYS)
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.stripe.param.PaymentIntentCreateParams;
|
|||
import com.stripe.param.PaymentIntentRetrieveParams;
|
||||
import com.stripe.param.PriceRetrieveParams;
|
||||
import com.stripe.param.SetupIntentCreateParams;
|
||||
import com.stripe.param.SetupIntentRetrieveParams;
|
||||
import com.stripe.param.SubscriptionCancelParams;
|
||||
import com.stripe.param.SubscriptionCreateParams;
|
||||
import com.stripe.param.SubscriptionItemListParams;
|
||||
|
@ -63,7 +64,9 @@ import javax.annotation.Nonnull;
|
|||
import javax.annotation.Nullable;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.ws.rs.ClientErrorException;
|
||||
import javax.ws.rs.InternalServerErrorException;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
@ -632,6 +635,33 @@ public class StripeManager implements SubscriptionProcessorManager {
|
|||
}, executor);
|
||||
}
|
||||
|
||||
public CompletableFuture<String> getGeneratedSepaIdFromSetupIntent(String setupIntentId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
SetupIntentRetrieveParams params = SetupIntentRetrieveParams.builder()
|
||||
.addExpand("latest_attempt")
|
||||
.build();
|
||||
try {
|
||||
final SetupIntent setupIntent = stripeClient.setupIntents().retrieve(setupIntentId, params, commonOptions());
|
||||
if (setupIntent.getLatestAttemptObject() == null
|
||||
|| setupIntent.getLatestAttemptObject().getPaymentMethodDetails() == null
|
||||
|| setupIntent.getLatestAttemptObject().getPaymentMethodDetails().getIdeal() == null
|
||||
|| setupIntent.getLatestAttemptObject().getPaymentMethodDetails().getIdeal().getGeneratedSepaDebit() == null) {
|
||||
// This usually indicates that the client has made requests out of order, either by not confirming
|
||||
// the SetupIntent or not having the user authorize the transaction.
|
||||
logger.debug("setupIntent {} missing expected fields", setupIntentId);
|
||||
throw new ClientErrorException(Status.CONFLICT);
|
||||
}
|
||||
return setupIntent.getLatestAttemptObject().getPaymentMethodDetails().getIdeal().getGeneratedSepaDebit();
|
||||
} catch (StripeException e) {
|
||||
if (e.getStatusCode() == 404) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
logger.error("unexpected error from Stripe when retrieving setupIntent {}", setupIntentId, e);
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* We use a client generated idempotency key for subscription updates due to not being able to distinguish between a
|
||||
* call to update to level 2, then back to level 1, then back to level 2. If this all happens within Stripe's
|
||||
|
|
Loading…
Reference in New Issue