ZooTraX

webhook

Aug 15th, 2025
104
0
10 hours
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 12.26 KB | Software | 0 0
  1. <?php
  2. declare(strict_types=1);
  3.  
  4. /**
  5.  * Global Payments webhook endpoint.
  6.  *
  7.  * Responsibilities:
  8.  *  - Accepts POSTed JSON from Global Payments webhooks
  9.  *  - Logs request (headers + payload) into a daily logfile
  10.  *  - Extracts business fields from _embedded.itemsPaid[0].myMetadata.data
  11.  *  - Saves a row into payment_provider_payment_log per mapping provided
  12.  *  - Emails a debug dump
  13.  *  - Returns JSON flags + extracted data
  14.  */
  15.  
  16. $ignoreAuth = true;
  17. require_once dirname(__FILE__) . '/../config/globals.php';
  18.  
  19. use iMedicWare\PaymentProvider\PaymentProviderHelper;
  20. use PHPMailer\PHPMailer\PHPMailer;
  21. use PHPMailer\PHPMailer\Exception;
  22.  
  23. /**
  24.  * Safely get header array in environments without getallheaders().
  25.  */
  26. if (!function_exists('getallheaders')) {
  27.     function getallheaders(): array {
  28.         $headers = [];
  29.         foreach ($_SERVER as $name => $value) {
  30.             if (strncmp($name, 'HTTP_', 5) === 0) {
  31.                 $key = str_replace('_', '-', ucwords(strtolower(substr($name, 5)), '_'));
  32.                 $headers[$key] = $value;
  33.             } elseif ($name === 'CONTENT_TYPE') {
  34.                 $headers['Content-Type'] = $value;
  35.             } elseif ($name === 'CONTENT_LENGTH') {
  36.                 $headers['Content-Length'] = $value;
  37.             }
  38.         }
  39.         return $headers;
  40.     }
  41. }
  42.  
  43. /**
  44.  * Render scalar for logs. (No PHP 8 "mixed" type-hint here for PHP 7.3 compatibility.)
  45.  */
  46. function scalarToString($value): string
  47. {
  48.     if ($value === null) return 'null';
  49.     if ($value === true) return 'true';
  50.     if ($value === false) return 'false';
  51.     if (is_int($value) || is_float($value)) return (string)$value;
  52.     return (string)$value;
  53. }
  54.  
  55. /**
  56.  * Extracts nested meta data from webhook payload.
  57.  *
  58.  * Returns array with keys:
  59.  *  patientId, orderId, operatorId, scheduleId, facilityId, encounterId
  60.  */
  61. function extractMetaFromPayload(array $decoded): array
  62. {
  63.     $itemsPaid = isset($decoded['_embedded']['itemsPaid']) ? $decoded['_embedded']['itemsPaid'] : null;
  64.     $meta = null;
  65.  
  66.     if (is_array($itemsPaid) && isset($itemsPaid[0]) && is_array($itemsPaid[0])) {
  67.         if (isset($itemsPaid[0]['myMetadata']['data']) && is_array($itemsPaid[0]['myMetadata']['data'])) {
  68.             $meta = $itemsPaid[0]['myMetadata']['data'];
  69.         } elseif (isset($itemsPaid[0]['my_metadata']['data']) && is_array($itemsPaid[0]['my_metadata']['data'])) {
  70.             $meta = $itemsPaid[0]['my_metadata']['data'];
  71.         }
  72.     }
  73.  
  74.     return [
  75.         'patientId'   => $meta['patientId']   ?? null,
  76.         'orderId'     => $meta['orderId']     ?? null,
  77.         'operatorId'  => $meta['operatorId']  ?? null,
  78.         'scheduleId'  => $meta['scheduleId']  ?? null,
  79.         'facilityId'  => $meta['facilityId']  ?? null,
  80.         'encounterId' => $meta['encounterId'] ?? null,
  81.     ];
  82. }
  83.  
  84. /**
  85.  * Convert an ISO 8601 datetime string (e.g., "2025-08-11T21:54:26.422Z")
  86.  * into MySQL DATETIME ("Y-m-d H:i:s") in UTC.
  87.  */
  88. function isoToMysqlDateTime(?string $iso): ?string
  89. {
  90.     if (!$iso) return null;
  91.     $ts = strtotime($iso);
  92.     if ($ts === false) return null;
  93.     return gmdate('Y-m-d H:i:s', $ts);
  94. }
  95.  
  96. // -------------------------------
  97. // Flags / state
  98. // -------------------------------
  99. $received    = false;
  100. $logged      = false;
  101. $emailSent   = false;
  102. $emailError  = '';
  103. $dbSaved     = false;
  104. $dbError     = '';
  105.  
  106. $extracted = [
  107.     'patientId'   => null,
  108.     'orderId'     => null,
  109.     'operatorId'  => null,
  110.     'scheduleId'  => null,
  111.     'facilityId'  => null,
  112.     'encounterId' => null,
  113. ];
  114.  
  115. // -------------------------------
  116. // Main
  117. // -------------------------------
  118. if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  119.     $received = true;
  120.  
  121.     // 1) Raw body and JSON decode
  122.     $rawPayload = file_get_contents('php://input');
  123.     $decoded = json_decode($rawPayload, true);
  124.     $payloadForLog = (json_last_error() === JSON_ERROR_NONE) ? print_r($decoded, true) : $rawPayload;
  125.  
  126.     // 2) Extract business fields if JSON
  127.     if (is_array($decoded)) {
  128.         $extracted = extractMetaFromPayload($decoded);
  129.     }
  130.  
  131.     // 3) Read headers
  132.     $headers = getallheaders();
  133.  
  134.     // 4) Log file path
  135.     $logFile = __DIR__ . '/webhook_test_' . date('Y-m-d') . '.log';
  136.  
  137.     // 5) Build log entry
  138.     $logEntry  = "==== Webhook Received at " . date('Y-m-d H:i:s') . " ====" . PHP_EOL;
  139.     $logEntry .= "Remote IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'n/a') . PHP_EOL;
  140.     $logEntry .= "Headers:" . PHP_EOL . print_r($headers, true);
  141.     $logEntry .= "Payload:" . PHP_EOL . $payloadForLog . PHP_EOL;
  142.  
  143.     // 5a) Also log extracted + key mapped fields we will save
  144.     $logEntry .= "Extracted (from _embedded.itemsPaid[0].myMetadata.data):" . PHP_EOL;
  145.     foreach ($extracted as $k => $v) {
  146.         $logEntry .= "  {$k}: " . scalarToString($v) . PHP_EOL;
  147.     }
  148.  
  149.     // 5b) Prepare DB mapping values per your specification
  150.     $prettyAmount           = is_array($decoded) ? ($decoded['prettyAmount'] ?? null) : null;
  151.     $referenceId            = is_array($decoded) ? ($decoded['referenceId'] ?? null) : null;
  152.     $originalTransactionId  = is_array($decoded) ? ($decoded['id'] ?? null) : null;
  153.     $paymentType            = is_array($decoded) ? ($decoded['paymentType'] ?? null) : null;
  154.     $capturedDate           = is_array($decoded) ? ($decoded['capturedDate'] ?? null) : null;
  155.     $timestampMySQL         = isoToMysqlDateTime($capturedDate);
  156.     $clientId               = is_array($decoded) ? ($decoded['_embedded']['customer']['id'] ?? null) : null;
  157.     $cardBrand              = is_array($decoded) ? ($decoded['cardBrand'] ?? null) : null;
  158.     $cardLast4              = is_array($decoded) ? ($decoded['cardLast4'] ?? null) : null;
  159.  
  160.     $paymentProvider = PaymentProviderHelper::GLOBAL_PAYMENTS;
  161.  
  162.     // Action and section per request
  163.     $action        = 'Purchase';
  164.     $sectionType   = 'servicecharge';
  165.     $actionContext = 'Webhook';
  166. //    $actionContext = 'SAIL';
  167.  
  168.     // 6) Append mapping preview to log
  169.     $logEntry .= "DB Mapping Preview:" . PHP_EOL;
  170.     $logEntry .= "  patientId: " . scalarToString($extracted['patientId']) . PHP_EOL;
  171.     $logEntry .= "  operator_id (authId): " . scalarToString($extracted['operatorId']) . PHP_EOL;
  172.     $logEntry .= "  scheduleID: " . scalarToString($extracted['scheduleId']) . PHP_EOL;
  173.     $logEntry .= "  facility_id: " . scalarToString($extracted['facilityId']) . PHP_EOL;
  174.     $logEntry .= "  encounterId: " . scalarToString($extracted['encounterId']) . PHP_EOL;
  175.     $logEntry .= "  amount (prettyAmount): " . scalarToString($prettyAmount) . PHP_EOL;
  176.     $logEntry .= "  action: " . $action . PHP_EOL;
  177.     $logEntry .= "  transactionId: " . scalarToString($referenceId) . PHP_EOL;
  178.     $logEntry .= "  originalTransactionId: " . scalarToString($originalTransactionId) . PHP_EOL;
  179.     $logEntry .= "  payMethod (paymentType): " . scalarToString($paymentType) . PHP_EOL;
  180.     $logEntry .= "  timestamp (capturedDate->MySQL): " . scalarToString($timestampMySQL) . PHP_EOL;
  181.     $logEntry .= "  clientId (_embedded.customer.id): " . scalarToString($clientId) . PHP_EOL;
  182.     $logEntry .= "  section_type: " . $sectionType . PHP_EOL;
  183.     $logEntry .= "  account_type (cardBrand): " . scalarToString($cardBrand) . PHP_EOL;
  184.     $logEntry .= "  account_number (cardLast4): " . scalarToString($cardLast4) . PHP_EOL;
  185.     $logEntry .= "  actionContext: " . $actionContext . PHP_EOL;
  186.     $logEntry .= "  payment_provider: " . scalarToString($paymentProvider) . PHP_EOL . PHP_EOL;
  187.  
  188.     // 7) Write log
  189.     $bytes = file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
  190.     if ($bytes !== false) {
  191.         $logged = true;
  192.     }
  193.  
  194.     // 8) Insert into DB (payment_provider_payment_log)
  195.     try {
  196.         $plog_req_qry = "INSERT INTO payment_provider_payment_log SET
  197.            patientId = ?,
  198.            operator_id = ?,
  199.            scheduleID = ?,
  200.            facility_id = ?,
  201.            encounterId = ?,
  202.            amount = ?,
  203.            action = ?,
  204.            transactionId = ?,
  205.            originalTransactionId = ?,
  206.            payMethod = ?,
  207.            transaction_date = '" . date('Y-m-d H:i:s') . "',
  208.            timestamp = ?,
  209.            serviceDate = '" . date('Y-m-d') . "',
  210.            clientId = ?,
  211.            section_type = ?,
  212.            account_type = ?,
  213.            account_number = ?,
  214.            actionContext = ?,
  215.            payment_provider = ?";
  216.  
  217.         $params = [
  218.             $extracted['patientId'],               // patientId
  219.             $extracted['operatorId'],              // operator_id (authId)
  220.             $extracted['scheduleId'],              // scheduleID
  221.             $extracted['facilityId'],              // facility_id (loggedInFacility)
  222.             $extracted['encounterId'],             // encounterId
  223.             $prettyAmount,                         // amount
  224.             $action,                               // action
  225.             $referenceId,                          // transactionId
  226.             $originalTransactionId,                // originalTransactionId
  227.             $paymentType,                          // payMethod
  228.             $timestampMySQL ?: date('Y-m-d H:i:s'),// timestamp (fallback to now if missing)
  229.             $clientId,                             // clientId
  230.             $sectionType,                          // section_type
  231.             $cardBrand,                            // account_type
  232.             $cardLast4,                            // account_number
  233.             $actionContext,                        // actionContext
  234.             $paymentProvider                       // payment_provider
  235.         ];
  236.  
  237.         $dbRes = imwQuery($plog_req_qry, $params);
  238.         if ($dbRes === false) {
  239.             if (function_exists('imw_error')) {
  240.                 $dbError = 'DB insert failed: ' . imw_error();
  241.             } else {
  242.                 $dbError = 'DB insert failed: unknown error';
  243.             }
  244.         } else {
  245.             $dbSaved = true;
  246.         }
  247.     } catch (Throwable $t) {
  248.         $dbError = 'Exception during DB insert: ' . $t->getMessage();
  249.         file_put_contents(
  250.             $logFile,
  251.             "==== DB Error at " . date('Y-m-d H:i:s') . " ====\n{$dbError}\n\n",
  252.             FILE_APPEND | LOCK_EX
  253.         );
  254.     }
  255.  
  256.     // 9) Send debug email with everything
  257.     try {
  258.         $mail = new PHPMailer(true);
  259.         $mail->isSMTP();
  260.         $mail->Host       = 'smtp.office365.com';
  261.         $mail->SMTPAuth   = true;
  262.         $mail->Username   = '[email protected]';
  263.         $mail->Password   = 'G)147559698280ah';
  264.         $mail->SMTPSecure = 'tls';
  265.         $mail->Port       = 587;
  266.  
  267.         $mail->setFrom('[email protected]', 'SMTP_IMW');
  268.         $mail->addAddress('[email protected]', 'Vladimir Kuraica');
  269.  
  270.         $mail->Subject = 'Webhook Debug: ' . date('Y-m-d H:i:s');
  271.  
  272.         $bodyText  = "Webhook received at " . date('Y-m-d H:i:s') . "\n\n";
  273.         $bodyText .= "Remote IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'n/a') . "\n\n";
  274.         $bodyText .= "Headers:\n" . print_r($headers, true) . "\n";
  275.         $bodyText .= "Payload:\n" . $payloadForLog . "\n";
  276.         $bodyText .= "Extracted:\n" . print_r($extracted, true) . "\n";
  277.         $bodyText .= "DB Saved: " . ($dbSaved ? 'true' : 'false') . "\n";
  278.         if (!$dbSaved && $dbError) {
  279.             $bodyText .= "DB Error: " . $dbError . "\n";
  280.         }
  281.  
  282.         $mail->Body    = nl2br(htmlspecialchars($bodyText));
  283.         $mail->AltBody = $bodyText;
  284.  
  285.         $mail->send();
  286.         $emailSent = true;
  287.     } catch (Exception $e) {
  288.         $emailError = $e->getMessage();
  289.         file_put_contents(
  290.             $logFile,
  291.             "==== Email Error at " . date('Y-m-d H:i:s') . " ====\n{$emailError}\n\n",
  292.             FILE_APPEND | LOCK_EX
  293.         );
  294.     }
  295. }
  296.  
  297. // JSON response
  298. http_response_code(200);
  299. header('Content-Type: application/json');
  300.  
  301. $response = [
  302.     'status'     => 'success',
  303.     'received'   => $received,
  304.     'logged'     => $logged,
  305.     'emailSent'  => $emailSent,
  306.     'dbSaved'    => $dbSaved,
  307.     'extracted'  => $extracted,
  308. ];
  309.  
  310. if ($received && !$logged)    { $response['logError']   = 'Failed to write log file'; }
  311. if ($received && !$emailSent) { $response['emailError'] = $emailError; }
  312. if ($received && !$dbSaved)   { $response['dbError']    = $dbError; }
  313.  
  314. echo json_encode($response);
  315.  
Advertisement
Add Comment
Please, Sign In to add comment