Skip to content

Commit

Permalink
Merge origin/master into Mollie/main
Browse files Browse the repository at this point in the history
  • Loading branch information
RubenB committed Nov 10, 2021
2 parents 841c938 + a1a6750 commit 5991632
Show file tree
Hide file tree
Showing 73 changed files with 1,590 additions and 308 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Add the following cartridges to the business manager site:
- `mollieDefaultEnabledTransactionAPI`: enum of strings
- `mollieEnableSingleClickPayments`: boolean
- `mollieComponentsEnabled`: boolean
- `mollieEnableQrCode`: boolean
- `mollieLogCategory`: string

## Custom Properties
Expand All @@ -60,12 +61,15 @@ Add the following cartridges to the business manager site:
- `molliePaymentMethodId`: string - external mollie payment method id
- `mollieOrderExpiryDays`: enum of int - expiry days of order
- `mollieEnabledTransactionAPI`: enum of strings - the enabled transaction API
- `mollieProductCategory`: enum of strings - category used for voucher method

### PaymentTransaction
- `molliePaymentId`: string - the id of the Mollie payment
- `molliePaymentStatus`: string - the payment status recieved from Mollie
- `molliePaymentDescription`: text - Generated payment description
- `mollieIssuerData`: text - selected issuer data
- `molliePaymentDetails`: text - mollie payment details
- `molliePaymentLink`: string - the link that is used to complete the payment

### Order
- `mollieOrderId`: string - the id of the Mollie order
Expand All @@ -84,7 +88,7 @@ Add the following cartridges to the business manager site:
Use the provided NPM scripts to compile and upload changes to your Sandbox.

#Testing
You can run `npm test` to execute all unit tests in the project. Run `npm run test:coverage` to get coverage information. Coverage will be available in coverage folder under root directory.
You can run `npm test` to execute all unit tests in the project. Run `npm run cover` to get coverage information. Coverage will be available in coverage folder under root directory.

# Mollie Documentation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ server.get('Start', csrfProtection.generateToken, function (req, res, next) {
}
}
if (paymentLink) {
Transaction.wrap(function () {
orderHelper.setPaymentLink(order, null, paymentLink);
});

res.render('order/payment/link/order_payment_link_send.isml', {
paymentLink: paymentLink,
orderId: orderNo,
Expand Down
8 changes: 3 additions & 5 deletions cartridges/bm_mollie/cartridge/controllers/MollieSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
var server = require('server');

var Site = require('dw/system/Site');
var ISML = require('dw/template/ISML');
var Transaction = require('dw/system/Transaction');
var renderTemplateHelper = require('*/cartridge/scripts/renderTemplateHelper');
var collections = require('*/cartridge/scripts/util/collections');
var paymentService = require('*/cartridge/scripts/payment/paymentService');
var config = require('*/cartridge/scripts/mollieConfig');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');

var valueTypeCodeMapping = {
Expand All @@ -34,14 +34,12 @@ var valueTypeCodeMapping = {
* @throws {MollieServiceException}
*/
function getMappedPreferences(preferences, molliePreferences) {
var fieldSettings = config.getCustomPageFieldSettings();
return collections.map(molliePreferences, function (preference) {
return {
ID: preference.ID,
displayName: preference.displayName,
defaultValue: preference.defaultValue,
description: fieldSettings[preference.ID] && fieldSettings[preference.ID].description,
mandotory: preference.mandatory,
mandatory: preference.mandatory,
selectedValue: preferences.getCustom()[preference.ID],
inputType: valueTypeCodeMapping[preference.valueTypeCode],
values: collections.map(preference.values, function (value) {
Expand Down Expand Up @@ -111,7 +109,7 @@ server.post('SavePreferences',
var param = request.httpParameterMap.get(paramName);
var preference = preferences.custom[paramName];
var paramValue = param.booleanValue || param.dateValue || param.doubleValue || param.intValue || param.value;
if (preference && paramValue !== preference.value) {
if (preference !== null && paramValue !== preference.value) {
Transaction.wrap(function () {
switch (paramValue) {
case 'checked':
Expand Down
8 changes: 7 additions & 1 deletion cartridges/bm_mollie/cartridge/static/default/mollie.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,16 @@ function hidePasswordField() {

function onClickSubmit() {
$(document).on('click', BM_PREF_SUBMIT, () => {
// Include unchecked checkboxes in the serialized array
const formData = $(BM_PREF_FORM).serializeArray();
$(`${BM_PREF_FORM} input[type="checkbox"]:not(:checked)`).each(function () {
formData.push({ name: this.name, value: this.checked ? "checked" : "unchecked" });
});

$.ajax({
url: $(BM_PREF_SUBMIT).attr('data-method-url'),
method: 'POST',
data: $(BM_PREF_FORM).serialize(),
data: formData,
success: function (data) {
if (data.error) {
showError('<strong>Error!</strong>');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
You can find your API key in your <a href='https://www.mollie.com/dashboard/org_9306261/developers/api-keys'>Mollie Profile</a>, it starts with test or live.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Read more about <a href='https://www.mollie.com/en/news/post/better-checkout-flows-with-mollie-components'>Mollie Components</a> and how it improves your conversion.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Click <a href='https://docs.mollie.com/orders/why-use-orders'>here</a> to read more about the differences between the Payment and Orders API
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Default expiry days for orders created with the Orders API
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Read more about <a href='https://docs.mollie.com/payments/qr-codes'>QR Codes</a>.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Read more about <a href='https://help.mollie.com/hc/en-us/articles/115000671249-What-are-single-click-payments-and-how-does-it-work-'>Single Click Payments</a> and how it improves your conversion.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Log category used by SFCC. The log category can be selected under: Administration -> Operations -> Custom Log Settings.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
You can find your Profile ID in your <a href='https://www.mollie.com/dashboard/org_9306261/developers/api-keys'>Mollie Profile</a>
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
</div>
</isif>
<input class="form-control ${preference.inputType === 'password' ? 'js-bm-preferences-password-field' : ''}" type="${preference.inputType}" name="${preference.ID}"
value="${preference.selectedValue || preference.defaultValue || ''}" ${preference.mandotory ? 'required' : ''} />
value="${preference.selectedValue || preference.defaultValue || ''}" ${preference.mandatory ? 'required' : ''} />
<isif condition="${preference.inputType === 'password'}">
<div class="input-group-append">
<button class="btn btn-outline-secondary js-bm-preferences-toggle-password" type="button">Show</button>
</div>
</isif>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<select class="form-control custom-select selectpicker" name="${preference.ID}" ${preference.mandotory ? 'required' : ''}>
<select class="form-control custom-select selectpicker" name="${preference.ID}" ${preference.mandatory ? 'required' : ''}>
<isloop items=${preference.values} var="preferenceOption">
<option value="${preferenceOption.value}" ${preferenceOption.value === preference.selectedValue.value ? 'selected' : ''}>
${preferenceOption.displayValue}
</option>
</isloop>
</select>
</select>
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<input class="checkbox" type="checkbox" data-toggle="toggle" value="checked" name="${preference.ID}"
${preference.selectedValue ? 'checked' : ''} />
<input type="hidden" name="${preference.ID}" value="unchecked" />
<input
class="checkbox"
type="checkbox"
data-toggle="toggle"
name="${preference.ID}"
value="checked"
${preference.selectedValue ? 'checked' : ''}
/>
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<form class="js-bm-preferences-form">
<fieldset>
<isloop items="${pdict.preferences}" var="preference" status="loopstatus">
<div class="row form-group ${preference.mandotory ? 'required' : ''}">
<div class="row form-group ${preference.mandatory ? 'required' : ''}">
<label class="col-md-2 control-label">${preference.displayName || preference.ID}</label>
<div class="col-md-4">
<isif condition="${preference.inputType === 'checkbox'}">
Expand All @@ -44,7 +44,9 @@
<isinclude template="preferences/input/input" />
</isif>
<span class="field-description mt-2">
<isprint value="${preference.description}" encoding="off" /></span>
<isset scope="page" name="descriptionTemplate" value="${'preferences/descriptions/' + preference.ID}"/>
<isinclude template="${descriptionTemplate}" />
</span>
</div>
</div>
<isif condition="${preference.ID === 'mollieProfileId'}">
Expand All @@ -58,4 +60,4 @@
</fieldset>
</form>
</div>
</div>
</div>
23 changes: 23 additions & 0 deletions cartridges/int_mollie/cartridge/models/order.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

var orderHelper = require('*/cartridge/scripts/order/orderHelper');

var base = module.superModule;

/**
* Order class that represents the current order
* @param {dw.order.LineItemCtnr} lineItemContainer - Current users's basket/order
* @param {Object} options - The current order's line items
* @param {Object} options.config - Object to help configure the orderModel
* @param {string} options.config.numberOfLineItems - helps determine the number of lineitems needed
* @param {string} options.countryCode - the current request country code
* @constructor
*/
function OrderModel(lineItemContainer, options) {
base.apply(this, [lineItemContainer, options]);

// Contains info like payment reference
this.paymentDetails = orderHelper.getPaymentDetails(lineItemContainer);
}

module.exports = OrderModel;
37 changes: 11 additions & 26 deletions cartridges/int_mollie/cartridge/models/payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
var base = require('*/cartridge/scripts/utils/superModule')(module);

var PaymentMgr = require('dw/order/PaymentMgr');
var paymentService = require('*/cartridge/scripts/payment/paymentService');
var collections = require('*/cartridge/scripts/util/collections');

/**
Expand Down Expand Up @@ -33,41 +32,27 @@ function getSelectedPaymentInstruments(selectedPaymentInstruments) {
* Creates an array of objects containing applicable payment methods
* @param {dw.util.ArrayList<dw.order.dw.order.PaymentMethod>} paymentMethods - An ArrayList of
* applicable payment methods that the user could use for the current basket.
* @param {dw.order.Basket} currentBasket - the target Basket object
* @param {string} countryCode - the associated Site countryCode
* @returns {Array} of object that contain information about the applicable payment methods for the
* current cart
*/
function applicablePaymentMethods(paymentMethods, currentBasket, countryCode) {
var getMethodResponse = paymentService.getMethods(currentBasket, countryCode);
var mollieMethods = {};
getMethodResponse.methods.forEach(function (mollieMethod) {
mollieMethods[mollieMethod.id] = mollieMethod;
});

var methods = [];
paymentMethods.toArray().forEach(function (method) {
var mollieMethodId = method.custom.molliePaymentMethodId;
var mollieMethod = mollieMethods[mollieMethodId];
if (mollieMethod || !mollieMethodId) {
methods.push({
ID: method.ID,
name: method.name,
image: method.image ? method.image.URL.toString() :
mollieMethod && mollieMethod.imageURL,
issuers: mollieMethod && mollieMethod.issuers
});
}
function applicablePaymentMethods(paymentMethods) {
return collections.map(paymentMethods, function (method) {
return {
ID: method.ID,
name: method.name,
image: method.image && method.image.URL.toString(),
molliePaymentMethodId: method.custom.molliePaymentMethodId,
processor: method.paymentProcessor.getID()
};
});

return methods;
}

/**
* Payment class that represents payment information for the current basket
* @param {dw.order.Basket} currentBasket - the target Basket object
* @param {dw.customer.Customer} currentCustomer - the associated Customer object
* @param {string} countryCode - the associated Site countryCode
* @param {string} view - the view of the line item (basket or order)
* @constructor
*/
function Payment(currentBasket, currentCustomer, countryCode) {
Expand All @@ -85,7 +70,7 @@ function Payment(currentBasket, currentCustomer, countryCode) {
// TODO: Should compare currentBasket and currentCustomer and countryCode to see
// if we need them or not
this.applicablePaymentMethods =
paymentMethods ? applicablePaymentMethods(paymentMethods, currentBasket, countryCode) : null;
paymentMethods ? applicablePaymentMethods(paymentMethods, countryCode) : null;

this.selectedPaymentInstruments = paymentInstruments ?
getSelectedPaymentInstruments(paymentInstruments) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ var HookMgr = require('dw/system/HookMgr');
var PaymentMgr = require('dw/order/PaymentMgr');
var BasketMgr = require('dw/order/BasketMgr');
var OrderMgr = require('dw/order/OrderMgr');
var URLUtils = require('dw/web/URLUtils');
var Transaction = require('dw/system/Transaction');
var Order = require('dw/order/Order');
var Resource = require('dw/web/Resource');
var MollieServiceException = require('*/cartridge/scripts/exceptions/MollieServiceException');
var Logger = require('*/cartridge/scripts/utils/logger');
var orderHelper = require('*/cartridge/scripts/order/orderHelper');
var config = require('*/cartridge/scripts/mollieConfig');
var renderTemplateHelper = require('*/cartridge/scripts/renderTemplateHelper');
var paymentService = require('*/cartridge/scripts/payment/paymentService');

// Require and extend
var COHelpers = require('*/cartridge/scripts/utils/superModule')(module);
Expand Down Expand Up @@ -48,14 +49,17 @@ COHelpers.handlePayments = function (order, orderNumber) {
paymentProcessor
);

if (authorizationResult.error) throw new MollieServiceException('Authorization hook failed');

return authorizationResult;
} catch (e) {
Logger.debug('PAYMENT :: ERROR :: ' + e.message);
var exception = e;
Logger.debug('PAYMENT :: ERROR :: ' + exception.message);

Transaction.wrap(function () { OrderMgr.failOrder(order, true); });
if (e.name === 'MollieServiceException') return { continueUrl: URLUtils.url('Checkout-Begin').toString() };
return { error: true };
return {
error: true,
fieldErrors: [],
serverErrors: [Resource.msg('error.technical', 'checkout', null)]
};
}
};

Expand All @@ -73,7 +77,7 @@ COHelpers.orderExists = function (orderNumber) {
* @param {string} lastOrderNumber - orderId of last order in session
* @returns {void}
*/
COHelpers.restoreOpenOrder = function (lastOrderNumber) {
COHelpers.restorePreviousBasket = function (lastOrderNumber) {
var currentBasket = BasketMgr.getCurrentBasket();

if (!currentBasket || currentBasket.getProductLineItems().length === 0) {
Expand All @@ -82,7 +86,8 @@ COHelpers.restoreOpenOrder = function (lastOrderNumber) {
if (order && order.getStatus().value === Order.ORDER_STATUS_CREATED
&& !orderHelper.getOrderIsAuthorized(order)) {
Transaction.wrap(function () {
OrderMgr.failOrder(order, true);
var message = 'PAYMENT :: Order failed because the basket cannot be restored for the customer otherwise.';
orderHelper.failOrder(order, message);
});
}
}
Expand Down Expand Up @@ -137,6 +142,7 @@ COHelpers.getMollieViewData = function (profile) {
return {
customerId: profile && profile.custom.mollieCustomerId,
enableSingleClickPayments: config.getEnableSingleClickPayments(),
enableQrCode: config.getEnableQrCode(),
mollieComponents: {
enabled: config.getComponentsEnabled(),
profileId: config.getProfileId(),
Expand Down Expand Up @@ -174,4 +180,36 @@ COHelpers.getPaymentSummaryTemplate = function (orderModel) {
}, 'checkout/billing/paymentOptions/paymentOptionsSummary');
};

/**
* Sets the Mollie payment methods based on the SFCC applicable payment methods
* @param {dw.order.Basket} currentBasket - the target Basket object
* @param {Object} orderModel - The current customer's order history
* @param {string} countryCode - customer country code
* @returns {Object} Mollie payment methods
*/
COHelpers.getMolliePaymentMethods = function (currentBasket, orderModel, countryCode) {
var paymentMethods = orderModel.billing.payment.applicablePaymentMethods;
var getMethodResponse = paymentService.getMethods(currentBasket, countryCode);
var mollieMethods = {};

getMethodResponse.methods.forEach(function (mollieMethod) {
mollieMethods[mollieMethod.id] = mollieMethod;
});

return paymentMethods.filter(function (method) {
return mollieMethods[method.molliePaymentMethodId] || !method.molliePaymentMethodId;
}).map(function (method) {
var mappedMethod = method;
if (method.molliePaymentMethodId) {
var mollieMethod = mollieMethods[method.molliePaymentMethodId];
mappedMethod.issuers = mollieMethod && mollieMethod.issuers;

if (mollieMethod.imageURL) {
mappedMethod.image = mollieMethod.imageURL;
}
}
return mappedMethod;
});
};

module.exports = COHelpers;
Loading

0 comments on commit 5991632

Please sign in to comment.