1// ===== cart.go =====
  2// Package main complete.go
  3package main
  4
  5import (
  6	"encoding/json"
  7	"fmt"
  8	"log"
  9	"strconv"
 10	"strings"
 11	"syscall/js"
 12)
 13
 14func updateCartDisplayWrapper(this js.Value, args []js.Value) interface{} {
 15	updateCartDisplay()
 16	return nil
 17}
 18
 19func saveCart() {
 20	cartJSON, err := json.Marshal(cart)
 21	if err != nil {
 22		log.Println(wasmName+":", "Error saving cart:", err)
 23		return
 24	}
 25	js.Global().Get("localStorage").Call("setItem", "cartItems", string(cartJSON))
 26	updateCartDisplay()
 27}
 28
 29func addToCart(this js.Value, args []js.Value) any {
 30	if len(args) < 2 {
 31		return "Error: Missing arguments"
 32	}
 33	var cartItem item
 34	index := -1
 35	id := args[0].String()
 36	qty := args[2].Int()
 37	if qty == 0 {
 38		qty = 1
 39	}
 40	amount := int(args[1].Float()) * qty
 41	for i, _ := range cart {
 42		if strings.Split(cart[i].ID, "|")[0] == strings.Split(id, "|")[0] {
 43			index = i
 44		}
 45	}
 46	if index > -1 {
 47		// update shipping
 48		if strings.Split(cart[index].ID, "|")[0] == "shipping-to" {
 49			cart[index].ID = id
 50			cart[index].Qty = 1
 51			cart[index].Amount = amount
 52		} else {
 53			cart[index].Qty = cart[index].Qty + qty
 54			cart[index].Amount = cart[index].Amount + amount
 55		}
 56	} else {
 57		cartItem = item{
 58			ID:     id,
 59			Amount: amount,
 60			Qty:    qty,
 61		}
 62		cart = append(cart, cartItem)
 63	}
 64	saveCart()
 65	return nil
 66}
 67
 68func addUnToCart(this js.Value, args []js.Value) interface{} {
 69	if len(args) < 2 {
 70		return "Error: Missing arguments"
 71	}
 72	id := args[0].String()
 73	price := args[1].Float()
 74	quantityInput := doc.Call("getElementById", fmt.Sprintf("qty-%s", id))
 75	if !quantityInput.Truthy() {
 76		log.Println(wasmName+":", "Error: Quantity input not found for item", id)
 77		return nil
 78	}
 79	quantity, err := strconv.Atoi(quantityInput.Get("value").String())
 80	if err != nil || quantity < 1 {
 81		quantity = 1
 82	}
 83
 84	addToCart(js.Value{}, []js.Value{
 85		js.ValueOf(id),
 86		js.ValueOf(int(price * 100)),
 87		js.ValueOf(quantity),
 88	})
 89	return nil
 90}
 91
 92func removeFromCart(this js.Value, inputs []js.Value) interface{} {
 93	id := inputs[0].String()
 94	newCart := []item{}
 95	for _, m := range cart {
 96		if m.ID != id {
 97			newCart = append(newCart, m)
 98		}
 99	}
100	cart = newCart
101	saveCart()
102	return nil
103}
104
105func loadCart() {
106	storedCart := js.Global().Get("localStorage").Call("getItem", "cartItems")
107	if !storedCart.IsUndefined() && !storedCart.IsNull() {
108		err := json.Unmarshal([]byte(storedCart.String()), &cart)
109		if err != nil {
110			log.Println(`can't unmarshal cart from local storage`)
111			cart = []item{}
112		}
113	}
114}
115
116func emptyCart(this js.Value, inputs []js.Value) interface{} {
117	js.Global().Get("localStorage").Call("removeItem", "cartItems")
118	cart = []item{}
119	updateCartDisplay()
120	return nil
121}
122
123func clearAll(this js.Value, inputs []js.Value) interface{} {
124	js.Global().Get("localStorage").Call("clear")
125	cart = []item{}
126	updateCartDisplay()
127	return nil
128}
129
130func updateCartDisplay() {
131	cartContainer := doc.Call("getElementById", "cart-items")
132	totalPriceElement := doc.Call("getElementById", "total-price")
133	table := cartContainer.Call("querySelector", "table")
134	if table.IsNull() {
135		table = doc.Call("createElement", "table")
136		thead := doc.Call("createElement", "thead")
137		thead.Set("innerHTML", `<tr><th>Item</th><th>Price</th><th>Quantity</th><th>Actions</th></tr>`)
138		table.Call("appendChild", thead)
139		tbody := doc.Call("createElement", "tbody")
140		tbody.Set("id", "cart-tbody")
141		table.Call("appendChild", tbody)
142		cartContainer.Call("appendChild", table)
143	}
144	tbody := doc.Call("getElementById", "cart-tbody")
145	tbody.Set("innerHTML", "")
146
147	total := 0
148	hasShipping := false
149	for _, m := range cart {
150		total += m.Amount
151		row := doc.Call("createElement", "tr")
152
153		row.Set("innerHTML", fmt.Sprintf(`<td>%s</td><td>$%.2f</td><td>%s</td><td><button onclick='removeFromCart("%s")'>Remove</button></td>`,
154			func() string {
155				parts := strings.Split(m.ID, "|")
156				if len(parts) < 8 {
157					return m.ID
158				}
159				hasShipping = true
160				return fmt.Sprintf("%s:<br>%s<br>%s<br>%s, %s %s<br>%s<br>%s", parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7])
161			}(),
162			float64(m.Amount)/100,
163			func() string {
164				if len(strings.Split(m.ID, "|")) == 8 {
165					return ""
166				}
167				return fmt.Sprintf(`<input type='number' value='%d' min='1' onchange='updateItemQuantity("%s", this.value)'>`, m.Qty, m.ID)
168			}(),
169			m.ID,
170		))
171		tbody.Call("appendChild", row)
172	}
173	totalPriceElement.Set("textContent", fmt.Sprintf("Total: $%.2f", float64(total)/100))
174
175	checkoutbutton := doc.Call("getElementById", "checkout-button")
176	if !checkoutbutton.Truthy() {
177		return
178	}
179
180	if len(cart) > 1 && hasShipping {
181		checkoutbutton.Call("removeAttribute", "disabled")
182	} else {
183		checkoutbutton.Call("setAttribute", "disabled", "true")
184	}
185}
186
187func updateItemQuantity(this js.Value, args []js.Value) interface{} {
188	id := args[0].String()
189	qty, err := strconv.Atoi(args[1].String())
190	if err != nil {
191		log.Println(err)
192	}
193	for i, _ := range cart {
194		if cart[i].ID == id {
195			unitPrice := cart[i].Amount / cart[i].Qty
196			cart[i].Qty = qty
197			cart[i].Amount = unitPrice * qty
198			break
199		}
200	}
201	saveCart()
202	return nil
203}
204
205
206// ===== checkout.go =====
207package main
208
209import (
210	"encoding/json"
211	"fmt"
212	"log"
213	"strconv"
214	"syscall/js"
215)
216
217func addShippingInfo(this js.Value, args []js.Value) interface{} {
218	event := args[0]
219	form := args[1]
220	event.Call("preventDefault")
221	getFormValue := func(name string) string {
222		return form.Call("querySelector", fmt.Sprintf("[name='%s']", name)).Get("value").String()
223	}
224	shippingInfo := fmt.Sprintf("shipping-to|%s|%s|%s|%s|%s|%s|%s",
225		getFormValue("shipping-name"),
226		getFormValue("shipping-address"),
227		getFormValue("shipping-city"),
228		getFormValue("shipping-state"),
229		getFormValue("shipping-zip"),
230		getFormValue("shipping-country"),
231		getFormValue("shipping-phone"),
232	)
233	priceStr := getFormValue("shipping-price")
234	price, err := strconv.ParseFloat(priceStr, 64)
235	if err != nil {
236		log.Println(wasmName+":", "Error: Failed to parse shipping price")
237		price = 0.0
238	}
239
240	addToCart(js.Value{}, []js.Value{
241		js.ValueOf(shippingInfo),
242		js.ValueOf(int(price * 100)),
243		js.ValueOf(1),
244	})
245	return false
246}
247
248var (
249	elements       js.Value
250	stripeValue    js.Value
251	stripe         js.Value
252	checkoutButton = doc.Call("getElementById", "checkout-button")
253	checkoutDiv    = doc.Call("getElementById", "checkout-container")
254	checkoutStripe = doc.Call("getElementById", "stripecheckout")
255)
256
257func goToCheckout(this js.Value, args []js.Value) any {
258	if stripeValue.IsUndefined() {
259		log.Println(`js.Global().Get("Stripe")`)
260		stripeValue = js.Global().Get("Stripe")
261		if stripeValue.IsUndefined() {
262			log.Println(`Stripe is undefined, attempting to load Stripe.js`)
263
264			doc := js.Global().Get("document")
265			head := doc.Call("querySelector", "head")
266			script := doc.Call("createElement", "script")
267			script.Set("src", "https://js.stripe.com/v3/")
268			script.Set("defer", true)
269
270			done := make(chan bool)
271			script.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
272				log.Println(wasmName+":", "Stripe.js script has been loaded")
273				done <- true
274				return nil
275			}))
276
277			head.Call("appendChild", script)
278
279			<-done
280
281			stripeValue = js.Global().Get("Stripe")
282			if stripeValue.IsUndefined() {
283				log.Println(wasmName+":", "Failed to load Stripe.js")
284				return nil
285			}
286		}
287	}
288
289	log.Println(wasmName+":", "Stripe.js loaded successfully")
290
291	if stripe.IsUndefined() {
292		log.Println(wasmName+":", "Invoking Stripe")
293		stripe = stripeValue.Invoke(stripePK)
294		if stripe.IsUndefined() {
295			log.Println(wasmName+":", "Failed to invoke Stripe")
296			return nil
297		}
298	}
299
300	log.Println(wasmName+":", "Stripe initialized")
301	checkoutStripe = doc.Call("getElementById", "stripecheckout")
302	if checkoutStripe.IsUndefined() {
303		log.Println(wasmName+":", "element with ID stripecheckout not found")
304	}
305
306	checkoutStripe.Call("showModal")
307	log.Println(wasmName+":", "initializePayment()")
308	initializePayment()
309
310	return nil
311}
312
313func cancelCheckout(this js.Value, args []js.Value) any {
314	log.Println(wasmName+":", "Cancelling checkout ; closing dialog")
315	checkoutStripe.Call("close")
316	updateCartDisplay()
317	return nil
318}
319
320func initializePayment() {
321	type cItem struct {
322		ID     string `json:"id"`
323		Amount int    `json:"amount"`
324	}
325	type checkout struct {
326		Items []cItem `json:"items"`
327	}
328	payload := checkout{
329		Items: func() []cItem {
330			var items []cItem
331			for _, it := range cart {
332				items = append(items, cItem{ID: it.ID + " X " + strconv.Itoa(it.Qty), Amount: it.Amount})
333			}
334			return items
335		}(),
336	}
337	payloadJSON, err := json.Marshal(payload)
338	if err != nil {
339		log.Println(wasmName+":", "Error marshaling JSON:", err)
340		return
341	}
342	fetchInit := map[string]interface{}{
343		"method": "POST",
344		"headers": map[string]interface{}{
345			"Content-Type": "application/json",
346		},
347		"body": string(payloadJSON),
348	}
349
350	log.Println(wasmName+":", "fetch  /create-payment-intent")
351	js.Global().Call("fetch", "/create-payment-intent", js.ValueOf(fetchInit)).
352		Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
353			response := args[0]
354			log.Println(wasmName+":", "got response from fetch /create-payment-intent")
355			if !response.Get("ok").Bool() {
356				log.Println(wasmName+":", "Fetch request failed with status:", response.Get("status").Int())
357				showMessage("Failed to create payment intent: " + response.Get("status").String())
358				return nil
359			}
360			response.Call("json").Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
361				clientSecret := args[0].Get("clientSecret").String()
362				log.Println(wasmName+":", "Client secret received:", clientSecret)
363				setupStripeElements(clientSecret)
364				return nil
365			})).Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
366				log.Println(wasmName+":", "Error parsing JSON response:", args[0])
367				showMessage("Failed to parse payment intent response.")
368				return nil
369			}))
370			return nil
371		})).
372		Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
373			log.Println(wasmName+":", "Error in fetch request:", args[0])
374			showMessage("Failed to communicate with the server.")
375			return nil
376		}))
377}
378
379func setupStripeElements(clientSecret string) {
380	elements = stripe.Call("elements", map[string]interface{}{
381		"clientSecret": clientSecret,
382	})
383	if elements.IsUndefined() {
384		log.Println(wasmName+":", "Failed to initialize Stripe Elements")
385		showMessage("Failed to initialize payment elements.")
386		return
387	}
388	paymentElement := elements.Call("create", "payment", map[string]interface{}{
389		"layout": "tabs",
390	})
391	if paymentElement.IsUndefined() {
392		log.Println(wasmName+":", "Failed to create payment element")
393		showMessage("Failed to create payment element.")
394		return
395	}
396	paymentElement.Call("mount", "#payment-element")
397	submitButton := doc.Call("getElementById", "submit")
398	submitButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
399		args[0].Call("preventDefault")
400		showSpinner(true)
401		confirmPayment(clientSecret)
402		return nil
403	}))
404}
405
406func confirmPayment(clientSecret string) {
407
408	windowLocation := js.Global().Get("window").Get("location")
409	protocol := windowLocation.Get("protocol").String()
410	hostname := windowLocation.Get("hostname").String()
411	port := windowLocation.Get("port").String()
412
413	baseURL := protocol + "//" + hostname
414	if port != "" {
415		baseURL += ":" + port
416	}
417	//	path := windowLocation.Get("pathname").String()
418	//    baseURL += strings.Split(path, "?")[0]
419	//    log.Println(wasmName+":","return url ", baseURL)
420
421	returnURL := baseURL + "/complete"
422	returnURL += "?payment_intent=" + clientSecret // + "#complete"
423	log.Println(wasmName+":", "Return URL for payment:", returnURL)
424
425	stripe.Call("confirmPayment", map[string]interface{}{
426		"elements": elements,
427		"confirmParams": map[string]interface{}{
428			"return_url": returnURL,
429		},
430	}).Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
431		result := args[0]
432		if result.Get("error").IsUndefined() {
433			log.Println(wasmName+":", "Payment successful:", result)
434			showMessage("Payment successful! Thank you for your order.")
435		} else {
436			log.Println(wasmName+":", "Payment error:", result.Get("error").Get("message").String())
437			showMessage("Payment failed: " + result.Get("error").Get("message").String())
438		}
439
440		showSpinner(false)
441		return nil
442	}))
443}
444
445func showMessage(message string) {
446	messageElement := doc.Call("getElementById", "payment-message")
447	messageElement.Set("innerText", message)
448	messageElement.Set("className", "")
449}
450
451func showSpinner(isLoading bool) {
452	spinner := doc.Call("getElementById", "spinner")
453	buttonText := doc.Call("getElementById", "button-text")
454
455	if isLoading {
456		spinner.Set("className", "")
457		buttonText.Set("className", "hidden")
458	} else {
459		spinner.Set("className", "hidden")
460		buttonText.Set("className", "")
461	}
462}
463
464
465// ===== complete.go =====
466// Package main complete.go
467package main
468
469import (
470	"encoding/json"
471	"log"
472	"syscall/js"
473)
474
475func completeLogic() {
476	initializeStripe()
477}
478
479func initializeStripe() {
480	if stripeValue.IsUndefined() {
481		log.Println(`js.Global().Get("Stripe")`)
482		stripeValue = js.Global().Get("Stripe")
483		if stripeValue.IsUndefined() {
484			log.Println(`Stripe is undefined, attempting to load Stripe.js`)
485
486			doc := js.Global().Get("document")
487			head := doc.Call("querySelector", "head")
488			script := doc.Call("createElement", "script")
489			script.Set("src", "https://js.stripe.com/v3/")
490			script.Set("defer", true)
491
492			done := make(chan bool)
493			script.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
494				log.Println(wasmName+":", "Stripe.js script has been loaded")
495				done <- true
496				return nil
497			}))
498
499			head.Call("appendChild", script)
500
501			<-done
502
503			stripeValue = js.Global().Get("Stripe")
504			if stripeValue.IsUndefined() {
505				log.Println(wasmName+":", "Failed to load Stripe.js")
506				return
507			}
508		}
509	}
510
511	log.Println(wasmName+":", "Stripe.js loaded successfully")
512
513	if stripe.IsUndefined() {
514		log.Println(wasmName+":", "Invoking Stripe")
515		stripe = stripeValue.Invoke(stripePK)
516		if stripe.IsUndefined() {
517			log.Println(wasmName+":", "Failed to invoke Stripe")
518			return
519		}
520	}
521
522	log.Println(wasmName+":", "Stripe initialized")
523	checkStatus()
524}
525
526var (
527	successIcon = `<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
528		<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4695 0.232963C15.8241 0.561287 15.8454 1.1149 15.5171 1.46949L6.14206 11.5945C5.97228 11.7778 5.73221 11.8799 5.48237 11.8748C5.23253 11.8698 4.99677 11.7582 4.83452 11.5681L0.459523 6.44311C0.145767 6.07557 0.18937 5.52327 0.556912 5.20951C0.924454 4.89575 1.47676 4.93936 1.79051 5.3069L5.52658 9.68343L14.233 0.280522C14.5613 -0.0740672 15.1149 -0.0953599 15.4695 0.232963Z" fill="white"/>
529	</svg>`
530
531	errorIcon = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
532		<path fill-rule="evenodd" clip-rule="evenodd" d="M1.25628 1.25628C1.59799 0.914573 2.15201 0.914573 2.49372 1.25628L8 6.76256L13.5063 1.25628C13.848 0.914573 14.402 0.914573 14.7437 1.25628C15.0854 1.59799 15.0854 2.15201 14.7437 2.49372L9.23744 8L14.7437 13.5063C15.0854 13.848 15.0854 14.402 14.7437 14.7437C14.402 15.0854 13.848 15.0854 13.5063 14.7437L8 9.23744L2.49372 14.7437C2.15201 15.0854 1.59799 15.0854 1.25628 14.7437C0.914573 14.402 0.914573 13.848 1.25628 13.5063L6.76256 8L1.25628 2.49372C0.914573 2.15201 0.914573 1.59799 1.25628 1.25628Z" fill="white"/>
533	</svg>`
534
535	infoIcon = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
536		<path fill-rule="evenodd" clip-rule="evenodd" d="M10 1.5H4C2.61929 1.5 1.5 2.61929 1.5 4V10C1.5 11.3807 2.61929 12.5 4 12.5H10C11.3807 12.5 12.5 11.3807 12.5 10V4C12.5 2.61929 11.3807 1.5 10 1.5ZM4 0C1.79086 0 0 1.79086 0 4V10C0 12.2091 1.79086 14 4 14H10C12.2091 14 14 12.2091 14 10V4C14 1.79086 12.2091 0 10 0H4Z" fill="white"/>
537		<path fill-rule="evenodd" clip-rule="evenodd" d="M5.25 7C5.25 6.58579 5.58579 6.25 6 6.25H7.25C7.66421 6.25 8 6.58579 8 7V10.5C8 10.9142 7.66421 11.25 7.25 11.25C6.83579 11.25 6.5 10.9142 6.5 10.5V7.75H6C5.58579 7.75 5.25 7.41421 5.25 7Z" fill="white"/>
538		<path d="M5.75 4C5.75 3.31075 6.31075 2.75 7 2.75C7.68925 2.75 8.25 3.31075 8.25 4C8.25 4.68925 7.68925 5.25 7 5.25C6.31075 5.25 5.75 4.68925 5.75 4Z" fill="white"/>
539	</svg>`
540)
541
542func setErrorState() {
543	js.Global().Get("document").Call("querySelector", "#status-icon").Set("style", map[string]interface{}{"backgroundColor": "#DF1B41"})
544	js.Global().Get("document").Call("querySelector", "#status-icon").Set("innerHTML", errorIcon)
545	js.Global().Get("document").Call("querySelector", "#status-text").Set("textContent", "Something went wrong, please try again.")
546	js.Global().Get("document").Call("querySelector", "#details-table").Call("classList").Call("add", "hidden")
547	js.Global().Get("document").Call("querySelector", "#view-details").Call("classList").Call("add", "hidden")
548}
549
550func checkStatus() {
551	clientSecret := js.Global().Get("URLSearchParams").New(js.Global().Get("window").Get("location").Get("search")).Call("get", "payment_intent_client_secret").String()
552
553	if clientSecret == "" {
554		setErrorState()
555		return
556	}
557
558	if stripe.IsUndefined() {
559		log.Println(wasmName+":", "Stripe is not initialized")
560		setErrorState()
561		return
562	}
563
564	stripe.Call("retrievePaymentIntent", clientSecret).Call("then", js.FuncOf(func(this js.Value, p []js.Value) interface{} {
565		paymentIntent := p[0].Get("paymentIntent")
566		setPaymentDetails(paymentIntent)
567		return nil
568	})).Call("catch", js.FuncOf(func(this js.Value, p []js.Value) interface{} {
569		setErrorState()
570		return nil
571	}))
572}
573
574func getAllLocalStorageData() map[string]interface{} {
575	localStorage := js.Global().Get("localStorage")
576	keys := js.Global().Get("Object").Call("keys", localStorage)
577	data := make(map[string]interface{})
578
579	for i := 0; i < keys.Length(); i++ {
580		key := keys.Index(i).String()
581		value := localStorage.Call("getItem", key).String()
582		var parsedValue interface{}
583		err := json.Unmarshal([]byte(value), &parsedValue)
584		if err != nil {
585			parsedValue = value // If not JSON, store raw value
586		}
587		data[key] = parsedValue
588	}
589	return data
590}
591
592func submitOrder(localStorageData map[string]interface{}, paymentIntentId string) {
593	orderData := map[string]interface{}{
594		"localStorageData": localStorageData,
595		"paymentIntentId":  paymentIntentId,
596	}
597
598	body, err := json.Marshal(orderData)
599	if err != nil {
600		log.Println(wasmName+":", "Error marshalling order data:", err)
601		return
602	}
603
604	fetch := js.Global().Get("fetch")
605	if fetch.IsUndefined() {
606		log.Println(wasmName+":", "Fetch API is not available")
607		return
608	}
609
610	options := map[string]interface{}{
611		"method": "POST",
612		"headers": map[string]interface{}{
613			"Content-Type": "application/json",
614		},
615		"body": string(body),
616	}
617
618	fetch.Invoke("/submit-order", js.ValueOf(options)).Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
619	    response := args[0]
620	    if !response.Get("ok").Bool() {
621	        response.Call("text").Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
622	            errText := args[0].String()
623	            log.Println(wasmName+":", "Order submit failed:", errText)
624	            js.Global().Call("alert", "Order submission failed:\n"+errText)
625	            return nil
626	        }))
627	        return nil
628	    }
629	    response.Call("json").Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
630	        data := args[0]
631	        log.Println(wasmName+":", "Order submitted successfully:", data)
632	        return nil
633	    }))
634	    return nil
635	}))
636}
637
638func setPaymentDetails(intent js.Value) {
639	var statusText, iconColor, icon string
640	statusText = "Something went wrong, please try again."
641	iconColor = "#DF1B41"
642	icon = errorIcon
643
644	if !intent.IsUndefined() {
645		intentStatus := intent.Get("status").String()
646		intentID := intent.Get("id").String()
647
648		allLocalStorageData := getAllLocalStorageData()
649
650		switch intentStatus {
651		case "succeeded":
652			statusText = "Payment succeeded"
653			iconColor = "#30B130"
654			icon = successIcon
655			if len(allLocalStorageData) > 0 {
656				submitOrder(allLocalStorageData, intentID)
657			} else {
658				log.Println(wasmName+":", "No data found in localStorage; order not submitted.")
659			}
660		case "processing":
661			statusText = "Your payment is processing."
662			iconColor = "#6D6E78"
663			icon = infoIcon
664			if len(allLocalStorageData) > 0 {
665				submitOrder(allLocalStorageData, intentID)
666			} else {
667				log.Println(wasmName+":", "No data found in localStorage; order not submitted.")
668			}
669		case "requires_payment_method":
670			statusText = "Your payment was not successful, please try again."
671		default:
672			statusText = "Unknown payment status."
673		}
674
675		// Update the status icon, text, and links
676		js.Global().Get("document").Call("querySelector", "#status-icon").Set("style", map[string]interface{}{"backgroundColor": iconColor})
677		js.Global().Get("document").Call("querySelector", "#status-icon").Set("innerHTML", icon)
678		js.Global().Get("document").Call("querySelector", "#status-text").Set("textContent", statusText)
679		js.Global().Get("document").Call("querySelector", "#intent-id").Set("textContent", intentID)
680		js.Global().Get("document").Call("querySelector", "#intent-status").Set("textContent", intentStatus)
681		js.Global().Get("document").Call("querySelector", "#view-details").Set("href", "https://dashboard.stripe.com/payments/"+intentID)
682
683		// Update the "Order Details" link with the paymentIntent ID
684		orderDetailsLink := js.Global().Get("document").Call("querySelector", "#order-details-link")
685		orderDetailsLink.Set("href", "/order/"+intentID)
686		orderDetailsLink.Set("onclick", nil) // Allow default behavior (navigation)
687
688	} else {
689		setErrorState()
690	}
691}
692
693
694// ===== main.go =====
695package main
696
697import (
698	"log"
699	"syscall/js"
700)
701
702// set client pk on compile
703var stripePK string
704
705type item struct {
706	ID     string `json:"id"`
707	Amount int    `json:"amount"`
708	Qty    int    `json:"quantity"`
709}
710
711var (
712	wasmName   string
713	doc        = js.Global().Get("document")
714	body       = doc.Call("querySelector", "body")
715	bodystring = body.Get("innerHTML").String()
716	cart       []item
717)
718
719func main() {
720	ready := make(chan struct{})
721
722	document := js.Global().Get("document")
723	readyState := document.Get("readyState").String()
724	if readyState == "interactive" || readyState == "complete" {
725		log.Println(wasmName+":", "WASM: DOM already fully loaded")
726		close(ready)
727	} else {
728		cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
729			log.Println(wasmName+":", "WASM: DOM fully loaded and parsed")
730			close(ready)
731			return nil
732		})
733		defer cb.Release()
734
735		document.Call("addEventListener", "DOMContentLoaded", cb)
736		log.Println(wasmName+":", "WASM: waiting for DOM to load")
737	}
738
739	<-ready
740
741	c := make(chan struct{}, 0)
742	if stripePK == "" {
743		log.Fatal("Stripe PK not found!")
744	}
745	window := js.Global().Get("window")
746	location := window.Get("location")
747	pathname := location.Get("pathname").String()
748
749	switch pathname {
750	case "/complete":
751		completeLogic()
752	default:
753		defaultLogic()
754	}
755	<-c
756}
757
758func defaultLogic() {
759	js.Global().Set("addToCart", js.FuncOf(addUnToCart))
760	js.Global().Set("clearStorage", js.FuncOf(clearAll))
761	js.Global().Set("emptyCart", js.FuncOf(emptyCart))
762	js.Global().Set("updateItemQuantity", js.FuncOf(updateItemQuantity))
763	js.Global().Set("removeFromCart", js.FuncOf(removeFromCart))
764	js.Global().Set("addShippingInfo", js.FuncOf(addShippingInfo))
765	js.Global().Set("goToCheckout", js.FuncOf(goToCheckout))
766	js.Global().Set("cancelCheckout", js.FuncOf(cancelCheckout))
767	js.Global().Set("callUpdateCartDisplay", js.FuncOf(updateCartDisplayWrapper))
768	loadCart()
769	updateCartDisplay()
770}
771
772