/*
jBilling - The Enterprise Open Source Billing System
Copyright (C) 2003-2011 Enterprise jBilling Software Ltd. and Emiliano Conde
This file is part of jbilling.
jbilling is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jbilling 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with jbilling. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sapienter.jbilling.server.payment.tasks;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.client.XmlRpcCommonsTransportFactory;
import com.sapienter.jbilling.server.payment.PaymentAuthorizationBL;
import com.sapienter.jbilling.server.payment.PaymentDTOEx;
import com.sapienter.jbilling.server.payment.db.PaymentAuthorizationDTO;
import com.sapienter.jbilling.server.payment.db.PaymentResultDAS;
import com.sapienter.jbilling.server.pluggableTask.PaymentTaskWithTimeout;
import com.sapienter.jbilling.server.pluggableTask.TaskException;
import com.sapienter.jbilling.server.pluggableTask.admin.ParameterDescription;
import com.sapienter.jbilling.server.pluggableTask.admin.PluggableTaskException;
import com.sapienter.jbilling.server.user.ContactBL;
import com.sapienter.jbilling.server.user.CreditCardBL;
import com.sapienter.jbilling.server.user.contact.db.ContactDTO;
import com.sapienter.jbilling.server.util.Constants;
public class PaymentAtlasTask extends PaymentTaskWithTimeout {
public static final ParameterDescription PARAMETER_MERCHANT_ACCOUNT_CODE =
new ParameterDescription("merchant_account_code", true, ParameterDescription.Type.STR);
public static final ParameterDescription PARAMETER_TEST =
new ParameterDescription("test", false, ParameterDescription.Type.STR);
public static final ParameterDescription PARAMETER_AVS =
new ParameterDescription("submit_avs", false, ParameterDescription.Type.STR);
public static final ParameterDescription PARAMETER_PASSWORD =
new ParameterDescription("password", true, ParameterDescription.Type.STR);
private static final String URL = "https://atlasportal.net:8443/gates/xmlrpc";
private static final String TEST_URL = "https://atlasbilling.net:8443/gates/xmlrpc";
private static final int CONNECTION_TIME_OUT = 10000; // in millisec
private static final int REPLY_TIME_OUT = 30000; // in millisec
private Logger log = Logger.getLogger(PaymentAtlasTask.class);
//initializer for pluggable params
{
descriptions.add(PARAMETER_AVS);
descriptions.add(PARAMETER_MERCHANT_ACCOUNT_CODE);
descriptions.add(PARAMETER_PASSWORD);
descriptions.add(PARAMETER_TEST);
}
public boolean process(PaymentDTOEx paymentInfo)
throws PluggableTaskException {
boolean retValue = false;
if (paymentInfo.getPayoutId() != null) {
return true;
}
try {
if (paymentInfo.getCreditCard() == null) {
log.error("Can't process without a credit card");
throw new TaskException("Credit card not present in payment");
}
if (paymentInfo.getAch() != null) {
log.error("Can't process with a cheque");
throw new TaskException("Can't process ACH charge");
}
if (paymentInfo.getIsRefund() == 1
&& (paymentInfo.getPayment() == null || paymentInfo
.getPayment().getAuthorization() == null)) {
log
.error("Can't process refund without a payment with an authorization record");
throw new TaskException("Refund without previous authorization");
}
validateParameters();
Map<String, Object> data;
if (paymentInfo.getIsRefund() == 0) {
data = getChargeData(paymentInfo);
} else {
data = getRefundData(paymentInfo);
}
if ("true".equals(getOptionalParameter(PARAMETER_AVS.getName(), "false"))) {
addAVSFields(paymentInfo.getUserId(), data);
log.debug("returning after avs " + data);
}
PaymentAuthorizationDTO response = makeCall(data, true);
paymentInfo.setAuthorization(response);
if ("1".equals(response.getCode1())) {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_OK));
log.debug("result is ok");
} else {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_FAIL));
log.debug("result is fail");
}
PaymentAuthorizationBL bl = new PaymentAuthorizationBL();
bl.create(response, paymentInfo.getId());
} catch (MalformedURLException e) {
log.error("MalformedURLException exception when calling Atlas", e);
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_UNAVAILABLE));
retValue = true;
} catch (XmlRpcException e) {
log.error("XmlRpcException exception when calling Atlas", e);
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_UNAVAILABLE));
retValue = false;
} catch (PluggableTaskException e) {
log.error("PluggableTaskException", e);
throw e;
} catch (Exception e) {
log.error("Exception", e);
throw new PluggableTaskException(e);
}
log.debug("returning " + retValue);
return retValue;
}
private void validateParameters() throws PluggableTaskException {
ensureGetParameter(PARAMETER_MERCHANT_ACCOUNT_CODE.getName());
ensureGetParameter(PARAMETER_PASSWORD.getName());
}
public void failure(Integer userId, Integer retry) {
}
private Map<String, Object> getData(PaymentDTOEx paymentInfo)
throws PluggableTaskException {
Map<String, Object> data = new HashMap<String, Object>();
data.put("merchantAccountCode",
ensureGetParameter(PARAMETER_MERCHANT_ACCOUNT_CODE.getName()));
if (paymentInfo.getUserId() != null)
data.put("customerAccountCode", String.valueOf(paymentInfo
.getUserId()));
data.put("accountNumber", paymentInfo.getCreditCard().getNumber());
data.put("name", paymentInfo.getCreditCard().getName());
data.put("amount", paymentInfo.getAmount().multiply(new BigDecimal("100")).intValue());
data.put("taxAmount", 0);
String securityCode = paymentInfo.getCreditCard().getSecurityCode();
if (securityCode != null)
data.put("cvv2", securityCode);
data.put("expirationDate", CreditCardBL.get4digitExpiry(paymentInfo
.getCreditCard()));
data.put("transactionDate", paymentInfo.getPaymentDate());
data.put("transactionCode", paymentInfo.getId() + "");
return data;
}
private Map<String, Object> getChargeData(PaymentDTOEx paymentInfo)
throws PluggableTaskException {
Map<String, Object> data = getData(paymentInfo);
data.put("creditIndicator", Boolean.FALSE);
data.put("type", "sale-request");
return data;
}
private Map<String, Object> getRefundData(PaymentDTOEx paymentInfo)
throws PluggableTaskException {
Map<String, Object> data = getData(paymentInfo);
data.put("itemCode", paymentInfo.getPayment().getAuthorization()
.getTransactionId());
data.put("creditIndicator", Boolean.TRUE);
data.put("type", "credit-request");
return data;
}
private void addAVSFields(Integer userId, Map<String, Object> data) {
try {
ContactBL contact = new ContactBL();
contact.set(userId);
ContactDTO entity = contact.getEntity();
data.put("city", entity.getCity());
data.put("email", entity.getEmail());
data.put("customerAccountCode", userId.toString());
data.put("phone", entity.getPhoneNumber());
data.put("state", entity.getStateProvince());
data.put("street", entity.getAddress1() + " "
+ entity.getAddress2());
data.put("zipCode", entity.getPostalCode());
data.put("isOrganization", Boolean.FALSE);
} catch (Exception e) {
log.error("Exception when trying to add the AVS fields", e);
}
}
private PaymentAuthorizationDTO makeCall(Map<String, Object> data,
boolean isCharge) throws XmlRpcException, MalformedURLException,
PluggableTaskException {
URL callURL = null;
if ("true".equals(getOptionalParameter(PARAMETER_TEST.getName(), "false"))) {
callURL = new URL(TEST_URL);
log.debug("Running Atlas task in test mode!");
} else {
callURL = new URL(URL);
}
String merchantAccountCode = ensureGetParameter(PARAMETER_MERCHANT_ACCOUNT_CODE.getName());
int merchantCode = Integer.parseInt(merchantAccountCode);
merchantCode = merchantCode - (merchantCode % 1000);
XmlRpcClient paymentProcessor = new XmlRpcClient();
paymentProcessor.setTransportFactory(new XmlRpcCommonsTransportFactory(
paymentProcessor));
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(callURL);
config.setEnabledForExtensions(true);
config.setConnectionTimeout(CONNECTION_TIME_OUT);
config.setReplyTimeout(REPLY_TIME_OUT);
paymentProcessor.setConfig(config);
List<Map<String, Object>> transactionRequestList = new ArrayList<Map<String, Object>>(
1);
transactionRequestList.add(data);
Map<String, Object> configParam = new HashMap<String, Object>();
if (isCharge) {
configParam.put("waitConfirmation", "ignore");
} else {
configParam.put("waitConfirmation", "terminate");
}
Object[] params = new Object[] { String.valueOf(merchantCode),
ensureGetParameter(PARAMETER_PASSWORD.getName()), transactionRequestList,
configParam };
Object[] resresponse = (Object[]) paymentProcessor.execute(
"XMLRPCGates.processRetail", params);
Map<String, Object> transactionResponseMap = (Map<String, Object>) resresponse[0];
log.debug("Got response:" + transactionResponseMap);
boolean isCredit = "credit-response".equals(transactionResponseMap
.get("type"));
PaymentAuthorizationDTO dbRow = new PaymentAuthorizationDTO();
if (!isCredit
&& "A01".equals(transactionResponseMap.get("responseCode"))) {
dbRow.setCode1("1"); // code if 1 it is ok
} else if (isCredit
&& "A02".equals(transactionResponseMap.get("responseCode"))) {
dbRow.setCode1("1");
} else if ('A' != ((String) transactionResponseMap.get("responseCode"))
.charAt(0)) {
dbRow.setCode1("2");
} else {
dbRow.setCode1("0");
}
dbRow.setCode3((String) transactionResponseMap.get("responseCode"));
dbRow.setResponseMessage((String) transactionResponseMap
.get("responseMessage"));
dbRow.setApprovalCode((String) transactionResponseMap
.get("processorCode"));
dbRow.setAvs((String) transactionResponseMap.get("avsResultCode"));
dbRow.setTransactionId((String) transactionResponseMap
.get("referenceNumber"));
dbRow.setProcessor("Intrannuity");
return dbRow;
}
public boolean preAuth(PaymentDTOEx payment) throws PluggableTaskException {
try {
validateParameters();
Map<String, Object> data = getChargeData(payment);
PaymentAuthorizationDTO response = makeCall(data, false);
PaymentAuthorizationDTO authDtoEx = new PaymentAuthorizationDTO(
response);
PaymentAuthorizationBL bl = new PaymentAuthorizationBL();
bl.create(authDtoEx, payment.getId());
payment.setAuthorization(authDtoEx);
return false;
} catch (Exception e) {
log.error("error trying to pre-authorize", e);
return true;
}
}
public boolean confirmPreAuth(PaymentAuthorizationDTO auth,
PaymentDTOEx paymentInfo) throws PluggableTaskException {
return true;
}
}