shopify app init
shopify app generate extension --template=web_pixel --name=my-webpixel
shopify app deploy
shopify app release
Once the app is installed on the store, it will be shown in Customer Events but disconnected.
You must run GraphQL to install it, USING THE CREDENTIALS FOR THE APP.
string shopDomain = "mystore.myshopify.com";
string apiVersion = "2025-10"; // use a current version
string clientId = "myclientId"; // In the Dev Dashboard
string clientSecret = "myClientSecret";
// gid://shopify/WebPixel/3094610295
string query = @"
mutation CreateWebPixel($settings: JSON!) {
webPixelCreate(webPixel: { settings: $settings }) {
userErrors { code field message }
webPixel { id settings }
}
}";
public async Task Main()
{
var variables = new { settings = "{\"accountID\":\"123\"}" };
var payload = new { query, variables };
var json = JsonSerializer.Serialize(payload);
try
{
var cache = new MemoryCache(new MemoryCacheOptions());
var services = new ServiceCollection()
.AddHttpClient() // registers IHttpClientFactory and the default plumbing
.BuildServiceProvider();
var factory = services.GetRequiredService<IHttpClientFactory>();
var client = factory.CreateClient("shopify");
var tokenService = new ShopifyTokenService(client, cache, shopDomain, clientId, clientSecret);
client.BaseAddress = new Uri($"https://{shopDomain}/admin/api/2025-04/graphql.json");
client.DefaultRequestHeaders.Add("X-Shopify-Access-Token", await tokenService.GetTokenAsync());
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("", content);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(response.StatusCode);
Console.WriteLine(responseString);
}
catch (Exception error)
{
while (error != null)
{
Console.WriteLine(error.Message);
error = error.InnerException;
}
}
}
public sealed class ShopifyTokenService
{
private readonly HttpClient httpClient;
private readonly IMemoryCache cache;
private readonly string shopDomain;
private readonly string clientId;
private readonly string clientSecret;
private readonly int fallbackTtl = 30;
private static readonly string cacheKey = "shopify_admin_token";
public ShopifyTokenService(HttpClient httpClient, IMemoryCache cache, string shopDomain, string clientId, string clientSecret)
{
this.httpClient = httpClient;
this.cache = cache;
this.shopDomain = shopDomain;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
public async Task<string> GetTokenAsync()
{
if (cache.TryGetValue(cacheKey, out string token))
return token;
var endpoint = $"https://{shopDomain}/admin/oauth/access_token";
var body = new { grant_type = "client_credentials", client_id = clientId, client_secret = clientSecret };
Debug.WriteLine("Requesting a Shopify access token");
var resp = await httpClient.PostAsync(endpoint,
new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json"));
var text = await resp.Content.ReadAsStringAsync();
resp.EnsureSuccessStatusCode();
var parsed = JsonSerializer.Deserialize<TokenResponse>(text, new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
?? throw new InvalidOperationException("Token parse error.");
TimeSpan ttl = TimeSpan.FromMinutes(fallbackTtl);
if (parsed.ExpiresInSeconds.HasValue)
{
ttl = TimeSpan.FromSeconds(parsed.ExpiresInSeconds.Value - 30);
}
Debug.WriteLine($"Access token received with a TTL of {ttl.TotalSeconds:N0} seconds ({ttl.TotalMinutes:0} minutes or {ttl.TotalHours:0} hours)");
cache.Set(cacheKey, parsed.AccessToken, ttl);
return parsed.AccessToken;
}
private sealed class TokenResponse
{
[JsonPropertyName("access_token")]
public string AccessToken { get; set; } = "";
[JsonPropertyName("expires_in")]
public int? ExpiresInSeconds { get; set; }
[JsonPropertyName("scope")]
public string Scope { get; set; }
}
}
No comments:
Post a Comment