Ebay Marketplace Account Deletion Notifications Handler

June 17, 2022 . 5 MIN READ

  1. <?php
  2.  
  3. ##########################################################
  4. ### Marketplace Account Deletion Notifications Handler ###
  5. ### By Swappart 2021 ###
  6. ##########################################################
  7.  
  8. ### Sign into eBay Developers Program and find your Client ID and Client Secret under 
  9. ### application access keys.
  10. ### Your endpoint and verificationToken are values you specify when subscribing to 
  11. ### Marketplace Account Deletion Notifications in your developer account.
  12. ### The endpoint URL must not contain the word eBay.
  13. ### The verification token has to be between 32 and 80 characters, and allowed 
  14. ### characters include alphanumeric characters, underscore (_), and hyphen (-).
  15. ### No other characters are allowed. Example: 654321abcdef654321abcefd123456fe;
  16.  
  17. $client_id = “Your client ID goes here”; //Also known as App ID.
  18. $client_secret = “Your client secret goes here”; //Also known as Cert ID.
  19. $verificationToken = “Your verification token goes here”;
  20. $endpoint = “YourEndpointURL”;
  21.  
  22. // Sets base file location to one level above webroot. You can
  23. // change this if you want to use a specific location.
  24. $fileStorageLocation = realpath(dirname(__FILE__) . ‘/..’);
  25.  
  26.  
  27. #####################################
  28. ### This part validates endpoint. ###
  29. #####################################
  30. if(isset($_GET[‘challenge_code’])){
  31. $challengeCode = $_GET[‘challenge_code’];
  32. header(‘Content-Type: application/json’); 
  33. $d=$challengeCode.$verificationToken.$endpoint; 
  34. $hd=array(“challengeResponse”=>hash(“sha256”, $d));
  35. echo(json_encode($hd));
  36. }
  37.  
  38. $json = file_get_contents(‘php://input’);
  39. $message = json_decode($json, true);
  40.  
  41. ####################################################
  42. ### Creates file to help with debugging script ###
  43. ### Find file debug.txt in the directory above ###
  44. ### webroot. May contain sensitive info, so do ###
  45. ### not post the contents if you have a problem. ###
  46. ####################################################
  47. $config[‘Decoded JSON Message’] = $message;
  48. $config[‘Server’] = $_SERVER;
  49. $data = parseArray($config);
  50. $debug = $fileStorageLocation . ‘/debug.txt’;
  51. write_to_file($debug, $data);
  52.  
  53.  
  54.  
  55.  
  56. #######################################
  57. ### This part handles notfications. ###
  58. #######################################
  59. if(isset($_SERVER[‘HTTP_X_EBAY_SIGNATURE’])){
  60. if (!$message) {
  61. throw new Exception(‘Invalid message’);
  62. }
  63. if (empty($_SERVER[‘HTTP_X_EBAY_SIGNATURE’])) {
  64. throw new Exception(‘No signature passed’);
  65. }
  66. $signature = json_decode(base64_decode($_SERVER[‘HTTP_X_EBAY_SIGNATURE’]), true) ?: [];
  67. if (empty($signature[‘kid’])) {
  68. throw new Exception(‘Signature not decoded’);
  69. }
  70.  
  71. $token = retrieveToken($client_id, $client_secret, $fileStorageLocation);
  72. $ch = curl_init();
  73. $fp = fopen($fileStorageLocation . ‘/curlLog.txt’, ‘w’) or die(‘Unable to open file!’);
  74. curl_setopt($ch, CURLOPT_URL, “https://api.ebay.com/commerce/notification/v1/public_key/” . $signature[‘kid’]);
  75. curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Content-Type = application/json’,‘Accept: application/json’, ‘Authorization:bearer ‘ . $token));
  76. curl_setopt($ch, CURLOPT_FILE, $fp);
  77. curl_setopt($ch, CURLOPT_HEADER, 0);
  78. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  79. $response = curl_exec($ch);
  80. $curl_errno = curl_errno($ch);
  81. $curl_error = curl_error($ch);
  82. if ($curl_errno > 0) {
  83. fwrite($fp, “cURL Error ($curl_errno): $curl_error\n”);
  84. } else {
  85. fwrite($fp, “Data received: $response\n”);
  86. }
  87. $publicKey = json_decode($response, true);
  88. curl_close($ch);
  89. fclose($fp);
  90. if (empty($publicKey[‘key’])) {
  91. throw new Exception(
  92. ‘getPublicKey response: ‘ . json_encode($publicKey) . ‘ for signature ‘ . $signature[‘kid’]
  93. );
  94. }
  95. if ($publicKey[‘algorithm’] !== ‘ECDSA’ || $publicKey[‘digest’] !== ‘SHA1’) {
  96. throw new Exception(‘Unsupported encryption algorithm/digest’);
  97. }
  98. if (preg_match(‘/^—–BEGIN PUBLIC KEY—–(.+)—–END PUBLIC KEY—–$/’, $publicKey[‘key’], $matches)) {
  99. $key = “—–BEGIN PUBLIC KEY—–\n”
  100. . implode(“\n”, str_split($matches[1], 64))
  101. . “\n—–END PUBLIC KEY—–“;
  102. } else {
  103. throw new Exception(‘Invalid key’);
  104. }
  105. $verificationResult = openssl_verify(
  106. json_encode($message),
  107. base64_decode($signature[‘signature’]),
  108. $key,
  109. OPENSSL_ALGO_SHA1
  110. );
  111. if ($verificationResult === 1) {
  112. echo ‘OK’;
  113. // If you need to check to see if you have any data related to the deleted user
  114. // this is where you do it. You would pass the below username and/or userId 
  115. // variables to a script which could check your database, and then handle the
  116. // result accordingly.
  117. $username = $message[‘notification’][‘data’][‘username’];
  118. $userId = $message[‘notification’][‘data’][‘userId’];
  119. $eiasToken = $message[‘notification’][‘data’][‘eiasToken’];
  120. } else {
  121. $myfile = fopen($fileStorageLocation . ‘/verification-error.txt’, ‘w’) or die(‘Unable to open file!’);
  122. $txt = “Verification Failed!”;
  123. fwrite($myfile, $txt);
  124. fclose($myfile);
  125. throw new Exception(‘Verification failure’, 412);
  126. }
  127. }
  128. #############################################################
  129. ### This part returns stored OAuth token, or new token if ###
  130. ### stored one is expired. Tokens only valid for 2 hours. ###
  131. #############################################################
  132. function retrieveToken($client_id, $client_secret, $fileStorageLocation){
  133. $date = new DateTime();
  134. $date->getTimestamp();
  135. $auth = null;
  136. if(file_exists($fileStorageLocation . ‘/auth.ini’)){
  137. $auth = parse_ini_file($fileStorageLocation . ‘/auth.ini’, true); 
  138. if(!isset($auth[‘time’]) || !isset($auth[‘token’])){
  139. //ini file exists but doesn’t contain token. We’ll fetch one and update the file.
  140. $token = getNewToken($client_id, $client_secret, $fileStorageLocation);
  141. }else{
  142. $s = $auth[‘time’];
  143. $t= new DateTime(“@$s”);
  144. if($date->getTimestamp() > $t->add(new DateInterval(‘PT7170S’))->getTimestamp()){
  145. //Token expired. We’ll fetch a new one.
  146. $token = getNewToken($client_id, $client_secret, $fileStorageLocation);
  147. }else{
  148. //Stored token still good! Using it”
  149. $token = $auth[‘token’];
  150. }
  151. }
  152. }else{
  153. //ini file doesn’t exist yet. We’ll fetch a token and create the file.
  154. $token = getNewToken($client_id, $client_secret, $fileStorageLocation); 
  155. }
  156. return $token;
  157. }
  158. ####################################################
  159. ### This part fetches new OAuth token as needed. ###
  160. ####################################################
  161. function getNewToken($client_id, $client_secret, $fileStorageLocation) {
  162. $ch = curl_init();
  163. curl_setopt($ch, CURLOPT_URL, ‘https://api.ebay.com/identity/v1/oauth2/token’);
  164. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  165. curl_setopt($ch, CURLOPT_POST, 1);
  166. curl_setopt($ch, CURLOPT_POSTFIELDS, “grant_type=client_credentials&scope=https://api.ebay.com/oauth/api_scope”);
  167. $headers = array();
  168. $headers[] = ‘Content-Type: application/x-www-form-urlencoded’;
  169. $headers[] = ‘Authorization: Basic ‘ . base64_encode($client_id . ‘:’ . $client_secret);
  170. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  171. $result = curl_exec($ch);
  172. if (curl_errno($ch)) {
  173. echo ‘Error:’ . curl_error($ch);
  174. }
  175. curl_close($ch);
  176. $tk = json_decode($result, true);
  177. $date = new DateTime();
  178. $time = $date->getTimestamp();
  179. // Update values
  180. $config[‘time’] = $time;
  181. $config[‘token’] = $tk[‘access_token’];
  182. $data = parseArray($config);
  183.  
  184. // Write ini file values
  185. $inifile = fopen($fileStorageLocation . ‘/auth.ini’, ‘w’) or die(‘Unable to open file!’);
  186. write_to_file($fileStorageLocation . ‘/auth.ini’, $data);
  187. fclose($inifile);
  188. return $tk[‘access_token’]; 
  189. }
  190. #######################################################################
  191. ### This part parses arrays. ###
  192. #######################################################################
  193. function parseArray($array = []) {
  194. // check second argument is array
  195. if (!is_array($array)) {
  196. throw new \InvalidArgumentException(‘Function argument must be an array.’);
  197. }
  198. // process array
  199. $data = array();
  200. foreach ($array as $key1 => $val1) {
  201. if (is_array($val1)) {
  202. $data[] = “[$key1]”;
  203. foreach ($val1 as $key2 => $val2) {
  204. if (is_array($val2)) {
  205. foreach ($val2 as $key3 => $val3) {
  206. if (is_array($val3)) {
  207. foreach ($val3 as $key4 => $val4) {
  208. if (is_numeric($key3)) {
  209. $data[] = $key3.‘[] = ‘.(is_numeric($val4) ? $val4 : (ctype_upper($val4) ? $val4 : ‘”‘.$val4.‘”‘));
  210. } else {
  211. $data[] = $key2.‘[‘.$key3.‘] [‘.$key4.‘]= ‘.(is_numeric($val4) ? $val4 : (ctype_upper($val4) ? $val4 : ‘”‘.$val4.‘”‘));
  212. }
  213. }
  214. }else {
  215. $data[] = $key2.‘[‘.$key3.‘] = ‘.(is_numeric($val3) ? $val3 : (ctype_upper($val3) ? $val3 : ‘”‘.$val3.‘”‘));
  216. }
  217. }
  218. } else {
  219. $data[] = $key2.‘ = ‘.(is_numeric($val2) ? $val2 : (ctype_upper($val2) ? $val2 : ‘”‘.$val2.‘”‘));
  220. }
  221. }
  222. } else {
  223. $data[] = $key1.‘ = ‘.(is_numeric($val1) ? $val1 : (ctype_upper($val1) ? $val1 : ‘”‘.$val1.‘”‘));
  224. }
  225. $data[] = null;
  226. }
  227. return $data;
  228. }
  229. ###########################################################
  230. ### This part writes arrays to file. ### 
  231. ### Used for storing OAuth token for later use. ###
  232. ### Also used to create debug file. ###
  233. ###########################################################
  234. function write_to_file($file, $dataArray = []) {
  235. // check first argument is string
  236. if (!is_string($file)) {
  237. throw new \InvalidArgumentException(‘Function argument 1 must be a string.’);
  238. }
  239. // check second argument is array
  240. if (!is_array($dataArray)) {
  241. throw new \InvalidArgumentException(‘Function argument 2 must be an array.’);
  242. }
  243. // open file pointer, init flock options
  244. $fp = fopen($file, ‘w’);
  245. $retries = 0;
  246. $max_retries = 100;
  247. if (!$fp) {
  248. return false;
  249. }
  250. // loop until get lock, or reach max retries
  251. do {
  252. if ($retries > 0) {
  253. usleep(rand(1, 5000));
  254. }
  255. $retries += 1;
  256. } while (!flock($fp, LOCK_EX) && $retries <= $max_retries);
  257. // couldn’t get the lock
  258. if ($retries == $max_retries) {
  259. return false;
  260. }
  261. // got lock, write data
  262. fwrite($fp, implode(PHP_EOL, $dataArray).PHP_EOL);
  263. // release lock
  264. flock($fp, LOCK_UN);
  265. fclose($fp);
  266. return true;
  267. }
  268.  
  269. ?> 

    Reference:https://forums.developer.ebay.com/questions/43122/working-code-marketplace-account-deletion-notifica.html

    https://stackoverflow.com/questions/68569773/ebay-marketplace-account-deletion-closure-notifications

Leave a Reply

Your email address will not be published. Required fields are marked *