forked from x402-foundation/x402
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtypes.go
More file actions
241 lines (205 loc) · 8.37 KB
/
types.go
File metadata and controls
241 lines (205 loc) · 8.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package x402
import (
"encoding/json"
"fmt"
"strings"
"github.com/x402-foundation/x402/go/types"
)
// Network represents a blockchain network identifier in CAIP-2 format
// Format: namespace:reference (e.g., "eip155:1" for Ethereum mainnet)
type Network string
// Parse splits the network into namespace and reference components
func (n Network) Parse() (namespace, reference string, err error) {
parts := strings.Split(string(n), ":")
if len(parts) != 2 {
return "", "", fmt.Errorf("invalid network format: %s", n)
}
return parts[0], parts[1], nil
}
// Match checks if this network matches a pattern (supports wildcards)
// e.g., "eip155:1" matches "eip155:*" and "eip155:*" matches "eip155:1"
func (n Network) Match(pattern Network) bool {
if n == pattern {
return true
}
nStr := string(n)
patternStr := string(pattern)
// Check if pattern has wildcard
if strings.HasSuffix(patternStr, ":*") {
prefix := strings.TrimSuffix(patternStr, "*")
return strings.HasPrefix(nStr, prefix)
}
// Check if n has wildcard (for bidirectional matching)
if strings.HasSuffix(nStr, ":*") {
prefix := strings.TrimSuffix(nStr, "*")
return strings.HasPrefix(patternStr, prefix)
}
return false
}
// Price represents a price that can be specified in various formats
type Price interface{}
// AssetAmount represents an amount of a specific asset
type AssetAmount struct {
Asset string `json:"asset"`
Amount string `json:"amount"`
Extra map[string]interface{} `json:"extra,omitempty"`
}
// PartialPaymentPayload contains only x402Version for version detection
// Used to detect protocol version before unmarshaling to specific types
type PartialPaymentPayload struct {
X402Version int `json:"x402Version"`
}
// Re-export V2 types as default in x402 package
// V2 types are defined in types/v2.go but re-exported here for convenience
type (
PaymentRequirements = types.PaymentRequirements
PaymentPayload = types.PaymentPayload
PaymentRequired = types.PaymentRequired
ResourceInfo = types.ResourceInfo
SupportedKind = types.SupportedKind
SupportedResponse = types.SupportedResponse
)
// Re-export V1 types for legacy facilitator support
type (
SupportedResponseV1 = types.SupportedResponseV1
)
// VerifyResponse contains the verification result
// If verification fails, an error (typically *VerifyError) is returned and this will be nil
type VerifyResponse struct {
IsValid bool `json:"isValid"`
InvalidReason string `json:"invalidReason,omitempty"`
InvalidMessage string `json:"invalidMessage,omitempty"`
Payer string `json:"payer,omitempty"`
Extensions map[string]interface{} `json:"extensions,omitempty"`
Extra map[string]interface{} `json:"extra,omitempty"`
// SkipHandler is an in-process directive set by an AfterVerifyHook that wants
// the HTTP layer to bypass the resource handler and settle inline. It is never
// serialized to the facilitator wire.
SkipHandler *SkipHandlerDirective `json:"-"`
}
// SettleResponse contains the settlement result
// If settlement fails, an error (typically *SettleError) is returned and this will be nil
type SettleResponse struct {
Success bool `json:"success"`
ErrorReason string `json:"errorReason,omitempty"`
ErrorMessage string `json:"errorMessage,omitempty"`
Payer string `json:"payer,omitempty"`
Transaction string `json:"transaction"`
Network Network `json:"network"`
Amount string `json:"amount,omitempty"`
Extensions map[string]interface{} `json:"extensions,omitempty"`
Extra map[string]interface{} `json:"extra,omitempty"`
}
// SettlementOverrides allows overriding settlement parameters.
// Used to support partial settlement (e.g., upto scheme billing by actual usage).
type SettlementOverrides struct {
// Amount to settle. Supports three formats:
// - Raw atomic units: "1000" settles exactly 1000 atomic units.
// - Percent: "50%" settles 50% of PaymentRequirements.Amount (up to 2 decimal places, floored).
// - Dollar price: "$0.05" converts to atomic units using Extra["decimals"] (default 6).
// The resolved amount must be <= the authorized maximum in PaymentRequirements.
Amount string `json:"amount,omitempty"`
}
// ResourceConfig defines payment configuration for a protected resource
type ResourceConfig struct {
Scheme string `json:"scheme"`
PayTo string `json:"payTo"`
Price Price `json:"price"`
Network Network `json:"network"`
MaxTimeoutSeconds int `json:"maxTimeoutSeconds,omitempty"`
Extra map[string]interface{} `json:"extra,omitempty"`
}
// ============================================================================
// View Interfaces for Selectors/Policies/Hooks
// ============================================================================
// PaymentRequirementsView is a unified interface for payment requirements
// Both V1 and V2 types implement this to work with selectors/policies/hooks
type PaymentRequirementsView interface {
GetScheme() string
GetNetwork() string // Returns network as string (can be converted to Network type)
GetAsset() string
GetAmount() string // V1: MaxAmountRequired, V2: Amount
GetPayTo() string
GetMaxTimeoutSeconds() int
GetExtra() map[string]interface{}
}
// PaymentPayloadView is a unified interface for payment payloads
// Both V1 and V2 types implement this to work with hooks
type PaymentPayloadView interface {
GetVersion() int
GetScheme() string
GetNetwork() string // Returns network as string (can be converted to Network type)
GetPayload() map[string]interface{}
}
// PaymentRequirementsSelector chooses which payment option to use
// Works with unified view interface
type PaymentRequirementsSelector func(requirements []PaymentRequirementsView) PaymentRequirementsView
// PaymentPolicy filters or transforms payment requirements
// Works with unified view interface
type PaymentPolicy func(requirements []PaymentRequirementsView) []PaymentRequirementsView
// DefaultPaymentSelector chooses the first available payment option
func DefaultPaymentSelector(requirements []PaymentRequirementsView) PaymentRequirementsView {
if len(requirements) == 0 {
panic("no payment requirements available")
}
return requirements[0]
}
// ============================================================================
// Utility Functions
// ============================================================================
// DeepEqual performs deep equality check on payment requirements
func DeepEqual(a, b interface{}) bool {
// Normalize to JSON and compare
aJSON, err := json.Marshal(a)
if err != nil {
return false
}
bJSON, err := json.Marshal(b)
if err != nil {
return false
}
var aNorm, bNorm interface{}
if err := json.Unmarshal(aJSON, &aNorm); err != nil {
return false
}
if err := json.Unmarshal(bJSON, &bNorm); err != nil {
return false
}
aNormJSON, _ := json.Marshal(aNorm)
bNormJSON, _ := json.Marshal(bNorm)
return string(aNormJSON) == string(bNormJSON)
}
// ParseNetwork parses a network string into Network type
func ParseNetwork(s string) Network {
return Network(s)
}
// IsWildcardNetwork checks if network is a wildcard pattern
func IsWildcardNetwork(network Network) bool {
return strings.HasSuffix(string(network), ":*")
}
// MatchesNetwork checks if a network matches a pattern (supports wildcards)
func MatchesNetwork(pattern Network, network Network) bool {
if pattern == network {
return true
}
if IsWildcardNetwork(pattern) {
prefix := strings.TrimSuffix(string(pattern), "*")
return strings.HasPrefix(string(network), prefix)
}
return false
}
// ============================================================================
// Helper Functions for View Conversion
// ============================================================================
// toViews converts a slice of concrete types to view interfaces
func toViews[T PaymentRequirementsView](reqs []T) []PaymentRequirementsView {
views := make([]PaymentRequirementsView, len(reqs))
for i, req := range reqs {
views[i] = req
}
return views
}
// fromView converts a view interface back to concrete type
func fromView[T PaymentRequirementsView](view PaymentRequirementsView) T {
return view.(T)
}