nl.strohalm.cyclos.controls.posweb.ReceivePaymentAction.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.controls.posweb.ReceivePaymentAction.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
Cyclos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.controls.posweb;

import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;

import javax.servlet.http.HttpServletRequest;

import nl.strohalm.cyclos.access.OperatorPermission;
import nl.strohalm.cyclos.annotations.Inject;
import nl.strohalm.cyclos.controls.ActionContext;
import nl.strohalm.cyclos.controls.payments.SchedulingType;
import nl.strohalm.cyclos.entities.access.Channel;
import nl.strohalm.cyclos.entities.access.Channel.Credentials;
import nl.strohalm.cyclos.entities.access.Channel.Principal;
import nl.strohalm.cyclos.entities.access.MemberUser;
import nl.strohalm.cyclos.entities.access.PrincipalType;
import nl.strohalm.cyclos.entities.accounts.AccountOwner;
import nl.strohalm.cyclos.entities.accounts.transactions.Payment;
import nl.strohalm.cyclos.entities.exceptions.EntityNotFoundException;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.entities.settings.LocalSettings;
import nl.strohalm.cyclos.services.access.ChannelService;
import nl.strohalm.cyclos.services.access.exceptions.BlockedCredentialsException;
import nl.strohalm.cyclos.services.access.exceptions.InvalidCardException;
import nl.strohalm.cyclos.services.access.exceptions.InvalidCredentialsException;
import nl.strohalm.cyclos.services.access.exceptions.InvalidUserForChannelException;
import nl.strohalm.cyclos.services.transactions.DoPaymentDTO;
import nl.strohalm.cyclos.services.transactions.ProjectionDTO;
import nl.strohalm.cyclos.services.transactions.ScheduledPaymentDTO;
import nl.strohalm.cyclos.services.transactions.TransactionContext;
import nl.strohalm.cyclos.utils.RequestHelper;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.binding.BeanBinder;
import nl.strohalm.cyclos.utils.binding.DataBinder;
import nl.strohalm.cyclos.utils.conversion.CoercionHelper;
import nl.strohalm.cyclos.utils.validation.InvalidError;
import nl.strohalm.cyclos.utils.validation.PositiveNonZeroValidation;
import nl.strohalm.cyclos.utils.validation.RequiredError;
import nl.strohalm.cyclos.utils.validation.TodayValidation;
import nl.strohalm.cyclos.utils.validation.ValidationError;
import nl.strohalm.cyclos.utils.validation.ValidationException;

import org.apache.commons.lang.StringUtils;

/**
 * Action for the posweb member to receive a payment
 * @author luis
 */
public class ReceivePaymentAction extends BasePosWebPaymentAction {

    private ChannelService channelService;

    @Inject
    public void setChannelService(final ChannelService channelService) {
        this.channelService = channelService;
    }

    @Override
    protected Payment doPayment(final ActionContext context, final DoPaymentDTO dto) {
        final Member from;
        try {
            // Load the member that is making the payment
            from = elementService.load(((Member) dto.getFrom()).getId(), Element.Relationships.USER);
            if (from == null) {
                throw new Exception();
            }
        } catch (final Exception e) {
            throw new ValidationException();
        }

        // run as the member, to ensure the model layer will test permissions as that member
        return LoggedUser.runAs(from.getUser(), new Callable<Payment>() {
            @Override
            public Payment call() throws Exception {
                // Check if the member has access to the "posweb" channel;
                if (!accessService.isChannelEnabledForMember(Channel.POSWEB, from)) {
                    throw new InvalidUserForChannelException(from.getUsername());
                }

                // Check the paying member credentials
                final ValidationException validation = checkCredentials(context, from);
                validation.throwIfHasErrors();

                // Ensure that the scheduled payment, if any, is shown to the user
                dto.setShowScheduledToReceiver(true);

                // Perform the payment itself
                return getPaymentService().doPayment(dto);
            }
        });
    }

    @Override
    protected OperatorPermission getPermission() {
        return OperatorPermission.PAYMENTS_POSWEB_RECEIVE_PAYMENT;
    }

    @Override
    protected DataBinder<DoPaymentDTO> initDataBinder() {
        final BeanBinder<DoPaymentDTO> binder = (BeanBinder<DoPaymentDTO>) super.initDataBinder();
        // As we will handle from member by principal, remove from the regular binder
        binder.getMappings().remove("from");
        return binder;
    }

    @Override
    protected void prepareForm(final ActionContext context) throws Exception {
        super.prepareForm(context);
        final ReceivePaymentForm form = context.getForm();
        final HttpServletRequest request = context.getRequest();
        final Channel channel = posWebChannel();
        final Credentials credentials = channel.getCredentials();
        boolean numericCredentials = false;
        boolean uppercasedCredentials = false;
        switch (credentials) {
        case PIN:
        case CARD_SECURITY_CODE:
            numericCredentials = true;
            break;
        case LOGIN_PASSWORD:
            numericCredentials = settingsService.getAccessSettings().isNumericPassword();
            break;
        case TRANSACTION_PASSWORD:
            numericCredentials = StringUtils
                    .containsOnly(settingsService.getAccessSettings().getTransactionPasswordChars(), "0123456789");
            uppercasedCredentials = true;
            break;
        }
        final PrincipalType selectedPrincipalType = channelService.resolvePrincipalType(channel.getInternalName(),
                form.getPrincipalType());
        form.setPrincipalType(selectedPrincipalType.toString());
        final Map<String, PrincipalType> principalTypes = new TreeMap<String, PrincipalType>();
        for (final PrincipalType principalType : channel.getPrincipalTypes()) {
            principalTypes.put(principalTypeLabel(principalType), principalType);
        }
        request.setAttribute("principalTypes", principalTypes);
        request.setAttribute("selectedPrincipalType", selectedPrincipalType);
        request.setAttribute("selectedPrincipalLabel", principalTypeLabel(selectedPrincipalType));
        request.setAttribute("credentials", credentials);
        request.setAttribute("numericCredentials", numericCredentials);
        request.setAttribute("uppercasedCredentials", uppercasedCredentials);
        request.setAttribute("credentialsKey", getCredentialsKey());
        final LocalSettings localSettings = settingsService.getLocalSettings();
        request.setAttribute("today", localSettings.getRawDateConverter().toString(Calendar.getInstance()));
        RequestHelper.storeEnum(request, SchedulingType.class, "schedulingTypes");
    }

    @Override
    protected DoPaymentDTO resolvePaymentDTO(final ActionContext context) {
        final Member member = (Member) context.getAccountOwner();
        final ReceivePaymentForm form = context.getForm();
        final DoPaymentDTO payment = getDataBinder().readFromString(form);

        try {
            final Member fromMember = CoercionHelper.coerce(Member.class, form.getFrom());
            payment.setFrom(fromMember);
        } catch (final EntityNotFoundException e) {
            // Leave null - will fail validation
        }

        payment.setChannel(Channel.POSWEB);
        payment.setContext(TransactionContext.PAYMENT);
        payment.setTo(member);
        if (context.isOperator()) {
            payment.setReceiver(context.getElement());
        }
        final LocalSettings localSettings = settingsService.getLocalSettings();

        // Handle scheduling
        SchedulingType schedulingType = CoercionHelper.coerce(SchedulingType.class, form.getSchedulingType());
        if (schedulingType == null) {
            schedulingType = SchedulingType.IMMEDIATELY;
        }
        List<ScheduledPaymentDTO> installments = null;
        switch (schedulingType) {
        case SINGLE_FUTURE:
            final ScheduledPaymentDTO installment = new ScheduledPaymentDTO();
            installment.setAmount(payment.getAmount());
            installment.setDate(localSettings.getRawDateConverter().valueOf(form.getScheduledFor()));
            installments = Collections.singletonList(installment);
            break;
        case MULTIPLE_FUTURE:
            final int paymentCount = CoercionHelper.coerce(int.class, form.getPaymentCount());
            final Calendar firstPaymentDate = localSettings.getRawDateConverter()
                    .valueOf(form.getFirstPaymentDate());
            if (paymentCount > 0 && firstPaymentDate != null) {
                final ProjectionDTO projection = new ProjectionDTO();
                projection.setTransferType(payment.getTransferType());
                projection.setAmount(payment.getAmount());
                projection.setFirstExpirationDate(firstPaymentDate);
                projection.setPaymentCount(paymentCount);
                installments = getPaymentService().calculatePaymentProjection(projection);
            }
            break;
        }
        payment.setPayments(installments);

        return payment;
    }

    @Override
    protected void validateForm(final ActionContext context) {
        final DoPaymentDTO dto = resolvePaymentDTO(context);
        dto.setPayments(null);

        ValidationException validation = null;
        try {
            getPaymentService().validate(dto);
            validation = new ValidationException();
        } catch (final ValidationException e) {
            validation = e;
        }
        final ReceivePaymentForm form = context.getForm();
        final LocalSettings localSettings = settingsService.getLocalSettings();

        final AccountOwner from = dto.getFrom();
        if (from instanceof Member) {
            validation.setPropertyKey("paymentCount", "transfer.paymentCount");
            validation.setPropertyKey("firstPaymentDate", "transfer.firstPaymentDate");
            validation.setPropertyKey("scheduledFor", "transfer.scheduledFor");
            validation.setPropertyKey("_credentials", getCredentialsKey());

            SchedulingType schedulingType = CoercionHelper.coerce(SchedulingType.class, form.getSchedulingType());
            if (schedulingType == null) {
                schedulingType = SchedulingType.IMMEDIATELY;
            }

            switch (schedulingType) {
            case SINGLE_FUTURE:
                // Validate the scheduled for date
                Calendar scheduledFor = null;
                try {
                    scheduledFor = localSettings.getRawDateConverter().valueOf(form.getScheduledFor());
                    ValidationError error = null;
                    if (scheduledFor == null) {
                        error = new RequiredError();
                    } else {
                        error = TodayValidation.future().validate(null, null, scheduledFor);
                    }
                    if (error != null) {
                        validation.addPropertyError("scheduledFor", error);
                    }
                } catch (final Exception e) {
                    validation.addPropertyError("scheduledFor", new InvalidError());
                }
                break;
            case MULTIPLE_FUTURE:
                // Validate the payment count
                Integer paymentCount;
                try {
                    paymentCount = CoercionHelper.coerce(Integer.class, form.getPaymentCount());
                    ValidationError error = null;
                    if (paymentCount == null) {
                        error = new RequiredError();
                    } else {
                        error = PositiveNonZeroValidation.instance().validate(null, null, paymentCount);
                    }
                    if (error != null) {
                        validation.addPropertyError("paymentCount", new InvalidError());
                    }
                } catch (final Exception e) {
                    validation.addPropertyError("paymentCount", new InvalidError());
                }

                // Validate the first payment date
                Calendar firstPaymentDate = null;
                try {
                    firstPaymentDate = localSettings.getRawDateConverter().valueOf(form.getFirstPaymentDate());
                    ValidationError error = null;
                    if (firstPaymentDate == null) {
                        error = new RequiredError();
                    } else {
                        error = TodayValidation.futureOrToday().validate(null, null, firstPaymentDate);
                    }
                    if (error != null) {
                        validation.addPropertyError("firstPaymentDate", error);
                    }
                } catch (final Exception e) {
                    validation.addPropertyError("firstPaymentDate", new InvalidError());
                }
                break;
            }

            // Validate the credentials
            if (StringUtils.isEmpty(form.getCredentials())) {
                validation.addPropertyError("_credentials", new RequiredError());
            } else {
                if (!validation.hasErrors()) {
                    final Member member = elementService.load(((Member) from).getId());
                    validation = checkCredentials(context, member);
                }
            }
        }
        validation.throwIfHasErrors();
    }

    private ValidationException checkCredentials(final ActionContext context, final Member member) {
        final String cardFormProperty = "_card";
        final String credentialsFormProperty = "_credentials";

        final ValidationException validation = new ValidationException();
        validation.setPropertyKey(cardFormProperty, "posweb.client.card");
        validation.setPropertyKey(credentialsFormProperty, getCredentialsKey());

        final ReceivePaymentForm form = context.getForm();
        final String credentials = form.getCredentials();

        // Validate the credentials
        if (StringUtils.isEmpty(credentials)) {
            // Missing
            validation.addPropertyError(credentialsFormProperty, new RequiredError());
        } else {
            // Check it
            final Member relatedMember = (Member) context.getAccountOwner();
            try {
                final MemberUser payer = CoercionHelper.coerce(MemberUser.class, form.getFrom());
                accessService.checkCredentials(posWebChannel(), payer, credentials,
                        context.getRequest().getRemoteAddr(), relatedMember);
            } catch (final InvalidCardException e) {
                validation.addPropertyError(cardFormProperty, new InvalidError());
            } catch (final InvalidCredentialsException e) {
                validation.addPropertyError(credentialsFormProperty, new InvalidError());
            } catch (final BlockedCredentialsException e) {
                String key;
                switch (e.getCredentialsType()) {
                case TRANSACTION_PASSWORD:
                    key = "transactionPassword.error.blockedByTrials";
                    break;
                case PIN:
                    key = "pin.error.blocked";
                    break;
                case CARD_SECURITY_CODE:
                    key = "cardSecurityCode.error.blocked";
                    break;
                default:
                    key = "login.error.blocked";
                    break;
                }
                validation.addGeneralError(new ValidationError(key));
            }
        }

        return validation;
    }

    private String getCredentialsKey() {
        final Channel channel = posWebChannel();
        String credentialsKey;
        switch (channel.getCredentials()) {
        case PIN:
            credentialsKey = "posweb.client.pin";
            break;
        case LOGIN_PASSWORD:
            credentialsKey = "posweb.client.loginPassword";
            break;
        case TRANSACTION_PASSWORD:
            credentialsKey = "posweb.client.transactionPassword";
            break;
        case CARD_SECURITY_CODE:
            credentialsKey = "posweb.client.cardSecurityCode";
            break;
        default:
            throw new IllegalStateException(
                    "Cannot use credentials type " + channel.getCredentials() + " for PosWeb");
        }
        return credentialsKey;
    }

    private Channel posWebChannel() {
        final Channel channel = channelService.loadByInternalName(Channel.POSWEB);
        return channel;
    }

    private String principalTypeLabel(final PrincipalType principalType) {
        final Principal principal = principalType.getPrincipal();
        switch (principal) {
        case USER:
            return messageHelper.message("posweb.client.username");
        case CUSTOM_FIELD:
            return principalType.getCustomField().getName();
        default:
            return messageHelper.message(principal.getKey());
        }
    }
}