Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions business.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) 2025 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package whatsmeow

import (
"context"
"fmt"
"strconv"

waBinary "go.mau.fi/whatsmeow/binary"
"go.mau.fi/whatsmeow/types"
)

// GetOrderDetails fetches the details of a specific order using its ID and token.
// Both token and orderID are found in the OrderMessage.
func (cli *Client) GetOrderDetails(ctx context.Context, orderID, tokenBase64 string) (*types.OrderDetails, error) {
resp, err := cli.sendIQ(ctx, infoQuery{
Namespace: "fb:thrift_iq",
Type: iqGet,
SMaxID: "5",
To: types.ServerJID,
Content: []waBinary.Node{{
Tag: "order",
Attrs: waBinary.Attrs{
"op": "get",
"id": orderID,
},
Content: []waBinary.Node{
{
Tag: "image_dimensions",
Content: []waBinary.Node{
{Tag: "width", Content: []byte("100")},
{Tag: "height", Content: []byte("100")},
},
},
{Tag: "token", Content: []byte(tokenBase64)},
},
}},
})
if err != nil {
return nil, fmt.Errorf("failed to send order IQ: %w", err)
}

orderNode, ok := resp.GetOptionalChildByTag("order")
if !ok {
return nil, &ElementMissingError{Tag: "order", In: "response to order query"}
}

return parseOrderDetailsNode(orderNode)
}

// Helper to get the string content of a child node.
func getStringChild(node waBinary.Node, tag string) string {
child, ok := node.GetOptionalChildByTag(tag)
if !ok {
return ""
}
content, _ := child.Content.([]byte)
return string(content)
}

func parseOrderDetailsNode(orderNode waBinary.Node) (*types.OrderDetails, error) {
ag := orderNode.AttrGetter()
details := &types.OrderDetails{
ID: ag.String("id"),
CreatedAt: ag.UnixTime("creation_ts"),
}
if err := ag.Error(); err != nil {
return nil, err
}

// Parse Price
priceNode, ok := orderNode.GetOptionalChildByTag("price")
if ok {
subtotal, _ := strconv.ParseInt(getStringChild(priceNode, "subtotal"), 10, 64)
total, _ := strconv.ParseInt(getStringChild(priceNode, "total"), 10, 64)
details.Price = types.OrderPrice{
Subtotal: subtotal,
Total: total,
Currency: getStringChild(priceNode, "currency"),
PriceStatus: getStringChild(priceNode, "price_status"),
}
}

// Parse Catalog ID
catalogNode, ok := orderNode.GetOptionalChildByTag("catalog")
if ok {
details.CatalogID = getStringChild(catalogNode, "id")
}

// Parse Products
for _, productNode := range orderNode.GetChildrenByTag("product") {
price, _ := strconv.ParseInt(getStringChild(productNode, "price"), 10, 64)
quantity, _ := strconv.Atoi(getStringChild(productNode, "quantity"))

product := types.OrderProduct{
ID: getStringChild(productNode, "id"),
Price: price,
Currency: getStringChild(productNode, "currency"),
Name: getStringChild(productNode, "name"),
Quantity: quantity,
}

// Parse Product Image
if imageNode, ok := productNode.GetOptionalChildByTag("image"); ok {
product.ImageID = getStringChild(imageNode, "id")
product.ImageURL = getStringChild(imageNode, "url")
}

// Parse Variant Info
if variantNode, ok := productNode.GetOptionalChildByTag("variant_info"); ok {
product.VariantInfo.Properties = getStringChild(variantNode, "properties")
}

details.Products = append(details.Products, product)
}

return details, nil
}
43 changes: 43 additions & 0 deletions types/business.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2025 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package types

import "time"

// OrderDetails contains the metadata, price, and products in a WhatsApp order.
type OrderDetails struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
CatalogID string `json:"catalog_id,omitempty"`
Price OrderPrice `json:"price"`
Products []OrderProduct `json:"products"`
}

// OrderProduct contains a single product entry in a WhatsApp order.
type OrderProduct struct {
ID string `json:"id"`
ImageID string `json:"image_id,omitempty"`
ImageURL string `json:"image_url,omitempty"`
Price int64 `json:"price"`
Currency string `json:"currency"`
Name string `json:"name"`
Quantity int `json:"quantity"`
VariantInfo VariantInfo `json:"variant_info,omitempty"`
}

// VariantInfo contains selected variant metadata for an order product.
type VariantInfo struct {
Properties string `json:"properties,omitempty"`
}

// OrderPrice contains the subtotal and total price metadata for a WhatsApp order.
type OrderPrice struct {
Subtotal int64 `json:"subtotal"`
Total int64 `json:"total"`
Currency string `json:"currency"`
PriceStatus string `json:"price_status,omitempty"`
}
Loading