/*
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.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.util.Calendar;
import org.apache.log4j.Logger;
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.PaymentTask;
import com.sapienter.jbilling.server.pluggableTask.PaymentTaskWithTimeout;
import com.sapienter.jbilling.server.pluggableTask.TaskException;
import com.sapienter.jbilling.server.pluggableTask.admin.PluggableTaskException;
import com.sapienter.jbilling.server.user.ContactBL;
import com.sapienter.jbilling.server.util.Constants;
public class PaymentBeanstreamTask extends PaymentTaskWithTimeout implements
PaymentTask {
// Required parameters
private static final String PARAMATER_MERCHANT_ID = "merchant_id";
private static final String PARAMATER_USERNAME = "username";
private static final String PARAMATER_PASSWORD = "password";
// Optional parameters
private static final String PARAMATER_CAV_ENABLED = "cav_enabled";
private static final String PARAMATER_CAV_PASSCODE = "cav_passcode";
private static final String PARAMATER_CAV_VERSION = "cav_version";
private static final String PARAMATER_VBV_ENABLED = "vbv_enabled";
private static final String PARAMATER_SC_ENABLED = "sc_enabled";
private static final String BeanstreamURL = "https://www.beanstream.com/scripts/process_transaction.asp";
private static final Logger LOG = Logger
.getLogger(PaymentBeanstreamTask.class);
public boolean process(PaymentDTOEx paymentInfo)
throws PluggableTaskException {
try {
if (paymentInfo.getCreditCard() == null) {
String error = "Credit card not present in payment";
LOG.error(error);
throw new TaskException(error);
}
if (paymentInfo.getAch() != null) {
String error = "ACH not supported by Beanstream processing API";
LOG.error(error);
throw new TaskException(error);
}
if (BigDecimal.ZERO.compareTo(paymentInfo.getAmount()) > 0 && paymentInfo.getIsRefund() == 0) {
String error = "Credits not linked to a previous transaction " +
" (refund) not supported by Beanstream processing API";
LOG.error(error);
// note: refunds haven't actually been coded for, either
throw new TaskException(error);
}
String POSTString = this.getPOSTString(paymentInfo, "P", null);
String HTTPResponse = doPost(POSTString, paymentInfo);
PaymentAuthorizationDTO paymentDTO = new BeanstreamResponseDTO()
.parseResponse(HTTPResponse);
if (paymentDTO.getCode1().equals("1")) {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_OK));
paymentInfo.setAuthorization(paymentDTO);
PaymentAuthorizationBL bl = new PaymentAuthorizationBL();
bl.create(paymentDTO, paymentInfo.getId());
return false;
} else {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_FAIL));
paymentInfo.setAuthorization(paymentDTO);
PaymentAuthorizationBL bl = new PaymentAuthorizationBL();
bl.create(paymentDTO, paymentInfo.getId());
return false;
}
} catch (Exception e) {
LOG.error(e);
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_UNAVAILABLE));
return true;
}
}
public boolean preAuth(PaymentDTOEx paymentInfo)
throws PluggableTaskException {
try {
String POSTString = this.getPOSTString(paymentInfo, "PA", null);
String HTTPResponse = doPost(POSTString, paymentInfo);
PaymentAuthorizationDTO paymentDTO = new BeanstreamResponseDTO()
.parseResponse(HTTPResponse);
if (paymentDTO.getCode1().equals("1")) {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_OK));
paymentInfo.setAuthorization(paymentDTO);
PaymentAuthorizationBL bl = new PaymentAuthorizationBL();
bl.create(paymentDTO, paymentInfo.getId());
return false;
} else {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_FAIL));
paymentInfo.setAuthorization(paymentDTO);
PaymentAuthorizationBL bl = new PaymentAuthorizationBL();
bl.create(paymentDTO, paymentInfo.getId());
return false;
}
} catch (Exception e) {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_UNAVAILABLE));
return true;
}
}
public boolean confirmPreAuth(PaymentAuthorizationDTO paymentDTO,
PaymentDTOEx paymentInfo) throws PluggableTaskException {
try {
String POSTString = this.getPOSTString(paymentInfo, "PAC",
paymentDTO.getTransactionId());
String HTTPResponse = doPost(POSTString, paymentInfo);
PaymentAuthorizationDTO newPaymentDTO = new BeanstreamResponseDTO()
.parseResponse(HTTPResponse);
if (newPaymentDTO.getCode1().equals("1")) {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_OK));
paymentInfo.setAuthorization(newPaymentDTO);
PaymentAuthorizationBL bl = new PaymentAuthorizationBL();
bl.create(newPaymentDTO, paymentInfo.getId());
return false;
} else {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_FAIL));
paymentInfo.setAuthorization(newPaymentDTO);
PaymentAuthorizationBL bl = new PaymentAuthorizationBL();
bl.create(newPaymentDTO, paymentInfo.getId());
return false;
}
} catch (Exception e) {
paymentInfo.setPaymentResult(new PaymentResultDAS().find(Constants.RESULT_UNAVAILABLE));
return true;
}
}
public void failure(Integer userId, Integer retry) {
}
/**
* Returns a string suitable for passing to Beanstream processing API
* (taking into account optional configuration parameters)
*
* @param paymentInfo
* @return String
* @throws PluggableTaskException
*/
private String getPOSTString(PaymentDTOEx paymentInfo, String paymentType,
String transactionId) throws PluggableTaskException {
String merchantId = ensureGetParameter(PARAMATER_MERCHANT_ID);
String username = ensureGetParameter(PARAMATER_USERNAME);
String password = ensureGetParameter(PARAMATER_PASSWORD);
String cav_enabled = getOptionalParameter(PARAMATER_CAV_ENABLED, "0");
String cav_passcode = getOptionalParameter(PARAMATER_CAV_PASSCODE, null);
String cav_version = getOptionalParameter(PARAMATER_CAV_VERSION, null);
String vbv_enabled = getOptionalParameter(PARAMATER_VBV_ENABLED, null);
String sc_enabled = getOptionalParameter(PARAMATER_SC_ENABLED, null);
if (merchantId.length() != 9) {
String error = "Invalid merchant_id for Beanstream payment processor";
LOG.error(error);
throw new PluggableTaskException(error);
}
try {
ContactBL contact = new ContactBL();
contact.set(paymentInfo.getUserId());
Calendar cal = Calendar.getInstance();
cal.setTime(paymentInfo.getCreditCard().getCcExpiry());
StringBuffer postVars = new StringBuffer("requestType=BACKEND&");
postVars.append("merchant_id=" + merchantId + "&");
postVars.append("username=" + username + "&");
postVars.append("password=" + password + "&");
postVars.append("trnCardOwner="
+ paymentInfo.getCreditCard().getName() + "&");
postVars.append("trnCardNumber="
+ paymentInfo.getCreditCard().getNumber() + "&");
postVars.append("trnExpMonth="
+ ((cal.get(Calendar.MONTH) < 10) ? ("0" + cal
.get(Calendar.MONTH)) : cal.get(Calendar.MONTH))
+ "&");
postVars.append("trnExpYear="
+ (Integer.toString(cal.get(Calendar.YEAR)))
.substring(2, 4) + "&");
postVars.append("trnOrderNumber=" + paymentInfo.getId() + "&");
postVars.append("trnAmount=" + paymentInfo.getAmount() + "&");
postVars.append("trnType=" + paymentType + "&");
postVars.append("trnId=" + getString(transactionId));
postVars.append("ordName="
+ (contact.getEntity().getFirstName() + " " + contact
.getEntity().getLastName()) + "&");
postVars.append("ordEmailAddress="
+ getString(contact.getEntity().getEmail()) + "&");
postVars.append("ordPhoneNumber="
+ getString(contact.getEntity().getPhoneNumber()) + "&");
postVars.append("ordAddress1="
+ getString(contact.getEntity().getAddress1()) + "&");
postVars.append("ordAddress2="
+ getString(contact.getEntity().getAddress2()) + "&");
postVars.append("ordCity="
+ getString(contact.getEntity().getCity()) + "&");
postVars.append("ordProvince="
+ getString(contact.getEntity().getStateProvince()) + "&");
postVars.append("ordPostalCode="
+ getString(contact.getEntity().getPostalCode()) + "&");
postVars.append("ordCountry="
+ getString(contact.getEntity().getCountryCode()) + "&");
postVars.append("cavEnabled=" + cav_enabled + "&");
postVars.append((cav_passcode != null) ? ("cavPassCode="
+ cav_passcode + "&") : "");
postVars.append("cavServiceVersion="
+ ((cav_version != null) ? cav_version : 0) + "&");
postVars.append("vbvEnabled="
+ ((vbv_enabled != null && vbv_enabled.equals("true")) ? 1
: 0) + "&");
postVars.append("scEnabled="
+ ((sc_enabled != null && sc_enabled.equals("true")) ? 1
: 0));
LOG.debug("HTTP POST vars going to beanstream: " + postVars);
return postVars.toString();
} catch (Exception e) {
throw new PluggableTaskException(e);
}
}
/**
* Performs an HTTPS POST request to the Beanstream payment processor
*
* @param postVars
* String The HTTP POST formatted as a GET string
* @return String
* @throws PluggableTaskException
*/
private String doPost(String postVars, PaymentDTOEx paymentInfo)
throws PluggableTaskException {
int ch;
StringBuffer responseText = new StringBuffer();
try {
// Set the location of the Beanstream payment gateway
URL url = new URL(BeanstreamURL);
// Open the connection
URLConnection conn = url.openConnection();
// Set the connection timeout
conn.setConnectTimeout(getTimeoutSeconds() * 1000);
// Set the DoOutput flag to true because we intend
// to use the URL connection for output
conn.setDoOutput(true);
// Send the transaction via HTTPS POST
OutputStream ostream = conn.getOutputStream();
ostream.write(postVars.getBytes());
ostream.close();
// Get the response from Beanstream
InputStream istream = conn.getInputStream();
while ((ch = istream.read()) != -1)
responseText.append((char) ch);
istream.close();
LOG.debug("Beanstream responseText: " + responseText);
return responseText.toString();
} catch (Exception e) {
LOG.error(e);
throw new PluggableTaskException(e);
}
}
class BeanstreamResponseDTO {
private String trnApproved;
private String trnId;
private String messageId;
private String messageText;
private String trnOrderNumber;
private String authCode;
private String hCode;
private String errorType;
private String errorFields;
private String responseType;
private String trnAmount;
private String trnDate;
private String avsProcessed;
private String avsId;
private String avsResult;
private String avsAddrMatch;
private String avsPostalMatch;
private String avsMessage;
private String rspCodeCav;
private String rspCodeAdd2;
private String rspCodeCredit1;
private String rspCodeCredit2;
private String rspCodeCredit3;
private String rspCodeCredit4;
private String rspCodeAddr1;
private String rspCodeAddr2;
private String rspCodeAddr3;
private String rspCodeAddr4;
private String rspCodeDob;
private String rspCustomerDec;
private String trnType;
private String paymentMethod;
private String ref1;
private String ref2;
private String ref3;
private String ref4;
private String ref5;
public BeanstreamResponseDTO() {
}
public PaymentAuthorizationDTO parseResponse(String responseText)
throws PluggableTaskException {
try {
BeanstreamResponseDTO responseDTO = new BeanstreamResponseDTO();
String[] reply = responseText.split("&");
for (int i = 0; i < reply.length; i++) {
String[] pair = reply[i].split("=");
Field field = responseDTO.getClass().getDeclaredField(
pair[0]);
field.set(responseDTO, (pair.length == 1) ? null : pair[1]);
}
PaymentAuthorizationDTO paymentDTO = new PaymentAuthorizationDTO();
paymentDTO.setTransactionId(responseDTO.trnId);
paymentDTO.setProcessor("Beanstream");
paymentDTO.setApprovalCode(responseDTO.authCode);
paymentDTO.setAvs(responseDTO.avsResult);
paymentDTO.setMD5(responseDTO.hCode);
// paymentDTO.setCardCode( ??? );
paymentDTO.setCreateDate(Calendar.getInstance().getTime());
paymentDTO.setResponseMessage(java.net.URLDecoder.decode(
responseDTO.messageText, "UTF-8"));
paymentDTO.setCode1(responseDTO.trnApproved);
return paymentDTO;
} catch (Exception e) {
throw new PluggableTaskException(e);
}
}
}
}