Replace Shopify Buy Button iframe with custom implementation using Storefront API 2026-01. Create interactive cart drawer with full product management capabilities (add, remove, update quantities) and seamless checkout flow. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
232 lines
5 KiB
JavaScript
232 lines
5 KiB
JavaScript
/**
|
|
* Shopify Storefront API Client
|
|
* Custom implementation using Cart API (2026-01)
|
|
*/
|
|
class ShopifyCart {
|
|
constructor(config) {
|
|
this.domain = config.domain;
|
|
this.storefrontAccessToken = config.storefrontAccessToken;
|
|
this.apiVersion = '2026-01';
|
|
this.endpoint = `https://${this.domain}/api/${this.apiVersion}/graphql.json`;
|
|
this.cartId = null;
|
|
this.cartItems = [];
|
|
|
|
// Load existing cart from localStorage
|
|
this.loadCart();
|
|
}
|
|
|
|
/**
|
|
* Make GraphQL request to Shopify Storefront API
|
|
*/
|
|
async query(query, variables = {}) {
|
|
const response = await fetch(this.endpoint, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Shopify-Storefront-Access-Token': this.storefrontAccessToken,
|
|
},
|
|
body: JSON.stringify({ query, variables }),
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.errors) {
|
|
console.error('Shopify API Error:', result.errors);
|
|
throw new Error(result.errors[0].message);
|
|
}
|
|
|
|
return result.data;
|
|
}
|
|
|
|
/**
|
|
* Get product information by ID
|
|
*/
|
|
async getProduct(productId) {
|
|
const query = `
|
|
query getProduct($id: ID!) {
|
|
product(id: $id) {
|
|
id
|
|
title
|
|
description
|
|
availableForSale
|
|
variants(first: 10) {
|
|
edges {
|
|
node {
|
|
id
|
|
title
|
|
price {
|
|
amount
|
|
currencyCode
|
|
}
|
|
availableForSale
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
const data = await this.query(query, {
|
|
id: `gid://shopify/Product/${productId}`
|
|
});
|
|
|
|
return data.product;
|
|
}
|
|
|
|
/**
|
|
* Create a new cart
|
|
*/
|
|
async createCart(lines = []) {
|
|
const query = `
|
|
mutation cartCreate($input: CartInput!) {
|
|
cartCreate(input: $input) {
|
|
cart {
|
|
id
|
|
checkoutUrl
|
|
lines(first: 10) {
|
|
edges {
|
|
node {
|
|
id
|
|
quantity
|
|
merchandise {
|
|
... on ProductVariant {
|
|
id
|
|
title
|
|
price {
|
|
amount
|
|
currencyCode
|
|
}
|
|
product {
|
|
title
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
userErrors {
|
|
field
|
|
message
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
const data = await this.query(query, {
|
|
input: { lines }
|
|
});
|
|
|
|
if (data.cartCreate.userErrors.length > 0) {
|
|
throw new Error(data.cartCreate.userErrors[0].message);
|
|
}
|
|
|
|
this.cartId = data.cartCreate.cart.id;
|
|
this.saveCart();
|
|
|
|
return data.cartCreate.cart;
|
|
}
|
|
|
|
/**
|
|
* Add item to cart
|
|
*/
|
|
async addToCart(variantId, quantity = 1) {
|
|
const lines = [{
|
|
merchandiseId: `gid://shopify/ProductVariant/${variantId}`,
|
|
quantity: quantity
|
|
}];
|
|
|
|
let cart;
|
|
|
|
if (this.cartId) {
|
|
// Add to existing cart
|
|
const query = `
|
|
mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
|
|
cartLinesAdd(cartId: $cartId, lines: $lines) {
|
|
cart {
|
|
id
|
|
checkoutUrl
|
|
lines(first: 10) {
|
|
edges {
|
|
node {
|
|
id
|
|
quantity
|
|
merchandise {
|
|
... on ProductVariant {
|
|
id
|
|
title
|
|
price {
|
|
amount
|
|
currencyCode
|
|
}
|
|
product {
|
|
title
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
userErrors {
|
|
field
|
|
message
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
const data = await this.query(query, {
|
|
cartId: this.cartId,
|
|
lines
|
|
});
|
|
|
|
if (data.cartLinesAdd.userErrors.length > 0) {
|
|
throw new Error(data.cartLinesAdd.userErrors[0].message);
|
|
}
|
|
|
|
cart = data.cartLinesAdd.cart;
|
|
} else {
|
|
// Create new cart
|
|
cart = await this.createCart(lines);
|
|
}
|
|
|
|
this.cartItems = cart.lines.edges;
|
|
return cart;
|
|
}
|
|
|
|
/**
|
|
* Get checkout URL to redirect user
|
|
*/
|
|
getCheckoutUrl(cart) {
|
|
return cart?.checkoutUrl || null;
|
|
}
|
|
|
|
/**
|
|
* Save cart ID to localStorage
|
|
*/
|
|
saveCart() {
|
|
if (this.cartId) {
|
|
localStorage.setItem('shopify_cart_id', this.cartId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load cart ID from localStorage
|
|
*/
|
|
loadCart() {
|
|
this.cartId = localStorage.getItem('shopify_cart_id');
|
|
}
|
|
|
|
/**
|
|
* Clear cart
|
|
*/
|
|
clearCart() {
|
|
this.cartId = null;
|
|
this.cartItems = [];
|
|
localStorage.removeItem('shopify_cart_id');
|
|
}
|
|
}
|
|
|
|
// Export for use in other scripts
|
|
window.ShopifyCart = ShopifyCart;
|