/*
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.webservices.pos;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import javax.jws.WebService;
import javax.servlet.http.HttpServletRequest;
import nl.strohalm.cyclos.entities.access.Channel;
import nl.strohalm.cyclos.entities.access.Channel.Principal;
import nl.strohalm.cyclos.entities.access.PrincipalType;
import nl.strohalm.cyclos.entities.accounts.AccountStatus;
import nl.strohalm.cyclos.entities.accounts.AccountType;
import nl.strohalm.cyclos.entities.accounts.MemberAccount;
import nl.strohalm.cyclos.entities.accounts.MemberAccountType;
import nl.strohalm.cyclos.entities.accounts.pos.MemberPos;
import nl.strohalm.cyclos.entities.accounts.transactions.Payment;
import nl.strohalm.cyclos.entities.accounts.transactions.Transfer;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferAuthorizationDTO;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferQuery;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.entities.customization.fields.MemberCustomField;
import nl.strohalm.cyclos.entities.exceptions.EntityNotFoundException;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.entities.settings.LocalSettings;
import nl.strohalm.cyclos.entities.settings.LocalSettings.TransactionNumber;
import nl.strohalm.cyclos.services.access.AccessService;
import nl.strohalm.cyclos.services.access.ChannelService;
import nl.strohalm.cyclos.services.access.exceptions.InvalidUserForChannelException;
import nl.strohalm.cyclos.services.accounts.AccountDTO;
import nl.strohalm.cyclos.services.accounts.AccountService;
import nl.strohalm.cyclos.services.accounts.pos.MemberPosServiceLocal;
import nl.strohalm.cyclos.services.elements.ElementService;
import nl.strohalm.cyclos.services.settings.SettingsService;
import nl.strohalm.cyclos.services.transactions.DoPaymentDTO;
import nl.strohalm.cyclos.services.transactions.PaymentService;
import nl.strohalm.cyclos.services.transactions.TransactionContext;
import nl.strohalm.cyclos.services.transactions.TransferAuthorizationService;
import nl.strohalm.cyclos.services.transfertypes.TransferTypeService;
import nl.strohalm.cyclos.utils.EntityHelper;
import nl.strohalm.cyclos.utils.MessageResolver;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.query.PageParameters;
import nl.strohalm.cyclos.utils.query.QueryParameters.ResultType;
import nl.strohalm.cyclos.webservices.WebServiceContext;
import nl.strohalm.cyclos.webservices.WebServiceFaultsEnum;
import nl.strohalm.cyclos.webservices.accounts.AccountHistoryResultPage;
import nl.strohalm.cyclos.webservices.model.AccountHistoryTransferVO;
import nl.strohalm.cyclos.webservices.model.AccountStatusVO;
import nl.strohalm.cyclos.webservices.model.DetailedAccountTypeVO;
import nl.strohalm.cyclos.webservices.model.PosInitializationVO;
import nl.strohalm.cyclos.webservices.model.TransactionNumberVO;
import nl.strohalm.cyclos.webservices.model.WSPrincipalTypeVO;
import nl.strohalm.cyclos.webservices.model.WSPrincipalTypeVO.WSPrincipal;
import nl.strohalm.cyclos.webservices.payments.ChargebackResult;
import nl.strohalm.cyclos.webservices.payments.ChargebackStatus;
import nl.strohalm.cyclos.webservices.payments.PaymentResult;
import nl.strohalm.cyclos.webservices.payments.PaymentStatus;
import nl.strohalm.cyclos.webservices.utils.AccountHelper;
import nl.strohalm.cyclos.webservices.utils.MemberHelper;
import nl.strohalm.cyclos.webservices.utils.PaymentHelper;
import nl.strohalm.cyclos.webservices.utils.WebServiceHelper;
/**
* Implementation for PosWebService <br>
*
* IMPORTANT: THIS WEB SERVICE IMPLEMENTATION DOESN'T USE SERVICE CLIENTS <br>
* THEN IT MUST USE THE REMOTE (SECURITY) SERVICES INSTEAD OF THE LOCAL SERVICES
* @author luis, ameyer
*/
@WebService(name = "pos", serviceName = "pos")
public class PosWebServiceImpl implements PosWebService {
private AccessService accessService;
private TransferTypeService transferTypeService;
private ElementService elementService;
private AccountService accountService;
private ChannelService channelService;
private SettingsService settingsService;
private PaymentService paymentService;
private AccountHelper accountHelper;
private PaymentHelper paymentHelper;
private MemberHelper memberHelper;
private WebServiceHelper webServiceHelper;
private TransferAuthorizationService transferAuthorizationService;
private MessageResolver messageResolver;
/**
* The MemberPosService is the only one accessed through the local interface, as it's not going to operate over the pos, but just validate it,
* through the checkPin method, which is on the local interface only
*/
private MemberPosServiceLocal memberPosService;
@Override
public ChargebackResult chargeback(final ChargebackParameters parameters) {
ChargebackStatus status = null;
final Member member = WebServiceContext.getMember();
// Find the transfer
Transfer transfer = null;
Transfer chargebackTransfer = null;
try {
transfer = paymentService.load(parameters.getTransferId());
// Ensure the member is the one who received the payment
if (!transfer.getToOwner().equals(member)) {
throw new EntityNotFoundException();
}
} catch (final EntityNotFoundException e) {
status = ChargebackStatus.TRANSFER_NOT_FOUND;
}
// Check if the transfer can be charged back
if (status == null && !paymentService.canChargeback(transfer, false)) {
if (transfer.getChargedBackBy() != null) {
status = ChargebackStatus.TRANSFER_ALREADY_CHARGEDBACK;
} else {
if (transfer.getStatus() == Payment.Status.PENDING) {
final TransferAuthorizationDTO transferAuthorizationDto = new TransferAuthorizationDTO();
transferAuthorizationDto.setTransfer(transfer);
transferAuthorizationDto.setShowToMember(false);
chargebackTransfer = transferAuthorizationService.cancel(transferAuthorizationDto);
status = ChargebackStatus.SUCCESS;
} else {
status = ChargebackStatus.TRANSFER_CANNOT_BE_CHARGEDBACK;
}
}
}
// Check the amount
if (status == null) {
final LocalSettings localSettings = settingsService.getLocalSettings();
if (!localSettings.truncate(transfer.getAmount()).equals(localSettings.truncate(parameters.getAmount()))) {
status = ChargebackStatus.INVALID_PARAMETERS;
}
}
// Do the chargeback
if (status == null) {
chargebackTransfer = paymentService.chargeback(transfer);
status = ChargebackStatus.SUCCESS;
}
if (!status.isSuccessful()) {
webServiceHelper.error("Chargeback result " + status);
}
// Build the result
if (status == ChargebackStatus.SUCCESS || status == ChargebackStatus.TRANSFER_ALREADY_CHARGEDBACK) {
final AccountHistoryTransferVO originalVO = accountHelper.toVO(member, transfer, null);
final AccountHistoryTransferVO chargebackVO = accountHelper.toVO(member, chargebackTransfer, null);
return new ChargebackResult(status, originalVO, chargebackVO);
} else {
return new ChargebackResult(status, null, null);
}
}
@Override
public AccountStatusVO getAccountStatus(final AccountStatusPosParameters parameters) {
final Member member = WebServiceContext.getMember();
final MemberAccountType accountType = EntityHelper.reference(MemberAccountType.class, parameters.getAccountTypeId());
final AccountStatus accountStatus = accountService.getCurrentStatus(new AccountDTO(member, accountType));
return accountHelper.toVO(accountStatus);
}
@Override
@SuppressWarnings("unchecked")
public PosInitializationVO getInitializationData(final InitializationParameters params) {
final MemberPos memberPos = WebServiceContext.getPos().getMemberPos();
final Member member = memberPos.getMember();
final PosInitializationVO initializationData = new PosInitializationVO();
// Set the owner
initializationData.setOwner(memberHelper.toVO(memberPos.getMember()));
// Get the POS channel principal type
final Channel posChannel = WebServiceContext.getChannel();
final List<WSPrincipalTypeVO> principalTypes = new ArrayList<WSPrincipalTypeVO>();
final PrincipalType defaultPrincipalType = posChannel.getDefaultPrincipalType();
for (final PrincipalType principalType : posChannel.getPrincipalTypes()) {
final WSPrincipalTypeVO type = new WSPrincipalTypeVO();
final Principal principal = principalType.getPrincipal();
type.setPrincipal(WSPrincipal.valueOf(principal.name()));
type.setDefault(principalType.equals(defaultPrincipalType));
final MemberCustomField customField = principalType.getCustomField();
if (customField != null) {
type.setCustomFieldInternalName(customField.getInternalName());
type.setLabel(customField.getName());
} else {
type.setLabel(messageResolver.message(principal.getKey()));
}
principalTypes.add(type);
}
initializationData.setPrincipalTypes(principalTypes);
// Get the member accounts
final List<DetailedAccountTypeVO> accountVOs = new ArrayList<DetailedAccountTypeVO>();
for (final MemberAccount memberAccount : (List<MemberAccount>) accountService.getAccounts(member)) {
if (memberAccount.getStatus() == MemberAccount.Status.ACTIVE) { // only add the active accounts
accountVOs.add(accountHelper.toDetailedTypeVO(Channel.POS, memberAccount));
}
}
initializationData.setAccountTypes(accountVOs);
// Get data from settings
final LocalSettings localSettings = settingsService.getLocalSettings();
final TransactionNumber transactionNumber = localSettings.getTransactionNumber();
if (transactionNumber != null) {
initializationData.setTransactionNumber(new TransactionNumberVO(transactionNumber.getPrefix(), transactionNumber.getPadLength(), transactionNumber.getSuffix()));
}
initializationData.setDecimalDigits(localSettings.getPrecision().getValue());
// Get data from MemberPos
initializationData.setMaxSchedulingPayments(memberPos.getMaxSchedulingPayments());
initializationData.setNumberOfCopies(memberPos.getNumberOfCopies());
initializationData.setResultPageSize(memberPos.getResultPageSize());
initializationData.setAllowMakePayment(memberPos.isAllowMakePayment());
return initializationData;
}
@Override
public PaymentResult makePayment(final MakePaymentParameters params) {
PaymentStatus status = null;
final MemberPos memberPos = WebServiceContext.getPos().getMemberPos();
DoPaymentDTO dto = null;
Member toMember = null;
TransferType transferType = null;
try {
// In that case the pos pin is not validated by the interceptor (MakePaymentParameters not implements the IPosPinParameter interface)
// because we want to set a status indicating the error
checkPin(params.getPosPin());
} catch (final Exception e) {
webServiceHelper.error(e);
status = paymentHelper.toStatus(e);
}
if (status == null) {
// Ensure make payment is enabled
if (!memberPos.isAllowMakePayment()) {
throw WebServiceHelper.fault(WebServiceFaultsEnum.UNAUTHORIZED_ACCESS, "Make payment is not allowed");
}
// Get the parameters
try {
final PrincipalType principalType = channelService.resolvePrincipalType(Channel.POS, params.getToMemberPrincipalType());
toMember = elementService.loadByPrincipal(principalType, params.getToMemberPrincipal());
transferType = transferTypeService.load(params.getTransferTypeId());
} catch (final Exception e) {
webServiceHelper.error(e);
status = PaymentStatus.INVALID_PARAMETERS;
}
}
// Ok so far: set the transfer DTO
if (status == null) {
dto = new DoPaymentDTO();
dto.setContext(TransactionContext.PAYMENT);
dto.setChannel(Channel.POS);
dto.setAmount(params.getAmount());
dto.setFrom(memberPos.getMember());
dto.setTo(toMember);
dto.setTransferType(transferType);
}
// Perform the payment
AccountHistoryTransferVO transferVO = null;
if (status == null) {
try {
final Transfer transfer = (Transfer) paymentService.doPayment(dto);
status = paymentHelper.toStatus(transfer);
transferVO = accountHelper.toVO(dto.getFrom(), transfer, null);
} catch (final Exception e) {
webServiceHelper.error(e);
status = paymentHelper.toStatus(e);
}
}
return new PaymentResult(status, transferVO);
}
@Override
public PaymentResult receivePayment(final ReceivePaymentParameters params) {
// Get the member pos
final Member member = WebServiceContext.getMember();
// Get the parameters
PaymentStatus status = null;
Member fromMember = null;
TransferType transferType = null;
AccountHistoryTransferVO transferVO = null;
try {
final HttpServletRequest request = WebServiceContext.getRequest();
final String credentials = params.getFromMemberCredentials();
final String remoteAddress = request.getRemoteAddr();
fromMember = memberHelper.loadByPrincipal(params.getFromMemberPrincipalType(), params.getFromMemberPrincipal());
if (fromMember == null) {
throw new EntityNotFoundException(Member.class);
}
// Check if the POS channel is enabled for the payer:
if (!accessService.isChannelEnabledForMember(Channel.POS, fromMember)) {
throw new InvalidUserForChannelException(fromMember.getUsername());
}
accessService.checkCredentials(WebServiceContext.getChannel(), fromMember.getMemberUser(), credentials, remoteAddress, member);
transferType = transferTypeService.load(params.getTransferTypeId());
final DoPaymentDTO dto = new DoPaymentDTO();
dto.setContext(TransactionContext.PAYMENT);
dto.setChannel(Channel.POS);
dto.setAmount(params.getAmount());
dto.setFrom(fromMember);
dto.setTo(member);
dto.setTransferType(transferType);
// run as the payer
final Transfer transfer = LoggedUser.runAs(fromMember.getUser(), new Callable<Transfer>() {
@Override
public Transfer call() throws Exception {
return (Transfer) paymentService.doPayment(dto);
}
});
status = paymentHelper.toStatus(transfer);
transferVO = accountHelper.toVO(member, transfer, null);
} catch (final Exception e) {
webServiceHelper.error(e);
status = paymentHelper.toStatus(e);
}
return new PaymentResult(status, transferVO);
}
@Override
public AccountHistoryResultPage searchAccountHistory(final AccountHistoryPosParameters parameters) {
final MemberPos memberPos = WebServiceContext.getPos().getMemberPos();
// Prepare the parameters
final Member member = memberPos.getMember();
final int pageSize = memberPos.getResultPageSize();
final int currentPage = parameters.getCurrentPage();
final AccountType accountType = EntityHelper.reference(AccountType.class, parameters.getAccountTypeId());
// Query the transfers
final TransferQuery query = new TransferQuery();
query.setOwner(member);
query.setType(accountType);
query.setResultType(ResultType.PAGE);
query.setPageParameters(new PageParameters(pageSize, currentPage));
final List<Transfer> transfers = paymentService.search(query);
return accountHelper.toAccountHistoryResultPage(member, transfers);
}
public void setAccessService(final AccessService accessService) {
this.accessService = accessService;
}
public void setAccountHelper(final AccountHelper accountHelper) {
this.accountHelper = accountHelper;
}
public void setAccountService(final AccountService accountService) {
this.accountService = accountService;
}
public void setChannelService(final ChannelService channelService) {
this.channelService = channelService;
}
public void setElementService(final ElementService elementService) {
this.elementService = elementService;
}
public void setMemberHelper(final MemberHelper memberHelper) {
this.memberHelper = memberHelper;
}
public void setMemberPosServiceLocal(final MemberPosServiceLocal memberPosService) {
this.memberPosService = memberPosService;
}
public void setMessageResolver(final MessageResolver messageResolver) {
this.messageResolver = messageResolver;
}
public void setPaymentHelper(final PaymentHelper paymentHelper) {
this.paymentHelper = paymentHelper;
}
public void setPaymentService(final PaymentService paymentService) {
this.paymentService = paymentService;
}
public void setSettingsService(final SettingsService settingsService) {
this.settingsService = settingsService;
}
public void setTransferAuthorizationService(final TransferAuthorizationService transferAuthorizationService) {
this.transferAuthorizationService = transferAuthorizationService;
}
public void setTransferTypeService(final TransferTypeService transferTypeService) {
this.transferTypeService = transferTypeService;
}
public void setWebServiceHelper(final WebServiceHelper webServiceHelper) {
this.webServiceHelper = webServiceHelper;
}
private void checkPin(final String posPin) {
memberPosService.checkPin(WebServiceContext.getPos().getMemberPos(), posPin);
}
}