Integrating CC Avenue with Salesforce Commerce Cloud

CC Avenue Integration with SFCC

Integrating CC Avenue with Salesforce Commerce Cloud (SFCC) involves several steps. CC Avenue, a popular payment gateway, seamlessly processes online payments when integrated with SFCC. Here is a comprehensive guide on how to integrate CC Avenue with SFCC

payment gateway integration

1. Obtain CC Avenue Credentials:

Before initiating the integration, sign up with CC Avenue and acquire essential credentials such as Merchant ID, Access Code, and Encryption Key (Working Key).

2. Configure CC Avenue in Business Manager:

  1. Log in to your SFCC Business Manager.
  2. Navigate to "Merchant Tools" > "Site Preferences" > "Payment Methods."
  3. Add a new payment method for CC Avenue.
  4. Enter relevant information, including Merchant ID, Access Code, and Encryption Key from CC Avenue.
  5. Configure other settings like payment flow, methods, and currencies as needed.

3. Set Up Payment Processing:

Configure the payment processing pipeline to include CC Avenue by setting up the appropriate payment processor and linking it to the CC Avenue payment method.

4. Code Changes:

Now, let's delve into the coding part. In this session, we will use the Non-seamless process, utilizing the Billing page provided by CC Avenue.
  1. Call the Handle hook to create a payment instrument in the basket.
  2. Create the order using COHelpers.createOrder(currentBasket).
  3. Submit the order data to CC Avenue from the front end; build the payload as per CC Avenue documentation. 
    API(transactionURL): https://test.ccavenue.com/transaction/transaction.do?command=initiateTransaction

    a) EncRequest before encryption
    'use strict';
    
    function PaymentRequest(order, merchantId, language, options) {
        const { /** @type {dw.web.URLUtils} */ URLUtils } = dw.web;
        const safeOptions = options || {};
    
        this.merchant_id = merchantId;
        this.order_id = order.orderNo;
        this.currency = order.currencyCode;
        this.amount = order.totalGrossPrice.value;
        this.customer_identifier = customer.ID;
        this.billing_email = order.customerEmail;
        const { billingAddress } = order;
        this.billing_name = billingAddress.fullName;
        this.billing_address = billingAddress.address1;
        this.billing_city = billingAddress.city;
        this.billing_state = billingAddress.stateCode;
        this.billing_zip = billingAddress.postalCode;
        this.billing_country = billingAddress.countryCode && billingAddress.countryCode.value === 'US' ? 'United States' : 'India';
        this.billing_tel = billingAddress.phone;
        const { shippingAddress } = order.defaultShipment;
        this.delivery_name = shippingAddress.fullName;
        this.delivery_address = shippingAddress.address1;
        this.delivery_city = shippingAddress.city;
        this.delivery_state = shippingAddress.stateCode;
        this.delivery_zip = shippingAddress.postalCode;
        this.delivery_country = shippingAddress.countryCode && shippingAddress.countryCode.value === 'US' ? 'United States' : 'India';
        this.delivery_tel = shippingAddress.phone;
        this.language = language;
        
        this.redirect_url = safeOptions.redirectURL ? URLUtils.abs(safeOptions.redirectURL).toString() : URLUtils.abs('CheckoutServices-PlaceOrder').toString();
        this.cancel_url = safeOptions.cancelURL ? URLUtils.abs(safeOptions.cancelURL).toString() : URLUtils.abs('CheckoutServices-PlaceOrder').toString();
    
        this.toRequestString = () => {
            let string = '';
            Object.keys(this).forEach(key => {
                if (this[key] && typeof this[key] !== 'function') {
                    string += key + '=' + encodeURIComponent(this[key]).replace('%20', '+') + '&';
                }
            });
            return string.substr(0, string.lastIndexOf('&'));
        };
    };
    
    module.exports = PaymentRequest;
    


    b) Encrypt the above request data
    "use strict";
    
    const encryptionUtils = {};
    var { Cipher, Encoding } = dw.crypto;
    var preferences = require("*/cartridge/config/preferences");
    
    /**
     * Function to get the key from the hex encoded working key using MD5 hash
     * @param {string} workingKey - The working key to be converted
     * @returns {string} The key
     */
    const getKey = (workingKey) => {
        const {
            /** @type {dw.crypto.Encoding} */ Encoding,
            /** @type {dw.crypto.WeakMessageDigest} */ WeakMessageDigest,
        } = dw.crypto;
        const { /** @type {dw.util.Bytes} */ Bytes } = dw.util;
        return Encoding.toBase64(
            new WeakMessageDigest("MD5").digestBytes(new Bytes(workingKey))
        );
    };
    
    /**
     * Function to encrypt the provided plain text using the provided algorithm or AES algorithm in CBC mode
     * with PKCS5Padding by default and return the encrypted value in hex encoding.
     * @param {string} plainText - The text that has to be encrypted
     * @param {Object|null} options - The object containing overrides for default option.
     * @returns {string|null} - The hex encoded encrypted text or null in case of any errors.
     */
    encryptionUtils.encrypt = (plainText, options) => {
        const workingKey = preferences.ccavenueWorkingKey;
        var iv = dw.crypto.Encoding.toBase64(
            new dw.util.Bytes(
                "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
            )
        );
        const cipher = new Cipher();
        var enKey = getKey(workingKey);
        return Encoding.toHex(
            Encoding.fromBase64(
                cipher.encrypt(
                    plainText,
                    enKey,
                    "AES/CBC/PKCS5Padding",
                    iv,
                    Number(10)
                )
            )
        );
    };
    
    /**
     * Function to decrypt the provided encrypted text using the provided algorithm or AES algorithm in CBC mode
     * with PKCS5Padding by default and return the decrypted value.
     * @param {string} encryptedText - The text that has to be decrypted in hex encoding
     * @param {Object|null} options - The object containing overrides for default option.
     * @returns {string|null} - The UTF-8 encoded plain text or null in case of any errors.
     */
    encryptionUtils.decrypt = (encryptedText) => {
        const cipher = new Cipher();
        const workingKey = preferences.ccavenueWorkingKey;
        const key = getKey(workingKey);
        var iv = dw.crypto.Encoding.toBase64(
            new dw.util.Bytes(
                "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
            )
        );
        return cipher.decrypt(
            Encoding.toBase64(Encoding.fromHex(encryptedText)),
            key,
            "AES/CBC/PKCS5Padding",
            iv,
            Number(10)
        );
    };
    
    module.exports = encryptionUtils;
    


    c) Payload
    //create payment details like encryption object req for cc avenue
    function getPaymentFormDetails(order, currentLanguage) {
        const encryptionUtils = require("*/cartridge/scripts/util/encryptionUtils");
        const PaymentRequestModel = require("*/cartridge/models/paymentRequest");
        var merchantID = preferences.ccAvenueMerchantID;
        const paymentRequest = new PaymentRequestModel(
            order,
            merchantID,
            currentLanguage
        );
        var paymentRequestStr = paymentRequest.toRequestString();
        const encryptedText = encryptionUtils.encrypt(paymentRequestStr, null);
        if (encryptedText) {
            session.privacy.orderToken = order.orderToken;
            session.privacy.orderNo = order.orderNo;
            return {
                paymentRequest: paymentRequest,
                encryptedText: encryptedText,
                accessCode: preferences.ccavenueAccessKey,
                transactionURL: preferences.ccAvenueTransactionURL,
            };
        }
        return null;
    }
    


    d) Submit form from client side 
    var transactionURL = data.ccAvenueRes.transactionURL;
    var encryptedText = data.ccAvenueRes.encryptedText;
    var accessCode = data.ccAvenueRes.accessCode;
    function submitForm(transactionURL, encryptedText, accessCode) {
        // Create a new form element
        var form = document.createElement("form");
        form.method = "post";
        form.action = transactionURL;
    
        // Create and append hidden input fields to the form
        var encRequestInput = document.createElement("input");
        encRequestInput.type = "hidden";
        encRequestInput.name = "encRequest";
        encRequestInput.value = encryptedText;
        form.appendChild(encRequestInput);
    
        var accessCodeInput = document.createElement("input");
        accessCodeInput.type = "hidden";
        accessCodeInput.name = "access_code";
        accessCodeInput.value = accessCode;
        form.appendChild(accessCodeInput);
    
        // Append the form to the document and submit it
        document.body.appendChild(form);
        form.submit();
    }
    
    submitForm(transactionURL, encryptedText, accessCode);
    


  4. After submitting the data as a payload, a new tab opens for billing information.

    cc avenue billing form

  5. Users provide billing details, make the payment, and are redirected to the specified redirect_url to load the billing form. The redirect_url can be any controller (e.g., place-order controller) to process the payment response.
  6. In this controller, decrypt the response and read the payment status using the decryption logic provided in the code snippet.
  7. If the payment status is OK, call the Authorize Hook, where the transaction ID and payment processor are set in the payment instrument. After executing the code, place the order.
  8. And the page will redirect to a confirmation page

5. Documentation:

Thoroughly document the integration for future reference and troubleshooting, including information about configuration, custom scripts, and specific considerations.

Important Note:

The integration process may vary based on business requirements and updates from CC Avenue or SFCC. Refer to the official documentation for both platforms and consider consulting technical experts if needed. The provided steps are general guidelines, and the specific documentation should be referred to for accurate and up-to-date information.

Comments

Popular posts from this blog

GTM for SFCC

Java Interview Questions

Javascript check if key exists in map