Nick Bechtold
Nick Bechtold

Reputation: 21

Payeezy / Firstdata Invalid signature received

I'm currently developing a site using payeezy/firstdata for payments. It has been quite a hassle integrating because their API docs are kind of weak.

I'm using ColdFusion and a cfhttp request. I've been following this to calculate my content digest and hmac hash: https://support.payeezy.com/hc/en-us/articles/203731149-API-Security-HMAC-Hash

I finally got the hashs to match the calculated hashs in the demo terminal but my problem is: I'm getting a strange error when sending the request. I get the error:

"Invalid signature received 'Fgx/lR############'."

where the first few characters change every time. Here's my code for the request:

postAction = https://api.demo.globalgatewaye4.firstdata.com/transaction/v19 key_id,hmac_value, content_digest have all been tested and are correct x_time = getIsoTimeString( now() )

<cfhttp url="#postAction#" method="POST">
            <cfhttpparam name="Authorization" type="header" value="CGGE4_API #key_id#:#hmac_value#">
            <cfhttpparam name="x-gge4-date" type="header" value="#x_time#">
            <cfhttpparam name="x-gge4-content-sha1" type="header" value="#LCase(content_digest)#">
            <cfhttpparam name="content-type" type="header" value="text/xml">
            <cfhttpparam name="accept" type="header" value="text/xml">      
            <cfhttpparam name="transaction_body" type="xml" value="#exact_xml#" />                
        </cfhttp>
<cfdump var="#cfhttp.fileContent#"><cfabort>

The submitted xml (without spaces or new lines)

<Transaction>
                <ExactID>#exact_id#</ExactID>
                <Password>#password#</Password>
                <Card_Number>#FORM.x_card_num#</Card_Number>
                <CardHoldersName>#FORM.x_first_name# #FORM.x_last_name#</CardHoldersName>
                <Transaction_Type>00</Transaction_Type>
                <Expiry_Date>#FORM.x_exp_date#</Expiry_Date>
                <DollarAmount>#amount#</DollarAmount>
                <Address>
                    <Address1>#FORM.x_address#</Address1>
                    <City>#FORM.x_city#</City>
                    <Zip>#FORM.x_zip#</Zip>
                </Address>
            </Transaction>

I've changed the authorization header to "Payeezy_Gateway_API #key_id#:#hmac_value#" and I get the error "Bad Authorization Header" when the hmac value and key id being used have been tested on the payeezy terminal many times.

Please, any help is much appreciated!

Upvotes: 2

Views: 2208

Answers (2)

Rahat Ahmed
Rahat Ahmed

Reputation: 2180

Both CGGE4_API and GGE4_API work for me when testing with their sample Python code on that support page you linked, but I called their support number and they told me I should be using GGE4_API.

Apparently the support page is outdated, and using Payeezy_Gateway_API does not work at all. Here is how the sample Python code on that page should look like:

from hashlib import sha1
from time import gmtime, strftime
import base64
import hmac
import httplib

payeezy_gateway_date = strftime("%Y-%m-%dT%H:%M:%S", gmtime()) + 'Z'
uri = '/transaction/v19'
key_id = '' # Add your key id here
key = '' # Add your HMAC key here
transaction_body = '' # Add your transaction request body here
method = 'POST'
content_digest = sha1(transaction_body).hexdigest()
content_type = 'text/xml' # Change this to 'application/json' if you're using JSON
host = 'api.demo.globalgatewaye4.firstdata.com'
headers = { 'Content-Type': content_type,
        'x-gge4-content-sha1': content_digest,
        'x-gge4-date': payeezy_gateway_date,
        'Authorization': 'GGE4_API ' + key_id + ':' + base64.b64encode(hmac.new(key, method + "\n" + content_type + "\n" + content_digest + "\n" + payeezy_gateway_date + "\n" + uri.split('?')[0], sha1).digest()) }
conn = httplib.HTTPSConnection(host)
conn.request(method, uri, transaction_body, headers)
print conn.getresponse().read()

In my application, I was also receiving the Invalid signature received. It turned out that I was adding charset=utf-8 to my Content-Type header, (credit to Leigh's comment). Removing that made my requests get accepted.

Upvotes: 2

Steven Janow
Steven Janow

Reputation: 1

I was also trying to solve this problem with Payeezy. I'm probably a day late and a dollar short, however I got this to work. I made several changes to what I was having trouble with and/or with Nick's original code:

  1. Removed the charset declaration in the transaction xml;
  2. Changed 'text' to 'application'
  3. For me to get the encryption to match the website's testing encryption, I changed the carriage return to char(10), and
  4. Changed the first header entry from 'CGGE4_API' to 'GGE_API'

Just FYI, if you're using v11, you don't need any headers or hashing code, just the xml.

Here's my entire testing code, including some code to break down the returning xml.

Request Code:

<cfset hmac_key="WO9QVjnis6eBb5oOYmA_DSShc82gteFw">
<cfset trans="<?xml version='1.0' ?><Transaction><ExactID>XX55555-55</ExactID><Password>testtest11</Password><Card_Number>5454545454545454</Card_Number><CardHoldersName>Bix Dirigible</CardHoldersName><Transaction_Type>00</Transaction_Type><Expiry_Date>0916</Expiry_Date><DollarAmount>12.03</DollarAmount></Transaction>">

<cfset key_id="555555">
<cfset content_digest=lcase(Hash(trans,"SHA"))>

<cfset curDate = Now()> 
<cfset utcDate = DateConvert("local2utc", curDate)> 
<cfset udate=dateformat(utcdate,"yyyy-mm-dd")><cfset utime=timeformat(utcdate,"HH:mm:ss")>
<cfset x_time=udate&"T"&utime&"Z" >

<cfset submitfinalhmac="POST"&chr(10)&"application/xml"&chr(10)&content_digest&chr(10)&x_time&chr(10)&"/transaction/v12">


<cfoutput>
    content_digest: #content_digest#<BR />
    <BR />

    <!--- Ben Nadel's encrypting code, http://www.bennadel.com/blog/1971-authenticating-twilio-request-signatures-using-coldfusion-and-hmac-sha1-hashing.htm --->
    <cfset secretKeySpec = createObject("java", "javax.crypto.spec.SecretKeySpec" ).init( toBinary( toBase64( hmac_key ) ), "HmacSHA1" )/>
    <cfset mac = createObject( "java", "javax.crypto.Mac" ).getInstance( "HmacSHA1" )/> 
    <cfset mac.init( secretKeySpec ) />
    <cfset encryptedBytes = mac.doFinal( toBinary( toBase64( submitfinalhmac ) )  ) /> 
    <cfset secureSignature = createObject( "java", "org.apache.commons.codec.binary.Base64" ).encodeBase64( encryptedBytes ) /> 
    <cfset hmac_value = toString( secureSignature ) />
    #hmac_value#
    <BR /><BR />
 </cfoutput>    

 <cfhttp method="Post" url="https://api.demo.globalgatewaye4.firstdata.com/transaction/v12"
    useragent="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.1599.69 Safari/537.36">
     <cfhttpparam name="Authorization" type="header" value="GGE4_API #key_id#:#hmac_value#">
     <cfhttpparam name="x-gge4-date" type="header" value="#x_time#">
     <cfhttpparam name="x-gge4-content-sha1" type="header" value="#content_digest#">
     <cfhttpparam name="content-type" type="header" value="application/xml">
     <cfhttpparam name="accept" type="header" value="application/xml">
     <cfhttpparam name="transaction_body" type="xml" value="#trans#" />  
</cfhttp>

Response code:

<cfset postresult=HTMLEditFormat(cfhttp.fileContent)>
<cfset postresult=replace(postresult,"&lt;","<","all")>
<cfset postresult=replace(postresult,"&gt;",">","all")>
<cfset postresult=replace(postresult,"##","-","all")>

<cfoutput> 
    #postresult#<BR />
    <cfset badtransaction=0><cfset badtransactionmessage="">
    <cfset rawerror="">
    <cfif findnocase("bad request",postresult)><cfset rawerror=trim(gettoken(postresult,2,"-"))>
        <cfset badtransactionmessage=badtransactionmessage&rawerror>
        <cfset badtransaction=1>
    </cfif>
    <cfif findnocase("unauthorized request",postresult)><cfset rawerror=trim(gettoken(postresult,2,"."))>
        <cfset badtransactionmessage=badtransactionmessage&rawerror>
        <cfset badtransaction=1>
    </cfif>

    <cfset resultarray=arraynew(2)>
    <cfset line=1>
    <cfset enterflag=0>
    <cfset startflag=0>
    <cfloop index="getchar" from="1" to="#len(postresult)-22#">
        <cfif mid(postresult,getchar,9) is "<exactid>" ><cfset startflag=1></cfif>
        <cfif mid(postresult,getchar,19) is "</TransactionResult>" ><cfset startflag=0></cfif>

        <cfif startflag is 1>
            <cfif enterflag is 2>
                <cfif mid(postresult,getchar,1)  is "<"><cfset enterflag=0><cfset line++>
            <cfelse>
                <cfset resultarray[line][2]=resultarray[line][2]&mid(postresult,getchar,1)>
            </cfif>
        </cfif>

        <cfif enterflag is 1>
            <cfif mid(postresult,getchar,1)  is ">" >
                <cfset enterflag=2>
            <cfelse>
                <cfset resultarray[line][1]=resultarray[line][1]&mid(postresult,getchar,1)>
            </cfif>
        </cfif>

        <cfif enterflag is 0>
            <cfif mid(postresult,getchar,1)  is "<" and mid(postresult,getchar+1,1) is not "/">
                <cfset enterflag=1>
                <cfset resultarray[line][1]="">
                <cfset resultarray[line][2]="">
            </cfif>
        </cfif>
    </cfif>
   </cfloop>

    <cfdump var="#resultarray#">

    <cfset transactiontag="">
    <cfset authorizationnum="">
    <cfset transactionapproved="">
    <cfset exactmessage="">
    <cfset exactresponsecode="">
    <cfset sequenceno="">
    <cfset retrievalrefno="">
    <cfset cardtype="">
    <cfset bankmessage="">

    <cfloop index="getresponses" from="1" to ="#arraylen(resultarray)#">
        <cfif resultarray[getresponses][1] is "Transaction_Tag"><cfset transactiontag=resultarray[getresponses][2]></cfif>
        <cfif resultarray[getresponses][1] is "Authorization_Num"><cfset AuthorizationNum=resultarray[getresponses][2]></cfif>
        <cfif resultarray[getresponses][1] is "Transaction_Approved"><cfset TransactionApproved=resultarray[getresponses][2]></cfif>
        <cfif resultarray[getresponses][1] is "EXact_Message"><cfset EXactMessage=resultarray[getresponses][2]></cfif>
        <cfif resultarray[getresponses][1] is "EXact_Resp_Code"><cfset EXactResponseCode=resultarray[getresponses][2]></cfif>
        <cfif resultarray[getresponses][1] is "SequenceNo"><cfset SequenceNo=resultarray[getresponses][2]></cfif>
        <cfif resultarray[getresponses][1] is "Retrieval_Ref_No"><cfset RetrievalRefNo=resultarray[getresponses][2]></cfif>
        <cfif resultarray[getresponses][1] is "CardType"><cfset CardType=resultarray[getresponses][2]></cfif>
        <cfif resultarray[getresponses][1] is "bank_message"><cfset bankmessage=resultarray[getresponses][2]></cfif>
  </cfloop>

    <BR />
    #transactiontag#<BR />
    #authorizationnum# <BR />
    #transactionapproved# <BR />
    #exactmessage#<BR />
    #exactresponsecode#<BR />
    #sequenceno#<BR />
    #retrievalrefno#<BR />
    #cardtype#<BR />
    #bankmessage#<BR />

    <cfif trim(transactionapproved) is not "true" and trim(transactionapproved) is not "">
        <cfset badtransaction=2>
        <cfset badtransactionmessage=badtransactionmessage&bankmessage>
    </cfif>

    <cfif badtransaction gt 0>
        ---#badtransactionmessage#<BR />
    </cfif>

</cfoutput>

Upvotes: -1

Related Questions