package conversions
import (
"context"
"fmt"
"net/http"
"strings"
"time"
plainrouter "github.com/plainrouter/sdk-go"
"github.com/plainrouter/sdk-go/openapi"
)
type Consent struct {
AdStorage, AdUserData, AdPersonalization, Source string
CapturedAt time.Time
}
type Purchase struct {
ID, Total, Currency, SourceURL string
PaidAt time.Time
Consent Consent
Email, Phone, Fbp, Fbc string // Optional; leave unavailable values empty.
}
func field(value any) *interface{} {
return &value
}
func PurchaseEvent(p Purchase) (openapi.CreateEventRequest, error) {
if strings.TrimSpace(p.ID) == "" || p.PaidAt.IsZero() || p.Consent.CapturedAt.IsZero() {
return openapi.CreateEventRequest{}, fmt.Errorf("order ID and original timestamps are required")
}
c := p.Consent
if c.AdStorage != "granted" || c.AdUserData != "granted" || c.AdPersonalization != "granted" {
return openapi.CreateEventRequest{}, fmt.Errorf("recorded advertising consent must permit this purchase")
}
eventTime := p.PaidAt.UTC().Format(time.RFC3339)
event := openapi.NewCreateEventRequestAnyOf("Purchase", "consent")
event.SetEventId("purchase:" + p.ID)
event.SetEventTime(openapi.CreateEventRequestAnyOfEventTime{String: &eventTime})
event.SetEventSource(p.SourceURL)
event.SetActionSource("website")
event.SetConsent(map[string]*interface{}{
"ad_storage": field(c.AdStorage),
"ad_user_data": field(c.AdUserData),
"ad_personalization": field(c.AdPersonalization),
"captured_at": field(c.CapturedAt.UTC().Format(time.RFC3339)),
"source": field(c.Source),
})
identity := map[string]*interface{}{}
for key, value := range map[string]string{"email": p.Email, "phone": p.Phone} {
if value != "" {
identity[key] = field(value)
}
}
if len(identity) > 0 {
event.SetUserData(identity)
}
clickIDs := map[string]*interface{}{}
for key, value := range map[string]string{"fbp": p.Fbp, "fbc": p.Fbc} {
if value != "" {
clickIDs[key] = field(value)
}
}
if len(clickIDs) > 0 {
event.SetClickIds(clickIDs)
}
value := openapi.NewCreateEventRequestAnyOfValueData()
value.SetValue(p.Total)
value.SetCurrency(p.Currency)
value.SetOrderId(p.ID)
event.SetValueData(*value)
return openapi.CreateEventRequest{CreateEventRequestAnyOf: event}, nil
}
func SendPurchase(
ctx context.Context,
client *plainrouter.Client,
event openapi.CreateEventRequest,
) (*openapi.CreateEvent200Response, error) {
result, response, err := client.Events.CreateEvent(ctx).
CreateEventRequest(event).Execute()
if response != nil {
defer response.Body.Close()
}
if err != nil {
if response != nil {
return nil, fmt.Errorf("create event failed (HTTP %d): %w", response.StatusCode, err)
}
return nil, fmt.Errorf("create event transport failed; acceptance is uncertain: %w", err)
}
if response == nil || (response.StatusCode != http.StatusOK && response.StatusCode != http.StatusAccepted) {
return nil, fmt.Errorf("create event returned no accepted HTTP receipt")
}
if result == nil || result.GetEventId() == "" {
return nil, fmt.Errorf("create event returned no event ID")
}
return result, nil
}