Como crear un nuevo Método de Pago para Liferay Commerce - (Parte 1)

DISCLAIMER: This blog  post has Spanish and English version.

Hola Liferayers, aquí estoy nuevamente, aprovechando el lanzamiento de Liferay Commerce 3.0  en esta semana, es un buen momento para ver el paso a paso de como crear de manera simple y sencilla un nuevo “Método de Pago” para Liferay Commerce. En este ejemplo utilizaremos los servicio de Redsys (un proveedor de pago muy conocido en España) como pasarela de pago. 

Si eres nuevo en el mundo de Liferay Commerce, te aconsejo echar un vistazo en todas extensiones posibles en Commerce. Eso os va a ahorrar mucho tiempo a la hora de tener que personalizar cualquier aspecto en la plataforma. 

  • Paso 1, creando nuestra nueva extensión de Método de Pago para Liferay Commerce:

1.  Con Blade CLI, podemos comenzar creando un Liferay Workspace, en caso de que no tengas uno ya para Liferay 7.3 (también recomiendo que revises las instrucciones 3 y 4 de un blog post anterior mío para configurar tu Liferay Workspace, en el caso de que no lo tengas configurado para el despliegue).

blade init -v 7.3 liferay-workspace-7-3

2.  En tu “Liferay Workspace”, creamos un nuevo módulo para un nuevo método de pago: 

blade create -t api -p com.liferay.commerce.payment.method.redsys -c RedsysCommercePaymentMethod commerce-payment-method-redsys

3.  Ahora, renombra el paquete de “com.liferay.commerce.payment.method.redsys.api” a “com.liferay.commerce.payment.method.redsys” para eliminar la palabra “api” que “blade CLI” genera automáticamente (esto es porque usamos la plantilla “api” previamente).

4.  En el fichero “bnd.bnd” elimina la línea “Export-Package:com.liferay.commerce.payment.method.redsys.api”.

5.  Abre la carpeta “resources” (i.e.:liferay-workspace-7-3/modules/commerce-discount-rule-by-age/src/main/resources) y borra todo en ella.

6.  Ahora internacionaliza tu módulo, creando la carpeta “content” en “src/main/resources/” y añada el fichero “Language.properties”. Para facilitar, copia las claves abajo y pega en el Language.properties creado que las iremos utilizando a lo largo del tutorial.

#################-----Labels from Payment Method module-----#####################
redsys-payment-with-card=Redsys
this-is-payment-method-with-card-redsys=Redsys Payment Method redsys-commerce-payment-method-card-group-service-configuration-name=Redsys Payment Engine Method Group Service
payment-attempts-max-count=Payment Attempts Max Count
redsys-configuration-help=The "Signature Secret" there is on Redsys, "Administration Module > Commerce Consult Data > See Key".
ds-signature-version=DS Signature Version
signature-secret=Signature Secret
merchant-code=Merchant Code
terminal=Terminal
type-of-transaction=Type of Transaction
authentication=Authentication
prod=Production
test=Test Integration
t0=Authorization

​​​​7.  Ahora añadimos las dependencias a nuestro proyecto. Abre el fichero build.gradle, elimina todo el contenido de este fichero y añade las siguientes dependencias:

dependencies {
  compileOnly group: "com.liferay.portal", name: "release.portal.api"
  compileOnly group: "com.liferay.commerce", name: "com.liferay.commerce.api", version: "22.2.4"
}

8.  Abre el fichero “RedsysCommercePaymentMethod.java” y cambia el tipo de interface a class ,que implementa la interfaz “CommercePaymentMethod” e implemente los siguientes métodos:

@Override
public String getDescription(Locale locale) {...}

@Override
public String getKey() {...}

@Override
public String getName(Locale locale) {...}

@Override
public int getPaymentType() {...}

@Override
public String getServletPath() {...}

@Override
public boolean isCancelEnabled() {...}

@Override
public boolean isCompleteEnabled() {...}

@Override
public boolean isProcessPaymentEnabled() {...}

@Override
public CommercePaymentResult cancelPayment(CommercePaymentRequest commercePaymentRequest) throws Exception {...}

@Override
public CommercePaymentResult completePayment(CommercePaymentRequest commercePaymentRequest) throws Exception {...}

@Override
public CommercePaymentResult processPayment(CommercePaymentRequest commercePaymentRequest) throws Exception {...}

9.  Crea la siguiente variable (KEY):

public static final String KEY = "redsys";
Ten en cuenta: “Esta KEY identificará a tu módulo,  es importante proveer una clave distinta para cada nuevo módulo (de extensión de método de pago), de forma que Liferay Commerce pueda distinguir unas de otras. Si reutilizas una clave existente que esté ya en uso, reemplazarás el módulo de existente”

10.  Anota esta clase con  @Component y establece 3 propiedades. Debería quedar así:

@Component(
  immediate = true,
  property = "commerce.payment.engine.method.key="+ RedsysCommercePaymentMethod.KEY,
  service = CommercePaymentMethod.class
)
  • En el array de propiedades, establece 1 valor necesario para registrar nuestro método de pago, utilizando la variable KEY:
    • "commerce.payment.engine.method.key="+ RedsysCommercePaymentMethod.KEY
  • Como servicio, declara la interfaz :
    • service = CommercePaymentMethod.class

11.  Ahora en el método “getKey” incluye la sentencia “return KEY”  para devolver la clave que declaramos antes.

12.  Los métodos “getDescription”  y “getName”, serán el nombre y la descripción que se presentará en el Panel de Control a la hora de activar el método de pago. Utilizamos el “ResourceBundleUtil” y “LanguageUtil” para retornar el valor de la clave de acuerdo con cada idioma necesario (y que esté  traducido, claro).

@Override
public String getDescription(Locale locale) {
   ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(
           "content.Language", locale, getClass());
   return LanguageUtil.get(
           resourceBundle, "this-is-payment-method-with-card-redsys");
}

@Override
public String getName(Locale locale) {
       ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(
               "content.Language", locale, getClass());
       return LanguageUtil.get(resourceBundle, "redsys-payment-with-card");
}

13.  Guarda los cambios y despliega el módulo ejecutando el siguiente comando dentro del directorio de tu módulo en el Liferay Workspace:

commerce-payment-method-redsys% ./../../gradlew deploy

Esos pasos de arriba son los mínimos necesarios para tener un módulo de Método de Pago, desplegado en Liferay Commerce. Ahora si abres tu Liferay Portal (ej.: localhost:8080), abre el Menu > Commerce > Channels > haz click  en un Channel (o cree uno en caso de que no lo tenga), en la sección de “Payment Methods”, te debería ya presentar tu módulo como opción:

Recuerda: desde la versión 2.1 de Liferay Commerce las opciones de pago están relacionadas con los canales, por lo que para ver el módulo de método de pago, abre algún “Channel” creado

El código usado en este paso está disponible en una etiqueta con nombre “payment-redsys-step-1” en mi repo de github. 

  • Paso 2, haciendo el Método de Pago configurable

Para tener un módulo reutilizable y con las configuraciones de acuerdo con sus respectivos entornos, debemos hacer configurable todos los inputs necesarios. En el caso de este blog post, utilizaremos las configuraciones de Redsys para el pago con conexión por redirección. Si lees la documentación podrás identificar que necesitaremos de algunas configuraciones. 

Siguiendo estos pasos, aprenderás a hacerlo y podrás aplicar la misma lógica para otros métodos de pago que quiera crear:

1.  Crea tres nuevos paquetes: 

  •     com.liferay.commerce.payment.method.redsys.constants
  •     com.liferay.commerce.payment.method.redsys.connector

  •     com.liferay.commerce.payment.method.redsys.configuration

2.  En “com.liferay.commerce.payment.method.redsys.constants” crea una clase enum llamada “RedsysTypeOfTransaction”, para enumerar los tipos de transacciones. 

Importante: en este tutorial solo vamos a hacer la implementación un tipo de transacción, el “Authorization”, dejo los comentarios de los demás tipos de transacción como un TODO para el caso de que queráis implementar otras de Redsys. De esta forma, os puede servir también a la hora de estimar la construcción de un método de pago. Tener en cuenta que no siempre será implementado un único tipo de transacción y que cada “nueva transacción” necesita una nueva implementación (la gran mayoría, dentro del método “processPayment”).

3.  Para mayor facilidad en el proceso, copia y pega los enumerados definidos abajo dentro del enumRedsysTypeOfTransaction”:

T0("0");
/**
* TODO
T1("1"),
T2("2"),
T3("3"),
T5("5"),
T6("6"),
T7("7"),
T8("8"),
T9("9"),
TO("O"),
TP("P"),
TQ("Q"),
TR("R"),
TS("S");
*/

private RedsysTypeOfTransaction(String transaction) {
_transaction = transaction;
}

public String getTypeOfTransaction() {
return _transaction;
}

private String _transaction;

4.  En “com.liferay.commerce.payment.method.redsys.connector” crea otra clase enum llamada “RedsysEnvironment”, para enumerar los dos enlaces de entornos para redsys, copia y pega dentro del enum el código abajo

PROD("https://sis.redsys.es/sis/realizarPago"),
TEST("https://sis-t.redsys.es:25443/sis/realizarPago");

public String getUrl() {
return _url;
}

private RedsysEnvironment(String url) {
_url = url;
}

private final String _url;

5.  En “com.liferay.commerce.payment.method.redsys.constants” crea una nueva clase “RedsysCommercePaymentMethodConstants” para las constantes que utilizaremos. Para mayor facilidad, copia y pega las constantes que vamos a utilizar a lo largo del tutorial, en esta clase:

public static final String[] MODES = {
StringUtil.toLowerCase(RedsysEnvironment.PROD.name()),
StringUtil.toLowerCase(RedsysEnvironment.TEST.name())
};

public static final String[] TYPES_OF_TRANSACTION = {
StringUtil.toLowerCase(RedsysTypeOfTransaction.T0.name())
/**
* TODO
StringUtil.toLowerCase(RedsysTypeOfTransaction.T1.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.T2.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.T3.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.T5.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.T6.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.T7.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.T8.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.T9.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.TO.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.TP.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.TQ.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.TR.name()),
StringUtil.toLowerCase(RedsysTypeOfTransaction.TS.name())

*/
};

public static final String SERVICE_NAME = "com.liferay.commerce.payment.engine.method.redsys";
public static final String SERVLET_PATH = "redsys-commerce-payment";
public static final String COMMERCE_CHANNEL_ID = "commerceChannelId";
public static final String REDIRECT_PARAM = "redirect=";
public static final String DS_MERCHANT_AMOUNT = "DS_MERCHANT_AMOUNT";
public static final String DS_MERCHANT_ORDER = "DS_MERCHANT_ORDER";
public static final String DS_MERCHANT_MERCHANTCODE = "DS_MERCHANT_MERCHANTCODE";
public static final String DS_MERCHANT_CURRENCY = "DS_MERCHANT_CURRENCY";
public static final String DS_MERCHANT_TRANSACTIONTYPE = "DS_MERCHANT_TRANSACTIONTYPE";
public static final String DS_MERCHANT_TERMINAL = "DS_MERCHANT_TERMINAL";
public static final String DS_MERCHANT_MERCHANTURL = "DS_MERCHANT_MERCHANTURL";
public static final String DS_MERCHANT_URLOK = "DS_MERCHANT_URLOK";
public static final String DS_MERCHANT_URLKO = "DS_MERCHANT_URLKO";
public static final String SETTINGS_DS_SIGNATURE_VERSION = "settings--ds_signature_version--";
public static final String SETTINGS_CLIENT_SECRET = "settings--clientSecret--";
public static final String SETTINGS_MERCHANT_CODE = "settings--merchantCode--";
public static final String SETTINGS_TERMINAL = "settings--terminal--";
public static final String SETTINGS_MODE = "settings--mode--";
public static final String SETTINGS_TYPES_OF_TRANSACTION = "settings--type-of-transaction--";
public static final String DS_MERCHANT_PARAMETERS = "Ds_MerchantParameters";
public static final String DS_SIGNATURE_VERSION = "Ds_SignatureVersion";
public static final String DS_SIGNATURE = "Ds_Signature";
public static final String REDIRECT_URL = "redirectUrl";
public static final String PARAMS = "params";
public static final String SIGNATURE = "signature";
public static final String DS_SIGNATURE_VERSION_PARAM = "dsSignatureVersion";
public static final String COD_RESPONSE_REDSYS =  "codResponseRedsys";
public static final String ERROR_SIGNATURE =  "Signature Calculed it's not equal Signature from Response";
public static final String ERROR_PARAMETERS_COMPARE= "Parameters from Order it's not equal Parameters from Response";

6.  En “com.liferay.commerce.payment.method.redsys.configuration” crea una nueva interfaceRedsysPaymentMethodCardGroupServiceConfiguration

7.  Utilice la  @ExtendedObjectClassDefinition para especificar la categoría y el alcance de la configuración.

@ExtendedObjectClassDefinition(
      category = "payment", scope = ExtendedObjectClassDefinition.Scope.GROUP
)
  • category = con el valor “payment” nuestro método de pago se categoriza junto con los demás métodos de pago en la sección de “System Settings”.
  • scope = lo definimos como “Scope.GROUP” para dejar la configuración a nivel de sitio web. 

8.  En seguida registramos esa interface como una configuración con la anotación “@Meta.OCD” y configuramos las propiedades “id”, “localization” y “name”.

@Meta.OCD(
id = "com.liferay.commerce.payment.method.redsys.configuration.RedsysPaymentMethodCardGroupServiceConfiguration",
localization = "content/Language",
name = "redsys-commerce-payment-method-card-group-service-configuration-name"
)

​​​​​​9.  Ahora ya dentro de la interface definimos los campos de entrada que necesitamos que sean configurables y con la anotación@Meta.AD” sus respectivos metadatos:

@Meta.AD(deflt="HMAC_SHA256_V1",name = "ds-signature-version", required = false)
public String dsSignatureVersion();

@Meta.AD(deflt="https://sis-t.redsys.es:25443/sis/realizarPag", name = "mode", required = false)
public String mode();

@Meta.AD(deflt="sq7HjrUOBfKmC576ILgskD5srU870gJ7",name = "client-secret", required = false)
public String clientSecret();

@Meta.AD(deflt = "999008881", name = "merchant-code", required = false)
public String merchantCode();

@Meta.AD(deflt = "0",name = "type-of-transaction", required = false)
public String typeTransaction();

@Meta.AD(deflt = "01",name = "terminal", required = false)
public String terminal();

10.  Guarda los cambios y despliega el módulo ejecutando el siguiente comando dentro del directorio de tu módulo en el Liferay Workspace:

commerce-payment-method-redsys% ./../../gradlew deploy

11.  Ahora abre tu Liferay Portal (ej.: localhost:8080), abre el Menu > Control Panel > System Settings. En la sección de “Commerce”, haz click en “Payments” y allí en el listado de métodos de pago, ya deberías tener tu configuración generada:

Vale, pero si vas a “Channel”, donde hemos activado el método de pago, no tenemos la configuración independiente aún para cada canal creado. Esto es lo que vamos a ver ahora a continuación.

  • Paso 2.1, haciendo el Método de Pago configurable por canal.

Ahora en este paso, utilizaremos el “Screen Navigation Framework” para poder agregar una segunda pestaña en la configuración del método de pago, cuando lo activamos desde el “Channel” y configurarlo allí a nível de canal.

1.  En la carpeta resources folder (i.e.: commerce-payment-method-redsys/src/main/resources), crea dos nuevas carpetas: la primera con nombre “META-INF” y la segunda y dentro de la anterior, con nombre “resources”.

2.  Dentro de “META-INF/resources crea un nuevo fichero JSP META con nombre “init.jsp” y otro JSP con nombre “configuration.jsp” 

3.  En el fichero “init.jsp”, copia y pega las líneas siguiente para importar algunas clases y taglibs que serán usadas en el fichero “configuration.jsp”

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %><%@
taglib uri="http://liferay.com/tld/commerce-ui" prefix="commerce-ui" %><%@
taglib uri="http://liferay.com/tld/frontend" prefix="liferay-frontend" %><%@
taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %>

<%@
page import="com.liferay.commerce.payment.method.redsys.configuration.RedsysPaymentMethodCardGroupServiceConfiguration" %><%@
page import="com.liferay.commerce.payment.method.redsys.constants.RedsysCommercePaymentMethodConstants" %><%@
page import="com.liferay.portal.kernel.language.LanguageUtil" %><%@
page import="com.liferay.portal.kernel.util.Constants" %><%@
page import="com.liferay.portal.kernel.util.ParamUtil" %><%@
page import="com.liferay.portal.kernel.util.URLCodec" %><%@
page import="com.liferay.petra.string.StringPool"%>
<liferay-frontend:defineObjects />
<liferay-theme:defineObjects />
<%
String redirect = ParamUtil.getString(request, "redirect");
%>

4.  Abre el fichero “build.gradle” y añade la siguiente dependencia:

compileOnly group: 'com.liferay.commerce', name: 'com.liferay.commerce.payment.api', version: '5.0.0'
compileOnly group: "com.liferay.commerce", name: "com.liferay.commerce.product.api", version: "29.0.2"

5.  Crea un nuevo paquete: 

  • com.liferay.commerce.payment.method.redsys.taglib.ui

6.  En “com.liferay.commerce.payment.method.redsys.taglib.ui” crea una nueva clase “RedsysCommercePaymentMethodConfigurationScreenNavigationEntry” e implemente la interface “ScreenNavigationEntry<CommercePaymentMethodGroupRel>”, a continuación,  implemente tambien todos los metodos. 

7.  Anota esta clase con  @Component y establece 2 propiedades. Debería quedar así:

@Component(
      property = "screen.navigation.category.order:Integer=20",
      service = ScreenNavigationEntry.class
)

8.  Crea la siguiente variable (KEY), y retorne la misma en el método “getEntryKey”:

public static final String ENTRY_KEY_CARD_COMMERCE_PAYMENT_METHOD_CONFIGURATION = "redsys-configuration";

@Override
public String getEntryKey() {
   return ENTRY_KEY_CARD_COMMERCE_PAYMENT_METHOD_CONFIGURATION;
}

9.  En los tres siguientes métodos (“getCategory”, ”getScreenNavigationKey”, ”getLabel”) retorne los siguientes valores a partir de las constantes de la clase “CommercePaymentScreenNavigationConstants”. 

    @Override
    public String getCategoryKey() {
        return CommercePaymentScreenNavigationConstants.CATEGORY_KEY_COMMERCE_PAYMENT_METHOD_CONFIGURATION;
    }

    @Override
    public String getScreenNavigationKey() {
        return CommercePaymentScreenNavigationConstants.SCREEN_NAVIGATION_KEY_COMMERCE_PAYMENT_METHOD;
    }

    @Override
    public String getLabel(Locale locale) {
        return LanguageUtil.get(locale, CommercePaymentScreenNavigationConstants.CATEGORY_KEY_COMMERCE_PAYMENT_METHOD_CONFIGURATION);   
    }

10.  En el método “isVisible”, hacemos la validación de cuando debe presentarse esa segunda pestaña para la configuración de nuestro método de pago:

@Override
    public boolean isVisible(User user, CommercePaymentMethodGroupRel commercePaymentMethod) {
     if (commercePaymentMethod == null) {
            return false;
        }
     if (RedsysCommercePaymentMethod.KEY.equals(commercePaymentMethod.getEngineKey())) {
            return true;
        }
        return false;   
    }

​​​​​​11.  Ahora antes de implementar la lógica en el método “render”, al final de la clase, inyecta el servicio “JSPRenderer” con la anotación @Reference

@Reference
private JSPRenderer _jspRenderer;

12.  Después, inyecta el servicio “ServletContext” con la anotación @Reference, y usa el parámetro “target” para referenciar el “Bundle-SymbolicName” (establecido en el fichero bnd.bnd) de tu módulo para localizar la JSP.

@Reference(target="(osgi.web.symbolicname=com.liferay.commerce.payment.method.redsys)")
private ServletContext _servletContext;

13.  Abre el fichero “bnd.bnd” y declara un “Web-ContextPath” (el valor debería ser único). De esta forma, tu ServletContext se generará correctamente.

Web-ContextPath: /redsys-commerce-payment-method

14.  Ahora inyectamos 2 servicios más y los logs para nuestra clase que iremos a utilizar en el render:

@Reference
private CommerceChannelService _commerceChannelService;

@Reference
private ConfigurationProvider _configurationProvider;

private static final Log _log = LogFactoryUtil.getLog(RedsysCommercePaymentMethodConfigurationScreenNavigationEntry.class);

15.  En el método “render”, implementamos la lógica para recuperar los valores de la configuración a nivel de Channel en caso que exista y lo guardamos en el “httpServletRequest”. Así siempre que vayamos a editar los campos, los valores guardados anteriormente serán presentados en los inputs. 

try {

  long commerceChannelId = ParamUtil.getLong(httpServletRequest, RedsysCommercePaymentMethodConstants.COMMERCE_CHANNEL_ID);
  CommerceChannel commerceChannel =_commerceChannelService.fetchCommerceChannel(commerceChannelId);

  if (Validator.isNotNull(commerceChannel)) {

      RedsysPaymentMethodCardGroupServiceConfiguration redsysPaymentMethodCardGroupServiceConfiguration =
              _configurationProvider.getConfiguration(
                      RedsysPaymentMethodCardGroupServiceConfiguration.class,
                      new ParameterMapSettingsLocator(httpServletRequest.getParameterMap(),
                              new GroupServiceSettingsLocator(commerceChannel.getGroupId(),
                                      RedsysCommercePaymentMethodConstants.SERVICE_NAME))
              );

      httpServletRequest.setAttribute(
              RedsysPaymentMethodCardGroupServiceConfiguration.class.getName(), redsysPaymentMethodCardGroupServiceConfiguration);
  }else {
      if (_log.isDebugEnabled()) {
          _log.debug("CommerceChannel null with the channelId " + commerceChannelId );
      }
  }

}catch (Exception ex) {
  _log.error(ex,ex);
}

16.  Por fin, al final de todo, dentro del método “render”, implementamos nuestra lógica para renderizar la JSP “configuration.jsp”

_jspRenderer.renderJSP(
      _servletContext, httpServletRequest, httpServletResponse,
      "/configuration.jsp");

17.  Abre el ficheroconfiguration.jspcreado anteriormente, copia y pega las siguientes líneas para:

  •  Recuperar de la request los valores de la configuración para este método de pago en este canal en caso que exista.
  •  Crear un “form action” con los campos de entrada de datos para la configuración de nuestro módulo.

<%@ include file="/init.jsp" %>

<%RedsysPaymentMethodCardGroupServiceConfiguration paymentMethodCardGroupServiceConfiguration = (RedsysPaymentMethodCardGroupServiceConfiguration)request.getAttribute(RedsysPaymentMethodCardGroupServiceConfiguration.class.getName());
long commerceChannelId = ParamUtil.getLong(request, RedsysCommercePaymentMethodConstants.COMMERCE_CHANNEL_ID);
%>
<portlet:actionURL name="editCommercePaymentMethodConfiguration" var="editCommercePaymentMethodActionURL" />
<aui:form action="<%= editCommercePaymentMethodActionURL %>" method="post" name="fm">
<aui:input name="<%= Constants.CMD %>" type="hidden" value="<%= Constants.UPDATE %>" />
<aui:input name="<%= RedsysCommercePaymentMethodConstants.COMMERCE_CHANNEL_ID %>" type="hidden" value="<%= commerceChannelId %>" />
<aui:input name="redirect" type="hidden" value="<%= currentURL %>" />

<commerce-ui:panel>
 <commerce-ui:info-box title="authentication">
   <div class="alert alert-info">
           <%= LanguageUtil.format(resourceBundle, "redsys-configuration-help", new Object[] {"<a href=\"https://pagosonline.redsys.es\" target=\"_blank\">", "</a>"}, false) %>
   </div>
        <aui:input id="redsys-ds-signature-version" label="ds-signature-version" name="<%= RedsysCommercePaymentMethodConstants.SETTINGS_DS_SIGNATURE_VERSION %>" value="<%= paymentMethodCardGroupServiceConfiguration.dsSignatureVersion() %>" />
        <aui:input id="redsys-signature-secret" label="signature-secret" name="<%= RedsysCommercePaymentMethodConstants.SETTINGS_CLIENT_SECRET %>" value="<%= paymentMethodCardGroupServiceConfiguration.clientSecret() %>" />
        <aui:input id="redsys-merchant-code" label="merchant-code" name="<%= RedsysCommercePaymentMethodConstants.SETTINGS_MERCHANT_CODE %>"  value="<%= paymentMethodCardGroupServiceConfiguration.merchantCode() %>" />
        <aui:input id="redsys-terminal" label="terminal" name="<%= RedsysCommercePaymentMethodConstants.SETTINGS_TERMINAL %>"  value="<%= paymentMethodCardGroupServiceConfiguration.terminal() %>" />
      
   <aui:select id="redsys-settings--mode" label="mode" name="<%= RedsysCommercePaymentMethodConstants.SETTINGS_MODE %>">
           <%
              for (String mode : RedsysCommercePaymentMethodConstants.MODES) {
           %>
             <aui:option label="<%= mode %>" selected="<%= mode.equals(paymentMethodCardGroupServiceConfiguration.mode()) %>" value="<%= mode %>" />
           <%
           }
           %>
   </aui:select>

        <aui:select id="redsys-settings--type-of-transaction" label="type-of-transaction" name="<%= RedsysCommercePaymentMethodConstants.SETTINGS_TYPES_OF_TRANSACTION %>">
           <%
              for (String typeTransaction : RedsysCommercePaymentMethodConstants.TYPES_OF_TRANSACTION) {
           %>
              <aui:option label="<%= typeTransaction %>" selected="<%= typeTransaction.equals(paymentMethodCardGroupServiceConfiguration.typeTransaction()) %>" value="<%= typeTransaction %>" />
           <%
           }
           %>
        </aui:select>
     </commerce-ui:info-box>
  </commerce-ui:panel>

  <aui:button-row>
     <aui:button cssClass="btn-lg" type="submit" />
     <aui:button cssClass="btn-lg" href="<%= redirect %>" type="cancel" />
  </aui:button-row>
</aui:form>
Recuerda: todos los campos de entrada (input’s) serán para cumplimentar la configuración que hemos creado anteriormente en “ RedsysPaymentMethodCardGroupServiceConfiguration”.

18.  Guarda los cambios y despliega el módulo ejecutando el siguiente comando dentro del directorio de tu módulo en el Liferay Workspace:

commerce-payment-method-redsys% ./../../gradlew deploy


19.  Ahora abre tu Liferay Portal (ej.: localhost:8080), abre el Menu > Commerce > Channels > haz click  en el Channel creado anteriormente. En la sección de “Payment Methods”, en el listado haz click en “edit” de Redsys, en la pestaña "Details" lo active y click en "Guardar", y entonces ya deberías tener una nueva pestaña para configuración (como en la imagen de abajo):

Recuerda: todos los campos de entrada (input’s) están vacíos, porque no tenemos valores de configuración guardado para este canal. 

Ahora si queremos cumplimentar los campos con los valores y guardarlos, necesitamos implementar nuestra “Action” llamada por el form al lanzar un submit.

20.  Crea un nuevo paquete: 

  • com.liferay.commerce.payment.method.redsys.portlet.action

21.  En ello, crea una clase llamada “EditRedsysCommercePaymentMethodConfigurationMVCActionCommand” que extienda “BaseMVCActionCommand” e implemente el método “doProcessAction”.

22. Anota esta clase con  @Component y establece 3 propiedades. Debería quedar así:

@Component(
      immediate = true,
      property = {
              "javax.portlet.name=" + CPPortletKeys.COMMERCE_PAYMENT_METHODS,
              "mvc.command.name=editCommercePaymentMethodConfiguration"
      },
      service = MVCActionCommand.class
)

​​​​​​

23.  En el array de propiedades, establece los siguientes 2 valores necesarios para nuestro portlet action:

  •  "mvc.command.name=editCommercePaymentMethodConfiguration"
  • "javax.portlet.name=" + CPPortletKeys.COMMERCE_PAYMENT_METHODS

  • Como servicio, declara la interfaz implementada por clase abstracta “BaseMVCActionCommand”:

    • service = MVCActionCommand.class . 

Atención: está “MVCActionCommand” se ejecuta dentro del portlet de pago en las configuraciones, por tanto el parámetro "javax.portlet.name” debe ser el nombre del portlet de pago.

24.  Al final de la clase, inyecta el servicio “UserLocalService” con la anotación @Reference.

@Reference
private CommerceChannelService _commerceChannelService;

@Reference
private SettingsFactory _settingsFactory;

25.  Dentro de la clase, crea el siguiente método:

private void _updateCommercePaymentMethod(ActionRequest actionRequest)
throws Exception {

long commerceChannelId = ParamUtil.getLong(actionRequest, RedsysCommercePaymentMethodConstants.COMMERCE_CHANNEL_ID);
CommerceChannel commerceChannel = _commerceChannelService.getCommerceChannel(commerceChannelId);
Settings settings = _settingsFactory.getSettings(new GroupServiceSettingsLocator(commerceChannel.getGroupId(),RedsysCommercePaymentMethodConstants.SERVICE_NAME));

ModifiableSettings modifiableSettings = settings.getModifiableSettings();

String dsSignatureVersion = ParamUtil.getString(actionRequest, RedsysCommercePaymentMethodConstants.SETTINGS_DS_SIGNATURE_VERSION);
modifiableSettings.setValue("dsSignatureVersion", dsSignatureVersion);

String clientSecret = ParamUtil.getString(actionRequest, RedsysCommercePaymentMethodConstants.SETTINGS_CLIENT_SECRET);
modifiableSettings.setValue("clientSecret", clientSecret);

String mode = ParamUtil.getString(actionRequest, RedsysCommercePaymentMethodConstants.SETTINGS_MODE);
modifiableSettings.setValue("mode", mode);

String merchantCode = ParamUtil.getString(actionRequest, RedsysCommercePaymentMethodConstants.SETTINGS_MERCHANT_CODE);
modifiableSettings.setValue("merchantCode", merchantCode);

String terminal = ParamUtil.getString(actionRequest, RedsysCommercePaymentMethodConstants.SETTINGS_TERMINAL);
modifiableSettings.setValue("terminal", terminal);

String typeTransaction = ParamUtil.getString(actionRequest, RedsysCommercePaymentMethodConstants.SETTINGS_TYPES_OF_TRANSACTION);
modifiableSettings.setValue("typeTransaction", typeTransaction);

modifiableSettings.store();
}

26.  Para finalizar, dentro del “doProcessAction”, realiza la validación del “comando” que viene en la request y siendo igual a “update”, llamaremos el método creado. El método deberá quedarse así:

@Override
    protected void doProcessAction(ActionRequest actionRequest, ActionResponse actionResponse) throws Exception {

        String cmd = ParamUtil.getString(actionRequest, Constants.CMD);

        if (cmd.equals(Constants.UPDATE)) {
            _updateCommercePaymentMethod(actionRequest);
        } 
    }

27.  Guarda los cambios y despliega el módulo ejecutando el siguiente comando dentro del directorio de tu módulo en el Liferay Workspace:

commerce-payment-method-redsys% ./../../gradlew deploy

28.  Ahora abre tu Liferay Portal (ej.: localhost:8080), abre el Menu > Commerce > Channels > haz click en el Channel creado anteriormente. En la sección de “Payment Methods”, en el listado haz click en “edit” de Redsys, en la pestaña de “configuración” cumplimente los campos para el entorno de “Test Integration” de Redsys y guarde (como en la imagen de abajo):

                             Muy bien, ahora ya tenemos el módulo con configuraciones independientes por Canal. 

En el próximo paso, empezaremos a implementar toda la lógica para la comunicación con la pasarela de pago que esté configurada (en este caso, Redsys).

El código usado en este paso está disponible en una etiqueta con nombre “payment-redsys-step-2.1” en mi repo de github.