Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AD-352 Chargeback notification on order #516

Merged
merged 2 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@
</editorArea:editorArea>
</context>


<context merge-by="type" type="CsTicket" component="editor-area">
<editorArea:editorArea xmlns:editorArea="http://www.hybris.com/cockpitng/component/editorArea">
<editorArea:tab name="hmc.adyen">
<editorArea:section name="hmc.adyen.details">
<editorArea:attribute qualifier="pspReference"/>
<editorArea:attribute qualifier="originalReference"/>
<editorArea:attribute qualifier="amount"/>
<editorArea:attribute qualifier="currency"/>
</editorArea:section>
</editorArea:tab>
</editorArea:editorArea>
</context>

</config>
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ hmc.adyen.notifications=Server Notification Settings
hmc.adyen.api=API Settings
hmc.adyen.advanced=Advanced Settings
hmc.adyen.pos=POS settings
hmc.adyen.details=Details
1 change: 1 addition & 0 deletions adyenv6core/extensioninfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<requires-extension name="acceleratorfacades"/>
<requires-extension name="commercefacades"/>
<requires-extension name="basecommerce"/>
<requires-extension name="ticketsystem"/>

<coremodule generated="true" manager="com.adyen.v6.jalo.Adyenv6coreManager" packageroot="com.adyen.v6"/>
</extension>
Expand Down
7 changes: 7 additions & 0 deletions adyenv6core/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@

# Specifies the location of the spring context file putted automatically to the global platform application context.
adyenv6core.application-context=adyenv6core-spring.xml

adyen.notification.chargeback.tickets.enabled=true
#value from CsTicketPriority
adyen.notification.chargeback.tickets.priority=Medium
#value from CsTicketCategory
adyen.notification.chargeback.tickets.category=Problem
adyen.notification.chargeback.tickets.agentGroup=adyenChargebackAgentGroup
7 changes: 7 additions & 0 deletions adyenv6core/resources/adyenv6core-beans.xml
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,11 @@
<property name="registrationNumber" type="String"/>
</bean>

<bean class="de.hybris.platform.ticketsystem.data.CsTicketParameter">
<property name="pspReference" type="String"/>
<property name="originalReference" type="String"/>
<property name="amount" type="java.math.BigDecimal"/>
<property name="currency" type="String"/>
</bean>

</beans>
18 changes: 18 additions & 0 deletions adyenv6core/resources/adyenv6core-items.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
</enumtype>
<enumtype code="OrderStatus" autocreate="false" generate="true" dynamic="true">
<value code="PAYMENT_PENDING"/>
<value code="CHARGEBACK"/>
</enumtype>
<enumtype code="AdyenRegions" dynamic="true">
<value code="EU"/>
Expand Down Expand Up @@ -727,5 +728,22 @@
</attribute>
</attributes>
</itemtype>
<itemtype code="CsTicket" autocreate="false" generate="false">
<attributes>
<attribute qualifier="pspReference" type="java.lang.String">
<persistence type="property"/>
</attribute>
<attribute qualifier="originalReference" type="java.lang.String">
<persistence type="property"/>
</attribute>
<attribute qualifier="amount" type="java.math.BigDecimal">
<persistence type="property"/>
</attribute>
<attribute qualifier="currency" type="java.lang.String">
<persistence type="property"/>
</attribute>
</attributes>
</itemtype>

</itemtypes>
</items>
16 changes: 16 additions & 0 deletions adyenv6core/resources/adyenv6core-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@
<property name="transactionManager" ref="txManager"/>
</bean>
</property>
<property name="ticketBusinessService" ref="ticketBusinessService"/>
<property name="ticketService" ref="ticketService"/>
<property name="configurationService" ref="configurationService"/>
</bean>

<bean id="adyenOrderCancelPaymentServiceAdapter" class="com.adyen.v6.service.AdyenOrderCancelPaymentServiceAdapter">
Expand Down Expand Up @@ -316,6 +319,11 @@
<property name="add" ref="adyenOrderEntryPopulator"/>
</bean>

<bean parent="modifyPopulatorList">
<property name="list" ref="ticketParameterConverter"/>
<property name="add" ref="adyenCsTicketParameterPopulator"/>
</bean>

<bean name="adyenCartPopulator" class="com.adyen.v6.populator.CartPopulator" />

<bean name="adyenProductPopulator" class="com.adyen.v6.populator.ProductPopulator" />
Expand All @@ -330,6 +338,8 @@
<bean id="adyenOrderCancelPopulator" parent="defaultOrderCancelPopulator"
class="com.adyen.v6.populator.AdyenOrderCancelPopulator"/>

<bean id="adyenCsTicketParameterPopulator" class="com.adyen.v6.populator.AdyenCsTicketParameterPopulator"/>

<alias name="defaultAdyenPartialOrderCancelDenialStrategy"
alias="adyenPartialOrderCancelDenialStrategy"/>
<bean id="defaultAdyenPartialOrderCancelDenialStrategy"
Expand Down Expand Up @@ -420,6 +430,12 @@
<property name="adyenNotificationService" ref="adyenNotificationService"/>
</bean>

<bean id="chargebackNotificationEventListener" class="com.adyen.v6.listeners.ChargebackNotificationEventListener" parent="abstractEventListener">
<property name="modelService" ref="modelService"/>
<property name="paymentTransactionRepository" ref="adyenPaymentTransactionRepository"/>
<property name="adyenNotificationService" ref="adyenNotificationService"/>
</bean>

<bean id="adyenExpressCheckoutFacade" class="com.adyen.v6.facades.impl.DefaultAdyenExpressCheckoutFacade">
<property name="cartFactory" ref="cartFactory"/>
<property name="cartService" ref="cartService"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
INSERT_UPDATE CsAgentGroup; uid[unique = true] ; name ; locName[lang = en]
; adyenChargebackAgentGroup ; Adyen Chargeback Agent Group ; Adyen Chargeback Agent Group

Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,9 @@ type.AdyenMerchantConfig.adyenMerchantType.name=Adyen merchant account type
type.AdyenMerchantConfig.adyenMerchantType.description=Type of account - WEB or POS

type.address.taxNumber.name=Company tax number
type.address.registrationNumber.name=Company registration number
type.address.registrationNumber.name=Company registration number

type.CsTicket.pspReference.name=PSP Reference
type.CsTicket.originalReference.name=Original Reference
type.CsTicket.amount.name=Amount
type.CsTicket.currency.name=Currency
9 changes: 9 additions & 0 deletions adyenv6core/src/com/adyen/v6/events/ChargebackEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.adyen.v6.events;

import com.adyen.v6.model.AdyenNotificationModel;

public class ChargebackEvent extends AbstractNotificationEvent {
public ChargebackEvent(AdyenNotificationModel adyenNotificationModel) {
super(adyenNotificationModel);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.adyen.v6.events.builder;

import com.adyen.v6.events.AbstractNotificationEvent;
import com.adyen.v6.events.ChargebackEvent;
import com.adyen.v6.events.RefundEvent;
import com.adyen.v6.model.AdyenNotificationModel;

public class ChargebackEventBuilder extends AbstractNotificationEventBuilder{
@Override
public AbstractNotificationEvent buildEvent(AdyenNotificationModel adyenNotificationModel) {
return new ChargebackEvent(adyenNotificationModel);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.adyen.v6.listeners;

import com.adyen.v6.events.ChargebackEvent;
import com.adyen.v6.events.RefundEvent;
import com.adyen.v6.model.AdyenNotificationModel;
import org.apache.log4j.Logger;

import java.util.Date;

public class ChargebackNotificationEventListener extends AbstractNotificationEventListener<ChargebackEvent> {


private static final Logger LOG = Logger.getLogger(ChargebackNotificationEventListener.class);

public ChargebackNotificationEventListener() {
super();
}

@Override
protected void onEvent(final ChargebackEvent event) {
AdyenNotificationModel notificationInfoModel = event.getNotificationRequestItem();
try {
getAdyenNotificationService().processChargebackEvent(notificationInfoModel);
LOG.info("Chargeback notification with PSPReference " + notificationInfoModel.getPspReference() + " was processed");
notificationInfoModel.setProcessedAt(new Date());
getModelService().save(notificationInfoModel);
} catch (Exception e) {
logException(notificationInfoModel, e, LOG);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.adyen.v6.populator;

import de.hybris.platform.converters.Populator;
import de.hybris.platform.servicelayer.dto.converter.ConversionException;
import de.hybris.platform.ticket.model.CsTicketModel;
import de.hybris.platform.ticketsystem.data.CsTicketParameter;

public class AdyenCsTicketParameterPopulator implements Populator<CsTicketParameter, CsTicketModel> {
@Override
public void populate(CsTicketParameter csTicketParameter, CsTicketModel csTicketModel) throws ConversionException {
csTicketModel.setPspReference(csTicketParameter.getPspReference());
csTicketModel.setOriginalReference(csTicketParameter.getOriginalReference());
csTicketModel.setAmount(csTicketParameter.getAmount());
csTicketModel.setCurrency(csTicketParameter.getCurrency());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,10 @@ public interface AdyenNotificationService {
*/
PaymentTransactionModel processOfferClosedEvent(AdyenNotificationModel notificationItem);

/**
* Process notification with eventCode=CHARGEBACK
* @return
*/
void processChargebackEvent(AdyenNotificationModel notificationItem);

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,38 @@
import de.hybris.platform.payment.enums.PaymentTransactionType;
import de.hybris.platform.payment.model.PaymentTransactionEntryModel;
import de.hybris.platform.payment.model.PaymentTransactionModel;
import de.hybris.platform.sap.productconfig.runtime.interf.impl.CsticParameter;
import de.hybris.platform.servicelayer.config.ConfigurationService;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.session.SessionService;
import de.hybris.platform.ticket.enums.CsTicketCategory;
import de.hybris.platform.ticket.enums.CsTicketPriority;
import de.hybris.platform.ticket.jalo.CsTicket;
import de.hybris.platform.ticket.model.CsAgentGroupModel;
import de.hybris.platform.ticket.model.CsTicketModel;
import de.hybris.platform.ticket.service.TicketBusinessService;
import de.hybris.platform.ticket.service.TicketService;
import de.hybris.platform.ticketsystem.data.CsTicketParameter;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.log4j.Logger;
import org.springframework.transaction.support.TransactionOperations;

import java.io.IOException;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;

public class DefaultAdyenNotificationService implements AdyenNotificationService {
public static final String PAYMENT_TRANSACTION_MODEL_IS_NULL_FOR_NOTIFICATION = "PaymentTransactionModel is null for notification: ";
public static final String ORDER_WITH_ORDER_CODE = "Order with orderCode: ";
public static final String CAUSE_AN_EXCEPTION = " cause an exception. \n";
public static final String EXCEPTION_DURING_PROCESSING_NOTIFICATION = "Exception during processing notification: ";

private static final String CHARGEBACK_TICKETS_ENABLED="adyen.notification.chargeback.tickets.enabled";
private static final String CHARGEBACK_TICKETS_PRIORITY="adyen.notification.chargeback.tickets.priority";
private static final String CHARGEBACK_TICKETS_CATEGORY="adyen.notification.chargeback.tickets.category";
private static final String CHARGEBACK_TICKETS_AGENT_GROUP="adyen.notification.chargeback.tickets.agentGroup";

private ModelService modelService;
private AdyenTransactionService adyenTransactionService;
private AdyenBusinessProcessService adyenBusinessProcessService;
Expand All @@ -62,6 +79,9 @@ public class DefaultAdyenNotificationService implements AdyenNotificationService
private CommercePlaceOrderStrategy commercePlaceOrderStrategy;
private SessionService sessionService;
private TransactionOperations transactionTemplate;
private TicketBusinessService ticketBusinessService;
private TicketService ticketService;
private ConfigurationService configurationService;


private static final Logger LOG = Logger.getLogger(DefaultAdyenNotificationService.class);
Expand Down Expand Up @@ -289,6 +309,63 @@ public PaymentTransactionModel processOfferClosedEvent(AdyenNotificationModel no
}
}

@Override
public void processChargebackEvent(AdyenNotificationModel notificationItemModel) {
String orderCode = notificationItemModel.getMerchantReference();
if (BooleanUtils.isFalse(notificationItemModel.getSuccess())) {
LOG.error("Order " + orderCode + " received unexpected CHARGEBACK event with success=false");
}

OrderModel orderModel = orderRepository.getOrderModel(orderCode);
if (orderModel == null) {
LOG.error("Order " + orderCode + " was not found, skipping CHARGEBACK event...");
return;
}

orderModel.setStatus(OrderStatus.CHARGEBACK);
orderModel.setStatusInfo("Adyen CHARGEBACK: " + notificationItemModel.getPspReference());
getModelService().save(orderModel);

boolean ticketsEnabled = configurationService.getConfiguration().getBoolean(CHARGEBACK_TICKETS_ENABLED, false);

if (ticketsEnabled) {
createChargebackTicket(notificationItemModel, orderModel);
}
}

private void createChargebackTicket(AdyenNotificationModel notificationItemModel, OrderModel orderModel) {
String priorityString = configurationService.getConfiguration().getString(CHARGEBACK_TICKETS_PRIORITY);
String categoryString = configurationService.getConfiguration().getString(CHARGEBACK_TICKETS_CATEGORY);
String agentGroupString = configurationService.getConfiguration().getString(CHARGEBACK_TICKETS_AGENT_GROUP);

CsTicketPriority priority = CsTicketPriority.valueOf(priorityString);
CsTicketCategory category = CsTicketCategory.valueOf(categoryString);

Optional<CsAgentGroupModel> adyenAgentGroup = ticketService.getAgentGroups().stream()
.filter(agentGroup -> agentGroup.getUid().equals(agentGroupString))
.findFirst();

CsTicketParameter ticketParameter = new CsTicketParameter();
ticketParameter.setAssociatedTo(orderModel);
ticketParameter.setCustomer(orderModel.getUser());
ticketParameter.setHeadline("Adyen CHARGEBACK " + notificationItemModel.getPspReference());
ticketParameter.setCreationNotes("Adyen CHARGEBACK " + notificationItemModel.getPspReference());
ticketParameter.setPriority(priority);
ticketParameter.setCategory(category);
ticketParameter.setPspReference(notificationItemModel.getPspReference());
ticketParameter.setOriginalReference(notificationItemModel.getOriginalReference());
ticketParameter.setAmount(notificationItemModel.getAmountValue());
ticketParameter.setCurrency(notificationItemModel.getAmountCurrency());

if (adyenAgentGroup.isPresent()) {
ticketParameter.setAssignedGroup(adyenAgentGroup.get());
} else {
LOG.warn("Agent group for chargeback notification ticket not defined");
}

ticketBusinessService.createTicket(ticketParameter);
}

protected boolean isTransactionAuthorized(final PaymentTransactionModel paymentTransactionModel) {
for (final PaymentTransactionEntryModel entry : paymentTransactionModel.getEntries()) {
if (entry.getType().equals(PaymentTransactionType.AUTHORIZATION)
Expand Down Expand Up @@ -431,4 +508,16 @@ public void setSessionService(SessionService sessionService) {
public void setTransactionTemplate(TransactionOperations transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}

public void setTicketBusinessService(TicketBusinessService ticketBusinessService) {
this.ticketBusinessService = ticketBusinessService;
}

public void setTicketService(TicketService ticketService) {
this.ticketService = ticketService;
}

public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class DefaultAdyenNotificationV2Service implements AdyenNotificationV2Ser
eventTemplateMap.put(NotificationRequestItem.EVENT_CODE_CAPTURE, new CaptureEventBuilder());
eventTemplateMap.put(NotificationRequestItem.EVENT_CODE_OFFER_CLOSED, new OfferClosedEventBuilder());
eventTemplateMap.put(NotificationRequestItem.EVENT_CODE_REFUND, new RefundEventBuilder());
eventTemplateMap.put(NotificationRequestItem.EVENT_CODE_CHARGEBACK, new ChargebackEventBuilder());
}

@Override
Expand Down
Loading