Skip to content
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 @@ -12,6 +12,7 @@
import com.adyen.model.checkout.DonationCampaignsResponse;
import com.adyen.model.checkout.DonationPaymentResponse;
import com.adyen.service.exception.ApiException;
import com.google.gson.Gson;
import de.hybris.platform.acceleratorservices.urlresolver.SiteBaseUrlResolutionService;
import de.hybris.platform.basecommerce.model.site.BaseSiteModel;
import de.hybris.platform.servicelayer.session.SessionService;
Expand All @@ -27,22 +28,22 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

@Controller
@RestController
@RequestMapping("/api/checkout/donations")
public class AdyenDonationsController {
private static final Logger LOG = Logger.getLogger(AdyenDonationsController.class);
private static final String COMPLETED = "completed";
private static final String SESSION_DONATION_CAMPAIGNS = "adyen_donation_campaigns";
private static final String SESSION_DONATION_CAMPAIGNS_CURRENCY = "adyen_donation_campaigns_currency";
private static final Gson GSON = new Gson();

private static final DonationCampaignValidator DONATION_CAMPAIGN_VALIDATOR = new DonationCampaignValidator();

Expand All @@ -65,7 +66,6 @@ public class AdyenDonationsController {
private SiteBaseUrlResolutionService siteBaseUrlResolutionService;

@GetMapping(value = "/context", produces = "application/json")
@ResponseBody
public ResponseEntity<DonationContextResponse> getContext() {
if (!adyenDonationsFacade.isGivingEnabled()) {
return ResponseEntity.noContent().build();
Expand All @@ -77,6 +77,8 @@ public ResponseEntity<DonationContextResponse> getContext() {
final String currency = sessionService.getAttribute(DefaultAdyenCheckoutFacade.SESSION_DONATION_ORIGINAL_AMOUNT_CURRENCY);
final String countryCode = sessionService.getAttribute(DefaultAdyenCheckoutFacade.SESSION_DONATION_COUNTRY_CODE);

clearDonationCampaignCache();

if (StringUtils.isAnyBlank(donationToken, originalPspReference, currency, countryCode) || commercialTxAmount == null) {
LOG.info("Giving context unavailable: token=" + StringUtils.isNotBlank(donationToken)
+ ", pspReference=" + StringUtils.isNotBlank(originalPspReference)
Expand All @@ -101,14 +103,14 @@ public ResponseEntity<DonationContextResponse> getContext() {
if (campaigns.isEmpty()) {
return ResponseEntity.noContent().build();
}
cacheDonationCampaigns(currency, campaigns);

return ResponseEntity.ok(new DonationContextResponse(baseStore.getAdyenClientKey(),
adyenCheckoutFacade.getEnvironmentMode(), adyenCheckoutFacade.getShopperLocale(),
commercialTxAmount, currency, countryCode, adyenCheckoutFacade.getCheckoutShopperHost(), campaigns));
}

@PostMapping(value = "/donate", consumes = "application/json", produces = "application/json")
@ResponseBody
public ResponseEntity<DonationResponse> donate(@Valid @RequestBody final DonationRequest request,
final HttpServletRequest httpServletRequest) {
if (!adyenDonationsFacade.isGivingEnabled()) {
Expand All @@ -127,7 +129,7 @@ public ResponseEntity<DonationResponse> donate(@Valid @RequestBody final Donatio

final DonationCampaign campaign;
try {
campaign = findCampaign(request.getDonationCampaignId(), getCampaigns(request.getAmount().getCurrency()));
campaign = findCampaign(request.getDonationCampaignId(), getCampaignsForDonation(request.getAmount().getCurrency()));
} catch (ApiException | IOException exception) {
LOG.warn("Unable to validate Adyen Giving campaign", exception);
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
Expand Down Expand Up @@ -207,7 +209,36 @@ protected void clearDonationContext(final HttpSession httpSession) {
sessionService.removeAttribute(DefaultAdyenCheckoutFacade.SESSION_DONATION_IN_PROGRESS);
sessionService.removeAttribute(DefaultAdyenCheckoutFacade.SESSION_DONATION_IDEMPOTENCY_KEY);
sessionService.removeAttribute(DefaultAdyenCheckoutFacade.SESSION_DONATION_REFERENCE);
clearDonationCampaignCache();
}
}

protected List<DonationCampaign> getCampaignsForDonation(final String currency) throws IOException, ApiException {
final String cachedCurrency = sessionService.getAttribute(SESSION_DONATION_CAMPAIGNS_CURRENCY);
final String cachedCampaigns = sessionService.getAttribute(SESSION_DONATION_CAMPAIGNS);
if (StringUtils.equalsIgnoreCase(currency, cachedCurrency)
&& StringUtils.isNotBlank(cachedCampaigns)) {
final DonationCampaign[] cachedCampaignArray = GSON.fromJson(cachedCampaigns, DonationCampaign[].class);
final List<DonationCampaign> campaigns = cachedCampaignArray == null
? null : Arrays.asList(cachedCampaignArray);
if (campaigns != null) {
return campaigns;
}
}

final List<DonationCampaign> campaigns = getCampaigns(currency);
cacheDonationCampaigns(currency, campaigns);
return campaigns;
}

protected void cacheDonationCampaigns(final String currency, final List<DonationCampaign> campaigns) {
sessionService.setAttribute(SESSION_DONATION_CAMPAIGNS, GSON.toJson(campaigns));
sessionService.setAttribute(SESSION_DONATION_CAMPAIGNS_CURRENCY, currency);
}

protected void clearDonationCampaignCache() {
sessionService.removeAttribute(SESSION_DONATION_CAMPAIGNS);
sessionService.removeAttribute(SESSION_DONATION_CAMPAIGNS_CURRENCY);
}

protected String getOrCreateDonationIdempotencyKey() {
Expand Down
46 changes: 46 additions & 0 deletions adyenv6core/resources/adyenv6core-items.xml
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,52 @@
<persistence type="property"/>
</attribute>
</attributes>
<indexes>
<index name="adyenNotificationPspEventIdx">
<key attribute="pspReference"/>
<key attribute="eventCode"/>
</index>
</indexes>
</itemtype>
<itemtype code="AdyenDonation" generate="true" autocreate="true">
<deployment table="adyenDonation" typecode="23000"/>
<attributes>
<attribute qualifier="reference" type="java.lang.String">
<persistence type="property"/>
<modifiers optional="false" initial="true" unique="true"/>
</attribute>
<attribute qualifier="pspReference" type="java.lang.String">
<persistence type="property"/>
</attribute>
<attribute qualifier="originalPspReference" type="java.lang.String">
<persistence type="property"/>
</attribute>
<attribute qualifier="status" type="java.lang.String">
<persistence type="property"/>
<modifiers optional="false" />
</attribute>
<attribute qualifier="amountValue" type="java.math.BigDecimal">
<persistence type="property"/>
</attribute>
<attribute qualifier="amountCurrency" type="java.lang.String">
<persistence type="property"/>
</attribute>
<attribute qualifier="order" type="Order">
<persistence type="property"/>
<modifiers optional="false" initial="true" />
</attribute>
</attributes>
<indexes>
<index name="adyenDonationReferenceIdx" unique="true">
<key attribute="reference" />
</index>
<index name="adyenDonationPspReferenceIdx">
<key attribute="pspReference" />
</index>
<index name="adyenDonationOrderIdx">
<key attribute="order" />
</index>
</indexes>
</itemtype>
<itemtype code="AdyenMerchantConfig" generate="true" autocreate="true">
<deployment table="adyenMerchantConfig" typecode="22938"/>
Expand Down
16 changes: 16 additions & 0 deletions adyenv6core/resources/adyenv6core-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@
<bean id="adyenPaymentTransactionRepository" class="com.adyen.v6.repository.PaymentTransactionRepository">
<property name="flexibleSearchService" ref="flexibleSearchService"/>
</bean>
<bean id="adyenNotificationRepository" class="com.adyen.v6.repository.AdyenNotificationRepository">
<property name="flexibleSearchService" ref="flexibleSearchService"/>
</bean>
<bean id="adyenDonationRepository" class="com.adyen.v6.repository.AdyenDonationRepository">
<property name="flexibleSearchService" ref="flexibleSearchService"/>
</bean>
<bean id="adyenDonationReconciliationService" class="com.adyen.v6.service.DefaultAdyenDonationReconciliationService">
<property name="modelService" ref="modelService"/>
<property name="adyenDonationRepository" ref="adyenDonationRepository"/>
<property name="paymentTransactionRepository" ref="adyenPaymentTransactionRepository"/>
</bean>
<bean id="adyenBaseStoreRepository" class="com.adyen.v6.repository.BaseStoreRepository">
<property name="flexibleSearchService" ref="flexibleSearchService"/>
<property name="orderRepository" ref="adyenOrderRepository" />
Expand Down Expand Up @@ -530,6 +541,11 @@
<property name="adyenNotificationService" ref="adyenNotificationService"/>
</bean>

<bean id="donationNotificationEventListener" class="com.adyen.v6.listeners.DonationNotificationEventListener" parent="abstractEventListener">
<property name="modelService" ref="modelService"/>
<property name="adyenDonationReconciliationService" ref="adyenDonationReconciliationService"/>
</bean>

<bean id="adyenExpressCheckoutFacade" class="com.adyen.commerce.facades.impl.DefaultAdyenExpressCheckoutFacade">
<property name="cartFactory" ref="cartFactory"/>
<property name="cartService" ref="cartService"/>
Expand Down
9 changes: 9 additions & 0 deletions adyenv6core/src/com/adyen/v6/events/DonationEvent.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 DonationEvent extends AbstractNotificationEvent {
public DonationEvent(final AdyenNotificationModel adyenNotificationModel) {
super(adyenNotificationModel);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.adyen.v6.events.builder;

import com.adyen.v6.events.AbstractNotificationEvent;
import com.adyen.v6.events.DonationEvent;
import com.adyen.v6.model.AdyenNotificationModel;

public class DonationEventBuilder extends AbstractNotificationEventBuilder {
@Override
public AbstractNotificationEvent buildEvent(final AdyenNotificationModel adyenNotificationModel) {
return new DonationEvent(adyenNotificationModel);
}
}
20 changes: 20 additions & 0 deletions adyenv6core/src/com/adyen/v6/jalo/AdyenDonation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.adyen.v6.jalo;

import de.hybris.platform.jalo.Item;
import de.hybris.platform.jalo.JaloBusinessException;
import de.hybris.platform.jalo.SessionContext;
import de.hybris.platform.jalo.type.ComposedType;
import org.apache.log4j.Logger;

public class AdyenDonation extends GeneratedAdyenDonation
{
@SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger( AdyenDonation.class.getName() );

@Override
protected Item createItem(final SessionContext ctx, final ComposedType type, final ItemAttributeMap allAttributes) throws JaloBusinessException
{
return super.createItem( ctx, type, allAttributes );
}

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

import com.adyen.v6.events.DonationEvent;
import com.adyen.v6.model.AdyenNotificationModel;
import com.adyen.v6.service.AdyenDonationReconciliationService;
import org.apache.log4j.Logger;

import java.util.Date;

public class DonationNotificationEventListener extends AbstractNotificationEventListener<DonationEvent> {
private static final Logger LOG = Logger.getLogger(DonationNotificationEventListener.class);

private AdyenDonationReconciliationService adyenDonationReconciliationService;

@Override
protected void onEvent(final DonationEvent event) {
final AdyenNotificationModel notification = event.getNotificationRequestItem();
try {
adyenDonationReconciliationService.reconcile(notification);
notification.setProcessedAt(new Date());
getModelService().save(notification);
LOG.info("DONATION notification with PSP reference " + notification.getPspReference() + " was processed");
} catch (final Exception exception) {
logException(notification, exception, LOG);
}
}

public void setAdyenDonationReconciliationService(
final AdyenDonationReconciliationService adyenDonationReconciliationService) {
this.adyenDonationReconciliationService = adyenDonationReconciliationService;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.adyen.v6.repository;

import com.adyen.v6.model.AdyenDonationModel;
import de.hybris.platform.servicelayer.search.FlexibleSearchQuery;

import java.util.Collections;

public class AdyenDonationRepository extends AbstractRepository {

public AdyenDonationModel findByReference(final String reference) {
if (reference == null) {
return null;
}
final FlexibleSearchQuery query = new FlexibleSearchQuery(
"SELECT {pk} FROM {" + AdyenDonationModel._TYPECODE + "}"
+ " WHERE {" + AdyenDonationModel.REFERENCE + "} = ?reference",
Collections.singletonMap("reference", reference));
return (AdyenDonationModel) getOneOrNull(query);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.adyen.v6.repository;

import com.adyen.v6.model.AdyenNotificationModel;
import de.hybris.platform.servicelayer.search.FlexibleSearchQuery;

import java.util.HashMap;
import java.util.Map;

public class AdyenNotificationRepository extends AbstractRepository {

public boolean isProcessed(final String pspReference, final String eventCode, final boolean success) {
final Map<String, Object> parameters = new HashMap<>();
parameters.put("pspReference", pspReference);
parameters.put("eventCode", eventCode);
parameters.put("success", success);

final FlexibleSearchQuery query = new FlexibleSearchQuery(
"SELECT {pk} FROM {" + AdyenNotificationModel._TYPECODE + "}"
+ " WHERE {" + AdyenNotificationModel.PSPREFERENCE + "} = ?pspReference"
+ " AND {" + AdyenNotificationModel.EVENTCODE + "} = ?eventCode"
+ " AND {" + AdyenNotificationModel.SUCCESS + "} = ?success"
+ " AND {" + AdyenNotificationModel.PROCESSEDAT + "} IS NOT NULL",
parameters);
query.setCount(1);
return !getFlexibleSearchService().search(query).getResult().isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.adyen.v6.service;

import com.adyen.v6.model.AdyenNotificationModel;

public interface AdyenDonationReconciliationService {
void reconcile(AdyenNotificationModel notification);
}
Loading
Loading