feat: integrate Rybbit analytics alongside OpenPanel
Some checks failed
Build and Deploy / build (push) Has been cancelled

- Add RybbitService for tracking e-commerce events
- Update useAnalytics hook to track with both OpenPanel and Rybbit
- Add Rybbit script to layout for page view tracking
- Track all applicable store events: product views, cart, checkout, orders, search, etc.
This commit is contained in:
Unchained
2026-03-31 00:38:38 +02:00
parent 044aefae94
commit b3efebd3e4
11 changed files with 1422 additions and 118 deletions

View File

@@ -0,0 +1,310 @@
#!/usr/bin/env node
/**
* Test script for checkout shipping cost calculation
* Creates a checkout via API and verifies totalPrice includes shipping
*/
const SALEOR_API_URL = process.env.NEXT_PUBLIC_SALEOR_API_URL || 'https://api.manoonoils.com/graphql/';
// Test data
const TEST_VARIANT_ID = 'UHJvZHVjdFZhcmlhbnQ6Mjk0'; // Replace with actual variant ID
const TEST_EMAIL = 'test@example.com';
const TEST_SHIPPING_ADDRESS = {
firstName: 'Test',
lastName: 'User',
streetAddress1: '123 Test Street',
city: 'Belgrade',
postalCode: '11000',
country: 'RS',
phone: '+38160123456'
};
async function saleorFetch(query, variables = {}, token = null) {
const headers = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `JWT ${token}`;
}
const response = await fetch(SALEOR_API_URL, {
method: 'POST',
headers,
body: JSON.stringify({ query, variables }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.errors) {
throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`);
}
return result.data;
}
async function testCheckoutWithShipping() {
console.log('🧪 Testing checkout shipping cost calculation...\n');
try {
// Step 1: Create checkout
console.log('Step 1: Creating checkout...');
const checkoutCreateMutation = `
mutation CheckoutCreate($input: CheckoutCreateInput!) {
checkoutCreate(input: $input) {
checkout {
id
token
totalPrice {
gross {
amount
currency
}
}
subtotalPrice {
gross {
amount
currency
}
}
}
errors {
field
message
}
}
}
`;
const checkoutResult = await saleorFetch(checkoutCreateMutation, {
input: {
channel: 'default-channel',
email: TEST_EMAIL,
lines: [],
languageCode: 'SR'
}
});
if (checkoutResult.checkoutCreate.errors?.length > 0) {
throw new Error(`Checkout creation failed: ${checkoutResult.checkoutCreate.errors[0].message}`);
}
const checkout = checkoutResult.checkoutCreate.checkout;
console.log(`✅ Checkout created: ${checkout.id}`);
console.log(` Token: ${checkout.token}`);
console.log(` Initial total: ${checkout.totalPrice.gross.amount} ${checkout.totalPrice.gross.currency}\n`);
// Step 2: Add product to checkout
console.log('Step 2: Adding product to checkout...');
const linesAddMutation = `
mutation CheckoutLinesAdd($checkoutId: ID!, $lines: [CheckoutLineInput!]!) {
checkoutLinesAdd(checkoutId: $checkoutId, lines: $lines) {
checkout {
id
totalPrice {
gross {
amount
currency
}
}
subtotalPrice {
gross {
amount
currency
}
}
lines {
id
quantity
totalPrice {
gross {
amount
}
}
}
}
errors {
field
message
}
}
}
`;
// First, let's query for available products to get a real variant ID
console.log(' Querying available products...');
const productsQuery = `
query Products {
products(channel: "default-channel", first: 1) {
edges {
node {
id
name
variants {
id
name
}
}
}
}
}
`;
const productsResult = await saleorFetch(productsQuery);
const product = productsResult.products.edges[0]?.node;
if (!product || !product.variants?.[0]) {
throw new Error('No products found in store');
}
const variantId = product.variants[0].id;
console.log(` Product: ${product.name}, Variant: ${product.variants[0].name}`);
const linesResult = await saleorFetch(linesAddMutation, {
checkoutId: checkout.id,
lines: [{ variantId, quantity: 1 }]
});
if (linesResult.checkoutLinesAdd.errors?.length > 0) {
throw new Error(`Adding lines failed: ${linesResult.checkoutLinesAdd.errors[0].message}`);
}
const checkoutWithLines = linesResult.checkoutLinesAdd.checkout;
const productTotal = checkoutWithLines.totalPrice.gross.amount;
console.log(`✅ Product added (qty: 1)`);
console.log(` Product total: ${productTotal} RSD\n`);
// Step 3: Set shipping address
console.log('Step 3: Setting shipping address...');
const shippingAddressMutation = `
mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: AddressInput!) {
checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) {
checkout {
id
shippingMethods {
id
name
price {
amount
currency
}
}
}
errors {
field
message
}
}
}
`;
const shippingResult = await saleorFetch(shippingAddressMutation, {
checkoutId: checkout.id,
shippingAddress: TEST_SHIPPING_ADDRESS
});
if (shippingResult.checkoutShippingAddressUpdate.errors?.length > 0) {
throw new Error(`Setting shipping address failed: ${shippingResult.checkoutShippingAddressUpdate.errors[0].message}`);
}
const availableMethods = shippingResult.checkoutShippingAddressUpdate.checkout.shippingMethods;
console.log(`✅ Shipping address set`);
console.log(` Available shipping methods: ${availableMethods.length}`);
if (availableMethods.length === 0) {
console.log(' ⚠️ No shipping methods available for this address/region');
return;
}
availableMethods.forEach((method, i) => {
console.log(` [${i + 1}] ${method.name}: ${method.price.amount} ${method.price.currency}`);
});
console.log('');
// Step 4: Set shipping method
const selectedMethod = availableMethods[0];
console.log(`Step 4: Selecting shipping method: ${selectedMethod.name} (${selectedMethod.price.amount} RSD)...`);
const shippingMethodMutation = `
mutation CheckoutShippingMethodUpdate($checkoutId: ID!, $shippingMethodId: ID!) {
checkoutShippingMethodUpdate(checkoutId: $checkoutId, shippingMethodId: $shippingMethodId) {
checkout {
id
totalPrice {
gross {
amount
currency
}
}
subtotalPrice {
gross {
amount
currency
}
}
shippingPrice {
gross {
amount
currency
}
}
}
errors {
field
message
}
}
}
`;
const methodResult = await saleorFetch(shippingMethodMutation, {
checkoutId: checkout.id,
shippingMethodId: selectedMethod.id
});
if (methodResult.checkoutShippingMethodUpdate.errors?.length > 0) {
throw new Error(`Setting shipping method failed: ${methodResult.checkoutShippingMethodUpdate.errors[0].message}`);
}
const finalCheckout = methodResult.checkoutShippingMethodUpdate.checkout;
const subtotal = finalCheckout.subtotalPrice.gross.amount;
const shipping = finalCheckout.shippingPrice.gross.amount;
const finalTotal = finalCheckout.totalPrice.gross.amount;
const expectedTotal = subtotal + shipping;
console.log(`✅ Shipping method set`);
console.log(` Subtotal: ${subtotal} RSD`);
console.log(` Shipping: ${shipping} RSD`);
console.log(` Total: ${finalTotal} RSD`);
console.log(` Expected: ${expectedTotal} RSD`);
console.log('');
// Verification
console.log('📊 VERIFICATION:');
if (finalTotal === expectedTotal) {
console.log('✅ PASS: Total includes shipping cost correctly');
console.log(` ${subtotal} + ${shipping} = ${finalTotal}`);
} else {
console.log('❌ FAIL: Total does NOT include shipping cost');
console.log(` Expected: ${expectedTotal}, Got: ${finalTotal}`);
console.log(` Difference: ${expectedTotal - finalTotal}`);
}
// Cleanup - delete checkout
console.log('\n🧹 Cleaning up test checkout...');
// Note: Checkout deletion requires admin permissions
console.log(` Checkout ID for manual cleanup: ${checkout.id}`);
} catch (error) {
console.error('\n❌ Test failed:', error.message);
process.exit(1);
}
}
// Run the test
testCheckoutWithShipping();

View File

137
scripts/test-frontend.mjs Normal file
View File

@@ -0,0 +1,137 @@
const SALEOR_API_URL = 'https://api.manoonoils.com/graphql/';
async function saleorFetch(query, variables = {}) {
const response = await fetch(SALEOR_API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables }),
});
const result = await response.json();
if (result.errors) {
console.error('GraphQL Errors:', JSON.stringify(result.errors, null, 2));
throw new Error(JSON.stringify(result.errors));
}
return result.data;
}
async function test() {
// Create checkout
const createResult = await saleorFetch(`
mutation {
checkoutCreate(input: {
channel: "default-channel"
email: "test@test.com"
lines: [{ variantId: "UHJvZHVjdFZhcmlhbnQ6Mjk0", quantity: 1 }]
languageCode: SR
}) {
checkout {
id
token
totalPrice { gross { amount } }
subtotalPrice { gross { amount } }
}
errors {
field
message
code
}
}
}
`);
if (createResult.checkoutCreate.errors?.length > 0) {
console.error('Checkout creation errors:', createResult.checkoutCreate.errors);
throw new Error('Checkout creation failed');
}
if (!createResult.checkoutCreate.checkout) {
console.error('Create result:', createResult);
throw new Error('Checkout creation returned null');
}
const checkout = createResult.checkoutCreate.checkout;
const token = checkout.token;
console.log('Created checkout:');
console.log(' ID:', checkout.id);
console.log(' Token:', token);
console.log(' Initial Total:', checkout.totalPrice.gross.amount);
// Set address
await saleorFetch(`
mutation {
checkoutShippingAddressUpdate(
checkoutId: "${checkout.id}"
shippingAddress: {
firstName: "Test"
lastName: "User"
streetAddress1: "123 Street"
city: "Belgrade"
postalCode: "11000"
country: "RS"
phone: "+38160123456"
}
) {
checkout {
shippingMethods { id name price { amount } }
}
}
}
`);
// Query by token (what refreshCheckout does)
const tokenQuery = await saleorFetch(`
query {
checkout(token: "${token}") {
id
token
totalPrice { gross { amount } }
subtotalPrice { gross { amount } }
shippingPrice { gross { amount } }
shippingMethods { id name price { amount } }
}
}
`);
console.log('\nQuery by token (before shipping method):');
console.log(' Total:', tokenQuery.checkout.totalPrice.gross.amount);
console.log(' Subtotal:', tokenQuery.checkout.subtotalPrice.gross.amount);
console.log(' Shipping:', tokenQuery.checkout.shippingPrice.gross.amount);
console.log(' Methods:', tokenQuery.checkout.shippingMethods.length);
if (tokenQuery.checkout.shippingMethods.length > 0) {
const methodId = tokenQuery.checkout.shippingMethods[0].id;
// Set shipping method
await saleorFetch(`
mutation {
checkoutShippingMethodUpdate(
checkoutId: "${checkout.id}"
shippingMethodId: "${methodId}"
) {
checkout {
totalPrice { gross { amount } }
subtotalPrice { gross { amount } }
shippingPrice { gross { amount } }
}
}
}
`);
// Query by token again (what should happen after refreshCheckout)
const afterMethod = await saleorFetch(`
query {
checkout(token: "${token}") {
totalPrice { gross { amount } }
subtotalPrice { gross { amount } }
shippingPrice { gross { amount } }
}
}
`);
console.log('\nQuery by token (AFTER shipping method):');
console.log(' Total:', afterMethod.checkout.totalPrice.gross.amount);
console.log(' Subtotal:', afterMethod.checkout.subtotalPrice.gross.amount);
console.log(' Shipping:', afterMethod.checkout.shippingPrice.gross.amount);
}
}
test().catch(console.error);

View File

@@ -0,0 +1,254 @@
#!/usr/bin/env node
/**
* Complete API test simulating frontend checkout flow
* Tests every step the frontend takes
*/
const SALEOR_API_URL = 'https://api.manoonoils.com/graphql/';
async function saleorFetch(query, variables = {}) {
const response = await fetch(SALEOR_API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query.replace(/\n\s*/g, ' '), variables }),
});
const result = await response.json();
if (result.errors) {
console.error('GraphQL Error:', JSON.stringify(result.errors, null, 2));
throw new Error(result.errors[0].message);
}
return result.data;
}
async function runTest() {
console.log('🧪 TESTING FRONTEND CHECKOUT FLOW\n');
console.log('=' .repeat(50));
let checkoutId = null;
let checkoutToken = null;
let shippingMethodId = null;
try {
// STEP 1: Create checkout (like frontend does on first cart add)
console.log('\n📦 STEP 1: Create Checkout');
console.log('-'.repeat(50));
const createResult = await saleorFetch(`
mutation CheckoutCreate($input: CheckoutCreateInput!) {
checkoutCreate(input: $input) {
checkout {
id
token
totalPrice { gross { amount currency } }
subtotalPrice { gross { amount } }
}
errors { field message }
}
}
`, {
input: {
channel: "default-channel",
email: "test@test.com",
lines: [],
languageCode: "SR"
}
});
checkoutId = createResult.checkoutCreate.checkout.id;
checkoutToken = createResult.checkoutCreate.checkout.token;
console.log('✅ Checkout created');
console.log(' ID:', checkoutId);
console.log(' Token:', checkoutToken);
console.log(' Initial Total:', createResult.checkoutCreate.checkout.totalPrice.gross.amount, 'RSD');
// STEP 2: Add product (like frontend does)
console.log('\n🛒 STEP 2: Add Product to Cart');
console.log('-'.repeat(50));
// Get a valid variant first
const productsResult = await saleorFetch(`
query {
products(channel: "default-channel", first: 1) {
edges {
node {
variants { id name }
}
}
}
}
`);
const variantId = productsResult.products.edges[0].node.variants[0].id;
const addLineResult = await saleorFetch(`
mutation CheckoutLinesAdd($checkoutId: ID!, $lines: [CheckoutLineInput!]!) {
checkoutLinesAdd(checkoutId: $checkoutId, lines: $lines) {
checkout {
id
token
totalPrice { gross { amount currency } }
subtotalPrice { gross { amount } }
}
errors { field message }
}
}
`, {
checkoutId: checkoutId,
lines: [{ variantId: variantId, quantity: 1 }]
});
const afterAdd = addLineResult.checkoutLinesAdd.checkout;
console.log('✅ Product added');
console.log(' Product Total:', afterAdd.totalPrice.gross.amount, 'RSD');
console.log(' Subtotal:', afterAdd.subtotalPrice.gross.amount, 'RSD');
// STEP 3: Refresh checkout by token (what refreshCheckout() does)
console.log('\n🔄 STEP 3: Refresh Checkout by Token');
console.log('-'.repeat(50));
console.log(' (This simulates what refreshCheckout() does in the store)');
const refreshResult = await saleorFetch(`
query GetCheckout($token: UUID!) {
checkout(token: $token) {
id
token
totalPrice { gross { amount currency } }
subtotalPrice { gross { amount } }
}
}
`, { token: checkoutToken });
console.log('✅ Refreshed checkout');
console.log(' Total from refresh:', refreshResult.checkout.totalPrice.gross.amount, 'RSD');
// STEP 4: Set shipping address
console.log('\n📍 STEP 4: Set Shipping Address');
console.log('-'.repeat(50));
const addressResult = await saleorFetch(`
mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: AddressInput!) {
checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) {
checkout {
id
shippingMethods { id name price { amount currency } }
}
errors { field message }
}
}
`, {
checkoutId: checkoutId,
shippingAddress: {
firstName: "Test",
lastName: "User",
streetAddress1: "123 Test Street",
city: "Belgrade",
postalCode: "11000",
country: "RS",
phone: "+38160123456"
}
});
const methods = addressResult.checkoutShippingAddressUpdate.checkout.shippingMethods;
console.log('✅ Address set');
console.log(' Available shipping methods:', methods.length);
if (methods.length === 0) {
console.log('❌ No shipping methods available!');
return;
}
methods.forEach((m, i) => {
console.log(` [${i+1}] ${m.name}: ${m.price.amount} ${m.price.currency}`);
});
shippingMethodId = methods[0].id;
const shippingPrice = methods[0].price.amount;
// STEP 5: Select shipping method (what happens when user clicks radio button)
console.log('\n🚚 STEP 5: Select Shipping Method');
console.log('-'.repeat(50));
console.log(` Selecting: ${methods[0].name} (${shippingPrice} RSD)`);
const methodResult = await saleorFetch(`
mutation CheckoutShippingMethodUpdate($checkoutId: ID!, $shippingMethodId: ID!) {
checkoutShippingMethodUpdate(checkoutId: $checkoutId, shippingMethodId: $shippingMethodId) {
checkout {
id
totalPrice { gross { amount currency } }
subtotalPrice { gross { amount } }
shippingPrice { gross { amount } }
}
errors { field message }
}
}
`, {
checkoutId: checkoutId,
shippingMethodId: shippingMethodId
});
const afterMethod = methodResult.checkoutShippingMethodUpdate.checkout;
console.log('✅ Shipping method set');
console.log(' Total:', afterMethod.totalPrice.gross.amount, 'RSD');
console.log(' Subtotal:', afterMethod.subtotalPrice.gross.amount, 'RSD');
console.log(' Shipping:', afterMethod.shippingPrice.gross.amount, 'RSD');
// STEP 6: Refresh checkout again (what refreshCheckout() does after setting method)
console.log('\n🔄 STEP 6: Refresh Checkout Again');
console.log('-'.repeat(50));
console.log(' (Simulating refreshCheckout() call in handleShippingMethodSelect)');
const finalRefresh = await saleorFetch(`
query GetCheckout($token: UUID!) {
checkout(token: $token) {
id
token
totalPrice { gross { amount currency } }
subtotalPrice { gross { amount } }
shippingPrice { gross { amount } }
}
}
`, { token: checkoutToken });
const final = finalRefresh.checkout;
console.log('✅ Final checkout state after refresh:');
console.log(' Total:', final.totalPrice.gross.amount, 'RSD');
console.log(' Subtotal:', final.subtotalPrice.gross.amount, 'RSD');
console.log(' Shipping:', final.shippingPrice.gross.amount, 'RSD');
// VERIFICATION
console.log('\n📊 VERIFICATION');
console.log('=' .repeat(50));
const expectedTotal = final.subtotalPrice.gross.amount + final.shippingPrice.gross.amount;
const actualTotal = final.totalPrice.gross.amount;
if (actualTotal === expectedTotal) {
console.log('✅ PASS: API returns correct total with shipping');
console.log(` ${final.subtotalPrice.gross.amount} + ${final.shippingPrice.gross.amount} = ${actualTotal}`);
} else {
console.log('❌ FAIL: API total does not include shipping');
console.log(` Expected: ${expectedTotal}, Got: ${actualTotal}`);
}
console.log('\n🔍 FRONTEND ISSUE ANALYSIS');
console.log('=' .repeat(50));
console.log('The API works correctly. The bug is in the frontend.');
console.log('');
console.log('What should happen:');
console.log(' 1. User selects shipping method → handleShippingMethodSelect()');
console.log(' 2. Calls checkoutService.updateShippingMethod() → API updates');
console.log(' 3. Calls refreshCheckout() → store updates with new checkout');
console.log(' 4. Component re-renders with new checkout.totalPrice');
console.log('');
console.log('Check browser console for:');
console.log(' - [Checkout Debug] logs showing totalPrice values');
console.log(' - Network tab showing the GraphQL mutation/refresh calls');
console.log(' - React DevTools showing if checkout object updates');
} catch (error) {
console.error('\n❌ Test failed:', error.message);
process.exit(1);
}
}
runTest();

View File

@@ -0,0 +1,232 @@
#!/usr/bin/env node
/**
* Full order creation test via API
* Tests complete checkout flow including order completion
*/
const SALEOR_API_URL = 'https://api.manoonoils.com/graphql/';
async function saleorFetch(query, variables = {}) {
const response = await fetch(SALEOR_API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query.replace(/\n\s*/g, ' '), variables }),
});
const result = await response.json();
if (result.errors) {
console.error('GraphQL Error:', JSON.stringify(result.errors, null, 2));
throw new Error(result.errors[0].message);
}
return result.data;
}
async function runOrderTest() {
console.log('🧪 FULL ORDER CREATION TEST ON DEV BRANCH\n');
console.log('=' .repeat(60));
try {
// STEP 1: Create checkout
console.log('\n📦 STEP 1: Create Checkout');
const createResult = await saleorFetch(`
mutation CheckoutCreate($input: CheckoutCreateInput!) {
checkoutCreate(input: $input) {
checkout {
id
token
totalPrice { gross { amount currency } }
}
errors { field message }
}
}
`, {
input: {
channel: "default-channel",
email: "test-order@example.com",
lines: [],
languageCode: "SR"
}
});
const checkoutId = createResult.checkoutCreate.checkout.id;
console.log('✅ Checkout created:', checkoutId);
// STEP 2: Get product and add to cart
console.log('\n🛒 STEP 2: Add Product');
const productsResult = await saleorFetch(`
query {
products(channel: "default-channel", first: 1) {
edges { node { variants { id name } } }
}
}
`);
const variantId = productsResult.products.edges[0].node.variants[0].id;
await saleorFetch(`
mutation CheckoutLinesAdd($checkoutId: ID!, $lines: [CheckoutLineInput!]!) {
checkoutLinesAdd(checkoutId: $checkoutId, lines: $lines) {
checkout { id }
errors { field message }
}
}
`, {
checkoutId: checkoutId,
lines: [{ variantId: variantId, quantity: 1 }]
});
console.log('✅ Product added');
// STEP 3: Update email
console.log('\n📧 STEP 3: Update Email');
await saleorFetch(`
mutation CheckoutEmailUpdate($checkoutId: ID!, $email: String!) {
checkoutEmailUpdate(checkoutId: $checkoutId, email: $email) {
checkout { id }
errors { field message }
}
}
`, { checkoutId: checkoutId, email: "test-order@example.com" });
console.log('✅ Email updated');
// STEP 4: Set shipping address
console.log('\n📍 STEP 4: Set Shipping Address');
await saleorFetch(`
mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: AddressInput!) {
checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) {
checkout {
id
shippingMethods { id name price { amount } }
}
errors { field message }
}
}
`, {
checkoutId: checkoutId,
shippingAddress: {
firstName: "Test",
lastName: "User",
streetAddress1: "123 Test Street",
city: "Belgrade",
postalCode: "11000",
country: "RS",
phone: "+38160123456"
}
});
// Get shipping methods
const methodsResult = await saleorFetch(`
query GetCheckout($token: UUID!) {
checkout(token: $token) {
shippingMethods { id name price { amount } }
}
}
`, { token: createResult.checkoutCreate.checkout.token });
const shippingMethodId = methodsResult.checkout.shippingMethods[0].id;
console.log('✅ Address set, shipping method available:', methodsResult.checkout.shippingMethods[0].name);
// STEP 5: Set billing address
console.log('\n💳 STEP 5: Set Billing Address');
await saleorFetch(`
mutation CheckoutBillingAddressUpdate($checkoutId: ID!, $billingAddress: AddressInput!) {
checkoutBillingAddressUpdate(checkoutId: $checkoutId, billingAddress: $billingAddress) {
checkout { id }
errors { field message }
}
}
`, {
checkoutId: checkoutId,
billingAddress: {
firstName: "Test",
lastName: "User",
streetAddress1: "123 Test Street",
city: "Belgrade",
postalCode: "11000",
country: "RS",
phone: "+38160123456"
}
});
console.log('✅ Billing address set');
// STEP 6: Select shipping method
console.log('\n🚚 STEP 6: Select Shipping Method');
await saleorFetch(`
mutation CheckoutShippingMethodUpdate($checkoutId: ID!, $shippingMethodId: ID!) {
checkoutShippingMethodUpdate(checkoutId: $checkoutId, shippingMethodId: $shippingMethodId) {
checkout {
id
totalPrice { gross { amount } }
subtotalPrice { gross { amount } }
shippingPrice { gross { amount } }
}
errors { field message }
}
}
`, { checkoutId: checkoutId, shippingMethodId: shippingMethodId });
console.log('✅ Shipping method selected');
// STEP 7: Complete checkout (create order)
console.log('\n✅ STEP 7: Complete Checkout (Create Order)');
console.log('-'.repeat(60));
const completeResult = await saleorFetch(`
mutation CheckoutComplete($checkoutId: ID!) {
checkoutComplete(checkoutId: $checkoutId) {
order {
id
number
status
created
total {
gross { amount currency }
}
subtotal {
gross { amount }
}
shippingPrice {
gross { amount }
}
}
errors { field message }
}
}
`, { checkoutId: checkoutId });
if (completeResult.checkoutComplete.errors?.length > 0) {
throw new Error(`Order creation failed: ${completeResult.checkoutComplete.errors[0].message}`);
}
const order = completeResult.checkoutComplete.order;
console.log('✅ ORDER CREATED SUCCESSFULLY!');
console.log('');
console.log('Order Details:');
console.log(' Order ID:', order.id);
console.log(' Order Number:', order.number);
console.log(' Status:', order.status);
console.log(' Created:', order.created);
console.log('');
console.log('Pricing:');
console.log(' Subtotal:', order.subtotal.gross.amount, 'RSD');
console.log(' Shipping:', order.shippingPrice.gross.amount, 'RSD');
console.log(' Total:', order.total.gross.amount, 'RSD');
// Verification
const expectedTotal = order.subtotal.gross.amount + order.shippingPrice.gross.amount;
console.log('');
console.log('📊 VERIFICATION:');
if (order.total.gross.amount === expectedTotal) {
console.log('✅ PASS: Order total includes shipping correctly');
console.log(` ${order.subtotal.gross.amount} + ${order.shippingPrice.gross.amount} = ${order.total.gross.amount}`);
} else {
console.log('❌ FAIL: Order total does not match expected');
}
console.log('');
console.log('🎉 DEV BRANCH TEST COMPLETE - ALL SYSTEMS GO!');
} catch (error) {
console.error('\n❌ Test failed:', error.message);
process.exit(1);
}
}
runOrderTest();