0

I have a desktop application Foxpro 9.0 Executive that needs to connect up to Payeezy and transfer and receive XML data via their API. I have used WinHttpRequest.5.1 to send and receive XML data from UPS Address verification API. But I seem to be having issue with the SHA-1 HMAC hash calculation header. Can anyone give me a little sample code on how to accomplish this in Foxpro? https://support.payeezy.com/hc/en-us/articles/203731149-API-Security-HMAC-Hash

*api.demo.globalgatewaye4.firstdata.com
***************************
If Vartype(loReq)='U'
  Public loReq
ENDIF
lcURL='https://api-cert.payeezy.com/v1/transactions/v12'
lcPassWd ='Password'
lcExactID='ExactID'
lcKeyCode='Keycode'
ldDate=dtos(DATE())
lcDate=SUBSTR(ldDate,1,4)+'-'+SUBSTR(ldDate,5,2)+'-'+SUBSTR(ldDate,7,2)
ltTime=TIME()
lcDateTime=lcDate+'T'+TRANSFORM(ltTime)+'Z'
uri='transaction/v12'
lcTranstype='00'
lcAmount='1299.00'
lctype='visa'
lcname='John Smith'
lncc_no='4788250000028291'
lcExp_Date='1020'
lccvv='123'
lcAddress='21 Jump Street'
lcCity='Los Angeles'
lcZip='90210'
lcPhone='5557891234'
lcOrderno='12345678'
CustID='87654321'
lcTransactionType="00"
lcShip_Name="Customer Name"
XMLRequest='<?xml version="1.0" encoding="utf-8" ?>'+Chr(13)+;
  '<Transaction>'+Chr(13)+;
  '<Transaction_Type>'+lcTranstype+'</Transaction_Type>'+CHR(13)+;
  '<DollarAmount>'+lcAmount+'</DollarAmount>'+CHR(13)+;
  '<Expiry_Date>'+lcExp_Date+'</Expiry_Date>'+CHR(13)+;
  '<CardHolderName>'+lcname+'</CardHolderName>'+Chr(13)+;
  '<Reference_No>'+lcOrderno+'</Reference_No>'+CHR(13)+;
  '<Customer_Ref>'+CustID+'</Customer_Ref>'+CHR(13)+;
  '<Reference_3>'+lcname+'</Reference_3>'+CHR(13)+;
  '<ExactID>'+lcExactID+'</ExactID>'+CHR(13)+;
  '<Password>'+lcPassWd+ '</Password>'+CHR(13)+;
  '<Card_Number>'+lncc_no+'</Card_Number>'+chr(13)+; 
  '</Transaction>'
Hashme='POST'+chr(13)+'SOAP'+chr(13)+XMLRequest+chr(13)+lcDateTime+chr(13)+lcURL
baseHash=STRCONV(Hashme, 13)
loReq = Createobject('WinHttp.WinHttpRequest.5.1')
loReq.SetTimeouts(2500, 2500, 2500, 2500)
loReq.Open('POST', 'https://api-cert.payeezy.com/v1/transactions/v12', .F.)
loReq.SetCredentials(lcExactID, lcPassWd , 0)
loReq.SetRequestHeader('authorization', 'GGE4_API 14:'+lcKeyCode)
loReq.SetRequestHeader('x-gge4-content-sha1',baseHash )
loReq.SetRequestHeader('content-type', 'application/xml')
loReq.SetRequestHeader('accept', 'text/xml')
loReq.Send(XMLRequest)          
Xmltocursor(loReq.responsetext,'Payeezy')
loReq=""
Uprightguy
  • 28
  • 5
  • please read the guide lines for posting – johnny 5 Jan 25 '16 at 20:17
  • @Uprightguy: you should review the answers that were written for you, vote them up if they're helpful, and accept the answer that is most helpful. This is not only for giving feedback to the people who bothered answering your question, but also for the benefit of other people who might have the same problem (so that they can gauge whether the answers were useful or not). – DarthGizka Feb 03 '16 at 13:38

2 Answers2

1

Your code stuffs a base64 encoding of m.Hashme into the authorization header. From what you told us it seems that you need to compute the SHA-1 hash of m.Hashme and put the hash into the header (after base64-encoding it).

Fox doesn't have a SHA-1 function built, so you need a secondary source for that. It is possible to use the Win32 CryptAPI in Fox, but that's needlessly messy and quite painful. There's _crypt.vcx among the FoxPro Foundation Classes (FFC) but that doesn't really help (and like all of FFC it isn't fit for production use anyway).

For what it's worth, here's a small .prg that can be used to compute hashes (default: SHA1) using the Win32 CryptAPI and _crypt.vcx:

#include WinCrypt.h

lparameters cData, nAlgorithmId

with createobject([CCryptAPIWrapper_])
   return .Hash(@m.cData, m.nAlgorithmId)
endwith   

*******************************************************************************
* _CryptAPI.hProviderHandle needs to be hacked to PROTECTED or PUBLIC
* and also most of the member functions called here

define class CCryptAPIWrapper_ as _CryptAPI of _crypt.vcx

   function Init

      * declare missing CryptAPI functions
      declare long CryptGetHashParam in WIN32API long, long, string@, long@, long

      return dodefault()

   procedure Destroy

      if not empty(this.hProviderHandle)
         this.CryptReleaseContext(this.hProviderHandle)
      endif

   function Hash (cData, nAlgorithmId)

      nAlgorithmId = evl(m.nAlgorithmId, dnALG_SID_SHA)

      local hHashContext, cHash

      hHashContext = 0
      cHash = .null.
      try
         this.CryptCreateHash(this.hProviderHandle, nAlgorithmId, 0, 0, @m.hHashContext)
         this.CryptHashData(m.hHashContext, m.cData, len(m.cData), 0)
         cHash = this.RetrieveHashFromContext(m.hHashContext)
      finally
         if not empty(m.hHashContext)
            this.CryptDestroyHash(m.hHashContext)
         endif
      endtry

      return m.cHash

   function RetrieveHashFromContext (hHashContext)

      local cHashSize, nXferSize

      cHashSize = replicate(chr(0), 4)
      nXferSize = len(m.cHashSize)
      CryptGetHashParam(m.hHashContext, dnHP_HASHSIZE, @m.cHashSize, @m.nXferSize, 0)

      assert m.nXferSize == 4

      local nHashSize, cHashData

      nHashSize = extract_UINT32_(m.cHashSize)
      nXferSize = m.nHashSize
      cHashData = space(m.nHashSize)
      CryptGetHashParam(m.hHashContext, dnHP_HASHVAL, @m.cHashData, @m.nXferSize, 0)

      assert m.nXferSize == m.nHashSize

      return m.cHashData

enddefine

*******************************************************************************
* note: BITOR() and BITLSHIFT() give a signed result -> can't use them here

function extract_UINT32_ (s)

   return asc(substr(m.s, 1, 1)) ;
        + asc(substr(m.s, 2, 1)) * 0x100 ;
        + asc(substr(m.s, 3, 1)) * 0x10000 ;
        + asc(substr(m.s, 4, 1)) * 0x1000000

Before this can be used, you need to hack _crypt.vcx as indicated by the comment above the class definition, because the classlib is b0rked even VFP9. Also, the VFP search path needs to include both the Fox home directory and its subdirectory FFC.

DarthGizka
  • 4,347
  • 1
  • 24
  • 36
  • What about VFPEncryption.fll seems to have a SHA-1 function. http://www.sweetpotatosoftware.com/spsblog/2005/11/27/VFPEncryptionFLLUpdate.aspx – Uprightguy Feb 02 '16 at 16:50
  • There are tons of third-party SHA implementations for Fox, and personally I'd opt indeed for an FLL in most cases. However, in some cases it can be preferable not to depend on extra binaries, be they DLLs, FLLs, OCXen or whatnot. YMMV. For production use I'd get rid of `_crypt.vcx` though and code the CryptAPI hasher class from scratch. `_crypt.vcx` is unsalvageable (like virtually everything else that ships with Fox or is generated by anything that ships with it). – DarthGizka Feb 02 '16 at 17:24
1

I work on the Payeezy team at First Data. I see that in the sample code you posted, you have mixed up two of our APIs, our REST API (https://api-cert.payeezy.com) and our SOAP based API (api.demo.globalgatewaye4.firstdata.com)

If you are using the REST API, then here is sample code to generate the HMAC in PHP.

<?php
$apiKey = "<your api key>";
$apiSecret = "<your consumer secret>";
$nonce = "<Crypographically strong random number>";
$timestamp = "<Epoch timestamp in milli seconds>";
$token = "<Merchant Token>";
$payload = "<For POST - Request body / For GET - empty string>";
$data = $apiKey + $nonce + $timestamp + $token + $payload;
$hashAlgorithm = "sha256";

<!-- Make sure the HMAC hash is in hex -->
$hmac = hash_hmac ( $hashAlgorithm , $data , $apiSecret, false );

<!-- Authorization : base64 of hmac hash -->
$authorization = base64_encode($hmac);
ehco $authorization;
?>

If you are using the SOAP based API, you will find the sample code here: https://support.payeezy.com/hc/en-us/articles/203731149-API-Security-HMAC-Hash

Rohit
  • 106
  • 5
  • Can you give me a better sample of the soap code? The link they have just gives you a form to enter the data. I need the to know the format of the fields it is expecting in the XML format – Uprightguy Feb 12 '16 at 00:00