/**
* \package com.bluevia.payment.client This package contains the clients of Bluevia Payment Service
*/
package com.bluevia.payment.client;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Random;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import org.apache.log4j.Logger;
import oauth.signpost.OAuthProviderListener;
import oauth.signpost.http.HttpRequest;
import oauth.signpost.http.HttpResponse;
import com.bluevia.commons.Encoding;
import com.bluevia.commons.Entity;
import com.bluevia.commons.GsdpConstants;
import com.bluevia.commons.Utils;
import com.bluevia.commons.connector.http.oauth.BVAuthorizationHeaderSigningStrategy;
import com.bluevia.commons.connector.http.oauth.IOAuth;
import com.bluevia.commons.connector.http.oauth.OAuthToken;
import com.bluevia.commons.connector.http.oauth.RequestToken;
import com.bluevia.commons.data.JaxbEntity;
import com.bluevia.commons.exception.BlueviaException;
import com.bluevia.commons.parser.xml.XmlConstants;
import com.bluevia.commons.parser.xml.XmlParser;
import com.bluevia.oauth.client.BVOauthClient;
import com.bluevia.payment.data.PaymentInfo;
import com.bluevia.payment.data.PaymentRequestTokenParams;
import com.bluevia.payment.data.PaymentResult;
import com.bluevia.payment.data.PaymentStatus;
import com.bluevia.payment.data.ServiceInfo;
import com.bluevia.payment.parser.PaymentSerializer;
import com.telefonica.schemas.unica.rpc.common.v1.SimpleReferenceType;
import com.telefonica.schemas.unica.rpc.payment.v1.GetPaymentStatusParamsType;
import com.telefonica.schemas.unica.rpc.payment.v1.MethodCallType;
import com.telefonica.schemas.unica.rpc.payment.v1.MethodResponseType;
import com.telefonica.schemas.unica.rpc.payment.v1.MethodType;
import com.telefonica.schemas.unica.rpc.payment.v1.PaymentInfoType;
import com.telefonica.schemas.unica.rpc.payment.v1.PaymentParamsType;
import com.telefonica.schemas.unica.rpc.payment.v1.MethodCallType.Params;
/**
* This class provides access to the set of functions which any user could use to access
* the Payment service functionality
*
* @author Telefonica R&D
*
*/
public class BVPayment extends BVOauthClient {
private static Logger log = Logger.getLogger(BVPayment.class.getName());
private static final String FEED_PAYMENT_BASE_URI = "/RPC/Payment";
protected static final String FEED_PAYMENT = "/payment";
protected static final String FEED_CANCEL_AUTHORIZATION = "/cancelAuthorization";
protected static final String FEED_GET_PAYMENT_STATUS = "/getPaymentStatus";
protected static final String OAUTH_API_NAME = "Payment";
protected static final String OAUTH_API_NAME_SANDBOX = "Payment_Sandbox";
protected static final String HEADER_TIMESTAMP = "oauth_timestamp";
/**
* Constructor
*
* @param mode
* @param consumerToken
* @param consumerSecret
* @throws BlueviaException
*/
public BVPayment(Mode mode, String consumerToken, String consumerSecret) throws BlueviaException {
super(mode, consumerToken, consumerSecret); //Calling super to initialize OauthClient
if (mode == Mode.TEST)
throw new BlueviaException("Payment API only available in LIVE and SANDBOX mode", BlueviaException.INVALID_MODE_EXCEPTION);
mEncoding = Encoding.APPLICATION_XML;
mParser = new XmlParser();
mSerializer = new PaymentSerializer();
mBaseUri += buildUri(mMode, FEED_PAYMENT_BASE_URI); //Building base uri
}
/**
* Gets a RequestToken for a Payment operation
*
* @param amount the cost of the digital good being sold, expressed in the minimum fractional monetary unit of the currency reflected in the next parameter (to avoid decimal digits).
* @param currency the currency of the payment, following ISO 4217 (EUR, GBP, MXN, etc.).
* @param name the name of the service for the payment
* @param serviceId the id of the service for the payment
* @return the request token
* @throws BlueviaException
*/
public RequestToken getPaymentRequestToken(int amount, String currency, String name, String serviceId) throws BlueviaException {
return getPaymentRequestToken(amount, currency, name, serviceId, null);
}
/**
* Gets a RequestToken for a Payment operation with callback
*
* @param amount the cost of the digital good being sold, expressed in the minimum fractional monetary unit of the currency reflected in the next parameter (to avoid decimal digits).
* @param currency the currency of the payment, following ISO 4217 (EUR, GBP, MXN, etc.).
* @param name the name of the service for the payment
* @param serviceId the id of the service for the payment
* @param callback the uri for callback notification
* @return the request token
* @throws BlueviaException
*/
public RequestToken getPaymentRequestToken(int amount, String currency, String name, String serviceId, String callback) throws BlueviaException {
//Reset token
((IOAuth)mConnector).setOauthToken(null);
ServiceInfo serviceInfo = new ServiceInfo(serviceId, name);
PaymentInfo info = new PaymentInfo(amount, currency);
PaymentRequestTokenParams request = new PaymentRequestTokenParams(serviceInfo, info);
if (!request.isValid())
throw new BlueviaException("Bad request: Please, check function parameters", BlueviaException.BAD_REQUEST_EXCEPTION);
byte[] data = serializeEntity(request);
OAuthProviderListener listener = new BVPayentOauthProviderListener();
return super.getRequestToken(callback, data, listener);
}
/**
* OAuthProviderListener to manage the body data in the getPaymentRequestToken operation.
*
*/
private class BVPayentOauthProviderListener implements OAuthProviderListener {
@Override
public void prepareSubmission(HttpRequest request) throws Exception {}
@Override
public void prepareRequest(HttpRequest request) throws Exception {
//Include apiName header
String apiName = "";
switch (mMode){
case LIVE:
case TEST:
apiName = OAUTH_API_NAME;
break;
case SANDBOX:
apiName = OAUTH_API_NAME_SANDBOX;
break;
}
request.setHeader("Authorization", "OAuth " +
BVAuthorizationHeaderSigningStrategy.OAUTH_API_NAME + "=\"" +
apiName + "\"");
}
@Override
public boolean onResponseReceived(HttpRequest request, HttpResponse response) throws Exception {
return false;
}
}
/**
* Gets an access token for a Payment operation
*
* @param oauthVerifier the OAuth verifier for the token
* @param token the request token previously obtained
* @param secret request token secret
* @return the access token
* @throws BlueviaException
*/
public OAuthToken getPaymentAccessToken(String oauthVerifier, String token, String secret) throws BlueviaException {
OAuthToken accessToken = super.getAccessToken(oauthVerifier, token, secret);
((IOAuth)mConnector).setOauthToken(accessToken);
return accessToken;
}
/**
*
* Orders an economic charge on the user's operator account.
*
* @param amount Amount to be charged, it may be an economic amount or a number of 'virtual units' (points, tickets, etc) (mandatory).
* @param currency Type of currency which corresponds with the amount above, following ISO 4217 (mandatory).
* @return Result of the payment operation.
* @throws BlueviaException An exception informing about errors encountered in the request/response.
* @throws IOException
*/
public PaymentResult payment(int amount, String currency) throws BlueviaException, IOException {
return payment(amount, currency, null, null);
}
/**
*
* Orders an economic charge on the user's operator account.
* @param amount Amount to be charged, it may be an economic amount or a number of 'virtual units' (points, tickets, etc) (mandatory).
* @param currency Type of currency which corresponds with the amount above, following ISO 4217 (mandatory).
* @param endpoint the endpoint to receive notifications of the payment operation
* @param correlator the correlator
* @return Result of the payment operation.
* @throws BlueviaException An exception informing about errors encountered in the request/response.
* @throws IOException
*/
public PaymentResult payment(int amount, String currency, String endpoint, String correlator) throws BlueviaException, IOException {
Entity response;
//Timestamp
Format formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = new Date();
String timestamp = formatter.format(date);
String millis = String.valueOf(date.getTime()/1000);
HashMap<String, String> additionalParams = new HashMap<String, String>();
additionalParams.put(HEADER_TIMESTAMP, millis);
((IOAuth)mConnector).setAdditionalParameters(additionalParams);
MethodCallType methodCallType = getMethodCallTypePayment(amount, currency, timestamp, endpoint, correlator);
HashMap<String, String> headers = new HashMap<String, String>();
headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding));
HashMap<String, String> queryParams = new HashMap<String, String>();
queryParams.put(XmlConstants.PARAM_VERSION_KEY, XmlConstants.VERSION_1);
JaxbEntity messageEntity= new JaxbEntity();
messageEntity.setJcInstance(XmlConstants.XSD_PAYMENT_API_INSTANCE);
messageEntity.setNamespace(XmlConstants.NS_PAYMENT_API_URI);
messageEntity.setQname(XmlConstants.XSD_RPC_METHOD_CALL);
messageEntity.setObject(methodCallType);
((XmlParser) mParser).setParseClass(MethodResponseType.class);
response = this.create(FEED_PAYMENT, messageEntity, queryParams, headers);
//Check if response is instance of JaxbEntity
if ((response == null) || (! (response instanceof JaxbEntity)))
throw new BlueviaException("Error during request. Response received does not correspond to an MethodResponseType",
BlueviaException.INTERNAL_CLIENT_ERROR);
JaxbEntity parseEntity= (JaxbEntity) response;
//Check if parseEntity is instance of MethodResponseType
MethodResponseType methodResponseType= new MethodResponseType();
if ((parseEntity.getObject() == null) || (! (parseEntity.getObject() instanceof MethodResponseType)))
throw new BlueviaException("Error during request. Response received does not correspond to an MethodResponseType",
BlueviaException.INTERNAL_CLIENT_ERROR);
methodResponseType = (MethodResponseType) parseEntity.getObject();
if ((methodResponseType == null) || (!(methodResponseType instanceof MethodResponseType)))
throw new BlueviaException("Error during request. Response received does not correspond to a " +
PaymentResult.class.getName(), BlueviaException.INTERNAL_CLIENT_ERROR);
PaymentResult paymentResult = new PaymentResult(methodResponseType);
return paymentResult;
}
/**
* Merchant can use this operation to invalidate the access token of the session.
* If the payment has been made before, it remains valid, but no more getPaymentStatus operations will be enabled.
*
* @throws IOException
* @throw BlueviaException An exception informing about errors encountered in the request/response.
*/
public void cancelAuthorization() throws BlueviaException, IOException {
MethodCallType methodCallType = getMethodCallTypeCancel();
HashMap<String, String> headers = new HashMap<String, String>();
headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding));
HashMap<String, String> queryParams = new HashMap<String, String>();
queryParams.put(XmlConstants.PARAM_VERSION_KEY, XmlConstants.VERSION_1);
JaxbEntity messageEntity= new JaxbEntity();
messageEntity.setJcInstance(XmlConstants.XSD_PAYMENT_API_INSTANCE);
messageEntity.setNamespace(XmlConstants.NS_PAYMENT_API_URI);
messageEntity.setQname(XmlConstants.XSD_RPC_METHOD_CALL);
messageEntity.setObject(methodCallType);
((XmlParser) mParser).setParseClass(MethodResponseType.class);
Entity response = this.create(FEED_CANCEL_AUTHORIZATION, messageEntity, queryParams, headers);
//Check if response is instance of JaxbEntity
if ((response == null) || (! (response instanceof JaxbEntity)))
throw new BlueviaException("Error during request. Response received does not correspond to an MethodResponseType",
BlueviaException.INTERNAL_CLIENT_ERROR);
JaxbEntity parseEntity= (JaxbEntity) response;
//Check if parseEntity is instance of MethodResponseType
MethodResponseType methodResponseType= new MethodResponseType();
if ((parseEntity.getObject() == null) || (! (parseEntity.getObject() instanceof MethodResponseType)))
throw new BlueviaException("Error during request. Response received does not correspond to an MethodResponseType",
BlueviaException.INTERNAL_CLIENT_ERROR);
methodResponseType = (MethodResponseType) parseEntity.getObject();
if ((methodResponseType == null) || (!(methodResponseType instanceof MethodResponseType)))
throw new BlueviaException("Error during request. Response received does not correspond to a " +
PaymentResult.class.getName(), BlueviaException.INTERNAL_CLIENT_ERROR);
}
/**
* Retrieves the status of a previous payment operation
*
* @param transactionId the id of the transaction
* @return the status of the payment
* @throws BlueviaException
* @throws IOException
*/
public PaymentStatus getPaymentStatus(String transactionId) throws BlueviaException, IOException{
HashMap<String, String> headers = new HashMap<String, String>();
headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding));
HashMap<String, String> queryParams = new HashMap<String, String>();
queryParams.put(XmlConstants.PARAM_VERSION_KEY, XmlConstants.VERSION_1);
Entity response = null;
MethodCallType methodCallType = getMethodCallTypeStatus(transactionId);
JaxbEntity messageEntity= new JaxbEntity();
messageEntity.setJcInstance(XmlConstants.XSD_PAYMENT_API_INSTANCE);
messageEntity.setNamespace(XmlConstants.NS_PAYMENT_API_URI);
messageEntity.setQname(XmlConstants.XSD_RPC_METHOD_CALL);
messageEntity.setObject(methodCallType);
((XmlParser) mParser).setParseClass(MethodResponseType.class);
response = this.create(FEED_GET_PAYMENT_STATUS, messageEntity, queryParams, headers);
//Check if response is instance of JaxbEntity
if ((response == null) || (! (response instanceof JaxbEntity)))
throw new BlueviaException("Error during request. Response received does not correspond to an MethodResponseType",
BlueviaException.INTERNAL_CLIENT_ERROR);
JaxbEntity parseEntity= (JaxbEntity) response;
//Check if parseEntity is instance of MethodResponseType
MethodResponseType methodResponseType= new MethodResponseType();
if ((parseEntity.getObject() == null) || (! (parseEntity.getObject() instanceof MethodResponseType)))
throw new BlueviaException("Error during request. Response received does not correspond to an MethodResponseType",
BlueviaException.INTERNAL_CLIENT_ERROR);
methodResponseType = (MethodResponseType) parseEntity.getObject();
if ((methodResponseType == null) || (!(methodResponseType instanceof MethodResponseType)))
throw new BlueviaException("Error during request. Response received does not correspond to a " +
PaymentResult.class.getName(), BlueviaException.INTERNAL_CLIENT_ERROR);
PaymentStatus paymentStatus= new PaymentStatus();
paymentStatus.setTransactionStatus(PaymentStatus.TransactionStatusType.valueOf(methodResponseType.getResult().getGetPaymentStatusResult().getTransactionStatus()));
paymentStatus.setTransactionStatusDescription(methodResponseType.getResult().getGetPaymentStatusResult().getTransactionStatusDescription());
return paymentStatus;
}
/**
* Sets the access token of the session
* This functions is used to change the token of the session to be able to get the
* payment status of an old operation, or cancel the authorization of a token.
*
* @param accessToken
*/
public void setAccessToken(OAuthToken accessToken){
((IOAuth)mConnector).setOauthToken(accessToken);
}
private MethodCallType getMethodCallTypePayment(int amount, String currency, String timestamp, String endpoint, String correlator) throws BlueviaException {
MethodCallType methodCallType= new MethodCallType();
String mId= String.valueOf(new Random().nextInt(99999));
methodCallType.setId(mId);
methodCallType.setMethod(MethodType.PAYMENT);
Params params= new Params();
PaymentParamsType ppt= new PaymentParamsType();
PaymentInfoType pit= new PaymentInfoType();
pit.setAmount(new BigDecimal(Integer.toString(amount)));
pit.setCurrency(currency);
ppt.setPaymentInfo(pit);
if (endpoint!=null && !(Utils.isEmpty(correlator))) {
SimpleReferenceType simple= new SimpleReferenceType();
simple.setCorrelator(correlator);
simple.setEndpoint(endpoint);
ppt.setReceiptRequest(simple);
}
try {
ppt.setTimestamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(timestamp));
} catch (DatatypeConfigurationException e) {
log.error("Invalid timestamp: " + e.getMessage());
throw new BlueviaException(e.getMessage(), e, BlueviaException.INTERNAL_CLIENT_ERROR);
}
params.setPaymentParams(ppt);
methodCallType.setParams(params);
methodCallType.setVersion(XmlConstants.VERSION_1);
return methodCallType;
}
private MethodCallType getMethodCallTypeCancel() {
MethodCallType methodCallType= new MethodCallType();
String mId= String.valueOf(new Random().nextInt(99999));
methodCallType.setId(mId);
methodCallType.setId(mId);
methodCallType.setMethod(MethodType.CANCEL_AUTHORIZATION);
methodCallType.setVersion(XmlConstants.VERSION_1);
return methodCallType;
}
private MethodCallType getMethodCallTypeStatus(String transactionId) {
MethodCallType methodCallType= new MethodCallType();
String mId= String.valueOf(new Random().nextInt(99999));
methodCallType.setId(mId);
methodCallType.setMethod(MethodType.GET_PAYMENT_STATUS);
Params params= new Params();
GetPaymentStatusParamsType gpsp= new GetPaymentStatusParamsType();
gpsp.setTransactionId(transactionId);
params.setGetPaymentStatusParams(gpsp);
methodCallType.setParams(params);
methodCallType.setVersion(XmlConstants.VERSION_1);
return methodCallType;
}
}