Skip to content

Conversation

@pjaneta
Copy link
Collaborator

@pjaneta pjaneta commented Nov 6, 2025

Description

Tested scenarios

Fixed issue:

@pjaneta pjaneta requested a review from kpieloch November 6, 2025 11:31
@pjaneta pjaneta requested a review from a team as a code owner November 6, 2025 11:31
}

@Override
public void updatePaymentRequest(PaymentRequest paymentRequest, CartData cartData, RecurringContractMode recurringContractMode, CustomerModel customerModel, Boolean is3DS2Allowed, Boolean guestUserTokenizationEnabled) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CardSubscriptionHandler.java, IdealSubscriptionHandler.java, KlarnaSubscriptionHandler.java, PayPalSubscriptionHandler.java

These four files share almost identical logic in updatePaymentRequest. Did you consider a generalisation of this code ? For example:

PaymentMethodHandler {

    protected abstract T getPaymentMethodDetails(PaymentRequest request);
    protected abstract T createNewPaymentMethodDetails();

    @Override
    public void updatePaymentRequest(PaymentRequest paymentRequest, CartData cartData, ...) {
        if (StringUtils.isNotEmpty(cartData.getAdyenSelectedReference())) {
            T details = getPaymentMethodDetails(paymentRequest);
            if (details == null) {
                details = createNewPaymentMethodDetails();
            }
            
            details.setStoredPaymentMethodId(cartData.getAdyenSelectedReference());
            
            CheckoutPaymentMethod checkoutPaymentMethod = new CheckoutPaymentMethod();
            checkoutPaymentMethod.setActualInstance(details);
            paymentRequest.setPaymentMethod(checkoutPaymentMethod);

            paymentRequest.setRecurringProcessingModel(PaymentRequest.RecurringProcessingModelEnum.SUBSCRIPTION);
            paymentRequest.setShopperInteraction(PaymentRequest.ShopperInteractionEnum.CONTAUTH);
        }
    }
}

paymentInfo.setAdyenSelectedReference(data.getStoredPaymentMethodId());
modelService.save(paymentInfo);
} else {
throw new NotImplementedException("TokenizationWebhookEventListener not implemented for type " + data.getEventType());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing NotImplementedException inside an Event Listener is risky. If the event system retries.

Suggested change
throw new NotImplementedException("TokenizationWebhookEventListener not implemented for type " + data.getEventType());
LOG.warn("Received Tokenization event of type [" + data.getEventType() + "] which is not currently handled.");

validationResult &= order.getStore().getAdyenMerchantAccount().equals(tokenWebhookRequestData.getMerchantAccount());

validationResult &= (order.getStore().getAdyenTestMode() && "test".equals(tokenWebhookRequestData.getEnvironment())) ||
(!order.getStore().getAdyenTestMode() && "live".equals(tokenWebhookRequestData.getEnvironment()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strings "test" and "live" are hardcoded in the cross-check logic.
Move these to constants in AdyenConstants or a similar configuration class to ensure consistency across the codebase.

typeService.getComposedTypeForClass(CartEntryModel.class), subscriptionOrder, (String) keyGenerator.generate());
cart.setSubscriptionOrder(Boolean.TRUE);
//TODO in refactoring
cart.setParent(null);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this cannot be refactored now, the comment should be more descriptive explaining why it is set to null (likely to detach the cloned cart from the original order context for the CronJob) and link to a Jira ticket/backlog item.


protected boolean tokenizeForSubscriptionProducts(CartData cartData) {
return !cartData.getSubscriptionOrder() && cartData.getEntries().stream()
.anyMatch(entry -> Objects.nonNull(entry.getProduct().getSubscriptionTerm()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic assumes that cartData.getSubscriptionOrder() is false for the initial checkout and true only for the subsequent recurring runs. This might throw a NullPointerException (unless it's a primitive boolean, but the bean definition showed java.lang.Boolean).

{
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initialization block is an instance initializer, but the variable is static. This means objectMapper is re-initialized every time the controller is instantiated (which is fine for Singleton beans, but technically incorrect Java semantics for static variables).

Suggested change
}
private static final ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants