FoxPro Payeezy 网关
FoxPro Payeezy Gateway
我有一个桌面应用程序 Foxpro 9.0 Executive,它需要连接到 Payeezy 并通过他们的 API 传输和接收 XML 数据。
我已经使用 WinHttpRequest.5.1 从 UPS 地址验证 API 发送和接收 XML 数据。但我似乎对 SHA-1 HMAC 哈希计算有问题 header。谁能给我一些关于如何在 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=""
您的代码将 m.Hashme
的 base64 编码填充到 authorization
header 中。根据您告诉我们的内容,您似乎需要计算 m.Hashme
的 SHA-1 散列并将散列放入 header(在对其进行 base64 编码之后)。
Fox 没有内置 SHA-1 函数,因此您需要一个辅助源。可以在 Fox 中使用 Win32 CryptAPI,但那是不必要的混乱而且相当痛苦。 FoxPro 基金会 类 (FFC) 中有 _crypt.vcx
但这并没有真正帮助(并且像所有 FFC 一样它不适合生产使用)。
这里有一个小的 .prg,可用于使用 Win32 CryptAPI 和 _crypt.vcx
:
计算哈希值(默认值:SHA1)
#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
在可以使用之前,您需要按照 class 定义上方的注释所示破解 _crypt.vcx
,因为 classlib 甚至 VFP9 也被 b0rked。此外,VFP 搜索路径需要包括 Fox 主目录及其子目录 FFC。
我在 First Data 的 Payeezy 团队工作。我看到在您发布的示例代码中,您混淆了我们的两个 API,我们的 REST API (https://api-cert.payeezy.com) 和我们基于 SOAP 的 API (api.demo.globalgatewaye4.firstdata.com)
如果您使用的是 REST API,那么这里是在 PHP 中生成 HMAC 的示例代码。
<?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;
?>
如果您使用的是基于 SOAP 的 API,您可以在此处找到示例代码:https://support.payeezy.com/hc/en-us/articles/203731149-API-Security-HMAC-Hash
我有一个桌面应用程序 Foxpro 9.0 Executive,它需要连接到 Payeezy 并通过他们的 API 传输和接收 XML 数据。 我已经使用 WinHttpRequest.5.1 从 UPS 地址验证 API 发送和接收 XML 数据。但我似乎对 SHA-1 HMAC 哈希计算有问题 header。谁能给我一些关于如何在 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=""
您的代码将 m.Hashme
的 base64 编码填充到 authorization
header 中。根据您告诉我们的内容,您似乎需要计算 m.Hashme
的 SHA-1 散列并将散列放入 header(在对其进行 base64 编码之后)。
Fox 没有内置 SHA-1 函数,因此您需要一个辅助源。可以在 Fox 中使用 Win32 CryptAPI,但那是不必要的混乱而且相当痛苦。 FoxPro 基金会 类 (FFC) 中有 _crypt.vcx
但这并没有真正帮助(并且像所有 FFC 一样它不适合生产使用)。
这里有一个小的 .prg,可用于使用 Win32 CryptAPI 和 _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
在可以使用之前,您需要按照 class 定义上方的注释所示破解 _crypt.vcx
,因为 classlib 甚至 VFP9 也被 b0rked。此外,VFP 搜索路径需要包括 Fox 主目录及其子目录 FFC。
我在 First Data 的 Payeezy 团队工作。我看到在您发布的示例代码中,您混淆了我们的两个 API,我们的 REST API (https://api-cert.payeezy.com) 和我们基于 SOAP 的 API (api.demo.globalgatewaye4.firstdata.com)
如果您使用的是 REST API,那么这里是在 PHP 中生成 HMAC 的示例代码。
<?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;
?>
如果您使用的是基于 SOAP 的 API,您可以在此处找到示例代码:https://support.payeezy.com/hc/en-us/articles/203731149-API-Security-HMAC-Hash