FoxPro Payeezy Gateway - visual-foxpro

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=""

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.

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

Related

Google Ads API: How to Send Batch Requests?

I'm using Google Ads API v11 to upload conversions and adjust conversions.
I send hundreds of conversions each day and want to start sending batch requests instead.
I've followed Google's documentation and I upload/ adjust conversions exactly the way they stated.
https://developers.google.com/google-ads/api/docs/conversions/upload-clicks
https://developers.google.com/google-ads/api/docs/conversions/upload-adjustments
I could not find any good explanation or example on how to send batch requests:
https://developers.google.com/google-ads/api/reference/rpc/v11/BatchJobService
Below is my code, an example of how I adjust hundreds of conversions.
An explanation of how to do so with batch requests would be very appreciated.
# Adjust the conversion value of an existing conversion, via Google Ads API
def adjust_offline_conversion(
client,
customer_id,
conversion_action_id,
gclid,
conversion_date_time,
adjustment_date_time,
restatement_value,
adjustment_type='RESTATEMENT'):
# Check that gclid is valid string else exit the function
if type(gclid) is not str:
return None
# Check if datetime or string, if string make as datetime
if type(conversion_date_time) is str:
conversion_date_time = datetime.strptime(conversion_date_time, '%Y-%m-%d %H:%M:%S')
# Add 1 day forward to conversion time to avoid this error (as explained by Google: "The Offline Conversion cannot happen before the ad click. Add 1-2 days to your conversion time in your upload, or check that the time zone is properly set.")
to_datetime_plus_one = conversion_date_time + timedelta(days=1)
# If time is bigger than now, set as now (it will be enough to avoid the original google error, but to avoid a new error since google does not support future dates that are bigger than now)
to_datetime_plus_one = to_datetime_plus_one if to_datetime_plus_one < datetime.utcnow() else datetime.utcnow()
# We must convert datetime back to string + add time zone suffix (+00:00 or -00:00 this is utc) **in order to work with google ads api**
adjusted_string_date = to_datetime_plus_one.strftime('%Y-%m-%d %H:%M:%S') + "+00:00"
conversion_adjustment_type_enum = client.enums.ConversionAdjustmentTypeEnum
# Determine the adjustment type.
conversion_adjustment_type = conversion_adjustment_type_enum[adjustment_type].value
# Associates conversion adjustments with the existing conversion action.
# The GCLID should have been uploaded before with a conversion
conversion_adjustment = client.get_type("ConversionAdjustment")
conversion_action_service = client.get_service("ConversionActionService")
conversion_adjustment.conversion_action = (
conversion_action_service.conversion_action_path(
customer_id, conversion_action_id
)
)
conversion_adjustment.adjustment_type = conversion_adjustment_type
conversion_adjustment.adjustment_date_time = adjustment_date_time.strftime('%Y-%m-%d %H:%M:%S') + "+00:00"
# Set the Gclid Date
conversion_adjustment.gclid_date_time_pair.gclid = gclid
conversion_adjustment.gclid_date_time_pair.conversion_date_time = adjusted_string_date
# Sets adjusted value for adjustment type RESTATEMENT.
if conversion_adjustment_type == conversion_adjustment_type_enum.RESTATEMENT.value:
conversion_adjustment.restatement_value.adjusted_value = float(restatement_value)
conversion_adjustment_upload_service = client.get_service("ConversionAdjustmentUploadService")
request = client.get_type("UploadConversionAdjustmentsRequest")
request.customer_id = customer_id
request.conversion_adjustments = [conversion_adjustment]
request.partial_failure = True
response = (
conversion_adjustment_upload_service.upload_conversion_adjustments(
request=request,
)
)
conversion_adjustment_result = response.results[0]
print(
f"Uploaded conversion that occurred at "
f'"{conversion_adjustment_result.adjustment_date_time}" '
f"from Gclid "
f'"{conversion_adjustment_result.gclid_date_time_pair.gclid}"'
f' to "{conversion_adjustment_result.conversion_action}"'
)
# Iterate every row (subscriber) and call the "adjust conversion" function for it
df.apply(lambda row: adjust_offline_conversion(client=client
, customer_id=customer_id
, conversion_action_id='xxxxxxx'
, gclid=row['click_id']
, conversion_date_time=row['subscription_time']
, adjustment_date_time=datetime.utcnow()
, restatement_value=row['revenue'])
, axis=1)
I managed to solve it in the following way:
The conversion upload and adjustment are not supported in the Batch Processing, as they are not listed here.
However, it is possible to upload multiple conversions in one request since the conversions[] field (list) could be populated with several conversions, not only a single conversion as I mistakenly thought.
So if you're uploading conversions/ adjusting conversions you can simply upload them in batch this way:
Instead of uploading one conversion:
request.conversions = [conversion]
Upload several:
request.conversions = [conversion_1, conversion_2, conversion_3...]
Going the same way for conversions adjustment upload:
request.conversion_adjustments = [conversion_adjustment_1, conversion_adjustment_2, conversion_adjustment_3...]

How to automatically get basic monitoring info such as CPU and Memory Usage from the IBM i?

I'm trying to get some basic performance data (such as CPU and Memory Usage) from the IBM i every minute or so.
Then I'm creating a Web App, which will display all of this in a centralized dashboard and also notify the user for any unusual values/events.
All I need is some kind of parsable data output from IBM i; could be JSON, CSV, perhaps even ODBC,...
I already tried running commands to get spool output, but that's not consistent so it can't really be parsed. The latest thing I found is collecting CSV files, but that is not automatic.
Inside the "IBM i Navigator -> Performance -> Investigate Data" there is an option to show a graph with my required data and it's even possible to export it as CSV.
However, I was wondering if it's possible to GET this data via a HTTP Request as JSON? I was searching around and found mentions of "Integrated Web Services" and "CICS Transactions Server HTTP Requests", but nothing specific on getting existing data, only on creating your own.
https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
https://www.ibm.com/docs/en/i/7.3?topic=tasks-integrated-web-application-server
Thank you!
I don't know if the data you search for available through a web request. What is the greater goal you want to achieve? Just curiosity? Centralized Monitoring for erratic values?
Usually, the requested class of data is exposed in more or less real time via SNMP and easily accessible by existing monitoring applications. It uses UDP and is much more efficient in terms of processor overhead than web requests.
The graphs you mention might be derived from the Performance Tools, something akin to sar & friends on Linux/Unix. However, this data is also not exported via web request. I think there are API calls within the OS to access this data. See Performance Tools for an overview.
Of course, this data is saved in tables and can be accessed via ODBC from outside IBM i, but I question the effort resulting from the probable lack of documentation about the table structure to be beneficial.
the system exposes all sorts of performance info as SQL table functions. here is the active job info table function
PHP can be used to write a web service which first calls the table function, then returns the resulting data as a JSON data stream.
<?php
$showColNameArr = array("JOB_NAME", "SUBSYSTEM", "JOB_TYPE", "FUNCTION", "FUNCTION_TYPE",
"JOB_STATUS", "CPU_TIME" ) ;
header("Content-type: text/javascript; charset=utf-8;");
// access an input, posted json object.
$postContents = file_get_contents('php://input') ;
$postObject = json_decode( $postContents ) ;
$action = isset($postObject->action) ? $postObject->action : '' ;
{
$conn = as400Connect('qgpl qtemp') ;
$sql = "SELECT *
from TABLE(QSYS2.ACTIVE_JOB_INFO( ))" ;
$stmt = db2_prepare($conn, $sql) ;
$result = db2_execute($stmt) ;
$colNames = db2Stmt_GetColNames( $stmt ) ;
$finalArr = array( ) ;
while( $row = db2_fetch_array($stmt))
{
$assocArr = array( ) ;
for( $jx = 0 ; $jx < sizeof($row) ; ++$jx )
{
$colName = $colNames[$jx] ;
if ( in_array( $colName, $showColNameArr ))
{
$vlu = $row[$jx] ;
$assocArr[$colName] = $vlu ;
}
}
$finalArr[] = $assocArr ;
}
echo json_encode( $finalArr ) ;
}
// ---------------------------- as400Connect ------------------------
function as400Connect( $libl )
{
$options = array('i5_naming' => DB2_I5_NAMING_ON);
if (strlen($libl) > 0)
{
$options['i5_libl'] = $libl ;
}
$conn = db2_connect("*LOCAL","","", $options);
if (!$conn) {
echo "Connection failed" ;
echo "<br>" ;
echo db2_conn_errormsg( ) ;
exit( ) ;
}
return $conn ;
}
// --------------------- db2Stmt_GetColNames ----------------
// build and return array of column names from a db2_execute
// executed $stmt.
function db2Stmt_GetColNames( $stmt )
{
$colCx = db2_num_fields($stmt);
$colNames = array( ) ;
for( $ix=0; $ix < $colCx; $ix++ )
{
array_push( $colNames, db2_field_name( $stmt, $ix )) ;
}
return $colNames ;
}
?>

Issue in Facebook Replies download from post comments

I am trying to download public comments and replies from the FACEBOOK public post by page.
my code is working until 5 Feb'18, Now it is showing below error for the "Replies".
Error in data.frame(from_id = json$from$id, from_name = json$from$name, :
arguments imply differing number of rows: 0, 1
Called from: data.frame(from_id = json$from$id, from_name = json$from$name,
message = ifelse(!is.null(json$message), json$message, NA),
created_time = json$created_time, likes_count = json$like_count,
comments_count = json$comment_count, id = json$id, stringsAsFactors = F)
please refer below code I am using.
data_fun=function(II,JJ,page,my_oauth){
test <- list()
test.reply<- list()
for (i in II:length(page$id)){
test[[i]] <- getPost(post=page$id[i], token = my_oauth,n= 100000, comments = TRUE, likes = FALSE)
if (nrow(test[[i]][["comments"]]) > 0) {
write.csv(test[[i]], file = paste0(page$from_name[2],"_comments_", i, ".csv"), row.names = F)
for (j in JJ:length(test[[i]]$comments$id)){
test.reply[[j]] <-getCommentReplies(comment_id=test[[i]]$comments$id[j],token=my_oauth,n = 100000, replies = TRUE,likes = FALSE)
if (nrow(test.reply[[j]][["replies"]]) > 0) {
write.csv(test.reply[[j]], file = paste0(page$from_name[2],"_replies_",i,"_and_", j, ".csv"), row.names = F)
}}}
}
Sys.sleep(10)}
Thanks For Your support In advance.
I had the very same problem as Facebook changed the api rules at the end of January. If you update your package with 'devtools' from Pablo Barbera's github, it should work for you.
I have amended my code (a little) and it works fine now for replies to comments.There is one frustrating thing though, is that Facebook dont appear to allow one to extract the user name. I have a pool of data already so I am now using that to train and predict gender.
If you have any questions and want to make contact - drop me an email at 'robert.chestnutt2#mail.dcu.ie'
By the way - it may not be an issue for you, but I have had challenges in the past writing the Rfacebook output to a csv. Saving output as an .RData file maintains the form a lot better

get random password with puppet function

I have a function that allow me to generate random password. My function is working well without a puppetmaster. When I tried with a master an error appear when I called the function :
Error 400 on SERVER: bad value for range
Here is my function:
module Puppet::Parser::Functions
newfunction(:get_random_password, :type => :rvalue, :doc => <<-EOS
Returns a random password.
EOS
) do |args|
raise(Puppet::ParseError, "get_random_password(): Wrong number of arguments " +
"given (#{args.size} for 1)") if args.size != 1
specials = ((33..33).to_a + (35..38).to_a + (40..47).to_a + (58..64).to_a + (91..93).to_a + (95..96).to_a + (123..125).to_a).pack('U*').chars.to_a
numbers = (0..9).to_a
alphal = ('a'..'z').to_a
alphau = ('A'..'Z').to_a
length = args[0]
CHARS = (alphal + specials + numbers + alphau)
pwd = CHARS.sort_by { rand }.join[0...length]
return pwd
end
end
The function is called in both case with $pwd = get_random_password(10).
When I specified the length directly in the function to 10 for example. The password is well generated in master mode.
Have you any idea why I can't specify the length value?
It's unclear why this works for puppet apply (if that's what you're insinuating), but the error is most likely a typing issue.
Try
length = args[0].to_i
To my Knowledge,For situations like this I use the puppet generate() function to create the random password and store it in a persistent data store on the master.
For instance,an SQLITE database or something. This way, the password is generated randomly if it does not exist and the same password is used if it does already exist.
It's important to have the resource always be managed, that way if the password is changed on the managed node Puppet will realize this, change it to the value you're managing, and report that it did so.

I want to calculate the page load time in Watir or Selenium

Here is the scenario:
1. Login to a web application with username and password and hit Enter (Start timer)
2. Load the login page (lap timer split, to mark the time for page load )
3. Click on a another page (split the lap timer)
4. Stop the stop watch
Any method call in Watir returns how long it takes, so this is a trivial task.
For example,
b.text_field(:id, 'uid').set username
b.text_field(:id, 'pwd').set password
time1 = b.button(:id, 'logon').click
time2 = b.button(:id, 'movepage').click
puts "Time One: #{time1} Time Two: #{time2} Total Time: #{time1+time2}"
There is a new Spec being introduced into all modern browsers. Currently Google Chrome, IE9 have it built in and Mozilla is waiting to apply the patch that has been supplied.
This new spec is called WebTimings and I wrote a blog post showing how to access it using C#. The way to access it is via javascript so can be used with all language bindings.
The JavaScript needed is
var performance = window.performance || window.webkitPerformance || window.mozPerformance window.msPerformance || {};
var timings = performance.timing || {};
return timings;
This returns a dictionary like this
/* The dictionary returned will contain something like the following.
* The values are in milliseconds since 1/1/1970
*
* connectEnd: 1280867925716
* connectStart: 1280867925687
* domainLookupEnd: 1280867925687
* domainLookupStart: 1280867925687
* fetchStart: 1280867925685
* legacyNavigationStart: 1280867926028
* loadEventEnd: 1280867926262
* loadEventStart: 1280867926155
* navigationStart: 1280867925685
* redirectEnd: 0
* redirectStart: 0
* requestEnd: 1280867925716
* requestStart: 1280867925716
* responseEnd: 1280867925940
* responseStart: 1280867925919
* unloadEventEnd: 1280867925940
*/

Resources