I am trying to implement the sendgrid email API.
I have followed all the instructions I can find to try and set up the sendgrid email API but I all I get is this:
Response status: 401 Response Headers - HTTP/1.1 401 Unauthorized
- Server: nginx - Date: Wed, 02 Mar 2022 21:56:45 GMT
- Content-Type: application/json
- Content-Length: 88
- Connection: keep-alive
- Access-Control-Allow-Origin: https://sendgrid.api-docs.io
- Access-Control-Allow-Methods: POST
- Access-Control-Allow-Headers: Authorization, Content-Type, On-behalf-of, x-sg-elas-acl
- Access-Control-Max-Age: 600
- X-No-CORS-Reason: https://sendgrid.com/docs/Classroom/Basics/API/cors.html
- Strict-Transport-Security: max-age=600; includeSubDomains
I installed the SendGrid helper library, ran the Composer command which created a composer.json file in the root of my project, installed the SendGrid helper library for PHP, along with its dependencies in a new directory named vendor.
I then used the following to create sendgrid.env:
echo "export SENDGRID_API_KEY='SG.XXX....XXXX'" > sendgrid.env
echo "sendgrid.env" >> .gitignore
source ./sendgrid.env
The API key works fine when I tested it using curl --request POST and email comes through ok.
But I have tried every combination I can think of to integrate the sendgrid.env e.g. changing the location, removing the single quotes etc. but I just get the same error message every time.
Here is my php script to send the email:
declare(strict_types=1);
require 'vendor/autoload.php';
use \SendGrid\Mail\Mail;
$email = new Mail();
// Replace the email address and name with your verified sender
$email->setFrom(
'paul#xxx.com',
'Paul xxx'
);
$email->setSubject('Sending with Twilio SendGrid is Fun');
// Replace the email address and name with your recipient
$email->addTo(
'paul#yyy.com',
'Paul yyy'
);
$email->addContent(
'text/html',
'<strong>and fast with the PHP helper library.</strong>'
);
$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));
try {
$response = $sendgrid->send($email);
printf("Response status: %d\n\n", $response->statusCode());
$headers = array_filter($response->headers());
echo "Response Headers\n\n";
foreach ($headers as $header) {
echo '- ' . $header . "\n";
}
} catch (Exception $e) {
echo 'Caught exception: '. $e->getMessage() ."\n";
}
This is the structure of the files:
My domain and email have been verified on Sendgrid.
I have a feeling that there's another step involved to get the API from the sendgrid.env file?
Thanks in advance.
I am trying to use the API from https://api.emea.fortify.com/swagger/ui/index#/
called Start Scan with Default.
I cannot find any documentation to suggest how to set the post up.
This is what I have so far, but I get an error and of course I am not sending the files to scan either, so I know it is not right.
I have tried a Get request, which works so I know it is authenticated etc.
I just need to know are the parameters correctly formatted and how do I upload the actual files to scan.
POST /api/v3/releases/43579/static-scans/start-scan-with-defaults?releaseId=43579& fragNo=22& offset=22& isRemediationScan=false& notes=hello HTTP/1.1
Host: api.emea.fortify.com
Content-Type: application/json
Authorization: Bearer [TOKEN HERE]
User-Agent: PostmanRuntime/7.13.0
Accept: */*
Cache-Control: no-cache
Postman-Token: 57e40c1d-c99c-40a4-a79b-06ef9a678a07,8ef4ad1e-327f-4eee-b6bb-bddb21b18d50
Host: api.emea.fortify.com
accept-encoding: gzip, deflate
content-length:
Connection: keep-alive
cache-control: no-cache
Response:
{
"errors": [
{
"errorCode": null,
"message": "Unexpected error processing request"
}
]
}
UPDATE
I have found this repo on Git written in Java, which I have tried to recreate in PowerShell with no success.
https://github.com/fod-dev/fod-uploader-java
My PowerShell:
[System.Net.WebRequest]::DefaultWebProxy = [System.Net.WebRequest]::GetSystemWebProxy()
[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
$zipDetails = Get-Content C:\Users\patemanc\Desktop\types.zip -Encoding Byte
Write-Host $zipDetails.Length
$releaseId = "43576"
$url = "https://api.emea.fortify.com/api/v3/releases/$releaseId/static-scans/start-scan-with-defaults?"
$url += "releaseId=$releaseId"
$url += "&fragNo=-1"
$url += "&offset=0"
$url += "&isRemediationScan=false"
$url += "¬es=PowrShell Test"
$long_lived_access_token = "ENTER TOKEN HERE"
$headers = #{Authorization = "bearer:$long_lived_access_token"}
$response = Invoke-WebRequest -ContentType "application/octet-stream" -Uri $url -Method POST -Body $zipDetails -Headers $headers -UseBasicParsing
Write-Host "Here is the end"
Write-Host $response
Error Response:
79212
Invoke-WebRequest : The underlying connection was closed: An unexpected error occurred on a send.
At line:22 char:13
+ $response = Invoke-WebRequest -ContentType "application/json" -Uri $ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Why postman? If you use some plugin to run it, from Jenkins for example, it works fine. I don't know how the plugins call it from the API.
I don't usually get this low level, so I think I'm just missing something obvious with the header.
Here's the code:
request_stream << "POST / HTTP/1.1\r\n"
"Host: localhost:5000 \r\n"
"Accept: */*\r\n"
"Content-Type: application/json\r\n";
request_stream << "Content-Length: " << json.length() << "\r\n\r\n";
request_stream << json;
The resulting string is
POST / HTTP/1.1
Host: localhost:5000
Accept: */*
Content-Type: application/json
Content-Length: 34
{'key1':'value1', 'key2':'value2'}
I then send the request_stream string to the server. I can connect to the server, but it says "bad request". I'm guessing there's something wrong with the above string. Any ideas?
Thanks.
Turns out that the request was fine. The issue was the simple Flask server I set up. I was calling
print(request.get_json())
when I should've have been calling
print(request.form.get('data'))
I have simple CSV file like:
device,template
dlinkrouter,DLink DGS Switch
cisco, DLink DGS Switch
And simple script like below:
$csv = Import-Csv "C:\Users\m.zurek\Desktop\devices.csv"
$csv.device | ForEach-Object {
Write-Host $_
$action = "http://localhost:8090/api/json/ncmsettings/addDevice"
$hash = #{}
$hash.Add("apiKey", "cb464d185c0cdf785295a6a0a1d227a2")
$hash.Add("IPADDRESS", $($_))
$hash.Add("DEVICE_BEHAVIOUR", "DLink DGS Switch")
$adddev = Invoke-WebRequest -Uri $action -UseBasicParsing -Body $hash -Method Post
return $adddev
}
Problem is that the script adds only the first device from the CSV file. On the rest I have response from API about error. Not sure if it's a problem with API or with my script.
StatusCode : 200
StatusDescription :
Content : {"Message":"Error in adding device. Zip the logs folder and send it to support.","isSuccess":false,"statusMsg":"Failure","bulkResourceIds":[]}
RawContent : HTTP/1.1 200
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST
Access-Control-Max-Age: 5000
Transfer-Encoding: chunked
Vary: Accept-Encoding
Content-Type: application/json;c...
Forms :
Headers : {[Access-Control-Allow-Origin, *], [Access-Control-Allow-Methods, GET,POST], [Access-Control-Max-Age, 5000], [Transfer-Encoding, chunked]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml :
RawContentLength : 144
Write-Host works fine and write name of all devices from CSV.
We have been working to update our PayPal IPN scripts in accord with seom recent changes. Here is the instruction in part by PayPal ...
Your Action Required before February 1, 2013 You will need to update
your IPN and/or PDT scripts to use HTTP 1.1, and include the "Host:
www.paypal.com" and "Connection: close" HTTP headers in the IPN and
PDT scripts.
We did that and the IPNs failed. PayPal Merchant Technical Service asked that we move to a SLL connection. Previously our connection to PayPal had been based upon ...
fsockopen ('www.paypal.com', 80, $errno, $errstr, 30)
Now it is ...
fsockopen ('ssl://ipnpb.paypal.com', 443, $errno, $errstr, 30)
We had to overcome an SSL problem with our host config to get this working, but it now makes the connection. However IPN requires us to post back to PayPal in order to receive a "VERIFIED" notification, at which point the script can do it's local processing based upon a confirmed payment.
This is where it fails.
The lines of the sample PayPal script are:
if (!$fp) {
// HTTP ERROR
} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp (trim($res), "VERIFIED") == 0) { ... rest of script
But the response comes back blank. I have inserted a diagnostic to email me the value of $res at each stage. If I do this with the old script it comes back with a "VERIFIED" value just fine. The old scripts on several sites have been stable for years. I have now tried these new updates on two distinct sites on two different servers and the result is the same.
When looking at the Apache logs I see this error:
PHP Warning: fgets() [function.fgets]: SSL: Connection reset by peer in /home/[sitename]/public_html/[IPN scriptname].php on line 145
So it appears that PayPal shuts down the connection without sending a Verified response. However PayPal support deny that this is a problem at this end since the connection is opening in the first place.
I have spent several days trying to fix this. I still have the deadline of Feb 1st after which PayPal say my old scripts may not work, but neither do the new ones.
Anyone else have any experience with this?
If anyone else has the same issue, CURL seems to be the recommend choice for IPN by PayPal.
Check out there code sample on Github at: https://github.com/paypal/ipn-code-samples/blob/master/paypal_ipn.php
After struggle this issue a few hours. I got it right.
first, the header should be correct.
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.sanbox.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";
second, the buffer should be big enough to receive whole msg from Paypal. the original sample code use 1024, but the result got truncated.
$res = stream_get_contents($fp, 2048);
After the two, the result return good.
Finaly found an answer! Paypal IPN Script, issue with feof and fgets
remove while(!feof($fp) ... and change it to $res = stream_get_contents($fp, 1024); also add $header .= "Conection: Close" to make sure that paypal closes the connection.
UPDATE: Jan 7th 2016
I noticed that the below works very well for tls://www.sandbox.paypal.com but when you go live to tls://www.paypal.com connection is refused! (It doesn't work) I found the problem is in the headers, sandbox and production level live paypal needs different headers! This is a bug but for you to get it work both in production level and sandbox, please use these headers respectively:
Production Level (Live PayPal Headers):
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
Sandbox PayPal Headers:
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";
* The code below is the same and it works fine, just replace the headers respectively.*
Here is the rest of the code (and answer):
I checked all of the answers here and there to make it work in Jan 5th 2016.
All of them had good points, but didn't work for the whole picture.
So first, here is the complete code that actually works and then I'll go through it:
<?php
// =========================================================================
// PayPal Official PHP Codes and Tutorial:
// https://developer.paypal.com/webapps/developer/docs/classic/ipn/gs_IPN/
// =========================================================================
// Send an empty HTTP 200 OK response to acknowledge receipt of the notification
header('HTTP/1.1 200 OK');
// Assign payment notification values to local variables
$item_name = $_POST['item_name'];
$item_number = $_POST['item_number'];
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
// Build the required acknowledgement message out of the notification just received
$req = 'cmd=_notify-validate'; // Add 'cmd=_notify-validate' to beginning of the acknowledgement
foreach ($_POST as $key => $value) {
// Loop through the notification NV pairs
$value = urlencode(stripslashes($value)); // Encode these values
$req .= "&$key=$value"; // Add the NV pairs to the acknowledgement
}
// Set up the acknowledgement request headers
// HTTP POST request
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.sanbox.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";
// Open a socket for the acknowledgement request
$fp = fsockopen('tls://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
// Send the HTTP POST request back to PayPal for validation
fputs($fp, $header . $req);
// Log the transaction:
file_put_contents("paypal_post.txt", date('Y-m-d H:i:s')."\n".file_get_contents("php://input")."\n\n", FILE_APPEND);
// While not EOF
while (!feof($fp)) {
// Get the acknowledgement response
// $res = fgets($fp, 1024);
$res = stream_get_contents($fp, 1024);
$responses = explode("\r\n", $res);
foreach ($responses as $response) {
if (strcmp ($response, "VERIFIED") == 0) {
// Response contains VERIFIED - process notification
// Authentication protocol is complete - OK to process notification contents
// Possible processing steps for a payment include the following:
// Check that the payment_status is Completed
// Check that txn_id has not been previously processed
// Check that receiver_email is your Primary PayPal email
// Check that payment_amount/payment_currency are correct
// Process payment
} else if (strcmp ($response, "INVALID") == 0) {
// Response contains INVALID - reject notification
// Authentication protocol is complete - begin error handling
}
}
}
// Close the file
fclose($fp);
?>
OK, now you have the code that works to listen for a PayPal IPN, re-compile and send it back to PayPal for verification, receive the headers back, process the headers line by line to find the VERIFIED validation.
Which should be provided by PayPal all-in-one package in a working condition but their tutorial lacked very critical parts and it didn't work for me and many here on this thread.
So now what are the critical parts that makes it work:
First, the headers provided by paypal didn't work, I found #jp_eagle 's headers work perfectly.
And paypal was wrong for $res = fgets($fp, 1024); as well...
However you dont need $res = stream_get_contents($fp, 2048); as #jp_eagle suggested, $res = stream_get_contents($fp, 1024); is just fine.
while (!feof($fp)) {} loop should stay there to make it work! Yes, even with the switch from fgets() to stream_get_contents() it should stay there!
#richbai90 was wrong for the suggestion to remove it.
Try the other way and it won't work...
And the most critical part to get the VALIDATION is this loop:
$res = stream_get_contents($fp, 1024);
$responses = explode("\r\n", $res);
foreach ($responses as $response) {}
Now you can look for the VALIDATION in each line here:
if (strcmp ($response, "VERIFIED") == 0) {}
Also logged the initial complete incoming POST request form paypal before anything else:
// Log the transaction:
file_put_contents("paypal_post.txt", date('Y-m-d H:i:s')."\n".file_get_contents("php://input")."\n\n", FILE_APPEND);
That's it.
Now go here and login with your paypal to see if it works:
https://developer.paypal.com/developer/ipnSimulator/
Give your url select "cart checkout" for example and send a test IPN.
If it works all good, go ahead and remove the .sandbox from the above paypal URL from www.sandbox.paypal.com to www.paypal.com and you are live.
Fill in the VERIFIED and INVALID sections in the if-else statements with your database actions and it is done.