Hi. I have configured Authorize.net, accept hosted payment method for my client requirement, where everything is happening fine, except Transaction response. I am using webhook event but which is displaying an error when i make request for list of available webhook events.
My doubts are:
`{ "status": 405, "reason": "Method Not Allowed", "message": "The requested resource does not support http method 'POST' for given parameters.", "correlationId": "ff90ee25-0ba7-4006-bb1e-225ea64897e3" }`
Following is my code
<?php $login_transKey = 'xxx:xxx'; //Login and Transaction Key of Authorize.net $jsonObj = '{ "name": "Get WebHooks", "request": { "url": "http://localhost:81/hosted_payment_form/webhookstwo.php", "method": "GET", "header": [ { "key": "Content-Type", "value": "application/json", "description": "" }, { "key": "Authorization", "value": "'.$login_transKey.'", "description": "" } ], "body": { "mode": "formdata", "formdata": [] }, "description": "" }, "response": [] }'; $jsonObj = json_encode($jsonObj); $url = "https://apitest.authorize.net/rest/v1/eventtypes"; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonObj); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false ); $content = curl_exec($ch); echo '<pre>'; print_r($content); die(); curl_close($ch); ?>
Please, let me know if you need anymore information.
Solved! Go to Solution.
02-19-2017 09:03 AM
I am not a PHP developer, so please take my suggestions with a grain of salt.
But I couldn't help but notice you have CURLOPT_POSTFIELDS in your code. If I undersand that cURL directive correctly, that should only be used with HTTP POST. However, the correct way to make this API call is with HTTP GET -- which is why you got an HTTP 405 Method Not Allowed response.
As this API call doesn't have any fields that must be submitted via HTTP POST, I would recommend dropping CURLOPT_POSTFIELDS from your code.
(Also, as an unrelated side-note: I also noticed you have CURLOPT_VERIFYPEER and CURLOPT_VERIFYHOST set to "false." This is not a recommended practice for PCI compliant applications, as your solution should be verifying the server hosting any TLS connections. This might be acceptable for early development, but I recommend setting both to "true" before using this code in a production environment.)
02-21-2017 08:24 AM
Just to build on what Lilith says, the Webhooks API is a REST API, which means requests are constructed using HTTP verbs and URLs, and only sometimes posting data. The request for listing the event types is a request you make via an HTTP GET, and doesn't actually require any JSON to be posted to make the request.
The next issue is authentication. Each request of the Webhooks API (whether POST or GET) has to have a standard HTTP basic authentication header in it to show that you're authorized. Authorization is explained in the documentation, but is basically a header with the key "Authorization" and the value of the word "Basic" followed by a base 64 encoding of your login ID and transaction key separated by a colon.
Here's an example of what a proper request for the event types list would look like:
<?php $login_transKey = 'xxx:xxx'; //Login and Transaction Key of Authorize.net $url = "https://apitest.authorize.net/rest/v1/eventtypes"; $headers = array( 'Content-Type:application/json','Authorization: Basic '. base64_encode("$login_transKey") );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $content = curl_exec($ch); echo '<pre>'; print_r($content); die(); curl_close($ch); ?>
I did it this way to make it clearer how the authentication header was being constructed, but you could also let curl create the header for you by adding a couple of more curl_setopt lines:
<?php $login_transKey = 'xxx:xxx'; //Login and Transaction Key of Authorize.net $url = "https://apitest.authorize.net/rest/v1/eventtypes";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $login_transkey); $content = curl_exec($ch); echo '<pre>'; print_r($content); die(); curl_close($ch); ?>
To do the Webhooks stuff that requires POST, you'd do it more like your code example (POSTing JSON to the server), but you don't include the Content-Type or Authorization in the JSON. They're HTTP headers. The JSON that you post should follow the examples in our documentation.
One more thing: If you use Postman, find the link that says "Webhooks REST API Postman Collection" in our documentation. If you don't use Postman, you should. :) It's an invaluable tool for seeing how this request response stuff works in HTTP, and testing your own requests.
02-21-2017 09:44 AM
Hi @ravuriauth200,
These are excellent questions, and I'll try my best to answer them all. If I miss any, just ask again!
Let's start with a little bit of background on Webhooks. Here are a couple of articles I found on the web discussing the basic concept, with a little bit of an eye towards developing and testing the listeners or receivers on your end:
At this point, our implementation of Webhooks is entirely controlled through the Webhooks API. There's nothing in the merchant interface that needs to be set up for Webhooks, nor is there anything in the interface that will tell you anything about your existing Webhook setup.
To setup a Webhook, you send a request through the API which tells us what events you want to be notified about, and which URL on your site you want us to post those requests to. Then, whenever one of those events that you've registered happens, we post something to that URL on your site that you gave us.
Since a registered Webhook will post a notification to a URL, that URL you send us has to be a working one that we can reach. That means you can't send us a localhost URL, as we'd never be able to post anything to it from our servers. If, during development, you can't yet set up a URL on your server that will accept a POST request, you could get a temporary URL from a service like http://requestb.in, which can receive the notifications for you and let you review them at will.
The ultimate goal is to set up a url on your end that we can do an HTTP post to. So, you might set up a listener URL like http://www.yourserver.com/webhooks/TransactionResponseListener. You'd have a script or program at that URL set to listen for the notification.
Then, you'd send the request to our Webhooks API to set up a Webhook to be notified of the net.authorize.payment.authcapture.created event. You only need to send this request once to register the URL. At that point, every time an authcapture transaction is created, our server would post a string of JSON data to that URL, so your script or application would receive it and process it appropriately.
The JSON and the code that you posted looks fine. If you sent that to our server, you'd either get a HTTP status code of 200, indicating that it's okay, or you would get another status code if there was an error. In your case, if you sent it as is, our system would give you an HTTP 400 error because it wouldn't recognize the localhost URL as valid. But, as long you sent in a valid URL, that would register that URL as a listener for the post that would happen every time a transaction was created.
When we post a notification in that URL, the notification itself will take the form of the response examples in our Webhooks documentation.
Since you said you were doing Accept Hosted, I want to point out that there's another way to get notified of transactions. If you are bringing up the payment form in an iframe, you can tell the payment form to also include another page from your site that has some javascript embedded as a listener. There's some information about that in our Accept Hosted documentation, but if you want to go down that road instead, we can definitely help you with that.
02-22-2017 10:32 AM
Hi @ravuriauth200,
I know you said you got it working in your next post, and I'll respond to that shortly, but I wanted to address testing.
Once you have registered a Webhook, you can request that we send a test notification for any Webhook using the "pings" command. A pings request to our server looks like this:
https://apitest.authorize.net/rest/v1/webhooks/{{webhookId}}/pings
or, as an example:
https://apitest.authorize.net/rest/v1/webhooks/72a55c78-66e6-4b1e-a4d6-3f925c00561f/pings
This request is a POST request with an empty message body. You construct the URL with the Webhook ID you want to test, and then do a POST to that URL. Once we receive that post, we'll send a notification to the registered URL for that Webhook just as if the event for that Webhook actually happened.
Note: To prevent confusion with Webhooks listener URLs that are listening for actual events to occur, the "pings" command only works on Webhooks that are set as inactive. To test a Webhook that isn't currently set to an inactive status, you must first set the status to inactive by sending a request to update the Webhook.
02-23-2017 10:01 AM
Hi @ravuriauth200,
For your first issue, you can get the Webhook ID (that unique code) in a couple of places. It's returned in the response when the Webhook is first created. However, if you didn't save the response (like if the Webhook was created by accident), you can always get the ID and status for all of your Webhooks by sending an HTTP GET request (with your authorization header) to
https://apitest.authorize.net/rest/v1/webhooks
The response will list all registered webhooks, with their IDs, status, and what event types they are registered for.
Then you'd delete the ones you don't need by sending an HTTP DELETE request to a url like this:
https://apitest.authorize.net/rest/v1/webhooks/{{webhookId}}
replace the placeholder with the Webhook ID at the end of the URL.
Now, curl by default will do a GET, or it will do a POST if you're posting data, but to do a DELETE request in HTTP using curl, you'll need to add one more curl_setopt line:
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
02-23-2017 12:41 PM
For the second issue, displaying the line items on the receipt is not something currently supported by Accept Hosted.
You are welcome to post this as a new feature using our Ideas forum. This will allow others to vote on and make suggestions to improve the request.
02-23-2017 01:00 PM
I am not a PHP developer, so please take my suggestions with a grain of salt.
But I couldn't help but notice you have CURLOPT_POSTFIELDS in your code. If I undersand that cURL directive correctly, that should only be used with HTTP POST. However, the correct way to make this API call is with HTTP GET -- which is why you got an HTTP 405 Method Not Allowed response.
As this API call doesn't have any fields that must be submitted via HTTP POST, I would recommend dropping CURLOPT_POSTFIELDS from your code.
(Also, as an unrelated side-note: I also noticed you have CURLOPT_VERIFYPEER and CURLOPT_VERIFYHOST set to "false." This is not a recommended practice for PCI compliant applications, as your solution should be verifying the server hosting any TLS connections. This might be acceptable for early development, but I recommend setting both to "true" before using this code in a production environment.)
02-21-2017 08:24 AM
Just to build on what Lilith says, the Webhooks API is a REST API, which means requests are constructed using HTTP verbs and URLs, and only sometimes posting data. The request for listing the event types is a request you make via an HTTP GET, and doesn't actually require any JSON to be posted to make the request.
The next issue is authentication. Each request of the Webhooks API (whether POST or GET) has to have a standard HTTP basic authentication header in it to show that you're authorized. Authorization is explained in the documentation, but is basically a header with the key "Authorization" and the value of the word "Basic" followed by a base 64 encoding of your login ID and transaction key separated by a colon.
Here's an example of what a proper request for the event types list would look like:
<?php $login_transKey = 'xxx:xxx'; //Login and Transaction Key of Authorize.net $url = "https://apitest.authorize.net/rest/v1/eventtypes"; $headers = array( 'Content-Type:application/json','Authorization: Basic '. base64_encode("$login_transKey") );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $content = curl_exec($ch); echo '<pre>'; print_r($content); die(); curl_close($ch); ?>
I did it this way to make it clearer how the authentication header was being constructed, but you could also let curl create the header for you by adding a couple of more curl_setopt lines:
<?php $login_transKey = 'xxx:xxx'; //Login and Transaction Key of Authorize.net $url = "https://apitest.authorize.net/rest/v1/eventtypes";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $login_transkey); $content = curl_exec($ch); echo '<pre>'; print_r($content); die(); curl_close($ch); ?>
To do the Webhooks stuff that requires POST, you'd do it more like your code example (POSTing JSON to the server), but you don't include the Content-Type or Authorization in the JSON. They're HTTP headers. The JSON that you post should follow the examples in our documentation.
One more thing: If you use Postman, find the link that says "Webhooks REST API Postman Collection" in our documentation. If you don't use Postman, you should. :) It's an invaluable tool for seeing how this request response stuff works in HTTP, and testing your own requests.
02-21-2017 09:44 AM
Thank you Aaron & Lilith.
Now everything works fine. But i have stucked at one point, which i have already raised in my first question. I would request you to provide, some inputs.
I am passing my Json like this.
$jsonQuery = '{ "url": "http://localhost:81/api/webhooks", "eventTypes": [ "net.authorize.payment.authcapture.created", "net.authorize.customer.created", "net.authorize.customer.paymentProfile.created", "net.authorize.customer.subscription.expiring" ], "status": "active" }';
and code is here
$url = "https://apitest.authorize.net/rest/v1/webhooks"; $headers = array( 'Content-Type:application/json','Authorization: Basic '.$login_transKey) ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonQuery); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false ); $content = curl_exec($ch); curl_close($ch); echo '<pre>'; print_r($content); die();
Please, provide some inputs, as i am new to this webhook concept. I have read that, by registering our server url to webhook, where we hold response and progress it programatically. How would i test it. i mean, after succesful transaction, shall i go to merchant panel to see anything about notifications.
How my custom url is registered to webhook ?
Once again. Thank you Aaron and Lilith.
02-22-2017 08:27 AM
Hi @ravuriauth200,
These are excellent questions, and I'll try my best to answer them all. If I miss any, just ask again!
Let's start with a little bit of background on Webhooks. Here are a couple of articles I found on the web discussing the basic concept, with a little bit of an eye towards developing and testing the listeners or receivers on your end:
At this point, our implementation of Webhooks is entirely controlled through the Webhooks API. There's nothing in the merchant interface that needs to be set up for Webhooks, nor is there anything in the interface that will tell you anything about your existing Webhook setup.
To setup a Webhook, you send a request through the API which tells us what events you want to be notified about, and which URL on your site you want us to post those requests to. Then, whenever one of those events that you've registered happens, we post something to that URL on your site that you gave us.
Since a registered Webhook will post a notification to a URL, that URL you send us has to be a working one that we can reach. That means you can't send us a localhost URL, as we'd never be able to post anything to it from our servers. If, during development, you can't yet set up a URL on your server that will accept a POST request, you could get a temporary URL from a service like http://requestb.in, which can receive the notifications for you and let you review them at will.
The ultimate goal is to set up a url on your end that we can do an HTTP post to. So, you might set up a listener URL like http://www.yourserver.com/webhooks/TransactionResponseListener. You'd have a script or program at that URL set to listen for the notification.
Then, you'd send the request to our Webhooks API to set up a Webhook to be notified of the net.authorize.payment.authcapture.created event. You only need to send this request once to register the URL. At that point, every time an authcapture transaction is created, our server would post a string of JSON data to that URL, so your script or application would receive it and process it appropriately.
The JSON and the code that you posted looks fine. If you sent that to our server, you'd either get a HTTP status code of 200, indicating that it's okay, or you would get another status code if there was an error. In your case, if you sent it as is, our system would give you an HTTP 400 error because it wouldn't recognize the localhost URL as valid. But, as long you sent in a valid URL, that would register that URL as a listener for the post that would happen every time a transaction was created.
When we post a notification in that URL, the notification itself will take the form of the response examples in our Webhooks documentation.
Since you said you were doing Accept Hosted, I want to point out that there's another way to get notified of transactions. If you are bringing up the payment form in an iframe, you can tell the payment form to also include another page from your site that has some javascript embedded as a listener. There's some information about that in our Accept Hosted documentation, but if you want to go down that road instead, we can definitely help you with that.
02-22-2017 10:32 AM
Hi Aron,
Thank you very much for your assistance. I have been through urls you mentioned for webhooks. I got clear idea of it, and implemented in this way.
My Code
$login_transKey = base64_encode('xxx:xxx'); $jsonQuery = '{ "url": "http://myserver.com/webhook.php", "eventTypes": [ "net.authorize.payment.authcapture.created" ], "status": "active" }'; $url = "https://apitest.authorize.net/rest/v1/webhooks"; $headers = array( 'Content-Type:application/json','Authorization: Basic '.$login_transKey); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonQuery); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false ); $content = curl_exec($ch); echo '<pre>'; print_r($content); curl_close($ch);
Response for above request
{ "_links": { "self": { "href": "/rest/v1/webhooks/0571926e-9b74-470c-a3b5-fa184e98bee6" } }, "webhookId": "0571926e-9b74-470c-a3b5-fa184e98bee6", "status": "active", "url": "http://myserver.com/webhook.php", "eventTypes": [ "net.authorize.payment.authcapture.created" ] }
This is looks like, my custom URL has been registered with webhook succesfully. After i did with one succeful transaction, now i must have one row inserted into my database, because, i written code in "http://myserver.com/webhook.php", where once i get reponse either GET/POST, that should be inserted into the database. Below is my code
Code in "http://myserver.com/webhook.php"
if($_REQUEST){ $con = mysqli_connect('localhost','username','pwd','db_name'); if (mysqli_connect_errno()) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); } $json = json_encode($_REQUEST); $query = "INSERT INTO webhooks(content,webhookTime) VALUES('".$json."',NOW())"; $status = mysqli_query($con,$query); if(!$status){ echo mysqli_error($con); } }
But it is not happening as i coded. when i checked in database, there were no records in the table. Aron, if everthing is fine till now. please, let me know how to proceed further.
I mean, how i can make sure that, notification is receving or not. how would i test it.
I cannot go with iframe, as you suggested, as my client wants to open payment gateway in new tab.
How would i proceed. please, suggest me something. how to test it ? Am i right till now ?
02-23-2017 01:54 AM
Hi Aaron,
Thank you very much. Everthing is working fine. Finally, i got it. I had not been done this task unless you would have not been there. Thank you very much.
Now everthing is working fine. but unfortunately i have been trapped with two more issues.
$url = "https://apitest.authorize.net/rest/v1/webhooks/72a55c78-66e6-4b1e-a4d6-3f925c00561f"; $headers = array( 'Content-Type:application/json','Authorization: Basic '."$login_transKey"); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $content = curl_exec($ch); echo '<pre>'; print_r($content); die(); curl_close($ch);
Displaying error as:
{ "status": 404, "reason": "NOT_FOUND", "message": "The requested resource could not be found.", "correlationId": "d8b50922-5916-4c99-834e-725820090a4e" } 1
<?xml version="1.0" encoding="utf-8"?> <getHostedPaymentPageRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"> <merchantAuthentication></merchantAuthentication> <transactionRequest> <transactionType>authCaptureTransaction</transactionType> <amount>'.$price.'</amount> <order> <invoiceNumber>15986</invoiceNumber> <description>This is memberhip payment</description> </order> <lineItems> <lineItem> <itemId>21256</itemId> <name>Sony Mobile</name> <description>Smart phone with gorilla glass</description> <quantity>2</quantity> <unitPrice>20</unitPrice> </lineItem> </lineItems> <tax> <amount>0</amount> <name>Service Tax</name> <description>Service Tax is Govt Tax, its mandatory</description> </tax> <customer> <type>individual</type> <id>'.$userId.'</id> <email>XXXXXXXXX</email> </customer> <billTo> <firstName>XXXXX</firstName> <lastName>XXXXXXXXX</lastName> <company>XXXX</company> <address>XXXXXXXXX</address> <city>XXXXXXX</city> <state>XXXXXXXXX</state> <zip>XXXXXXXXXXX</zip> <country>XXXXXXXXX</country> <phoneNumber>+91 1234567895</phoneNumber> </billTo> </transactionRequest> <hostedPaymentSettings> <setting> <settingName>hostedPaymentBillingAddressOptions</settingName> <settingValue>{"show": true, "required":true}</settingValue> </setting> <setting> <settingName>hostedPaymentCustomerOptions</settingName> <settingValue>{"showEmail": true, "requiredEmail":true}</settingValue> </setting> <setting> <settingName>hostedPaymentButtonOptions</settingName> <settingValue>{"text": "Submit Payment"}</settingValue> </setting> <setting> <settingName>hostedPaymentPaymentOptions</settingName> <settingValue>{"cardCodeRequired": true}</settingValue> </setting> <setting> <settingName>hostedPaymentSecurityOptions</settingName> <settingValue>{"captcha":true}</settingValue> </setting> <setting> <settingName>hostedPaymentOrderOptions</settingName> <settingValue>{"show": true, "merchantName": "XXXX"}</settingValue> </setting> <setting> <settingName>hostedPaymentStyleOptions</settingName> <settingValue>{"bgColor": "green"}</settingValue> </setting> <setting> <settingName>hostedPaymentIFrameCommunicatorUrl</settingName> <settingValue>{"url": "http://localhost:81/auth_accept_hosted/iCommunicator.html"}</settingValue> </setting> <setting> <settingName>hostedPaymentReturnOptions</settingName> <settingValue> { "showReceipt":true, "url":"http://localhost:81/auth_accept_hosted/iCommunicator.html?eventId=123", "urlText":"Continue", "cancelUrl":"http://localhost:81/authorizeNet/cancel.php", "cancelUrlText":"Cancel" } </settingValue> </setting> </hostedPaymentSettings> </getHostedPaymentPageRequest>
Please, help me with these two issues. Aaron. Once again thank you very much for your great help.
02-23-2017 07:49 AM
Hi @ravuriauth200,
I know you said you got it working in your next post, and I'll respond to that shortly, but I wanted to address testing.
Once you have registered a Webhook, you can request that we send a test notification for any Webhook using the "pings" command. A pings request to our server looks like this:
https://apitest.authorize.net/rest/v1/webhooks/{{webhookId}}/pings
or, as an example:
https://apitest.authorize.net/rest/v1/webhooks/72a55c78-66e6-4b1e-a4d6-3f925c00561f/pings
This request is a POST request with an empty message body. You construct the URL with the Webhook ID you want to test, and then do a POST to that URL. Once we receive that post, we'll send a notification to the registered URL for that Webhook just as if the event for that Webhook actually happened.
Note: To prevent confusion with Webhooks listener URLs that are listening for actual events to occur, the "pings" command only works on Webhooks that are set as inactive. To test a Webhook that isn't currently set to an inactive status, you must first set the status to inactive by sending a request to update the Webhook.
02-23-2017 10:01 AM
Hi @ravuriauth200,
For your first issue, you can get the Webhook ID (that unique code) in a couple of places. It's returned in the response when the Webhook is first created. However, if you didn't save the response (like if the Webhook was created by accident), you can always get the ID and status for all of your Webhooks by sending an HTTP GET request (with your authorization header) to
https://apitest.authorize.net/rest/v1/webhooks
The response will list all registered webhooks, with their IDs, status, and what event types they are registered for.
Then you'd delete the ones you don't need by sending an HTTP DELETE request to a url like this:
https://apitest.authorize.net/rest/v1/webhooks/{{webhookId}}
replace the placeholder with the Webhook ID at the end of the URL.
Now, curl by default will do a GET, or it will do a POST if you're posting data, but to do a DELETE request in HTTP using curl, you'll need to add one more curl_setopt line:
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
02-23-2017 12:41 PM
For the second issue, displaying the line items on the receipt is not something currently supported by Accept Hosted.
You are welcome to post this as a new feature using our Ideas forum. This will allow others to vote on and make suggestions to improve the request.
02-23-2017 01:00 PM