Difference between revisions of "Webhooks"
Line 32: | Line 32: | ||
This is an advanced feature; you are expected to know how to write and debug your own scripts, so we won't help you do that. | This is an advanced feature; you are expected to know how to write and debug your own scripts, so we won't help you do that. | ||
− | However, if you | + | However, if you need a different kind of help - or if you think something is wrong at our end, do a '''[[CasperVend_2/Getting_Help#How_Can_I_Get_Help.3F | ticket to Casper.]]''' The rest of the support staff are NOT scripters and CANNOT help. |
= <span style="color:#00528c">Example Code</span> = | = <span style="color:#00528c">Example Code</span> = |
Revision as of 04:44, 20 February 2018
New for 2018, our Webhook system is a beefed-up version of CasperVend's ANS notification system. This is an advanced system which requires programming knowledge. It's designed to reliably send events to server(s) under your control.
Benefits
- * Increased reliability. If your server goes offline, we'll keep sending until it goes through (though see the caveats below)
- * Not just for vendor sales, but can also notify for marketplace sales and redeliveries
- * Not just for CasperVend, but will be rolled out for CasperLet, CasperSafe, CasperUpdate, etc. in the future.
- * JSON encoded so can be read by nearly all backend frameworks
- * In the event of a failure to reach your endpoint, we will notify your avatar by IM (after 5 attempts).
- * SL Marketplace notifications include the matched CasperVend product ID (zero if not associated).
Events Currently Tracked
- * Product purchase inworld
- * SL Marketplace purchase
Tracking Coming Soon For
- * Redeliveries
- * Update Delivery
(This list will be added to and corrected as features and additional event tracking are built in.)
Caveats
- * If we can't get through to your server, we will try again on an exponential backoff. The first 5 attempts happen every minute, but after that the length of time doubles for each attempt.
- * If we still cannot get through to your server after 43 attempts (approximately 24 hours), we will drop all pending notifications and disable your webhook.
- * Because of the retry mechanism, your code must be prepared to accept duplicate requests.
- * We send events in date order, so if an early event fails, you won't receive any later events until your endpoint responds correctly.
- * Your endpoint must respond within 3 seconds.
Getting Help
This is an advanced feature; you are expected to know how to write and debug your own scripts, so we won't help you do that.
However, if you need a different kind of help - or if you think something is wrong at our end, do a ticket to Casper. The rest of the support staff are NOT scripters and CANNOT help.
Example Code
MySQL
Should work with any modern MySQL daemon. Tested with MariaDB 10.2.12
1 CREATE DATABASE IF NOT EXISTS `webhooks`;
2 USE `webhooks`;
3
4 CREATE TABLE IF NOT EXISTS `events` (
5 `eventID` char(36) NOT NULL,
6 `received` datetime NOT NULL DEFAULT current_timestamp(),
7 `processed` tinyint(1) unsigned NOT NULL DEFAULT 0,
8 `data` mediumblob DEFAULT NULL,
9 PRIMARY KEY (`eventID`),
10 KEY `processed` (`processed`)
11 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
PHP
This code assumes that you have a database created using the SQL above.
1 <?php
2
3 // Configuration options
4
5 define('WEBHOOK_SALT', 'YOUR SALT HERE');
6
7 define('ENABLE_DEBUG', true);
8 define('DEBUG_EMAIL', 'your@email.address');
9
10 define('DATABASE_HOST', '127.0.0.1');
11 define('DATABASE_USER', 'webhooks');
12 define('DATABASE_PASSWORD', 'somepassword');
13 define('DATABASE_DATABASE', 'webhooks');
14
15 // Get the raw POST data
16 $postData = file_get_contents("php://input");
17
18 // Integrity check
19 if (!isset($_SERVER['HTTP_X_CASPER_WEBHOOK_INTEGRITY_HASH']) || $_SERVER['HTTP_X_CASPER_WEBHOOK_INTEGRITY_HASH'] !== sha1($postData) || !isset($_SERVER['HTTP_X_CASPER_WEBHOOK_VERIFY_HASH']))
20 {
21 http_response_code(400);
22 die('Corruption detected');
23 }
24
25 // Connect to the database
26 $dsn = 'mysql:dbname='.DATABASE_DATABASE.';host='.DATABASE_HOST;
27
28 try
29 {
30 $dbh = new PDO($dsn, DATABASE_USER, DATABASE_PASSWORD);
31 } catch (PDOException $e)
32 {
33 if (ENABLE_DEBUG) mail(DEBUG_EMAIL, 'Webhook Script Error', 'Database connection failed');
34 die('Database connection failed');
35 }
36
37 // Now we check if the request is actually intended for us
38
39 $hashCheck = sha1(sha1($postData).":".WEBHOOK_SALT);
40
41 if ($_SERVER['HTTP_X_CASPER_WEBHOOK_VERIFY_HASH'] !== $hashCheck)
42 {
43 http_response_code(403);
44 if (ENABLE_DEBUG) mail(DEBUG_EMAIL, 'Webhook Script Error', 'Unauthorised request');
45 die('Unauthorised');
46 }
47
48 // By this stage we have a validated webhook event. Now decode the json
49
50 $data = json_decode($postData, true);
51
52 if ($data === false)
53 {
54 // JSON decode failure. This should never happen since the payload is signed..
55 if (ENABLE_DEBUG) mail(DEBUG_EMAIL, 'Webhook Script Error', 'JSON decode failure');
56 die('Invalid payload');
57 }
58
59
60 // Get the event ID
61 $eventID = $data['metadata']['eventID'];
62
63 // Insert the event into the database.
64
65 // IMPORTANT: To avoid duplicates, make sure that eventID is a primary key.
66
67 $stmt = $dbh->prepare("INSERT IGNORE INTO `events` (`eventID`, `received`, `data`) VALUES (:eventID, NOW(), :data)");
68
69 if (!$stmt)
70 {
71 if (ENABLE_DEBUG) mail(DEBUG_EMAIL, 'Database error', var_export($dbh->errorInfo(), true));
72 }
73
74 if (!$stmt->execute([
75 ":eventID" => $eventID,
76 ":data" => $postData
77 ]))
78 {
79 if (ENABLE_DEBUG) mail(DEBUG_EMAIL, 'Database statement error', var_export($stmt->errorInfo(), true));
80 }
81
82
83 if ($stmt->rowCount() == 0)
84 {
85 if (ENABLE_DEBUG) mail(DEBUG_EMAIL,'duplicate ans event', $postData);
86 // Duplicate event
87 die();
88 }
89 else
90 {
91 if (!function_exists('fastcgi_finish_request'))
92 {
93 // If you're not using fastcgi, PLEASE don't keep the remote
94 // server waiting while you do your event processing. Remember
95 // you must respond within 3 seconds.
96
97 // Run a script via a cron job to process your incoming events.
98
99 die();
100 }
101 fastcgi_finish_request();
102
103
104 // Process any unprocessed events. You can do this here, or offload it into a cron job or something.
105
106 $stmt = $dbh->query("SELECT `data`,`eventID` FROM `events` WHERE `processed` = 0 ORDER BY `received` ASC");
107 while($row = $stmt->fetch(\PDO::FETCH_ASSOC))
108 {
109 // Do your processing
110
111 // blah blah blah
112
113 // All done? Mark the event as processed
114 $substmt = $dbh->prepare("UPDATE `events` SET `processed` = 1 WHERE `eventID` = :id");
115 $substmt->execute([":id" => $row['eventID']]);
116 }
117 }