b2b-commerce-on-lightning-quickstart icon indicating copy to clipboard operation
b2b-commerce-on-lightning-quickstart copied to clipboard

Payeezy Integration results in 403

Open jonathanwiesel opened this issue 5 years ago • 2 comments

Using the supplied example for Payeezy and creating a new account in Payeezy to use the apiKey and merchant token respectively results in the following response:

{
    "code": "403",
    "message": "HMAC validation Failure"
}

jonathanwiesel avatar Jan 25 '21 12:01 jonathanwiesel

Looking at the docs seems the following header values are missing:

nonce
timestamp
Authorization (HMAC that is failing to verify)

jonathanwiesel avatar Jan 25 '21 12:01 jonathanwiesel

Create an HMAC you need the same body information inside of your header encode. I passed the card number and cvv inside additional data so that I could access it inside of my header and build my body properly. This is not an ideal solution. Salesforce/B2B needs to allow for access of the payment data merge fields inside of the header for gateway implementations like this.

/**
 * Function to set authorization headers in a request
 * Use this to add authorization parameters required for the request
 */
protected void setAuthorizationHeaders(commercepayments.PaymentMethodTokenizationRequest tokenizationRequest, HttpRequest req){
    req.setHeader('Content-Type', 'application/json');
    //Username and password stored in the named credential that is linked with the payment gateway record
    String apiKey = 'apiKey';
    req.setHeader('apikey',apiKey); //'{!$Credential.Username}');

    String token = 'token';
    req.setHeader('token',token); //'{!$Credential.Password}');

    String nonce = generateNonce();
    req.setHeader('nonce', nonce);

    String timestamp = generateTimestampInMilliseconds();
    req.setHeader('timestamp', timestamp);


    String payload = mergePayloadFields(req.getBody(),(Map<String, String>) tokenizationRequest.additionalData);

    String apiSecret = 'apiSecret';

    req.setHeader('Authorization', generateHMAC(apiKey,nonce,timestamp,token,payload,apiSecret));
}

/*
Find and replace values in request payload to be transferred to encoded in the header
 */
private String mergePayloadFields(String payload, Map<String, String> additionalData){

    for (String key : additionalData.keySet()){
        if (payload.contains(key)){
            payload = payload.replace(key,additionalData.get(key));
        }
    }
    System.debug(payload);
    return payload;
}

private String generateNonce(){
    String hashString = '1000' + String.valueOf(Datetime.now().formatGmt('yyyy-MM-dd HH:mm:ss.SSS'));
    Blob hash = Crypto.generateDigest('MD5', Blob.valueOf(hashString));
    String hexDigest = EncodingUtil.convertToHex(hash);
    return hexDigest;
}

private String generateTimestampInMilliseconds(){
    return String.valueOf(Datetime.now().getTime());
}

private String generateHMAC(String apiKey, String nonce, String timestamp, String token, String payload, String apiSecret){
    String data = apiKey + nonce + timestamp + token + payload;
    Blob privateKeyBlob = Blob.valueOf(apiSecret);
    Blob urlBlob = Blob.valueOf(data);
    Blob signatureBlob = Crypto.generateMac('HmacSHA256', urlBlob, privateKeyBlob);
    String str=EncodingUtil.convertToHex(signatureBlob);
    System.debug(str);
    Blob b=Blob.valueOf(str);
    String hmac = EncodingUtil.base64Encode(b);
    System.debug('signature is : ' +hmac);
    return hmac;
}

walters954 avatar Jul 29 '21 15:07 walters954