Chakray servicios-pagos-paypal-wso2esb-20140701

12
www.chakray.com Implementación del servicio de pagos con TPV y Paypal contra el ESB. Ivan Fontanals En este documento se explica con detalle el proceso de pagos usando el ESB de WSO2 como intermediador. El ejemplo acepta pagos a través de TPV de Sermepa y pagos a través de Paypal

description

En este documento se explica con detalle el proceso de pagos usando el ESB de WSO2 como intermediador. El ejemplo acepta pagos a través de TPV de Sermepa y pagos a través de Paypal

Transcript of Chakray servicios-pagos-paypal-wso2esb-20140701

Page 1: Chakray servicios-pagos-paypal-wso2esb-20140701

w w w . c h a k r a y . c o m

Implementación del servicio de

pagos con TPV y Paypal contra el

ESB. Ivan Fontanals En este documento se explica con detalle el proceso de pagos usando el ESB de WSO2

como intermediador. El ejemplo acepta pagos a través de TPV de Sermepa y pagos a

través de Paypal

Page 2: Chakray servicios-pagos-paypal-wso2esb-20140701

2

Por Ivan Fontanals IT Consultant

Índice

1. Introducción ............................................................................................................. 3

2. Proxy para los pagos ............................................................................................... 4

3. Validaciones ............................................................................................................. 5 Validación de la petición TPV ..................................................................................... 7 Validación de la petición Paypal ................................................................................. 8

4. Encolar una validación de pago ............................................................................... 9

5. Flujo de negocio para procesar el pago ................................................................. 10

Autor: ............................................................................................................................ 12

Revisado por: ............................................................................................................... 12

Page 3: Chakray servicios-pagos-paypal-wso2esb-20140701

3

Por Ivan Fontanals IT Consultant

1. Introducción

Un servicio de pagos es hoy en día algo muy habitual en un e-commerce. La idea de

este documento es ver como podemos integrar el típico TPV de Sermepa o bien

Paypal en nuestro sistema, usando para ello el ESB de WSO2 como receptor de la

petición. Una vez validada el pago, se podrá invocar al servicio correspondiente para

que realice las acciones pertinentes en nuestra BBDD o procesos internos.

Para recibir las peticiones, crearemos un nuevo Proxy en el ESB que llamaremos

PaymentsProxy. Será aquí donde vamos a distinguir si la petición recibida proviene

del TPV o del Paypal.

Debido a que las respuestas recibidas de TPV son diferentes a las recibidas de

Paypal, explicaremos por separado cada una de las notificaciones y validaciones.

La Url de notificación, en ambos casos, es la misma, sólo que le pasamos un

parámetro distinto a cada una de ellas para poder distinguir la fuente en el ESB.

Las URLs que usaremos, serán las siguientes:

TPV: http://esb:8280/services/PaymentsProxy?source=tpv

Paypal: http://esb:8280/services/PaymentsProxy?source=paypal

Page 4: Chakray servicios-pagos-paypal-wso2esb-20140701

4

Por Ivan Fontanals IT Consultant

2. Proxy para los pagos

Tal y como hemos comentado, TPV o Paypal invocarán a este servicio pasando como

parámetro el origen de la petición. Esto es lo que usaremos para filtrar la petición y

decidir cómo validar la petición.

<?xml version="1.0" encoding="UTF-8"?> <proxy xmlns="http://ws.apache.org/ns/synapse" name="PaymentsProxy" transports="https,http" statistics="disable" trace="disable" startOnLoad="true"> <target faultSequence="customFault"> <inSequence> <property name="FORCE_ERROR_ON_SOAP_FAULT" value="true"/> <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/> <property name="SOURCE" expression="//source"/> <store messageStore="AuditMessageStore"/> <filter source="//source" regex="paypal"> <send> <endpoint key="PaypalValidation_EP"/> </send> </filter> <filter source="//source" regex="tpv"> <send> <endpoint key="TpvValidation_EP"/> </send> </filter> </inSequence> <outSequence> <log level="full"/> <filter source="//status" regex="SUCCESS"> <log level="custom"> <property name="STATUS" value=" PAGO OK! PROCESAMOS "/> </log> <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/> <store messageStore="PaymentMessageStore"/> </filter> <property name="HTTP_SC" value="200" scope="axis2"/> <send/> </outSequence> </target> <description/> </proxy>

Lo primero que se hace es invocar al message store AuditMessageStore, que cogerá

la petición original, la encolará en la cola JMS y se procesará posteriormente para

Page 5: Chakray servicios-pagos-paypal-wso2esb-20140701

5

Por Ivan Fontanals IT Consultant

registrar en BD la traza a modo de LOG. No explicaremos este paso puesto que no es

relevante para el proceso de pagos, pero si es una buena práctica guardar todas las

peticiones recibidas por si tenemos un problema en un futuro.

Tal y como se muestra en el código XML, si la petición viene de tpv, se acaba

invocando al EndPoint TpvValidation_EP, mientras que si se trata de paypal, a

PaypalValidation. Ambos EndPoints, tal y como veremos, acaban devolviendo un

XML con el estado del pago, el ID de transacción y el importe que se ha recibido. Es

precisamente esta respuesta la que sirve para encolar la petición a la cola JMS y que

se procese posteriormente de forma automática.

3. Validaciones Las validaciones de las peticiones, se van a realizar a través de un servicio REST publicado con Apache CXF y Spring, quien recibirá la petición con todos sus parámetros (en el estricto orden en el que se reciben) y devolverá un XML con la siguiente estructura: <response> <status>SUCCESS</status> <amount>1500</amount> <txnId>EEDD3212343</txnId> </response>

La configuración de spring que se necesitará para el ejemplo, es la siguiente:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <bean id="paypalBean" class="com.test.payments.rest.PaypalValidationImpl"/> <bean id="tpvBean" class="com.test.payments.rest.TpvPaymentValidationImpl"/> <jaxrs:server id="validationService" address="/rest"> <jaxrs:serviceBeans> <ref bean="paypalBean" /> <ref bean="tpvBean" /> </jaxrs:serviceBeans> </jaxrs:server>

Page 6: Chakray servicios-pagos-paypal-wso2esb-20140701

6

Por Ivan Fontanals IT Consultant

</beans> Estos servicios REST se publicarán en el AS de WSO2, requeriendo el contexto de Spring correspondiente pero que no comento en este tutorial por no ser lo relevante a comentar. En los siguientes puntos podremos ver cómo hacer la validación para cada uno de los tipos de petición existentes.

Page 7: Chakray servicios-pagos-paypal-wso2esb-20140701

7

Por Ivan Fontanals IT Consultant

Validación de la petición TPV

La verificación de la petición TPV, consiste en validar que la firma digital enviada en la

petición es correcta, lo que nos permitirá asegurar que no se ha modificado y que

viene directamente del servidor de Sermepa.

@Path("/tpv") public class TpvPaymentValidationImpl { final protected transient Logger logger = LoggerFactory.getLogger(this.getClass()); @Path("/validate") @POST @Produces("application/xml") public String validateTpvRequest(MultivaluedMap<String, String> queryParams) throws Exception { TpvResponse response = createTpvResponse(queryParams); //Convierte los parametros al DTO PaymentStatus status = PaymentStatus.ERROR; try { Boolean verified =verifyTpvResponse(response); status = verified ? PaymentStatus.SUCCESS : PaymentStatus.ERROR; } catch (Exception e) { status = PaymentStatus.ERROR; } Float amount = response.getDs_Amount(); return "<response><status>" + status + "</status>” + “<amount>" + amount + "</amount>” + ”<txnId>" + response.getDs_Order() + "</txnId>” + ”</response>"; } public boolean verifyTpvResponse(TpvResponse response) throws Exception { String secretKey = “xxxxxxxxxxxxxxxxxxxx” //Algoritmo de validación del TPV // Digest=SHA-1(Ds_ Amount + Ds_ Order + Ds_MerchantCode + // Ds_ Currency + Ds _Response + CLAVE SECRETA) StringBuffer msgToDigest = new StringBuffer(); msgToDigest.append(response.getDs_Amount()); msgToDigest.append(response.getDs_Order()); msgToDigest.append(response.getDs_MerchantCode()); msgToDigest.append(response.getDs_Currency()); msgToDigest.append(response.getDs_Response()); msgToDigest.append(secretKey); //Generamos SHA1

Page 8: Chakray servicios-pagos-paypal-wso2esb-20140701

8

Por Ivan Fontanals IT Consultant

byte[] digest = HashUtils.createDigest(msgToDigest.toString().getBytes(), HashUtils.SHA1); //Convertimos a HEXADECIMAL String digestHex = HexFunctions.byte2hex(digest); return digestHex.equalsIgnoreCase(response.getDs_Signature()); } }    

Validación de la petición Paypal

Para el caso de Paypal, es un poco distinta la validación. En este caso se recibe una

notificación IPN, y tenemos que reenviar la misma petición a Paypal ( en el mismo

orden ) poniendo como prefijo cmd=_notify-validate

@Path("/paypal") public class PaypalValidationImpl { @Path("/validate") @POST @Produces("application/xml") public String validatePaypalRequest(MultivaluedMap<String, String> queryParams) throws Exception { // Envio del ACK String responseStatus = sendResponseStatus(queryParams); String mcGross = queryParams.getFirst("mc_gross"); String txnId = queryParams.getFirst("custom"); return "<response><status>" + responseStatus + "</status>” + “<amount>" + responseStatus + "</amount>” + ”<txnId>" + txnId + "</txnId>” + ”</response>"; } private String sendResponseStatus(MultivaluedMap<String, String> queryParams) throws Exception { try { String str = "cmd=_notify-validate"; String charset = queryParams.getFirst("charset"); Iterator it = queryParams.keySet().iterator(); String theKey = null; while (it.hasNext()) { theKey = (String) it.next(); str = str + "&" + theKey + "=" + URLEncoder.encode(queryParams.getFirst(theKey), "UTF-8"); } String paypalServer = “URL_PAYPAL”; URL u = new URL(paypalServer); HttpsURLConnection uc = (HttpsURLConnection) u.openConnection(); uc.setDoOutput(true); uc.setRequestProperty("Content-Type", "application/x-www-form-

Page 9: Chakray servicios-pagos-paypal-wso2esb-20140701

9

Por Ivan Fontanals IT Consultant

urlencoded"); uc.setRequestProperty("Host", paypalServer); PrintWriter pw = new PrintWriter(uc.getOutputStream()); pw.println(str); pw.close(); BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream())); String res = in.readLine(); in.close(); if (res == null || !res.equalsIgnoreCase("VERIFIED")) return PaymentStatus.ERROR.toString(); return PaymentStatus.SUCCESS.toString(); } catch (Exception e) { e.printStackTrace(); throw new Exception(e.toString()); } } }

4. Encolar una validación de pago

Una vez llegados en este punto, tenemos ya la respuesta de la validación realizada

por el servicio anterior, y lo que tenemos que hacer ahora es asegurarnos de que el

pago está en estado SUCCESS para encolarlo en la cola JMS

(PaymentMessageStore) correspondiente y que se inicie el MessageProcessor de

Validación de pago. Así pues, la salida de nuestro Proxy verificará dicho estado y

encolará la respuesta en la JMS.

<outSequence> <log level="full"/> <filter source="//status" regex="SUCCESS"> <log level="custom"> <property name="STATUS" value=" PAGO OK! PROCESAMOS "/> </log> <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/> <store messageStore="PaymentMessageStore"/> </filter> <property name="HTTP_SC" value="200" scope="axis2"/> <send/> </outSequence>

La configuración del MessageProcessor de pagos es la siguiente:

Page 10: Chakray servicios-pagos-paypal-wso2esb-20140701

10

Por Ivan Fontanals IT Consultant

<messageProcessor name="PaymentMessageProcessor" class="org.apache.synapse.message.processor.impl.sampler.SamplingProcessor" messageStore="PaymentMessageStore" xmlns="http://ws.apache.org/ns/synapse"> <parameter name="interval">1000</parameter> <parameter name="concurrency">1</parameter> <parameter name="sequence">ValidatePaymentSequence</parameter> <parameter name="is.active">true</parameter> </messageProcessor>

A través de esta configuración, se invocará automáticamente la secuencia

ValidatePaymentSequence una vez llegue un mensaje a la cola.

5. Flujo de negocio para procesar el pago

Cuando el MessageProcessor se invoca por la llegada de un nuevo pago, deberemos

ejecutar nuestro proceso de negocio. Para este ejemplo, será el propio servicio

publicado en el ESB quien invocará al servicio de validación de pagos ( actualiza el

estado de un pedido ) y después generará un PDF que se le enviará al cliente a través

de un email.

Veamos primero como es el XML que configura la sequence:

<sequence xmlns="http://ws.apache.org/ns/synapse" name="ValidatePaymentSequence" onError="customFault"> <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"></property> <property xmlns:ns="http://org.apache.synapse/xsd" xmlns:ser="http://service.orders. test.com/" name="TXN_ID" expression="//txnId"></property> <property xmlns:ns="http://org.apache.synapse/xsd" xmlns:ser="http://service.orders. test.com/" name="AMOUNT" expression="//amount"></property> <property name="FORCE_ERROR_ON_SOAP_FAULT" value="true"></property> <payloadFactory media-type="xml"> <format> <ser:validatePayment xmlns:ser="http://service.orders.test.com/"> <arg1 xmlns="">$1</arg1> <arg2 xmlns="">$2</arg2> </ser:validatePayment> </format> <args> <arg xmlns:ns="http://org.apache.synapse/xsd" expression="get-property('TXN_ID')" evaluator="xml"></arg> <arg xmlns:ns="http://org.apache.synapse/xsd" expression="get-property('AMOUNT')" evaluator="xml"></arg> </args> </payloadFactory> <call> <endpoint key="OrdersService_EP"></endpoint> </call> <property xmlns:ns="http://org.apache.synapse/xsd" xmlns:ser="http://service.orders. test.com/" name="ORDER_ID" expression="//orderId"></property> <property xmlns:ns="http://org.apache.synapse/xsd" xmlns:ser="http://service.orders.

Page 11: Chakray servicios-pagos-paypal-wso2esb-20140701

11

Por Ivan Fontanals IT Consultant

test.com/" name="PHASE" expression="//phaseType"></property> <switch xmlns:ns="http://org.apache.synapse/xsd" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" source="//phaseType"> <case regex="FIRST"> <sequence key="generateInvoice_SEQ"></sequence> <sequence key="sendInvoiceToCustomer_SEQ"></sequence> </case> <default></default> </switch> <drop></drop> </sequence>

Lo primero que hacemos en el proxy, es recuperar el Id de transacción e importe

pagado. Esto es así puesto nuestro servicio de validación de pagos, devuelve un XML

con estos datos.

Después preparamos la llamada al servicio de validación, que recibirá estos

parámetros. Es importante destacar que este servicio funcionaría perfectamente para

validar transferencias de forma manual; de un modo similar, pero sin pasar por la JMS.

Finalmente, y para simplificar el proceso de negocio, el proxy invocará a la secuencia

que general el PDF con la factura y a la que envía el email con la factura adjunta. Esto

sucederá si estamos en Fase 1, un estado que devuelve nuestro servicio de validación

y que es muy particular del ejemplo seleccionado.

La property FORCE_ERROR_ON_SOAP_FAULT, se usa para que cuando se produzca un

error en alguna de las secuencias o mediators, no siga con la ejecución.

Page 12: Chakray servicios-pagos-paypal-wso2esb-20140701

12

Por Ivan Fontanals IT Consultant

Autor: Ivan Fontanals IT Consultant

Revisado por: Roger Carhuatocto IT Consultant Chakray Consulting S.L. www.chakray.com