diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..d2da681 Binary files /dev/null and b/.DS_Store differ diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index abc8fef..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,10 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -## [2.0.0] - -### Added -- Introduced headless cartridge `cartridges/int_marketpay_headless` for Salesforce Composable Storefront (PWA). -- Added support for API-based payment flow integration. -- Added support for React-based PWA integration. -- Added payment form page styling options. \ No newline at end of file diff --git a/README.md b/README.md index d951ac0..c41042b 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,6 @@ AltaPay has made it easier for merchants and developers to accept secure payment - Salesforce Commerce Cloud 18.10 - SFRA v3.2.0 -## Changelog - -See [Changelog](CHANGELOG.md) for all the release notes. - ## License Distributed under the MIT License. See [LICENSE](LICENSE) for more information. diff --git a/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/api.json b/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/api.json index 7790fcd..def077d 100644 --- a/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/api.json +++ b/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/api.json @@ -1,7 +1,5 @@ { - "endpoints": [ - {"endpoint": "createCheckoutSession", "schema": "schema.yaml", "implementation": "script"}, - {"endpoint": "getNextOrderId", "schema": "schema.yaml", "implementation": "script"}, - {"endpoint": "getPaymentMethodConfigration", "schema": "schema.yaml", "implementation": "script"} + "endpoints": [ + {"endpoint": "createCheckoutSession", "schema": "schema.yaml", "implementation": "script"} ] } diff --git a/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/schema.yaml b/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/schema.yaml index 559e66f..cd383d6 100644 --- a/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/schema.yaml +++ b/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/schema.yaml @@ -29,6 +29,12 @@ paths: schema: type: string minLength: 1 + - in: query + name: c_customerId + required: true + schema: + type: string + minLength: 1 - in: query name: locale required: false @@ -44,6 +50,7 @@ paths: required: - order - configuration + - callbacks properties: order: type: object @@ -121,6 +128,8 @@ paths: transactionInfo: type: object additionalProperties: true + callbacks: + type: object configuration: type: object required: diff --git a/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/script.js b/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/script.js index 53afbe5..c02ef76 100644 --- a/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/script.js +++ b/cartridges/int_marketpay_headless/cartridge/rest-apis/marketpay/script.js @@ -1,33 +1,46 @@ const RESTResponseMgr = require('dw/system/RESTResponseMgr'); const marketPay = require('*/cartridge/scripts/services/marketPay') -const Logger = require('dw/system/Logger'); +const Logger = require('dw/system/Logger').getLogger('MarketPay','MarketPay'); +const SCAPIService = require('*/cartridge/scripts/services/scapiService'); exports.createCheckoutSession = function () { - var requestBody = request.httpParameterMap.requestBodyAsString; - var requestData = JSON.parse(requestBody); - var result = marketPay.getTokenAndSessionId(requestData); - var paymentMethods = marketPay.getPaymentMethods(result.token, result.sessionId); - - Logger.info(`MarketPayLog: ${JSON.stringify(paymentMethods)}`); + var customerId = request.httpParameterMap.c_customerId; + var customerIdValue = customerId.stringValue; try { + var requestBody = request.httpParameterMap.requestBodyAsString; + var requestData = JSON.parse(requestBody); + var result = marketPay.getTokenAndSessionId(requestData); + var paymentMethods = marketPay.getPaymentMethods(result.token, result.sessionId); + + const Transaction = require('dw/system/Transaction'); + var CustomObjectMgr = require('dw/object/CustomObjectMgr'); + + Transaction.wrap(function () { + var marketPayDataObj = CustomObjectMgr.getCustomObject('MarketPayData', customerIdValue); + if (!marketPayDataObj) { + marketPayDataObj = CustomObjectMgr.createCustomObject('MarketPayData', customerIdValue); + } + marketPayDataObj.custom.sessionID = result.sessionId; + marketPayDataObj.custom.customerID = customerIdValue; + marketPayDataObj.custom.token = result.token; + marketPayDataObj.custom.paymentMethods = JSON.stringify(paymentMethods); + }); + RESTResponseMgr - .createSuccess(result) + .createEmptySuccess(200) .render(); } catch (error) { + + Logger.error('Error creating session ' + error.message); + RESTResponseMgr .createError(404, 'Session-error', 'Not created', 'please reach out the SFCC developers.') .render(); - } -}; -exports.getNextOrderId = function () { -}; - -exports.getPaymentMethodConfigration = function () { + + } }; exports.createCheckoutSession.public = true; -exports.getPaymentMethodConfigration.public = true; -exports.getNextOrderId.public = true; diff --git a/cartridges/int_marketpay_headless/cartridge/scripts/helpers/marketPayDataHelper.js b/cartridges/int_marketpay_headless/cartridge/scripts/helpers/marketPayDataHelper.js index 5f98a2e..0d99b81 100644 --- a/cartridges/int_marketpay_headless/cartridge/scripts/helpers/marketPayDataHelper.js +++ b/cartridges/int_marketpay_headless/cartridge/scripts/helpers/marketPayDataHelper.js @@ -1,10 +1,8 @@ 'use strict'; const Site = require('dw/system/Site'); -const Logger = require('dw/system/Logger'); function getFormattedDataForMarketPaySession(basket) { - const Locale = require('dw/util/Locale'); const URLUtils = require('dw/web/URLUtils'); var currentLocale = Locale.getLocale(request.locale); @@ -13,7 +11,7 @@ function getFormattedDataForMarketPaySession(basket) { // Initialize the order data object var orderData = { order: { - orderId: basket.custom.marketPayUsedOrderNo, + orderId: basket.getUUID(), amount: { value: basket.getTotalGrossPrice().getValue(), currency: basket.getCurrencyCode() @@ -32,8 +30,8 @@ function getFormattedDataForMarketPaySession(basket) { type: "URL", value: URLUtils.https('MarketPay-PaymentFail').toString() }, - redirect: URLUtils.https('MarketPay-Redirect').toString() - //notification: URLUtils.https('MarketPay-PaymentNotification').toString() + redirect: URLUtils.https('MarketPay-Redirect').toString(), + notification: URLUtils.https('MarketPay-PaymentNotification').toString() }, configuration: { paymentType: "PAYMENT", @@ -41,7 +39,7 @@ function getFormattedDataForMarketPaySession(basket) { autoCapture: false, paymentDisplayType: "REDIRECT", // country: countryCode, @todo Get the country from basket or customer profile - language: Site.getCurrent().getDefaultLocale().split('_')[0] || "en" + language: Site.getCurrent().getDefaultLocale().split('_')[0] || 'en_US' } }; @@ -51,7 +49,7 @@ function getFormattedDataForMarketPaySession(basket) { for (var i = 0; i < productLineItems.length; i++) { var lineItem = productLineItems[i]; orderData.order.orderLines.push({ - itemId: lineItem.getProductID() || lineItem.getUUID(), + itemId: lineItem.getProductID(), description: lineItem.getProductName() || '', quantity: lineItem.getQuantityValue(), unitPrice: lineItem.getAdjustedPrice().getValue() @@ -109,6 +107,124 @@ function getFormattedDataForMarketPaySession(basket) { return orderData; } +function getDataForUpdateSession(order) { + const Locale = require('dw/util/Locale'); + const URLUtils = require('dw/web/URLUtils'); + var currentLocale = Locale.getLocale(request.locale); + + var orderData = { + order: { + orderId: order.getOrderNo(), + amount: { + value: order.getTotalGrossPrice().getValue(), + currency: order.getCurrencyCode() + }, + orderLines: [], + customer: null, + transactionInfo: {} + }, + callbacks: { + formStyling: URLUtils.https('MarketPay-CallbackForm').toString(), + success: { + type: "URL", + value: URLUtils.https('MarketPay-PaymentSuccess').toString() + }, + failure: { + type: "URL", + value: URLUtils.https('MarketPay-PaymentFail').toString() + }, + redirect: URLUtils.https('MarketPay-Redirect').toString(), + notification: URLUtils.https('MarketPay-PaymentNotification').toString() + }, + configuration: { + paymentType: "PAYMENT", + bodyFormat: "JSON", + autoCapture: false, + paymentDisplayType: "REDIRECT", + language: Site.getCurrent().getDefaultLocale().split('_')[0] || 'en_US' + } + }; + + // Format order lines from order product line items + var productLineItems = order.getProductLineItems(); + if (productLineItems && productLineItems.length > 0) { + for (var i = 0; i < productLineItems.length; i++) { + var lineItem = productLineItems[i]; + orderData.order.orderLines.push({ + itemId: lineItem.getProductID(), + description: lineItem.getProductName() || '', + quantity: lineItem.getQuantityValue(), + unitPrice: lineItem.getAdjustedPrice().getValue() + }); + } + } + + // Format customer information with validation + var customer = order.getCustomer(); + var billingAddress = order.getBillingAddress(); + var defaultShipment = order.getDefaultShipment(); + var shippingAddress = defaultShipment ? defaultShipment.getShippingAddress() : null; + + if (customer || billingAddress || shippingAddress) { + orderData.order.customer = {}; + + if (customer && customer.isRegistered()) { + var profile = customer.getProfile(); + if (profile) { + orderData.order.customer.firstName = profile.getFirstName() || ''; + orderData.order.customer.lastName = profile.getLastName() || ''; + orderData.order.customer.email = profile.getEmail() || ''; + } + } + + if (!orderData.order.customer.email && billingAddress) { + orderData.order.customer.firstName = billingAddress.getFirstName() || ''; + orderData.order.customer.lastName = billingAddress.getLastName() || ''; + orderData.order.customer.email = order.getCustomerEmail() || ''; + } + + if (billingAddress) { + orderData.order.customer.billingAddress = { + street: billingAddress.getAddress1() || '', + city: billingAddress.getCity() || '', + country: billingAddress.getCountryCode() ? billingAddress.getCountryCode().getValue() : '', + zipCode: billingAddress.getPostalCode() || '' + }; + } + + if (shippingAddress) { + orderData.order.customer.shippingAddress = { + street: shippingAddress.getAddress1() || '', + city: shippingAddress.getCity() || '', + country: shippingAddress.getCountryCode() ? shippingAddress.getCountryCode().getValue() : '', + zipCode: shippingAddress.getPostalCode() || '' + }; + } + } + + return orderData; +} + +function getOnInitiatePaymentURL(selectedPaymentMethod, marketPayPaymentMethods) { + marketPayPaymentMethods = JSON.parse(marketPayPaymentMethods); + + if (!selectedPaymentMethod || !marketPayPaymentMethods || !marketPayPaymentMethods.methods) { + return null; + } + + for (var i = 0; i < marketPayPaymentMethods.methods.length; i++) { + var method = marketPayPaymentMethods.methods[i]; + if (method.id === selectedPaymentMethod) { + return method.onInitiatePayment && method.onInitiatePayment.value ? method.onInitiatePayment.value : null; + } + } + + return null; +} + + module.exports = { - getFormattedDataForMarketPaySession: getFormattedDataForMarketPaySession + getFormattedDataForMarketPaySession: getFormattedDataForMarketPaySession, + getDataForUpdateSession: getDataForUpdateSession, + getOnInitiatePaymentURL: getOnInitiatePaymentURL }; diff --git a/cartridges/int_marketpay_headless/cartridge/scripts/helpers/site.js b/cartridges/int_marketpay_headless/cartridge/scripts/helpers/site.js index eebfb10..52bf9ae 100644 --- a/cartridges/int_marketpay_headless/cartridge/scripts/helpers/site.js +++ b/cartridges/int_marketpay_headless/cartridge/scripts/helpers/site.js @@ -1,6 +1,5 @@ 'use strict'; -const Logger = require('dw/system/Logger'); const Site = require('dw/system/Site'); /** diff --git a/cartridges/int_marketpay_headless/cartridge/scripts/hooks/basketPaymentHooks.js b/cartridges/int_marketpay_headless/cartridge/scripts/hooks/basketPaymentHooks.js index cc0ba21..24b83d4 100644 --- a/cartridges/int_marketpay_headless/cartridge/scripts/hooks/basketPaymentHooks.js +++ b/cartridges/int_marketpay_headless/cartridge/scripts/hooks/basketPaymentHooks.js @@ -1,43 +1,37 @@ 'use strict'; const Site = require('dw/system/Site'); -const Logger = require('dw/system/Logger'); -const BasketMgr = require('dw/order/BasketMgr'); +const Logger = require('dw/system/Logger').getLogger('MarketPay', 'MarketPay'); +const Transaction = require('dw/system/Transaction'); +const SCAPIService = require('*/cartridge/scripts/services/scapiService'); -exports.afterPOST = function (basketId) { - Logger.info("basket afterPost Called "); - - var HookMgr = require('dw/system/HookMgr'); +exports.modifyGETResponse_v2 = function (basket, paymentMethodResultResponse) { - if (HookMgr.hasHook('dw.order.createOrderNo')) { - var orderNo = HookMgr.callHook('dw.order.createOrderNo', 'createOrderNo'); - Logger.info("MarketPayOrderNo Created via hook: " + orderNo); - } else { - Logger.error("dw.order.createOrderNo hook not found"); - } -} + const marketPayDataHelper = require('*/cartridge/scripts/helpers/marketPayDataHelper'); + const site = require('*/cartridge/scripts/helpers/site.js'); -exports.modifyGETResponse_v2 = function (basket, paymentMethodResultResponse) { + var result = SCAPIService.createMarketPaySession(basket.customer.ID, marketPayDataHelper.getFormattedDataForMarketPaySession(basket)); - try { + if (!result) { - const marketPayService = require('*/cartridge/scripts/services/marketPay'); - const marketPayDataHelper = require('*/cartridge/scripts/helpers/marketPayDataHelper'); + return new dw.system.Status(dw.system.Status.ERROR, 'MarketPay: Unable to create Payment Session'); + } - var marketPayTokenAndSession = marketPayService.getTokenAndSessionId(marketPayDataHelper.getFormattedDataForMarketPaySession(basket)); - var marketPayPaymentMethods = marketPayService.getPaymentMethods(marketPayTokenAndSession.token, marketPayTokenAndSession.sessionId); + try { + var CustomObjectMgr = require('dw/object/CustomObjectMgr'); + var marketPayDataObj = CustomObjectMgr.getCustomObject('MarketPayData', basket.customer.ID); + var marketPayPaymentMethods = JSON.parse(marketPayDataObj.custom.paymentMethods); - const site = require('*/cartridge/scripts/helpers/site.js'); - var marketPayTerminalsMapping = site.getCustomPreference('marketpayTerminals'); + var marketPayTerminalsMapping = site.getCustomPreference('marketPayTerminals'); var paymentMethods = paymentMethodResultResponse.applicablePaymentMethods; var currentLocale = Site.getCurrent().defaultLocale; var currencyCode = Site.getCurrent().getDefaultCurrency(); - + // Parse JSON if it's a string if (typeof marketPayTerminalsMapping === 'string') { marketPayTerminalsMapping = JSON.parse(marketPayTerminalsMapping); - } + } // MarketPay specific payment method IDs to validate var marketPayMethods = [ @@ -65,102 +59,105 @@ exports.modifyGETResponse_v2 = function (basket, paymentMethodResultResponse) { // Convert ArrayList to JavaScript array, then filter var filteredMethods = paymentMethods.toArray().filter(function (method) { - // Only process MarketPay payment methods - if (marketPayMethods.indexOf(method.id) === -1) { - return false; - } + // Only process MarketPay payment methods, pass through all others + if (marketPayMethods.indexOf(method.id) === -1) { + return true; + } - // Check if terminals exist for the default currency - if (!marketPayTerminalsMapping.terminals[currencyCode]) { - return false; - } + // Check if terminals exist for the default currency + if (!marketPayTerminalsMapping.terminals[currencyCode]) { + return false; + } - // Get terminals for the current currency - var currencyTerminals = marketPayTerminalsMapping.terminals[currencyCode]; + // Get terminals for the current currency + var currencyTerminals = marketPayTerminalsMapping.terminals[currencyCode]; - // Find if there's a terminal mapping for this payment method with matching locale - var terminalMapping = null; - for (var i = 0; i < currencyTerminals.length; i++) { - var terminal = currencyTerminals[i]; - if (terminal.id === method.id && terminal.allowedlocales.indexOf(currentLocale) !== -1) { - terminalMapping = terminal; - break; + // Find if there's a terminal mapping for this payment method with matching locale + var terminalMapping = null; + for (var i = 0; i < currencyTerminals.length; i++) { + var terminal = currencyTerminals[i]; + if (terminal.id === method.id && terminal.allowedlocales.indexOf(currentLocale) !== -1) { + terminalMapping = terminal; + break; + } } - } - // If mapping exists, add terminal info to the method - if (terminalMapping !== null) { - - // Match terminalMapping.name with marketPayPaymentMethods metadata.terminalName - if (marketPayPaymentMethods && marketPayPaymentMethods.methods && marketPayPaymentMethods.methods.length > 0) { - for (var k = 0; k < marketPayPaymentMethods.methods.length; k++) { - var paymentMethod = marketPayPaymentMethods.methods[k]; - if (paymentMethod.metadata && paymentMethod.metadata.terminalName === terminalMapping.name) { - // Include the matched payment method - method.c_marketPay = { - access_token: marketPayTokenAndSession.token, - sessionId: marketPayTokenAndSession.sessionId, - paymentMethod: paymentMethod - } - break; + // If mapping exists, add terminal info to the method + if (terminalMapping !== null) { + + // Match terminalMapping.name with marketPayPaymentMethods metadata.terminalName + if (marketPayPaymentMethods && marketPayPaymentMethods.methods && marketPayPaymentMethods.methods.length > 0) { + for (var k = 0; k < marketPayPaymentMethods.methods.length; k++) { + var paymentMethod = marketPayPaymentMethods.methods[k]; + if (paymentMethod.metadata && paymentMethod.metadata.terminalName === terminalMapping.name) { + // Include the matched payment method + delete paymentMethod.onInitiatePayment; + + method.c_marketPay = { + //sessionId: marketPayTokenAndSession.sessionId, + paymentMethod: paymentMethod + } + break; + } } } + return true; } - return true; - } - return false; - }); + return false; + }); - paymentMethodResultResponse.applicablePaymentMethods = filteredMethods; + paymentMethodResultResponse.applicablePaymentMethods = filteredMethods; } catch (e) { Logger.error("Error in modifyGETResponse_v2: " + e.message); Logger.error("Stack trace: " + e.stack); + + return new dw.system.Status(dw.system.Status.ERROR, 'MarketPay Error:' + e.message); // Return original payment methods on error - return; } }; -exports.modifyPOSTResponse = function(basket, basketResponse, paymentInstrumentRequest ) { - Logger.info("MarketPay: payment instrument modifyPOSTREsponse "); +exports.afterPOST = function (basket, paymentInstrument) { - const marketPayService = require('*/cartridge/scripts/services/marketPay'); + var paymentInstrumentRequest = paymentInstrument; + var basketResponse = basket; const marketPayDataHelper = require('*/cartridge/scripts/helpers/marketPayDataHelper'); - if (paymentInstrumentRequest.c_marketpayPaymentMethodID && - paymentInstrumentRequest.c_marketPayToken && - paymentInstrumentRequest.c_marketPaySessionID) { + if (paymentInstrumentRequest.c_marketPayPaymentMethodID) { - Logger.info("c_marketpayPaymentMethodID: " + paymentInstrumentRequest.c_marketpayPaymentMethodID); + // Get sessionId from Custom Object MarketPayData for this user + var CustomObjectMgr = require('dw/object/CustomObjectMgr'); + var marketPayDataObj = CustomObjectMgr.getCustomObject('MarketPayData', basket.customer.ID); + var marketPayToken = marketPayDataObj ? marketPayDataObj.custom.token : null; + var marketPaySessionId = marketPayDataObj ? marketPayDataObj.custom.sessionID : null; + var marketPayPaymentMethods = marketPayDataObj ? marketPayDataObj.custom.paymentMethods : null; + var onInitiatePaymentURL = marketPayDataHelper.getOnInitiatePaymentURL(paymentInstrumentRequest.c_marketPayPaymentMethodID, marketPayPaymentMethods); - // Token and sessionId should be sent from PWA in the request body - Logger.info("beforePost token: " + paymentInstrumentRequest.c_marketPayToken); - Logger.info("beforePost sessionID: " + paymentInstrumentRequest.c_marketPaySessionID); + if (!marketPayDataObj || !marketPaySessionId || !marketPayPaymentMethods || !onInitiatePaymentURL) { + Logger.error('MarketPay: No Active Payment Session found for the user'); + return new dw.system.Status(dw.system.Status.ERROR, 'MarketPay: No Active Payment Session found for the user'); + } + + if (basketResponse.paymentInstruments && basketResponse.paymentInstruments.length > 0) { + for (var i = 0; i < basketResponse.paymentInstruments.length; i++) { + var paymentInstrument = basketResponse.paymentInstruments[i]; + if (paymentInstrument.paymentMethod === paymentInstrumentRequest.paymentMethodId) { + Transaction.wrap(function () { + paymentInstrument.custom.marketPayPaymentMethodID = paymentInstrumentRequest.c_marketPayPaymentMethodID; + }); + break; + } + } + } - // Token and sessionId should be sent from PWA in the request body - var mpPayment = marketPayService.createPayment(paymentInstrumentRequest.c_marketPayToken, - paymentInstrumentRequest.c_marketPaySessionID, - paymentInstrumentRequest.c_marketpayPaymentMethodID); - basketResponse.c_marketPay = mpPayment; } else { var missingFields = []; - if (!paymentInstrumentRequest.c_marketpayPaymentMethodID) { - missingFields.push('c_marketpayPaymentMethodID'); - } - if (!paymentInstrumentRequest.c_marketPayToken) { - missingFields.push('c_marketPayToken'); - } - if (!paymentInstrumentRequest.c_marketPaySessionID) { - missingFields.push('c_marketPaySessionID'); + if (!paymentInstrumentRequest.c_marketPayPaymentMethodID) { + missingFields.push('c_marketPayPaymentMethodID'); } Logger.error("MarketPay: Missing required fields: " + missingFields.join(', ')); - basketResponse.c_marketPayError = { - error: true, - message: "Missing required MarketPay fields: " + missingFields.join(', ') - }; - - return; + return new dw.system.Status(dw.system.Status.ERROR, 'MarketPay: Missing required fields:' + missingFields.join(', ')); } -}; +}; \ No newline at end of file diff --git a/cartridges/int_marketpay_headless/cartridge/scripts/hooks/orderHooks.js b/cartridges/int_marketpay_headless/cartridge/scripts/hooks/orderHooks.js index 1b688b9..8ad84a4 100644 --- a/cartridges/int_marketpay_headless/cartridge/scripts/hooks/orderHooks.js +++ b/cartridges/int_marketpay_headless/cartridge/scripts/hooks/orderHooks.js @@ -1,54 +1,54 @@ 'use strict'; -var Logger = require('dw/system/Logger'); -var BasketMgr = require('dw/order/BasketMgr'); - -/** - * Hook to provide custom order number when creating an order - * Uses the pre-generated order number stored in basket.custom.marketPayUsedOrderNo - * @returns {string|null} - Custom order number or null to use default - */ -exports.createOrderNo = function () { - var orderNo; - var isOrderExist; - - const OrderMgr = require('dw/order/OrderMgr'); - const Transaction = require('dw/system/Transaction'); - +var Logger = require('dw/system/Logger').getLogger('MarketPay', 'MarketPay'); + +exports.afterPOST = function (order) { + + Logger.info("AfterPost order "); + try { + var CustomObjectMgr = require('dw/object/CustomObjectMgr'); + var marketPayDataObj = CustomObjectMgr.getCustomObject('MarketPayData', order.customer.ID); - var basket = BasketMgr.getCurrentBasket(); - Logger.info("createOrderNo Hook called, basket is null: " + (basket == null)); - - Transaction.begin(); - orderNo = basket.custom.marketPayUsedOrderNo; - - if (!orderNo) { - orderNo = OrderMgr.createOrderSequenceNo(); - basket.custom.marketPayUsedOrderNo = orderNo; - } else { - try { - isOrderExist = !empty(OrderMgr.getOrder(orderNo)); - - if (isOrderExist) { - orderNo = OrderMgr.createOrderSequenceNo(); - basket.custom.marketPayUsedOrderNo = orderNo; - } - } catch (error) { - Logger.error("Error in createOrderNo: " + error.message); - orderNo = OrderMgr.createOrderSequenceNo(); - basket.custom.marketPayUsedOrderNo = orderNo; - } + if (!marketPayDataObj.custom.paymentMethods || !marketPayDataObj.custom.token || !marketPayDataObj.custom.sessionID) { + Logger.error('MarketPay: No Active Payment Session found for the user'); + return; } - Transaction.commit(); + var marketPayToken = marketPayDataObj.custom.token; + var marketPaySessionId = marketPayDataObj.custom.sessionID; + + const marketPayDataHelper = require('*/cartridge/scripts/helpers/marketPayDataHelper'); + const marketPayService = require('*/cartridge/scripts/services/marketPay'); + var data = marketPayDataHelper.getDataForUpdateSession(order); + + marketPayService.updateSession(marketPayToken, marketPaySessionId, data); + + var onInitiatePaymentURL = marketPayDataHelper.getOnInitiatePaymentURL( + order.paymentInstrument.custom.marketPayPaymentMethodID, + marketPayDataObj.custom.paymentMethods); + + var mpPayment = marketPayService.createPayment(marketPayToken, + marketPaySessionId, + order.paymentInstrument.custom.marketPayPaymentMethodID, + onInitiatePaymentURL); + + const Transaction = require('dw/system/Transaction'); + + Transaction.wrap(function () { + order.paymentInstrument.custom.marketPayPaymentURL = mpPayment.url; + }); + + // clean up + Transaction.wrap(function () { + CustomObjectMgr.remove(marketPayDataObj); + }); + } catch (e) { - Transaction.rollback(); - Logger.error("Transaction error in createOrderNo: " + e.message); - throw e; + Logger.error("MarketPay: Error updating session: " + e.message); } +}; + + - Logger.info("createOrderNo Hook called, OrderNo: " + orderNo); - return orderNo; -}; diff --git a/cartridges/int_marketpay_headless/cartridge/scripts/services/marketPay.js b/cartridges/int_marketpay_headless/cartridge/scripts/services/marketPay.js index b8875dc..64706f2 100644 --- a/cartridges/int_marketpay_headless/cartridge/scripts/services/marketPay.js +++ b/cartridges/int_marketpay_headless/cartridge/scripts/services/marketPay.js @@ -3,7 +3,7 @@ const LocalServiceRegistry = require('dw/svc/LocalServiceRegistry'); const Encoding = require('dw/crypto/Encoding'); const Bytes = require('dw/util/Bytes'); -const Logger = require('dw/system/Logger'); +const Logger = require('dw/system/Logger').getLogger('MarketPay', 'MarketPay'); /** * Creates and returns a LocalServiceRegistry service for authenticating with MarketPay. @@ -18,55 +18,46 @@ const Logger = require('dw/system/Logger'); * @returns {dw.svc.HTTPService} * A configured MarketPay authentication service instance. */ -function getMarketPayAuthenticateService() { - let authString; - let encodedAuthString; - return LocalServiceRegistry.createService('int.marketpay.auth', { - createRequest: function (svc, payload) { - svc.setRequestMethod('POST'); - svc.addHeader('Content-Type', 'application/json'); - - authString = payload.username + ':' + payload.password; - encodedAuthString = Encoding.toBase64(new Bytes(authString)); - svc.addHeader('Authorization', 'Basic ' + encodedAuthString); +function fetchNewToken() { + const tokenService = LocalServiceRegistry.createService("int.marketpay.auth", { + createRequest: function (service, params) { + service.setRequestMethod("POST"); + service.addHeader("Content-Type", "application/x-www-form-urlencoded"); - return JSON.stringify(payload); - }, + const credentials = service.getConfiguration().getCredential(); + const clientId = credentials.getUser(); + const clientSecret = credentials.getPassword(); - parseResponse: function (svc, client) { - try { - return JSON.parse(client.text); - } catch (e) { - throw new Error('Failed to parse authentication response: ' + e.message); - } - }, + const authString = clientId + ":" + clientSecret; + const base64Auth = Encoding.toBase64(new Bytes(authString)); + service.addHeader("Authorization", "Basic " + base64Auth); - filterLogMessage: function (msg) { - return msg; // Mask if needed + return JSON.stringify({}); }, - - mockCall: function () { - return { - status: 'SUCCESS', - token: 'mock-auth-token' - }; + parseResponse: function (service, response) { + return JSON.parse(response.text); } }); -} -function getService(serviceType, method) { - return LocalServiceRegistry.createService('marketpay.http.service', { - createRequest: function (svc, payload) { + const result = tokenService.call(); + + if (result.ok && result.object && result.object.token) { - var log = Logger.getLogger("marketpay"); + return result.object.token; + } - log.info("GetService Marketing _url:" + svc.getURL()+'/'+serviceType); - log.info("GetService token:" + payload.token); - log.info("GetService body:" + JSON.stringify(payload.requestBody)); + throw new Error("Failed to get token: " + (result.errorMessage || 'Unknown error')); +} +function getService(serviceType, method, url) { + return LocalServiceRegistry.createService('marketpay.http.service', { + createRequest: function (svc, payload) { - svc.setURL(svc.getURL()+'/'+serviceType); + if (url == null) + svc.setURL(svc.getURL() + '/' + serviceType); + else + svc.setURL(url); svc.setRequestMethod(method); svc.addHeader('Content-Type', 'application/json'); @@ -110,12 +101,10 @@ function getService(serviceType, method) { * A configured MarketPay session service instance. */ - function getMarketPaySessionService() { return LocalServiceRegistry.createService('int.marketpay.session', { createRequest: function (svc, payload) { - svc.setRequestMethod('POST'); svc.addHeader('Content-Type', 'application/json'); svc.addHeader('Authorization', 'Bearer ' + payload.token); @@ -144,38 +133,6 @@ function getMarketPaySessionService() { }); } -/** - * Retrieves an authentication token from the MarketPay API. - * - * Calls the MarketPay authentication service using credentials configured - * as custom site preferences and returns the issued token. - * - * @function getAuthToken - * - * @throws {Error} - * Thrown when the authentication service call fails or does not return a valid token. - * - * @returns {string} - * The MarketPay authentication token. - */ -function getAuthToken() { - const site = require('*/cartridge/scripts/helpers/site.js'); - const authService = getMarketPayAuthenticateService(); - const payload = { - username: site.getCustomPreference('marketpayUsername'), - password: site.getCustomPreference('marketpayPassword') - }; - - const result = authService.call(payload); - - if (!result.ok || !result.object || !result.object.token) { - Logger.error('MarketPay Authenticate API error'); - throw new Error('Failed to retrieve MarketPay authentication token'); - } - - return result.object.token; -} - /** * Retrieves a MarketPay authentication token and creates a MarketPay session. * @@ -197,14 +154,14 @@ function getAuthToken() { * - token: The MarketPay authentication token * - sessionId: The created MarketPay session ID */ -function getTokenAndSessionId(requestBody) { - const token = getAuthToken(); +function getTokenAndSessionId(requestBody) { + const token = fetchNewToken(); const sessionService = getMarketPaySessionService(); - const result = sessionService.call({ - token: token, - requestBody: requestBody - }); + const result = sessionService.call({ + token: token, + requestBody: requestBody + }); if (!result.ok || !result.object || !result.object.sessionId) { Logger.error('MarketPay Session API error', result.errorMessage); @@ -217,38 +174,48 @@ function getTokenAndSessionId(requestBody) { }; } -function getPaymentMethods(authToken, checkoutSessionId) { +function updateSession(token, checkoutSessionId, requestBody) { + + const service = getService(`session/${checkoutSessionId}`, 'PUT'); + const result = service.call({ + token: token, + requestBody: requestBody + }); + + if (!result.ok) { + Logger.error('MarketPay updateSession API error', result.errorMessage); + throw new Error('Failed to update MarketPay session'); + } + + return result.ok; +} +function getPaymentMethods(token, checkoutSessionId) { const service = getService(`session/${checkoutSessionId}/payment-methods`, 'GET'); - const result = service.call({ - token: authToken, - requestBody: {} - }); + const result = service.call({ + token: token, + requestBody: {} + }); - //Logger.info('MarketPay PaymentMethods', JSON.stringify(result)); - if (!result.ok) { Logger.error('MarketPay PaymentMethods API error', result.errorMessage); throw new Error('Failed to retrieve MarketPay Payment Methods'); } - - return result.object; } -function createPayment(authToken, checkoutSessionId, paymentMethodId) { - - const service = getService(`payment`, 'POST'); - const result = service.call({ - token: authToken, - requestBody: { - paymentMethodId: paymentMethodId, - sessionId: checkoutSessionId - } - }); +function createPayment(token, checkoutSessionId, paymentMethodId, onInitiatePaymentURL) { + const service = getService(`payment`, 'POST', onInitiatePaymentURL); + const result = service.call({ + token: token, + requestBody: { + paymentMethodId: paymentMethodId, + sessionId: checkoutSessionId + } + }); - if (!result.ok || !result.object ) { + if (!result.ok || !result.object) { Logger.error('MarketPay createPayment API error', result.errorMessage); throw new Error('Failed to create MarketPay session ID'); } @@ -258,8 +225,7 @@ function createPayment(authToken, checkoutSessionId, paymentMethodId) { module.exports = { getTokenAndSessionId: getTokenAndSessionId, - getPaymentMethods: getPaymentMethods, + updateSession: updateSession, + getPaymentMethods: getPaymentMethods, createPayment: createPayment }; - - diff --git a/cartridges/int_marketpay_headless/cartridge/scripts/services/scapiService.js b/cartridges/int_marketpay_headless/cartridge/scripts/services/scapiService.js new file mode 100644 index 0000000..01c3bcb --- /dev/null +++ b/cartridges/int_marketpay_headless/cartridge/scripts/services/scapiService.js @@ -0,0 +1,177 @@ +'use strict'; + +var LocalServiceRegistry = require('dw/svc/LocalServiceRegistry'); +var Logger = require('dw/system/Logger').getLogger('MarketPay', 'MarketPay'); +var Site = require('dw/system/Site'); +var Encoding = require('dw/crypto/Encoding'); +var Bytes = require('dw/util/Bytes'); + +/** + * Get SLAS credentials from site preferences + */ +function getSLASCredentials() { + return { + organizationId: Site.current.getCustomPreferenceValue('marketPayOrganizationID'), + shortCode: Site.current.getCustomPreferenceValue('marketPayOrganizationShortCode'), + siteId: Site.current.ID + }; +} + +/** + * Get guest access token using SLAS + * @returns {Object} { access_token, token_type, expires_in, refresh_token, usid, customer_id, enc_user_id, idp_access_token } + */ +function getGuestAccessToken() { + var credentials = getSLASCredentials(); + + if (!credentials.organizationId) { + Logger.error('SLAS credentials not configured in site preferences'); + return null; + } + + var service = LocalServiceRegistry.createService('int.marketpay.slas', { + createRequest: function (svc, params) { + svc.setRequestMethod('POST'); + + const credentials = svc.getConfiguration().getCredential(); + const clientId = credentials.getUser(); + const clientSecret = credentials.getPassword(); + + // Set headers + svc.addHeader('Content-Type', 'application/x-www-form-urlencoded'); + svc.addHeader('Authorization', getBasicAuthHeader(clientId, clientSecret)); + + // Replace URL placeholders + var url = svc.getURL(); + url = url.replace('{shortcode}', params.shortCode); + url = url.replace('{orgId}', params.organizationId); + svc.setURL(url); + + // Build form data for guest login + var formData = []; + formData.push('grant_type=client_credentials'); + formData.push('channel_id=' + params.siteId); + //formData.push('scope=SALESFORCE_COMMERCE_API:' + params.organizationId + ' sfcc.shopper-baskets-orders sfcc.shopper-customers.login sfcc.shopper-myaccount.baskets sfcc.shopper-myaccount.orders'); + + return formData.join('&'); + }, + + parseResponse: function (svc, client) { + + if (client.statusCode === 200) { + return JSON.parse(client.text); + } + + return null; + }, + + filterLogMessage: function (msg) { + // Remove sensitive data from logs + return msg.replace(/Authorization: Basic [^\s]+/g, 'Authorization: Basic ***') + .replace(/client_secret=[^&]+/g, 'client_secret=***'); + } + }); + + try { + var result = service.call(credentials); + + if (result.ok && result.object) { + return result.object; + } else { + Logger.error('SLAS guest login failed: ' + result.errorMessage); + return null; + } + } catch (e) { + Logger.error('SLAS guest login exception: ' + e.message); + return null; + } +} + +/** + * Generate Basic Auth header + */ +function getBasicAuthHeader(clientId, clientSecret) { + var credentials = clientId + ':' + clientSecret; + var credentialsBytes = new Bytes(credentials); + var encodedCredentials = Encoding.toBase64(credentialsBytes); + return 'Basic ' + encodedCredentials; +} + +/** + * Create checkout session via custom SCAPI + * @param {Object} orderData - Order data + * @param {Object} configuration - Payment configuration + * @param {Object} customParams - Custom query parameters + * @returns {Object} Service result + */ +function createMarketPaySession(customerID, requestBody) { + var service = LocalServiceRegistry.createService('int.marketpay.custom', { + createRequest: function (svc, params) { + svc.setRequestMethod('POST'); + svc.addHeader('Content-Type', 'application/json'); + + // Get access token + var token = getGuestAccessToken(); + if (!token) { + throw new Error('Unable to obtain access token'); + } + + Logger.info("access Token: " + token.access_token); + // Add authorization header + svc.addHeader('Authorization', 'Bearer ' + token.access_token); + + // Replace URL placeholders + var credentials = getSLASCredentials(); + var url = svc.getURL(); + url = url.replace('{shortcode}', credentials.shortCode); + url = url.replace('{orgId}', credentials.organizationId); + + // Build query parameters + var queryParams = []; + queryParams.push('siteId=' + encodeURIComponent(params.siteId)); + queryParams.push('c_customerId=' + params.customerID); + + var urlWithParams = url + '?' + queryParams.join('&'); + svc.setURL(urlWithParams); + + return JSON.stringify(params.requestBody); + }, + + parseResponse: function (svc, client) { + if (client.statusCode === 200 || client.statusCode === 201) { + return JSON.parse(client.text); + } + + return null; + }, + + filterLogMessage: function (msg) { + // Remove token from logs + return msg.replace(/Bearer [^\s]+/g, 'Bearer ***'); + } + }); + + try { + var result = service.call({ + siteId: Site.current.ID, + locale: request.locale || 'default', + customerID: customerID, + requestBody: requestBody + }); + + if (result.ok) { + return true; + } else { + Logger.error('Checkout session creation failed: ' + result.errorMessage); + return false; + } + } catch (e) { + Logger.error('Checkout session exception: ' + e.message + '\n' + e.stack); + return false; + } +} + +module.exports = { + getGuestAccessToken: getGuestAccessToken, + createMarketPaySession: createMarketPaySession +}; \ No newline at end of file diff --git a/cartridges/int_marketpay_headless/hooks.json b/cartridges/int_marketpay_headless/hooks.json index 5452dee..01560c9 100644 --- a/cartridges/int_marketpay_headless/hooks.json +++ b/cartridges/int_marketpay_headless/hooks.json @@ -1,25 +1,18 @@ { - "hooks": [ - { - "name": "dw.order.createOrderNo", - "script": "./cartridge/scripts/hooks/orderHooks.js" - }, - { - "name": "dw.ocapi.shop.basket.afterPOST", - "script": "./cartridge/scripts/hooks/basketPaymentHooks.js" - - }, + "hooks": [ { "name": "dw.ocapi.shop.basket.payment_methods.modifyGETResponse_v2", "script": "./cartridge/scripts/hooks/basketPaymentHooks.js" }, { - "name": "dw.ocapi.shop.basket.payment_instrument.beforePOST", - "script": "./cartridge/scripts/hooks/basketPaymentHooks.js" - }, + "name": "dw.ocapi.shop.basket.payment_instrument.afterPOST", + "script": "./cartridge/scripts/hooks/basketPaymentHooks.js" + }, { - "name": "dw.ocapi.shop.basket.payment_instrument.modifyPOSTResponse", - "script": "./cartridge/scripts/hooks/basketPaymentHooks.js" - } + "name": "dw.ocapi.shop.order.afterPOST", + "script": "./cartridge/scripts/hooks/orderHooks.js" + } ] + } + diff --git a/cartridges/int_marketpay_headless/package.json b/cartridges/int_marketpay_headless/package.json index dbc747c..4b2c1d4 100644 --- a/cartridges/int_marketpay_headless/package.json +++ b/cartridges/int_marketpay_headless/package.json @@ -1,3 +1,3 @@ { - "hooks": "./hooks.json" -} + "hooks": "./hooks.json" +} \ No newline at end of file diff --git a/docs/headless/cartridge_path.png b/docs/headless/cartridge_path.png deleted file mode 100644 index 15e4be0..0000000 Binary files a/docs/headless/cartridge_path.png and /dev/null differ diff --git a/docs/headless/import_site_data.png b/docs/headless/import_site_data.png deleted file mode 100644 index 00b875f..0000000 Binary files a/docs/headless/import_site_data.png and /dev/null differ diff --git a/docs/headless/terminals.png b/docs/headless/terminals.png deleted file mode 100644 index 42f363b..0000000 Binary files a/docs/headless/terminals.png and /dev/null differ diff --git a/metadata/MarketPay-Headless/meta/custom-objecttype-definitions.xml b/metadata/MarketPay-Headless/meta/custom-objecttype-definitions.xml new file mode 100644 index 0000000..147368f --- /dev/null +++ b/metadata/MarketPay-Headless/meta/custom-objecttype-definitions.xml @@ -0,0 +1,38 @@ + + + + Customer ID + MarketPay Data + no-staging + site + 1 + + string + 0 + + + + MarketPay Payment Methods + text + false + false + false + + + MarketPay Session ID + string + false + false + false + 0 + + + MarketPay Token + text + false + false + false + + + + diff --git a/metadata/MarketPay-Headless/meta/system-objecttype-extensions.xml b/metadata/MarketPay-Headless/meta/system-objecttype-extensions.xml new file mode 100644 index 0000000..0214e76 --- /dev/null +++ b/metadata/MarketPay-Headless/meta/system-objecttype-extensions.xml @@ -0,0 +1,70 @@ + + + + + + + string + false + false + 0 + + + MarketPay Payment Redirect URL + string + false + false + 0 + + + + + + + Organization ID + string + false + false + 0 + + + Organization ShortCode + string + false + false + 0 + + + URL where MarketPay Redirects After Failed Payment + string + true + false + 0 + + + URL where MarketPay Redirects After Successful Payment + string + true + false + 0 + + + MarketPay Terminals + text + false + false + + + + + + MarketPay + + + + + + + + + diff --git a/metadata/MarketPay-Headless/meta/~$stom-objecttype-definitions.xml b/metadata/MarketPay-Headless/meta/~$stom-objecttype-definitions.xml new file mode 100644 index 0000000..984a40e Binary files /dev/null and b/metadata/MarketPay-Headless/meta/~$stom-objecttype-definitions.xml differ diff --git a/metadata/MarketPay-Headless/services.xml b/metadata/MarketPay-Headless/services.xml new file mode 100644 index 0000000..06bd570 --- /dev/null +++ b/metadata/MarketPay-Headless/services.xml @@ -0,0 +1,141 @@ + + + + + + https://testgateway.altapaysecure.com/checkout/v1/api/authenticate + + + + + + https://{shortcode}.api.commercecloud.salesforce.com/custom/marketpay/v1/organizations/{orgId}/checkoutsession + + + + + + https://testgateway.altapaysecure.com/checkout/v1/api/session + + + + + + https://{shortcode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/{orgId}/oauth2/token + + + + + + https://testgateway.altapaysecure.com/checkout/v1/api + + + + + + 3000 + false + 0 + 0 + true + 0 + 0 + + + + 5000 + false + 0 + 0 + true + 0 + 0 + + + + 3000 + false + 0 + 0 + true + 0 + 0 + + + + 5000 + false + 0 + 0 + true + 0 + 0 + + + + 3000 + false + 0 + 0 + true + 0 + 0 + + + + HTTP + true + marketpay + false + true + false + int.maketpay.auth + int.maketpay.auth + + + + HTTP + true + + false + true + false + int.marketpay.custom.profile + int.marketpay.custom.credential + + + + HTTP + true + marketpay + true + true + false + int.marketpay.session + int.marketpay.session + + + + HTTP + true + + false + true + false + int.marketpay.slas.profile + int.marketpay.slas.credentials + + + + HTTP + true + + false + true + false + marketpay.profile + marketpay.credentials + + + + diff --git a/metadata/MarketPay-Headless/sites/RefArchGlobal/payment-methods.xml b/metadata/MarketPay-Headless/sites/RefArchGlobal/payment-methods.xml new file mode 100644 index 0000000..d3bfb29 --- /dev/null +++ b/metadata/MarketPay-Headless/sites/RefArchGlobal/payment-methods.xml @@ -0,0 +1,83 @@ + + + + + Credit Card + true + MARKETPAY + + + + MobilePay + true + MARKETPAY + + + + Vipps + true + MARKETPAY + + + + Klarna + true + MARKETPAY + + + + iDEAL + true + MARKETPAY + + + + ViaBill + true + MARKETPAY + + + + Swish + true + MARKETPAY + + + + Bancontact + true + MARKETPAY + + + + Bank Payment + true + MARKETPAY + + + + Twint + true + MARKETPAY + + + + Trustly + true + MARKETPAY + + + + Przelewy24 + true + MARKETPAY + + + + PayPal + true + MARKETPAY + + + + diff --git a/metadata/MarketPay-Headless/sites/RefArchGlobal/payment-processors.xml b/metadata/MarketPay-Headless/sites/RefArchGlobal/payment-processors.xml new file mode 100644 index 0000000..cd65a66 --- /dev/null +++ b/metadata/MarketPay-Headless/sites/RefArchGlobal/payment-processors.xml @@ -0,0 +1,14 @@ + + + + + MarketPay online credit card authorization (test and production systems). + + + + + + + + + diff --git a/metadata/Metadata_Valitor.xml b/metadata/SFRA/Metadata_Valitor.xml similarity index 100% rename from metadata/Metadata_Valitor.xml rename to metadata/SFRA/Metadata_Valitor.xml diff --git a/metadata/valitor_sfra_metadata.xml b/metadata/SFRA/valitor_sfra_metadata.xml similarity index 100% rename from metadata/valitor_sfra_metadata.xml rename to metadata/SFRA/valitor_sfra_metadata.xml diff --git a/metadata/valitor_sfra_paymentmethods.xml b/metadata/SFRA/valitor_sfra_paymentmethods.xml similarity index 100% rename from metadata/valitor_sfra_paymentmethods.xml rename to metadata/SFRA/valitor_sfra_paymentmethods.xml diff --git a/metadata/valitor_sfra_webservice.xml b/metadata/SFRA/valitor_sfra_webservice.xml similarity index 100% rename from metadata/valitor_sfra_webservice.xml rename to metadata/SFRA/valitor_sfra_webservice.xml diff --git a/metadata/int_marketpay_headless/int_marketpay_headless_metadate.xml b/metadata/int_marketpay_headless/int_marketpay_headless_metadate.xml deleted file mode 100644 index 3ed817e..0000000 --- a/metadata/int_marketpay_headless/int_marketpay_headless_metadate.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - Marketpay Password - This field is used to store the password of the market pay terminals. - string - false - false - 0 - - - Marketpay Username - This field is used to store the username of the market pay terminals. - string - false - false - 0 - - - Organization ID - This field is used to store the organization ID. - string - false - false - 0 - - - Organization Short Code - This field is used to store the organization's short code. - string - false - false - 0 - - - - - Marketpay - - - - - - - - diff --git a/metadata/int_marketpay_headless/int_marketpay_headless_services.xml b/metadata/int_marketpay_headless/int_marketpay_headless_services.xml deleted file mode 100644 index eca81ea..0000000 --- a/metadata/int_marketpay_headless/int_marketpay_headless_services.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - Service URL - - ***************************************** - - - - Service URL - - ***************************************** - - - - 1000 - false - 0 - 0 - true - 0 - 0 - - - - 1000 - false - 0 - 0 - true - 0 - 0 - - - - HTTP - true - marketpay - true - true - false - int.maketpay.auth - int.maketpay.auth - - - - HTTP - true - marketpay - true - true - false - int.marketpay.session - int.marketpay.session - - - diff --git a/wiki-composable-storefront.md b/wiki-composable-storefront.md deleted file mode 100644 index 05d26db..0000000 --- a/wiki-composable-storefront.md +++ /dev/null @@ -1,141 +0,0 @@ -# AltaPay for Salesforce Composable Storefront PWA - -This plugin enables **AltaPay** as the Payment Service Provider (PSP) for storefronts using the Salesforce Composable Storefront PWA. - -**Table of Contents** - -[Prerequisites](#prerequisites) - -[Installation](#installation) - -- [Upload Cartridge](#upload-cartridge) - -- [Business Manager Configuration](#business-manager-configuration) - -- [Import MarketPay Data](#import-marketpay-data) - -[Configuration](#configuration) - -- [Credentials](#credentials) - -- [Services Configuration](#services-configuration) - -## Prerequisites - -Before configuring the cartridges, you need the below information. These can -be provided by AltaPay. - -1. AltaPay credentials: - - Username - - Password -2. AltaPay gateway information: - - AltaPay Terminal Configuration JSON: Used for mapping payment methods in Salesforce to terminals in the AltaPay payment gateway. - - Gateway - -> **Note:** -> If the API user credentials have not yet been created, refer to the [Creating a New API User](#creating-a-new-api-user) section for step-by-step instructions. - -## Installation - -Download the AltaPay for Salesforce Commerce Cloud `int_marketpay_headless` cartridge from [Github](https://github.com/AltaPay/plugin-salesforce) for headless implementation. - -Then follow the steps below to complete the installation. - -### Upload Cartridge - -1. Open the **int_marketpay_headless** cartridge into the VS Code. - -2. Add your sandbox credentials to `dw.json` file. - -3. Open the Prophet extension. - -3. Click on the `Prophet: Clean Project/Upload all` from CARTRIDGES window. - -4. The Cartridge extension will upload cartridges to the linked sandbox. - -### Business Manager Configuration - -1. Log into the **SFCC Business Manager** on your sandbox. - -2. Navigate to: **Administration** > **Manage Sites** > **[store front site]** > **settings** tab. - -9. Add **int_marketpay_headless** cartridge in the cartridge path and click the **Apply** button. - - ![cartridge_path](docs/headless/cartridge_path.png) - -### Import MarketPay Data - -1. Go to `metadata/MarketPay-Headless/sites` and rename `RefArchGlobal` to match your site ID. - -2. Zip `MarketPay-Headless` folder. - -3. From the SFCC Business Manager: Navigate to: **Administration** > **Site Development** > **Site Import & Export**. - - ![import_site_data.png](docs/headless/import_site_data.png) - -4. Choose file from the **Import** section, click the **Upload** link or button. - -Once the import is successful, all the necessary fields will be created in Salesforce to allow our solution to function. You will have configuration fields to set up the environment, as well as payment data fields that help you understand the payment status and provide information necessary for debugging. - -## Configuration - -From the SFCC Business Manager: - -1. Select your site from the list in the top navigation bar. - -2. Navigate to: **Merchant Tools** > **Site Preferences** > **Custom Preferences** > **MarketPay**: - -3. This is where the merchant can access and configure the MarketPay integration. - -4. Fill out the settings as desired. Descriptions of the site preferences are listed in the tables below. - - ### Credentials - Use the following preferences to configure your AltaPay **credentials**. - - | **Preference** | **Description** | - |----------------|-----------------| - | **MarketPay Terminals / Payment Methods** | Mapping of payment methods in Salesforce and terminals in the MarketPay payment gateway.
A terminal can only contain one payment method and one currency, However, it is possible to use the same terminal for multiple payment methods. By sharing your requirements with MarketPay, we will ensure that the correct configuration and JSON mapping are created to meet your needs.
![terminals.png](docs/headless/terminals.png)
The setting must be structured as shown in the screen illustration.
The attribute **id** must correspond with the payment method added in: **Merchant Tools** > **Ordering** > **Payment Methods** plus the preferred currency. The attribute **name** is the name and identifier of the MarketPay terminal. The attribute **allowedlocales** defines which locales that can use the terminal.

**Note:** The JSON file containing the terminal configuration will be provided by AltaPay. This file defines the mapping between Salesforce payment methods and the terminals configured in the MarketPay payment gateway. | - | **Organization ID** | To find the organization ID in Business Manager, click App Launcher and then select Administration > Site Development > Salesforce Commerce API Settings. Example: `f_ecom_zzdc_001` | - | **Organization ShortCode** | To find your short code in Business Manager, click App Launcher and then select Administration > Site Development > Salesforce Commerce API Settings. Example: `kv7kzm78` | - - --- - - | **Preference** | **Description** | - |----------------|-----------------| - | **URL where MarketPay Redirects After Successful Payment** | Any valid URL. | - | **URL where MarketPay Redirects After Failed Payment** | Any valid URL. | - -### Services Configuration - -From the SFCC Business Manager: - -1. Navigate to: **Administration** > **Operations** > **Services** > **Service Credentials** - -2. Click on **int.marketpay.auth**. - -3. Update the URL, replace `https://testgateway.altapaysecure.com` with your gateway URL, and enter Gateway API user and Password and click **Apply** button. - -4. Click on **int.marketpay.slas.credentials**. - -5. Enter the SLAS User and Password and click **Apply** button. - -## Creating a New API User - -To create a new API user in your AltaPay account, please follow these steps: - -- Log in to your AltaPay account. -- From the left menu, navigate to **Settings** > **API Keys**. - - ![api_keys](docs/api_keys.png) - -- Click on the **Create New API Key** button from top right corner. -- Fill in the required fields: - - **Your current password** - - **Username** - - **Password** - - **Assign Shops** - - ![api_key](docs/create_api_key.png) -- After entering the details, click **Create**. - -The new credentials can now be used as the API Login and API Password in the AltaPay API Login section. \ No newline at end of file