+ Start a Discussion
Prashant Bhatt 2Prashant Bhatt 2 

Cybersource silent order API integration

Hi Guys, 

Has anyone tried Cybersource silent order API integration with Salesforce? Looking for some help.
Thanks in advance.

Thanks,
Prashant.
Nabeela Mubarak 9Nabeela Mubarak 9
Hi Prashant,

   Did you find solution for the above. We are looking for the same.Please help if you knew the solution
Prashant Bhatt 2Prashant Bhatt 2
Hi Nabeela, 
Yes i had implmented it  last month. I have used couple of references to get the good understading.
Look at the reply by moggy to below thread.I  have used the same approach except i havent created my own page where customer needs to fill card details etc rather i posted eveything to cybersource server. 
https://developer.salesforce.com/forums/?id=906F00000008zDzIAI (http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_Hosted_Checkout/Secure_Acceptance_Hosted_Checkout.pdf)

Also below cybersource document was a big help too-
http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_Hosted_Checkout/Secure_Acceptance_Hosted_Checkout.pdf

Give it a try and let me know how it goes. 

Thanks,
Prashant.
Tom LinTom Lin

I followed moggy's guide, but I'm facing the error message of "You are not authorized to view this page. The transaction has not been processed."

Can anyone kindly advise where I went wrong in my code?

<apex:page id="CSPayment" showHeader="false" controller="CyberSourcePaymentController" title="Application Payment">
    <apex:define name="header">
        <center>
            <apex:form id="theInput" >
                <apex:panelGrid columns="2" style="margin-top:1em;">
                    <apex:outputLabel value="Amount" for="amount"/>
                    <apex:inputText required="true" id="amount" value="{!amount}" disabled="{!enabled==false}"/>
                    <apex:outputLabel value="Currency" for="curren"/>
                    <apex:inputText required="true" id="curren" value="{!curren}" disabled="{!enabled==false}"/>           
                </apex:panelGrid> 
                <apex:outputPanel id="testout" rendered="{!enabled==true}">
                    <apex:outputText value="{!enabled}"/>
                    <apex:panelGrid columns="1" style="margin-top:2em;">
                        <apex:commandButton id="visa" image="/img/icon/cash16.png" action="{!visa}" disabled="{!enabled==false}" />
                    </apex:panelGrid> 
                </apex:outputPanel>  
            </apex:form>
        </center>
    </apex:define>
    <apex:outputPanel id="testcard" rendered="{!enabled==false}">
        <apex:define name="body" >  
            <center>
                <form id="theForm" action="{!endp}" method="post">
                    <apex:panelGrid columns="1" style="margin-top:1em;">
                        <apex:image id="visa" value="/img/icon/cash16.png"/>
                        <div>
                            <apex:outputText escape="false" value="{!ParametersValues}"/>
                        </div>
                        <!--<apex:outputText escape="false" value="{!ParametersValuesHidden}"/>-->
                        <apex:outputText escape="false" value="{!SignedData}"/>
                    </apex:panelGrid>
                    <apex:panelGrid columns="2" style="margin-top:1em;">
                        <input type="hidden" name="card_type" value="{!card_type}"/>
                        <span>Card Number     :</span><input type="text" name="card_number"/><br/>
                        <span>Card Expiry Date:</span><input type="text" name="card_expiry_date"/><span> format MM-YYYY</span><br/>
                        <input type="submit" id="submit" value="Confirm "/>  
                    </apex:panelGrid> 
                    <br/>
                </form>
            </center>
        </apex:define>
    </apex:outputPanel>
</apex:page>
 
public with sharing class CyberSourcePaymentController {
    
    private Map<String,String> oPassedParams = new Map<String,String>();
    
    private String access_key = getHOLO();
    private String transaction_uuid = getUUID();
    private String signed_field_names = 'access_key,profile_id,transaction_uuid,signed_field_names,unsigned_field_names,signed_date_time,locale,transaction_type,reference_number,amount,currency,payment_method';
    private String signed_date_time = getUTCDateTime();
    private String locale = 'en';
    public String transaction_type {get;private set;} //either sale or sale,create_payment_token
    private String reference_number = UserInfo.getUserId();
    public String amount {get;set;}
    public String curren {get;set;}
    public String payment_method {get;set;}
    public String card_type {get;set;}
    public String card_number {get;set;}
    public String card_expiry_date {get;private set;}
    private Boolean enabled = true;
    private String ccSelection;
    
    public static String sign(Map<String, String> paramsArray)  {
        String result = sign(buildDataToSign(paramsArray), CyberSourcePaymentController.recover());
        return result;
    }
    
    public String getUTCDateTime(){
        DateTime oUTSDateTime = System.now();
        String strUTCDateTime = oUTSDateTime.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'');
        return strUTCDateTime;
    }
    
    public String getHOLO() {
        return CyberSource__c.getValues('testcenter').AccessKey__c;
    }
    
    public String getHOLO1() {
        return CyberSource__c.getValues('testcenter').ProfileId__c;
    }
    
    public String getParametersValues() {
        String result = '';
        
        for(String oKey : oPassedParams.keySet()) { 
            result += '<div>';
            result += '<input type="hidden" id="' + oKey + '" name="' + oKey + '" value="' + oPassedParams.get(oKey) + '"/>';
            result += '</div>';
        }
        return result;
    }
    
    /*public String getParametersValuesHidden() {
        String result = '';
        for(String oKey : oPassedParams.keySet()) { 
            result += '<input type="hidden" id="' + oKey + '" name="' + oKey + '" value="' + oPassedParams.get(oKey) + '"/>';
        }
        return result;
    }*/
    
    public String getSignedData() {
        String result = '';
        result += '<input type="hidden" id="signature" name="signature" value="' + CyberSourcePaymentController.sign(oPassedParams) + '"/>';
        return result;
    }
    
    public String getEndp() {
        return 'https://testsecureacceptance.cybersource.com/pay';
    }
    
    private void popParams() {
        oPassedParams.put('access_key',getHOLO());
        oPassedParams.put('profile_id',getHOLO1());
        oPassedParams.put('transaction_uuid',getUUID());
        oPassedParams.put('signed_field_names','access_key,profile_id,transaction_uuid,signed_field_names,unsigned_field_names,' +
                          'signed_date_time,locale,transaction_type,reference_number,amount,currency,payment_method');
        oPassedParams.put('unsigned_field_names','card_type,card_number,card_expiry_date'); 
        oPassedParams.put('signed_date_time',getUTCDateTime());
        oPassedParams.put('locale','en');
        oPassedParams.put('transaction_type','sale,create_payment_token');
        oPassedParams.put('reference_number',reference_number);
        oPassedParams.put('amount',amount);
        oPassedParams.put('currency',curren);  
        oPassedParams.put('payment_method',payment_method);     
    }
    
    private static String recover() {
        return CyberSource__c.getValues('testcenter').SecKey1__c + CyberSource__c.getValues('testcenter').SecKey2__c;
    }
    
    private static String commaSeparate(List<String> dataToSign) {
        String result = '';
        for(String str : dataToSign) {
            result += (result==''?'':',') + str;
        }
        return result;                         
    }
    
    private static String buildDataToSign(Map<String,String> paramsArray) {
        String[] signedFieldNames = paramsArray.get('signed_field_names').split(',');
        List<String> dataToSign = new List<String>();
        
        for(String oSignedFieldName : signedFieldNames) {
            dataToSign.add(oSignedFieldName + '=' + paramsArray.get(oSignedFieldName));
        }
        return commaSeparate(dataToSign);
    }
    
    private static String sign(String data, String secretKey) {
        String result = EncodingUtil.base64Encode(Crypto.generateMac('hmacSHA256', Blob.valueOf(data), Blob.valueOf(secretKey)));
        return result;
    }
    
    public String getUUID() {
        Blob b = Crypto.generateAesKey(128);
        String h = EncodingUtil.convertToHex(b);
        String guid = h.substring(0,8) + '-' + h.substring(8,12) + '-' + h.substring(12,16) + '-' + h.substring(16,20) + '-' + h.substring(20);        
        return guid;
    }

    public PageReference visa() {
        enabled = false;
        payment_method = 'card';
        card_type = '001';
        popParams();
        return null;
    }
    
    public Boolean getEnabled() {
        return enabled;
    }
}
WK IntegrationWK Integration
Hi Prasant/Tom,

I am alos getting same issue "You are not authorized to view this page. The transaction has not been processed.".  Please help.

Thanks.
Tom LinTom Lin
I finally figured it out!

The reason for my failure was wrong timestamp. I had to minus 8 hours (I'm based in a GMT + 8) from system.now() as the signed_date_field parameter value. 
Vipul Sharma 15Vipul Sharma 15

Hello,
we are trying to access CyberSource hosted checkout page and using tokenization in salesforce.

In salesforce we have created a custom Page asking for required fields fields like "Amount and currency fields" and once user click on submit button , page will redirect user to Cybersource hosted page. 
I was trying to access the above approaches but getting error 403 not authorized to access the page.
We have the cybersouce logins with details like profile Id,access key and secret key.
Any help in this regard will be appreciated.

Tom LinTom Lin
Hi Vipul,

I've managed to do something similar, successfully before.

I can help :).
Vipul Sharma 15Vipul Sharma 15
Hi Tom, 
Its great to see your reply, I have followed your approach till now and its nice to see you.

To give you some background, I am into a basic POC feasibility where we would like to use Cybersouce checkout hosted page to be accessed using salesforce and create a token to be used for transactions.
My page is taking just two inputs may be currency and amount fields and once these details along with signed/unsigned data are submmitted , we should be shown cybersource hosted checkout page to enter the card level details.
I have the profile Id, access key and Secret keys which I got from Cybersource testing environment. 

Going with this approach, everytime I make a submit it is giving me error 403 you are not authorized to view this page. Below is my Sample Vf and controller.
 
<apex:page showHeader="false" controller="HostedCheckoutDemo_Controller" title="Payment gateway">
  <apex:outputPanel id="testcard"  >
    <apex:define name="body" >  
      <center>
        <!--form id="theForm" action="{!endp}" method="post"-->
        <form id="theForm" action="https://testsecureacceptance.cybersource.com/token/create" method="post">
            <apex:panelGrid columns="1" style="margin-top:1em;">
                     <apex:outputtext escape="false" value="{!ParametersValues}"/>
                    <apex:outputtext escape="false" value="{!ParametersValuesHidden}"/ >
                    <apex:outputtext escape="false" value="{!SignedData}" />
                    </apex:panelGrid>
                    <apex:panelGrid columns="2" style="margin-top:1em;">
                    <input type="hidden" name="card_type" value="003"/>
                    <span>Currency :</span><input type="text" name="Currency_cust"/><br/>
                    <span>Amount   :</span><input type="text" name="Amount_cust"/><span></span><br/>
                    <input type="submit" id="submit" value="Confirm "/>  
                    </apex:panelGrid> 
                  <br/>
        </form>
     </center>
  </apex:define>
</apex:outputPanel>
</apex:page>
 
public with sharing class HostedCheckoutDemo_Controller {
public String amount {get;set;}
    public String current {get;set;}

    public static String sign(Map<string, string> paramsArray)  {
        String result = sign(buildDataToSign(paramsArray), HostedCheckoutDemo_Controller.recover());
        return result;
    }
    
     public String getUUID(){
        Blob b = Crypto.generateAesKey(128);
        String h = EncodingUtil.convertToHex(b);
        String guid = h.substring(0,8) + '-' + h.substring(8,12) + '-' + h.substring(12,16) + '-' + h.substring(16,20) + '-' + h.substring(20);       
        return guid;
    }
    
    public String getUTCDateTime(){
        DateTime oUTSDateTime = System.now();
        DateTime oUTSDateTime1 = oUTSDateTime - (8/24);
        String strUTCDateTime = oUTSDateTime1.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'');
        return strUTCDateTime;
    }
  
   public String getParametersValues(){
        String result = '';
        oPassedParams.put('access_key','61*******************78a');  //hard coded access key for now
        oPassedParams.put('profile_id','58**********************9'); //hard coded profile ID for now
        oPassedParams.put('transaction_uuid','12345TransactionId'); ////hard coded transaction ID for now
        oPassedParams.put('signed_field_names','access_key,profile_id,transaction_uuid,signed_field_names,unsigned_field_names,' +
                      'signed_date_time,locale,transaction_type,reference_number,amount,currency,payment_method');
        
       oPassedParams.put('unsigned_field_names','card_type,card_number,card_expiry_date'); 
      
        oPassedParams.put('unsigned_field_names','card_type,Currency_cust,Amount_cust'); 
        oPassedParams.put('signed_date_time',getUTCDateTime());
        oPassedParams.put('locale','en');
        oPassedParams.put('transaction_type','create_payment_token');
        oPassedParams.put('reference_number',userinfo.getuserid());
        oPassedParams.put('amount',amount);
        oPassedParams.put('currency', currency);
        oPassedParams.put('payment_method','card');
        
       for (String oKey : oPassedParams.KeySet()){ 
            result += '<div>';
            result += '<input type="hidden" id="' + oKey + '" name="' + oKey + '" value="' + oPassedParams.get(oKey) + '"/>';
            result += '</div>';
        }
        system.debug('--ParametersValues' +result);
        return result;
    }
    
    public String getParametersValuesHidden(){
        String result = '';
        for (String oKey : oPassedParams.KeySet()){ 
            result += '<input type="hidden" id="' + oKey + '" name="' + oKey + '" value="' + oPassedParams.get(oKey) + '"/>';
        }
        system.debug('--ParametersValuesHidden' + result);
        return result;
    }
    
    public String getSignedData(){
        String result = '';
        result += '<input type="hidden" id="signature" name="signature" value="' + HostedCheckoutDemo_Controller.sign(oPassedParams) + '"/>';
        system.debug('-- getSignedData' + result);
        return result;
    }
    
  
    
    private Static String recover(){
    return HostedPageDemo_CS__c.getValues('test').SecretKey1_cs__c + HostedPageDemo_CS__c.getValues('test').SecretKey2_cs__c;
   }
  
    private static String commaSeparate(List<string> dataToSign) {
        String result = '';
        for(String Str : dataToSign) {
            result += (result==''?'':',')+Str;
        }
        return result;                         
    }

    private static String buildDataToSign(Map<string,string> paramsArray) {
        String[] signedFieldNames = paramsArray.get('signed_field_names').Split(',');
        List<string> dataToSign = new List<string>();

        for (String oSignedFieldName : signedFieldNames){
             dataToSign.Add(oSignedFieldName + '=' + paramsArray.get(oSignedFieldName));
        }
        return commaSeparate(dataToSign);
    }
    
     private static String sign(String data, String secretKey) {
        String result = EncodingUtil.base64Encode(Crypto.generateMac('hmacSHA256', Blob.valueOf(data), Blob.valueOf(secretKey)));
        return result;
    }
}

​​​​​​​
Tom LinTom Lin
Hi Vipul,

No problem glad to help.

Just one issue I've spotted at first glance (sorry I don't have time today to take a closer look):

1. For transaction_type, I'm using sale. I've never used create_payment_token before and am unfamiliar with this.

Have you tried with sale?
Tom LinTom Lin
Also I noticed you seem to be overriding the value of unsigned_field_names in the map.
Tom LinTom Lin
Another issue is for this function:

getUTCDateTime() - I had to hardcode something like this to account for time difference:

    private static String getUTCDateTime() {
        DateTime oUTSDateTime = System.now().addHours(-8);
        String strUTCDateTime = oUTSDateTime.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'');
        return strUTCDateTime;
    }    
Vipul Sharma 15Vipul Sharma 15
I am really Thankful for your insights into this , that too quickly .
Yes all you suggestions were an absolute hit and I am able to access this checkout page. 
Tom LinTom Lin
No worries, glad you managed to integrate successfully :)
Vipul Sharma 15Vipul Sharma 15
Hey Tom, I thought I will bother you again.
As per my code shared above
<form id="theForm" action="https://testsecureacceptance.cybersource.com/token/create"method="post">
HTML form is submitted and we get the checkout hosted page and payment is done, using tokenization. Goes well.
But I need the response from Cybersource, how to capture it in salesforce, since the response got the token information and that will be used for subsequent transactions, I have a public salesforce page linked to response url but I believe there has to be some altercations to it.
Any pointers.... 
Tom LinTom Lin
Hi Vipul,

No worries.

Again, I must share that I am not familiar with the token endpoint. In my use case, the user selected a payment to make in a table, pressed a button, and I post the form data to redirect them to the Cybersource gateway. Certain fields like email, last name, first name, amount and currency will be autofilled. User will key in their credit card details within Cybersource.

In my case, this was the endpoint I was posting the form data to: https://testsecureacceptance.cybersource.com/pay.

Subsequently within Cybersource, there are two settings to configure: Merchant Post URL and Customer Response Page.

For production-readiness, it is good to configure Merchant Post URL kind of as a backup for intermittent loss of network connection etc. But it should still work if the following steps are performed:

1. Create public site in Salesforce
2. Create a visualforce page which will be in this public site.
3. Configure the Customer Response Page to be the URL of this visualforce page hosted within the public site.
4. Within the visualforce page, the Apex controller will be responsible for parsing the response sent back from Cybersource. In the controller constructor, you would have access to these parameters through the ApexPages.currentPage().getParameters() method.
5. Using page action (because DML action is not allowed in Apex class constructor), I perform the necessary DML I need based on the instance variables of the class whose values are populated by the constructor.
Vipul Sharma 15Vipul Sharma 15
Hey Tim tell me something you dont know.
You are amazing.
I was on track and did everything same as you mentioned (above 5 steps) what I did'nt do was to debug the ApexPages.currentPage().getParameters() .

Thanks again for your knowledge and taking me out of it.  
Tom LinTom Lin
That's great!

I'm happy to share this because it took me forever to even get past authentication. The timestamp issue killed me for awhile. And the fact that the authentication mechanism for refund is different is another painful memory.
Vipul Sharma 15Vipul Sharma 15
Hey Tim, just wanted to check if you had payer authentication 3DS 2.0 in your implementation..
Coz I am able to do using 3DS 1.0 but need to have setup using 3DS 2.0 as well where user will be shown additional authentication but there is not really good help available in documentation.
Tom LinTom Lin
Unfortunately not.. 
SAI GANESH REPAKASAI GANESH REPAKA
Hi Tom and Vipul,

I am trying to implement Cybersource Secure Hosted Checkout and facing 403(Access forbidden) error. I have followed your approach. Could you please let me know where I was wrong? Below is the code snippet thanks


VfPage:

<apex:page showHeader="false" controller="HostedCheckoutDemo_Controller" title="Payment gateway">
    <apex:outputPanel id="testcard"  >
        <apex:define name="body" >
            <center>
                <!--form id="theForm" action="{!endp}" method="post"-->
                <form id="theForm" action="https://testsecureacceptance.cybersource.com/token/create" method="post">
                    <apex:panelGrid columns="1" style="margin-top:1em;">
                        <apex:outputtext escape="false" value="{!ParametersValues}"/>
                        <!--<apex:outputtext escape="false" value="{!ParametersValuesHidden}"/>-->
                        <apex:outputtext escape="false" value="{!SignedData}" />
                    </apex:panelGrid>
                    <apex:panelGrid columns="2" style="margin-top:1em;">
                        <input type="submit" id="submit" value="Confirm "/>
                    </apex:panelGrid>
                    <br/>
                </form>
            </center>
        </apex:define>
    </apex:outputPanel>
</apex:page>

Controller:

public with sharing class HostedCheckoutDemo_Controller {
    private Map<String,String> oPassedParams = new Map<String,String>();
    public String ParametersValues = getParametersValues();
    public String SignedData = getSignedData();

    public static String sign(Map<string, string> paramsArray)  {
        String result = sign(buildDataToSign(paramsArray), HostedCheckoutDemo_Controller.recover());
        System.debug('sign result'+result);
        return result;
    }

    public String getUUID(){
        Blob b = Crypto.generateAesKey(128);
        String h = EncodingUtil.convertToHex(b);
        String guid = h.substring(0,8) + '-' + h.substring(8,12) + '-' + h.substring(12,16) + '-' + h.substring(16,20) + '-' + h.substring(20);
        return guid;
    }

    private static String getUTCDateTime() {
        DateTime oUTSDateTime = System.now().addHours(-8);
        String strUTCDateTime = oUTSDateTime.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'');
        return strUTCDateTime;
    }

    public String getParametersValues(){
        String result = '';
        oPassedParams.put('access_key','*********');  //hard coded access key for now
        oPassedParams.put('profile_id','************'); //hard coded profile ID for now
        oPassedParams.put('transaction_uuid','02815b4f08e56882751a043839b7b481'); ////hard coded transaction ID for now
        oPassedParams.put('signed' +
                '_field_names','access_key,profile_id,transaction_uuid,signed_field_names,unsigned_field_names,' +
                'signed_date_time,locale,transaction_type,reference_number,amount,currency,payment_method');

        oPassedParams.put('unsigned_field_names','card_type,card_number,card_expiry_date');
        oPassedParams.put('signed_date_time',getUTCDateTime());
        oPassedParams.put('locale','en');
        oPassedParams.put('transaction_type','create_payment_token');
        oPassedParams.put('reference_number','2432432');
        oPassedParams.put('amount','1.00');
        oPassedParams.put('currency', 'usd');
        oPassedParams.put('payment_method','card');
        /*oPassedParams.put('bill_to_forename','sai');
        oPassedParams.put('bill_to_surname', 'gan');
        oPassedParams.put('bill_to_email','sai.repaka@8x8.com');
        oPassedParams.put('bill_to_address_line1','gfdsgdfg');
        oPassedParams.put('bill_to_address_city','dfgsgsd');
        oPassedParams.put('bill_to_address_postal_code', '12345');
        oPassedParams.put('bill_to_address_state','ca');
        oPassedParams.put('bill_to_address_country','usa');
        oPassedParams.put('card_type','001');
        oPassedParams.put('card_number', '4111111111111111');
        oPassedParams.put('card_cvn','005');*/

        for (String oKey : oPassedParams.KeySet()){
            result += '<div>';
            result += '<input type="hidden" id="' + oKey + '" name="' + oKey + '" value="' + oPassedParams.get(oKey) + '"/>';
            result += '</div>';
        }
        system.debug('--ParametersValues' +result);
        return result;
    }

   /*public String getParametersValuesHidden(){
        String result = '';
        for (String oKey : oPassedParams.KeySet()){
            result += '<input type="hidden" id="' + oKey + '" name="' + oKey + '" value="' + oPassedParams.get(oKey) + '"/>';
        }
        system.debug('--ParametersValuesHidden' + result);
        return result;
    }*/

    public String getSignedData(){
        String result = '';
        result += '<input type="hidden" id="signature" name="signature" value="' + HostedCheckoutDemo_Controller.sign(oPassedParams) + '"/>';
        system.debug('-- getSignedData' + result);
        return result;
    }



    private Static String recover(){
        return '**************';
    }

    private static String commaSeparate(List<string> dataToSign) {
        String result = '';
        for(String Str : dataToSign) {
            result += (result==''?'':',')+Str;
        }
        return result;
    }

    private static String buildDataToSign(Map<string,string> paramsArray) {
        String[] signedFieldNames = paramsArray.get('signed_field_names').Split(',');
        List<string> dataToSign = new List<string>();

        for (String oSignedFieldName : signedFieldNames){
            dataToSign.Add(oSignedFieldName + '=' + paramsArray.get(oSignedFieldName));
        }
        return commaSeparate(dataToSign);
    }

    private static String sign(String data, String secretKey) {
        String result = EncodingUtil.base64Encode(Crypto.generateMac('hmacSHA256', Blob.valueOf(data), Blob.valueOf(secretKey)));
        return result;
    }
}
venkat rvenkat r
Hi Tom and Vipul
I have previously coded the cybersource payment functionality in .NET and it works fine. Now I need to move it to salesforce. I created a VF page form with amount and 2 custom fields. I followed the exact steps outlined in your code and I am getting the "You are not authorized to view this page. The transaction has not been processed". I was told by cybersource that it could be: access_key, security_key, missing signed-values, missing required fields and incorrect signed_date_time_stamp. I don't think any of them are valid in my case. I changed the signed_date_time_stamp value but it does not seem to work. I am in EST and behind UTC by 4 hours, I even tried: dateTime.now()-4,dateTime.now()+4, dateTime.now() etc but nothing works. 

Whan am I missing ??

 
venkat rvenkat r
here's the response from cybersource:
Here is the error from the server: Request Processing Error - Reference: [E-C15017F3DD7E484EBD918CAFE5B0F605] Exception Class: [TransactionSecurityError] Exception Message: [Merchant supplied signature  does not match generated signature] Here are some things you should check. Some things the merchant should check:     - Invalid/Missing Merchant ID         - Blank, or greater than thirty (30) characters in length     - Invalid/Missing Profile ID         - Blank, or less than seven (7) characters in length         - Can be any value; no longer a required field     - Invalid/Missing/Expired Secret and/or Access Key     - Supplied signature does not match the generated one     - The supplied signed_field_name parameter value is missing a mandatory signed_field     - An empty field name (,,) was in the supplied signed_field_name or unsigned_field_name parameter values     - The supplied signed_date_time parameter value was not in a valid UTC xml format (end in Z)

 
Vipul Sharma 15Vipul Sharma 15
Hi Venkat,
The approach followed by me was cybersource secure acceptance hosted payment page (HPP) which make use of http post and passing all the relevant information to cybersource for authorization and verification.

I will suggest you two approaches  :

1. In your case it seems to me your request is getting failed at first step itself that is the authorization during which a signature is created using the values that you send and same siganture is validated by cybsersource.
The response from cybersource is right , you need to check for the following values as they are indeed cross verified by cybersource and is needed in signature generation : 
a. Access key
b. Profile id
c. Security recovery key

2. Keep your code as it is since you mentioned it was working with .NET and try with this syntax for UTC timezone -
System.now().addHours(4) 

I hope above information helps you and it works smoothly.
venkat rvenkat r
Hi Vipul Thanks for replying. It did not work with the time (adding 4 hrs). Here's my code Please let me know if I am doing anything wrong here. I have replaced access_key, profile_id and secret key with xxxxx Visualforce Page Amount to charge :
Your Department/Division/Unit:
Comments:

Controller public class Payment_Controller_Cybersource { private Map oPassedParams = new Map(); public String amount {get;set;} public static String sign(Map paramsArray) { String result = sign(buildDataToSign(paramsArray), Payment_Controller_Cybersource.recover()); return result; } public String getUUID(){ Blob b = Crypto.generateAesKey(128); String h = EncodingUtil.convertToHex(b); String guid = h.substring(0,8) + '-' + h.substring(8,12) + '-' + h.substring(12,16) + '-' + h.substring(16,20) + '-' + h.substring(20); return guid; } public static String getUTCDateTime() { //datetime oUTSDateTime = datetime.now(); datetime oUTSDateTime = System.now().addHours(4); String strUTCDateTime = oUTSDateTime.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\''); return strUTCDateTime; } public String getParametersValues(){ String result = ''; oPassedParams.put('access_key','xxxx'); oPassedParams.put('profile_id','xxxx'); oPassedParams.put('transaction_uuid',getUUID()); oPassedParams.put('signed_field_names','access_key,profile_id,transaction_uuid,signed_field_names,unsigned_field_names,signed_date_time,locale,transaction_type,reference_number,amount,currency, payment_method'); oPassedParams.put('unsigned_field_names','merchant_defined_data1,merchant_defined_data2'); oPassedParams.put('signed_date_time',getUTCDateTime()); oPassedParams.put('locale','en-us'); oPassedParams.put('transaction_type','sale'); oPassedParams.put('reference_number','100'); oPassedParams.put('amount',amount); oPassedParams.put('currency', 'USD'); oPassedParams.put('payment_method','card'); for (String oKey : oPassedParams.KeySet()){ result += '
'; result += ''; result += '
'; } system.debug('--ParametersValues' +result); return result; } public String getParametersValuesHidden(){ String result = ''; for (String oKey : oPassedParams.KeySet()){ result += ''; } system.debug('--ParametersValuesHidden' + result); return result; } public String getSignedData(){ String result = ''; result += ''; system.debug('-- getSignedData' + result); return result; } private Static String recover(){ string ss='xxxx'; //secret key return ss; } private static String commaSeparate(List dataToSign) { String result = ''; for(String Str : dataToSign) { result += (result==''?'':',')+Str; } return result; } private static String buildDataToSign(Map paramsArray) { String[] signedFieldNames = paramsArray.get('signed_field_names').Split(','); List dataToSign = new List(); for (String oSignedFieldName : signedFieldNames){ dataToSign.Add(oSignedFieldName + '=' + paramsArray.get(oSignedFieldName)); } return commaSeparate(dataToSign); } private static String sign(String data, String secretKey) { String result = EncodingUtil.base64Encode(Crypto.generateMac('hmacSHA256', Blob.valueOf(data), Blob.valueOf(secretKey))); return result; } } Thanks for your help.
venkat rvenkat r
will report code again, it did not display as expected
venkat rvenkat r
Here's my code

Visualforce Page

<apex:page showHeader="false" controller="Payment_Controller_Cybersource" title="Payment gateway">
  <apex:outputPanel id="testcard">
    <apex:define name="body">  
      <center>
          <form id="theForm" action="https://secureacceptance.cybersource.com/pay" method="post">
            <apex:panelGrid columns="1" style="margin-top:1em;">
                   <apex:outputtext escape="false" value="{!ParametersValues}"/>
                   <apex:outputtext escape="false" value="{!ParametersValuesHidden}" />
                   <apex:outputtext escape="false" value="{!SignedData}" />
             </apex:panelGrid>

              <apex:panelGrid columns="2" style="margin-top:1em;">                   
                    <span>Amount to charge   :</span><input type="text" name="amount" /><br/>
                    <span><b>Your Department/Division/Unit:</b></span><input type="text" name="merchant_defined_data1" /><br/>
                    <span><b>Comments:</b></span><input type="text" name="merchant_defined_data2"  /><br/>
                  <input type="submit" id="submit" value="Confirm "/>  
                    </apex:panelGrid> 
                  <br/>
        </form>
     </center>
  </apex:define>
</apex:outputPanel>
</apex:page>

Controller
public class Payment_Controller_Cybersource {
private Map<String,String> oPassedParams = new Map<String,String>();
public String amount {get;set;}


    public static String sign(Map<string, string> paramsArray)  {
        String result = sign(buildDataToSign(paramsArray), Payment_Controller_Cybersource.recover());
       return result;

    }


     public String getUUID(){
        Blob b = Crypto.generateAesKey(128);
        String h = EncodingUtil.convertToHex(b);
        String guid = h.substring(0,8) + '-' + h.substring(8,12) + '-' + h.substring(12,16) + '-' + h.substring(16,20) + '-' + h.substring(20);       
        return guid;

    }

    public static String getUTCDateTime() {
        //datetime oUTSDateTime = datetime.now();
        datetime oUTSDateTime = System.now().addHours(4);
        String strUTCDateTime = oUTSDateTime.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'');
        return strUTCDateTime;

    }    

 

 public String getParametersValues(){
        String result = '';           
        oPassedParams.put('access_key','xxxx');   
        oPassedParams.put('profile_id','xxxx'); 
        oPassedParams.put('transaction_uuid',getUUID());         oPassedParams.put('signed_field_names','access_key,profile_id,transaction_uuid,signed_field_names,unsigned_field_names,signed_date_time,locale,transaction_type,reference_number,amount,currency, payment_method');       
        oPassedParams.put('unsigned_field_names','merchant_defined_data1,merchant_defined_data2'); 
        oPassedParams.put('signed_date_time',getUTCDateTime());
        oPassedParams.put('locale','en-us');
        oPassedParams.put('transaction_type','sale');
        oPassedParams.put('reference_number','100');
        oPassedParams.put('amount',amount);
        oPassedParams.put('currency', 'USD');
        oPassedParams.put('payment_method','card');
        
       for (String oKey : oPassedParams.KeySet()){ 
            result += '<div>';
            result += '<input type="hidden" id="' + oKey + '" name="' + oKey + '" value="' + oPassedParams.get(oKey) + '"/>';
            result += '</div>';
        }

        system.debug('--ParametersValues' +result);
        return result;

    }

    

    public String getParametersValuesHidden(){
        String result = '';
        for (String oKey : oPassedParams.KeySet()){ 
            result += '<input type="hidden" id="' + oKey + '" name="' + oKey + '" value="' + oPassedParams.get(oKey) + '"/>';
        }
        system.debug('--ParametersValuesHidden' + result);
        return result;

    }
    
    public String getSignedData(){
       String result = '';
        result += '<input type="hidden" id="signature" name="signature" value="' + Payment_Controller_Cybersource.sign(oPassedParams) + '"/>';
        system.debug('-- getSignedData' + result);
        return result;
    }
    

    private Static String recover(){
    string ss='xxxx';  //secret key
     return ss;
    }

  

    private static String commaSeparate(List<string> dataToSign) {
        String result = '';
        for(String Str : dataToSign) {
            result += (result==''?'':',')+Str;
        }
        return result;                         

    }


    private static String buildDataToSign(Map<string,string> paramsArray) {
        String[] signedFieldNames = paramsArray.get('signed_field_names').Split(',');
        List<string> dataToSign = new List<string>();

        for (String oSignedFieldName : signedFieldNames){
             dataToSign.Add(oSignedFieldName + '=' + paramsArray.get(oSignedFieldName));
        }
        return commaSeparate(dataToSign);
    }

     private static String sign(String data, String secretKey) {
        String result = EncodingUtil.base64Encode(Crypto.generateMac('hmacSHA256', Blob.valueOf(data), Blob.valueOf(secretKey)));
        return result;

    }

}
Vipul Sharma 15Vipul Sharma 15
Okay, one issue is see it with the UI field mapping, their values are not set .
Can you set hard coded value for amount as of now, and see the outcome.
 oPassedParams.put('amount','111');
Vipul Sharma 15Vipul Sharma 15
1. Another thing to be noticed is you are using production live end point, shouldn't it be that of sandbox ?
2. Have you configured secure acceptance HPP profile correctly in your cybersource environment.
venkat rvenkat r
Hi Vipul
I tried hard-coding amount and pointing end point to test. Both don't work.
I have not changed the secure acceptance HPP profile as my current .NET payment site is working as expected. I am only setting value for this 
Custom Redirect After Checkout and this should not matter as I am not saving any return values from the postback.
Originally when I set this up in .NET, it was very quick and easy but there seems to be something wrong in my apex code with the response values that's causing the error.  


 
venkat rvenkat r
Hi Vipul

I sent the error code to cybersource and they told me to fix the payment_method since I was not sending that value. There was an extra space in the signed_field_names before the payment_method. I fixed that and got the error again. I sent the error to cybersource and here's their response:

The error code you submitted returned the following error from our server: Request Processing Error - Reference: [E-A308FA76B66C4A7C81A02A2839BE78A3] Exception Class: [TransactionSecurityError] Exception Message: [Merchant supplied signature hqxc6SIZs7caWZQmjF3M4SekBM2tWkgtGd2oe66ABc8= does not match generated signature 9YH7MtzWLhIMNzFsxnBcvVbItm4uOFo7W7IUDxgv440=] Please note, this error is the result of the merchant application creating a signature that is different from the system generated signature. This can only be resolved by correcting the merchant application. We haven't any more information than I have provided in the above error message since we cannot reverse the signature to see what fields do not match. This will require your team to study your application to identify the fields that are incorrect and edit them.
venkat rvenkat r
Hi Vipul

Just realized I was passing the merchant_id (6 digit) vs the 36 digit profile_id  for the Profile_id request field. I fixed my code but still get the same error
venkat rvenkat r
Hi Vipul

My issue is resolved. The sample code provided here creates the signature before I enter the amount. The amount will never match with the amount in the signature. I had to capture the amount and then create the signature with the amount before posting to cybersource. 
Thanks for the help 
venkat rvenkat r
Hi Guys

Just when I thought I had all of this wrapped up, another issue has come up. I was able to get to the hosted payment page from my visualforce page by clicking the preview button. 
I then created a public force.com site and added the visualforce page to the site. When I go through the sites page,  I get the same error as before:
"You are not authorized to view this page. The transaction has not been processed". 

Any idea what's different about the force.com site page access
venkat rvenkat r
The System.now() is different between a visualforce page on a force.com site vs internal visualforce page. The difference is 4 hrs. Force.com sites is 4 hrs ahead of local time. I had to adjust my time accordingly.

For guys out there needing help with cybersource integration, please do not hesitate to contact me. I learned a lot from this.