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