Skip to content

Instantly share code, notes, and snippets.

@anova
Created February 9, 2026 11:43
Show Gist options
  • Select an option

  • Save anova/6545da90d69db918dc87b87c7caf6615 to your computer and use it in GitHub Desktop.

Select an option

Save anova/6545da90d69db918dc87b87c7caf6615 to your computer and use it in GitHub Desktop.
Shopify access token retriever. Needs .env file on same folder.
<?php
/**
* Shopify OAuth Access Token Handler
* This script verifies the HMAC from Shopify and exchanges the authorization code for an access token.
*/
// 1. Load environment variables from .env
function loadEnv($path) {
if (!file_exists($path)) {
return false;
}
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos(trim($line), '#') === 0) continue;
$parts = explode('=', $line, 2);
if (count($parts) === 2) {
$key = trim($parts[0]);
$value = trim($parts[1]);
putenv("$key=$value");
$_ENV[$key] = $value;
}
}
return true;
}
loadEnv(__DIR__ . '/.env');
$apiKey = getenv('CLIENT_ID');
$apiSecret = getenv('SECRET');
// 2. Capture parameters from Shopify
$params = $_GET;
$hmac = $params['hmac'] ?? '';
// Note: In this context, Shopify sends hmac, host, shop, and timestamp
if (empty($hmac) || empty($params['shop']) || empty($params['timestamp'])) {
http_response_code(400);
die("Missing required parameters (hmac, shop, or timestamp).");
}
// 3. Verify HMAC
// Remove hmac from the params to verify
unset($params['hmac']);
ksort($params);
// Reconstruct the query string using key=value pairs joined by &
$pairs = [];
foreach ($params as $key => $value) {
$pairs[] = "$key=$value";
}
$computedString = implode('&', $pairs);
$calculatedHmac = hash_hmac('sha256', $computedString, $apiSecret);
if (!hash_equals($hmac, $calculatedHmac)) {
http_response_code(401);
die("HMAC verification failed. The request is not authentic.");
}
// 4. Get Access Token via client_credentials
// Since we don't have a 'code', we use client_credentials as seen in your Node.js script.
$query = [
"grant_type" => "client_credentials",
"client_id" => $apiKey,
"client_secret" => $apiSecret
];
$accessTokenUrl = "https://" . $params['shop'] . "/admin/oauth/access_token";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $accessTokenUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($query));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Accept: application/json"]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
http_response_code(500);
die("CURL Error: " . $error);
}
$result = json_decode($response, true);
if ($httpCode === 200 && isset($result['access_token'])) {
header('Content-Type: application/json');
echo json_encode([
"status" => "success",
"access_token" => $result['access_token'],
"shop" => $params['shop']
], JSON_PRETTY_PRINT);
} else {
http_response_code($httpCode);
header('Content-Type: application/json');
echo json_encode([
"status" => "error",
"message" => "Failed to retrieve access token using client_credentials",
"details" => $result
], JSON_PRETTY_PRINT);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment