Skip to content
This repository has been archived by the owner on May 24, 2023. It is now read-only.

Commit

Permalink
Merge pull request #69 from commercetools/56_pending_notification_web…
Browse files Browse the repository at this point in the history
…hook

#56 add payment sale pending notification processor
closes #56
  • Loading branch information
andrii-kovalenko-ct committed Sep 13, 2017
2 parents eedef29 + c50354d commit dcefe2f
Show file tree
Hide file tree
Showing 22 changed files with 392 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*/
public final class NotificationEventData {

public static final String ID = "id";

public static final String AMOUNT = "amount";

public static final String TOTAL = "total";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ public class NotificationEventDispatcherProviderImpl implements NotificationEven

@Autowired
public NotificationEventDispatcherProviderImpl(@Nonnull TenantConfigFactory configFactory,
@Nonnull DefaultNotificationProcessor defaultNotificationProcessor,
// when you have Map here instead of ImmutableMap, Spring injects a default bean map
// which is not notificationProcessors that is defined in ApplicationConfig
@Nonnull NotificationProcessorContainer notificationProcessors) {
this.configFactory = configFactory;
this.processors = notificationProcessors;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ public interface NotificationProcessorContainer {
* For a Paypal Plus event, returns a corresponding processor.
* If none is found, return {@link com.commercetools.pspadapter.notification.processor.impl.DefaultNotificationProcessor}
*/
@Nonnull
NotificationProcessor getNotificationProcessorOrDefault(@Nonnull String paypalEventTypeName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class DefaultNotificationProcessor extends NotificationProcessorBase {
}

@Override
List<UpdateAction<Payment>> createUpdateCtpTransactionActions(@Nonnull Payment ctpPayment, @Nonnull Event event) {
List<UpdateAction<Payment>> createUpdatePaymentActions(@Nonnull Payment ctpPayment, @Nonnull Event event) {
return Collections.emptyList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public abstract class NotificationProcessorBase implements NotificationProcessor
this.gson = gson;
}

abstract List<? extends UpdateAction<Payment>> createUpdateCtpTransactionActions(@Nonnull Payment ctpPayment, @Nonnull Event event);
abstract List<UpdateAction<Payment>> createUpdatePaymentActions(@Nonnull Payment ctpPayment, @Nonnull Event event);

@Override
public CompletionStage<Payment> processEventNotification(@Nonnull CtpFacade ctpFacade,
Expand All @@ -59,7 +59,7 @@ protected List<UpdateAction<Payment>> createPaymentUpdates(@Nonnull Payment ctpP
@Nonnull Event event) {
ArrayList<UpdateAction<Payment>> updateActions = new ArrayList<>();
updateActions.add(createAddInterfaceInteractionAction(event));
updateActions.addAll(createUpdateCtpTransactionActions(ctpPayment, event));
updateActions.addAll(createUpdatePaymentActions(ctpPayment, event));
return updateActions;
}

Expand All @@ -77,6 +77,7 @@ protected CompletionStage<Optional<Payment>> getRelatedCtpPayment(@Nonnull CtpFa
}
}

@Nonnull
protected AddInterfaceInteraction createAddInterfaceInteractionAction(@Nonnull PayPalModel model) {
String json = gson.toJson(model);
return AddInterfaceInteraction.ofTypeKeyAndObjects(InterfaceInteractionType.NOTIFICATION.getInterfaceKey(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public NotificationProcessorContainerImpl(@Nonnull List<PaymentSaleNotificationP
}

@Override
@Nonnull
public NotificationProcessor getNotificationProcessorOrDefault(@Nonnull String paypalEventTypeName) {
return notificationProcessorsMap.getOrDefault(paypalEventTypeName, this.defaultNotificationProcessor);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@

import com.commercetools.payment.constants.paypalPlus.NotificationEventType;
import com.google.gson.Gson;
import com.paypal.api.payments.Event;
import io.sphere.sdk.commands.UpdateAction;
import io.sphere.sdk.payments.Payment;
import io.sphere.sdk.payments.TransactionState;
import io.sphere.sdk.payments.TransactionType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import java.util.List;

/**
* Processes event notification of type PAYMENT.SALE.COMPLETED
*/
@Component
public class PaymentSaleCompletedProcessor extends PaymentSaleSimpleProcessorBase {
public class PaymentSaleCompletedProcessor extends PaymentSaleNotificationProcessorBase {

@Autowired
public PaymentSaleCompletedProcessor(@Nonnull Gson gson) {
Expand All @@ -29,8 +26,15 @@ public NotificationEventType getNotificationEventType() {
return NotificationEventType.PAYMENT_SALE_COMPLETED;
}

@Nonnull
@Override
protected TransactionType getExpectedTransactionType() {
return TransactionType.CHARGE;
}

@Nonnull
@Override
List<? extends UpdateAction<Payment>> createUpdateCtpTransactionActions(@Nonnull Payment ctpPayment, @Nonnull Event event) {
return createUpdateCtpTransactionActions(ctpPayment, event, TransactionState.SUCCESS);
protected TransactionState getExpectedTransactionState() {
return TransactionState.SUCCESS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@

import com.commercetools.payment.constants.paypalPlus.NotificationEventType;
import com.google.gson.Gson;
import com.paypal.api.payments.Event;
import io.sphere.sdk.commands.UpdateAction;
import io.sphere.sdk.payments.Payment;
import io.sphere.sdk.payments.TransactionState;
import io.sphere.sdk.payments.TransactionType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import java.util.List;

/**
* Processes PAYMENT.SALE.DENIED event. Change charge state of the corresponding CTP payment to FAILURE
*/
@Component
public class PaymentSaleDeniedProcessor extends PaymentSaleSimpleProcessorBase {
public class PaymentSaleDeniedProcessor extends PaymentSaleNotificationProcessorBase {

@Autowired
public PaymentSaleDeniedProcessor(@Nonnull Gson gson) {
Expand All @@ -29,8 +26,15 @@ public NotificationEventType getNotificationEventType() {
return NotificationEventType.PAYMENT_SALE_DENIED;
}

@Nonnull
@Override
protected TransactionType getExpectedTransactionType() {
return TransactionType.CHARGE;
}

@Nonnull
@Override
List<? extends UpdateAction<Payment>> createUpdateCtpTransactionActions(@Nonnull Payment ctpPayment, @Nonnull Event event) {
return createUpdateCtpTransactionActions(ctpPayment, event, TransactionState.FAILURE);
protected TransactionState getExpectedTransactionState() {
return TransactionState.FAILURE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,36 @@
import com.commercetools.payment.constants.paypalPlus.NotificationEventType;
import com.google.gson.Gson;
import com.paypal.api.payments.Event;
import io.sphere.sdk.commands.UpdateAction;
import io.sphere.sdk.payments.Payment;
import io.sphere.sdk.payments.Transaction;
import io.sphere.sdk.payments.TransactionDraft;
import io.sphere.sdk.payments.TransactionDraftBuilder;
import io.sphere.sdk.payments.TransactionState;
import io.sphere.sdk.payments.TransactionType;
import io.sphere.sdk.payments.commands.updateactions.AddTransaction;
import io.sphere.sdk.payments.commands.updateactions.ChangeTransactionState;
import org.javamoney.moneta.Money;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.commercetools.payment.constants.paypalPlus.NotificationEventData.*;
import static com.commercetools.util.CtpPaymentUtil.findTransactionByInteractionId;
import static com.commercetools.util.TimeUtil.toZonedDateTime;

/**
* Base class for all Payment Sale notification processors
*/
public abstract class PaymentSaleNotificationProcessorBase extends NotificationProcessorBase {

private final static Logger logger = LoggerFactory.getLogger(PaymentSaleNotificationProcessorBase.class);

PaymentSaleNotificationProcessorBase(Gson gson) {
super(gson);
}
Expand All @@ -35,19 +46,63 @@ public boolean canProcess(@Nonnull Event event) {
.equalsIgnoreCase(event.getEventType());
}

protected AddTransaction createAddTransactionAction(@Nonnull Event event,
@Nonnull TransactionType transactionType) {
protected List<UpdateAction<Payment>> createUpdatePaymentActions(@Nonnull Payment ctpPayment, @Nonnull Event event) {
String resourceId = getResourceId(event);
return findTransactionByInteractionId(ctpPayment.getTransactions(), resourceId)
.map(this::processNotificationForTransaction)
.orElseGet(() -> createAddTransactionActionList(event, getExpectedTransactionType()));
}

private List<UpdateAction<Payment>> processNotificationForTransaction(@Nonnull Transaction txn) {
if (isTxnAlreadyUpdated(txn)) {
// can't do Collections.emptyList() here because map() and generics
// together generates unexpected return results
return Collections.emptyList();
} else {
return createChangeTransactionStateActionList(txn);
}
}

@Nonnull
abstract protected TransactionType getExpectedTransactionType();

@Nonnull
abstract protected TransactionState getExpectedTransactionState();

protected List<UpdateAction<Payment>> createChangeTransactionStateActionList(Transaction txn) {
return Collections.singletonList(ChangeTransactionState.of(getExpectedTransactionState(), txn.getId()));
}

protected List<UpdateAction<Payment>> createAddTransactionActionList(@Nonnull Event event,
@Nonnull TransactionType transactionType) {
try {
Map resource = (Map) event.getResource();
String resourceId = (String) resource.get(ID);
Map amount = (Map) resource.get(AMOUNT);
BigDecimal total = new BigDecimal((String) amount.get(TOTAL));
String currencyCode = (String) amount.get(CURRENCY);
String createTime = (String) resource.get(CREATE_TIME);

TransactionDraft transactionDraft = TransactionDraftBuilder
.of(transactionType, Money.of(total, currencyCode))
.timestamp(toZonedDateTime(createTime))
.interactionId(resourceId)
.state(getExpectedTransactionState())
.build();
return Collections.singletonList(AddTransaction.of(transactionDraft));
} catch (Throwable e) {
logger.error("Unexpected error while creating addTransactionActions " +
"for transactionType=[%s] and for Paypal Plus event=[%s]", transactionType, event, e);
return Collections.emptyList();
}
}

protected String getResourceId(@Nonnull Event event) {
Map resource = (Map) event.getResource();
Map amount = (Map) resource.get(AMOUNT);
BigDecimal total = new BigDecimal((String) amount.get(TOTAL));
String currencyCode = (String) amount.get(CURRENCY);
String createTime = (String) resource.get(CREATE_TIME);

TransactionDraft transactionDraft = TransactionDraftBuilder
.of(transactionType, Money.of(total, currencyCode))
.timestamp(toZonedDateTime(createTime))
.state(TransactionState.SUCCESS)
.build();
return AddTransaction.of(transactionDraft);
return (String) resource.get(ID);
}

private boolean isTxnAlreadyUpdated(Transaction txn) {
return txn.getType().equals(getExpectedTransactionType()) && txn.getState().equals(getExpectedTransactionState());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.commercetools.pspadapter.notification.processor.impl;

import com.commercetools.payment.constants.paypalPlus.NotificationEventType;
import com.google.gson.Gson;
import io.sphere.sdk.payments.TransactionState;
import io.sphere.sdk.payments.TransactionType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;

/**
* Processes event notification of type PAYMENT.SALE.PENDING
*/
@Component
public class PaymentSalePendingProcessor extends PaymentSaleNotificationProcessorBase {

@Autowired
public PaymentSalePendingProcessor(@Nonnull Gson gson) {
super(gson);
}

@Override
@Nonnull
public NotificationEventType getNotificationEventType() {
return NotificationEventType.PAYMENT_SALE_PENDING;
}

@Nonnull
@Override
protected TransactionType getExpectedTransactionType() {
return TransactionType.CHARGE;
}

@Nonnull
@Override
protected TransactionState getExpectedTransactionState() {
return TransactionState.PENDING;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@

import com.commercetools.payment.constants.paypalPlus.NotificationEventType;
import com.google.gson.Gson;
import com.paypal.api.payments.Event;
import io.sphere.sdk.commands.UpdateAction;
import io.sphere.sdk.payments.Payment;
import io.sphere.sdk.payments.TransactionState;
import io.sphere.sdk.payments.TransactionType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import java.util.List;

/**
* Processes PAYMENT.SALE.REFUNDED event. Updates CTP payment with a new refund transaction.
*/
@Component
public class PaymentSaleRefundedProcessor extends PaymentSaleReturnProcessorBase {
public class PaymentSaleRefundedProcessor extends PaymentSaleNotificationProcessorBase {

@Autowired
public PaymentSaleRefundedProcessor(@Nonnull Gson gson) {
Expand All @@ -29,9 +26,15 @@ public NotificationEventType getNotificationEventType() {
return NotificationEventType.PAYMENT_SALE_REFUNDED;
}

@Nonnull
@Override
List<? extends UpdateAction<Payment>> createUpdateCtpTransactionActions(@Nonnull Payment ctpPayment, @Nonnull Event event) {
return createUpdateCtpTransactionActions(ctpPayment, event, TransactionType.REFUND);
protected TransactionType getExpectedTransactionType() {
return TransactionType.REFUND;
}

@Nonnull
@Override
protected TransactionState getExpectedTransactionState() {
return TransactionState.SUCCESS;
}
}

This file was deleted.

Loading

0 comments on commit dcefe2f

Please sign in to comment.