Skip to content
Merged
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
> **Note:** We've moved the x402 repo under the x402 Foundation repo. All issues and PRs were transferred here: [github.com/x402-foundation/x402](https://github.com/x402-foundation/x402)
>
> Our repo ([coinbase/x402](https://github.com/coinbase/x402)) is now a development fork.
# x402

x402 is an open standard for internet native payments. It aims to support all networks (both crypto & fiat) and forms of value (stablecoins, tokens, fiat).
Expand Down
3 changes: 3 additions & 0 deletions go/.changes/unreleased/bazaar-resource-service-metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Added
body: Bazaar service metadata fields (`serviceName`, `tags`, `iconUrl`) on `types.ResourceInfo`, plus `isValidServiceName` / `sanitizeTags` / `isValidIconUrl` / `sanitizeResourceServiceMetadata` helpers in `extensions/bazaar` that facilitator extraction now applies with soft-drop semantics.
time: 2026-05-04T12:00:00.000000-07:00
80 changes: 80 additions & 0 deletions go/extensions/bazaar/bazaar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bazaar_test

import (
"encoding/json"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -2888,3 +2889,82 @@ func TestDynamicRoutesCatalogConsolidation(t *testing.T) {
assert.Equal(t, discovered1.ResourceURL, discovered2.ResourceURL,
"requests to the same parameterized route should consolidate to one catalog entry")
}

func TestExtractDiscoveredResource_ServiceMetadata(t *testing.T) {
declared, _ := bazaar.DeclareDiscoveryExtension(
bazaar.MethodGET,
map[string]interface{}{"city": "NYC"},
bazaar.JSONSchema{
"properties": map[string]interface{}{
"city": map[string]interface{}{"type": "string"},
},
},
"",
nil,
)

build := func(resource *x402.ResourceInfo) []byte {
payload := x402.PaymentPayload{
X402Version: 2,
Accepted: x402.PaymentRequirements{Scheme: "exact", Network: "eip155:8453"},
Payload: map[string]interface{}{},
Resource: resource,
Extensions: map[string]interface{}{bazaar.BAZAAR.Key(): declared},
}
b, _ := json.Marshal(payload)
return b
}

t.Run("surfaces sanitized serviceName / tags / iconUrl from PaymentPayload", func(t *testing.T) {
bytes := build(&x402.ResourceInfo{
URL: "https://api.example.com/weather",
Description: "Weather API",
MimeType: "application/json",
ServiceName: "Example Weather",
Tags: []string{"weather", "forecast"},
IconUrl: "https://api.example.com/icon.png",
})
discovered, err := bazaar.ExtractDiscoveredResourceFromPaymentPayload(bytes, nil, false)
require.NoError(t, err)
require.NotNil(t, discovered)
assert.Equal(t, "Example Weather", discovered.ServiceName)
assert.Equal(t, []string{"weather", "forecast"}, discovered.Tags)
assert.Equal(t, "https://api.example.com/icon.png", discovered.IconUrl)
})

t.Run("soft-drops invalid metadata fields independently from PaymentPayload", func(t *testing.T) {
bytes := build(&x402.ResourceInfo{
URL: "https://api.example.com/weather",
ServiceName: strings.Repeat("a", 33),
Tags: []string{"weather", "", "forecast"},
IconUrl: "http://localhost/icon.png",
})
discovered, err := bazaar.ExtractDiscoveredResourceFromPaymentPayload(bytes, nil, false)
require.NoError(t, err)
require.NotNil(t, discovered)
assert.Equal(t, "", discovered.ServiceName)
assert.Equal(t, []string{"weather", "forecast"}, discovered.Tags)
assert.Equal(t, "", discovered.IconUrl)
})

t.Run("surfaces sanitized metadata from PaymentRequired", func(t *testing.T) {
paymentRequired := x402.PaymentRequired{
X402Version: 2,
Resource: &x402.ResourceInfo{
URL: "https://api.example.com/weather",
ServiceName: "Example Weather",
Tags: []string{"weather"},
IconUrl: "https://api.example.com/icon.png",
},
Accepts: []x402.PaymentRequirements{{Scheme: "exact", Network: "eip155:8453"}},
Extensions: map[string]interface{}{bazaar.BAZAAR.Key(): declared},
}
b, _ := json.Marshal(paymentRequired)
discovered, err := bazaar.ExtractDiscoveredResourceFromPaymentRequired(b, false)
require.NoError(t, err)
require.NotNil(t, discovered)
assert.Equal(t, "Example Weather", discovered.ServiceName)
assert.Equal(t, []string{"weather"}, discovered.Tags)
assert.Equal(t, "https://api.example.com/icon.png", discovered.IconUrl)
})
}
Loading
Loading