11package api
22
33import (
4+ "encoding/base64"
5+ "encoding/json"
46 "net/http"
57
8+ "github.com/go-playground/validator/v10"
69 "github.com/labstack/echo/v4"
710 echomiddleware "github.com/labstack/echo/v4/middleware"
11+ _ "github.com/rabbitprincess/x402-facilitator/api/swagger"
12+ echoSwagger "github.com/swaggo/echo-swagger"
13+
814 "github.com/rabbitprincess/x402-facilitator/api/middleware"
915 "github.com/rabbitprincess/x402-facilitator/facilitator"
16+ "github.com/rabbitprincess/x402-facilitator/types"
1017)
1118
12- // server represents the HTTP server for the API
19+ // @title x402 Facilitator API
20+ // @version 1.0
21+ // @description API server for x402 payment facilitator
22+ // @host localhost:8080
23+ // @BasePath /
24+ // @schemes http
1325type server struct {
1426 * echo.Echo
1527 facilitator facilitator.Facilitator
1628}
1729
18- // Ensure server implements http.Handler
1930var _ http.Handler = (* server )(nil )
2031
21- // NewServer creates and configures a new API server
2232func NewServer (facilitator facilitator.Facilitator ) * server {
2333 s := & server {
2434 Echo : echo .New (),
2535 facilitator : facilitator ,
2636 }
2737
28- // Register middleware
2938 s .Use (middleware .RequestID ())
3039 s .Use (middleware .Logger ())
3140 s .Use (middleware .ErrorWrapper ())
@@ -34,9 +43,114 @@ func NewServer(facilitator facilitator.Facilitator) *server {
3443 }))
3544 s .Use (echomiddleware .CORS ())
3645
37- // Register routes
3846 s .POST ("/verify" , s .Verify )
3947 s .POST ("/settle" , s .Settle )
48+ s .GET ("/supported" , s .Supported )
49+ s .GET ("/swagger/*" , echoSwagger .WrapHandler )
4050
4151 return s
4252}
53+
54+ var (
55+ validate = validator .New (validator .WithRequiredStructEnabled ())
56+ )
57+
58+ // Settle handles payment settlement requests
59+ // @Summary Settle payment
60+ // @Description Settle a payment using the facilitator
61+ // @Tags payments
62+ // @Accept json
63+ // @Produce json
64+ // @Param body body types.PaymentSettleRequest true "Settlement request"
65+ // @Success 200 {object} types.PaymentSettleResponse
66+ // @Failure 400 {object} echo.HTTPError
67+ // @Failure 500 {object} echo.HTTPError
68+ // @Router /settle [post]
69+ func (s * server ) Settle (c echo.Context ) error {
70+ ctx := c .Request ().Context ()
71+
72+ requirement := & types.PaymentSettleRequest {}
73+ if err := json .NewDecoder (c .Request ().Body ).Decode (requirement ); err != nil {
74+ return echo .NewHTTPError (http .StatusBadRequest , "Received malformed settlement request" )
75+ }
76+ if err := validate .Struct (requirement ); err != nil {
77+ return echo .NewHTTPError (http .StatusBadRequest , "Received invalid settlement request" )
78+ }
79+ payment := & types.PaymentPayload {}
80+ paymentDecoded , err := base64 .StdEncoding .DecodeString (requirement .PaymentHeader )
81+ if err != nil {
82+ return echo .NewHTTPError (http .StatusBadRequest , "Received malformed Payment header" )
83+ }
84+ if err := json .Unmarshal (paymentDecoded , payment ); err != nil {
85+ return echo .NewHTTPError (http .StatusBadRequest , "Received malformed Payment header" )
86+ }
87+ if err := validate .Struct (payment ); err != nil {
88+ return echo .NewHTTPError (http .StatusBadRequest , "Received invalid Payment header" )
89+ }
90+ settle , err := s .facilitator .Settle (ctx , payment , & requirement .PaymentRequirements )
91+ if err != nil {
92+ return echo .NewHTTPError (http .StatusInternalServerError , err .Error ())
93+ }
94+ return c .JSON (http .StatusOK , settle )
95+ }
96+
97+ // Verify handles payment verification requests
98+ // @Summary Verify payment
99+ // @Description Verify a payment using the facilitator
100+ // @Tags payments
101+ // @Accept json
102+ // @Produce json
103+ // @Param body body types.PaymentVerifyRequest true "Payment verification request"
104+ // @Success 200 {object} types.PaymentVerifyResponse
105+ // @Failure 400 {object} echo.HTTPError
106+ // @Failure 500 {object} echo.HTTPError
107+ // @Router /verify [post]
108+ func (s * server ) Verify (c echo.Context ) error {
109+ ctx := c .Request ().Context ()
110+
111+ // validate payment requirements
112+ requirement := & types.PaymentVerifyRequest {}
113+ if err := json .NewDecoder (c .Request ().Body ).Decode (requirement ); err != nil {
114+ return echo .NewHTTPError (http .StatusBadRequest , "Received malformed payment requirements" )
115+ }
116+ if err := validate .Struct (requirement ); err != nil {
117+ return echo .NewHTTPError (http .StatusBadRequest , "Received invalid payment requirements" )
118+ }
119+
120+ // validate payment payload
121+ payment := & types.PaymentPayload {}
122+ paymentDecoded , err := base64 .StdEncoding .DecodeString (requirement .PaymentHeader )
123+ if err != nil {
124+ return echo .NewHTTPError (http .StatusBadRequest , "Received malformed Payment header" )
125+ }
126+ if err := json .Unmarshal (paymentDecoded , payment ); err != nil {
127+ return echo .NewHTTPError (http .StatusBadRequest , "Received malformed Payment header" )
128+ }
129+ if err := validate .Struct (payment ); err != nil {
130+ return echo .NewHTTPError (http .StatusBadRequest , "Received invalid Payment header" )
131+ }
132+
133+ verified , err := s .facilitator .Verify (ctx , payment , & requirement .PaymentRequirements )
134+ if err != nil {
135+ return echo .NewHTTPError (http .StatusInternalServerError , err .Error ())
136+ }
137+
138+ return c .JSON (http .StatusOK , verified )
139+ }
140+
141+ // Supported returns the list of supported payment kinds
142+ // @Summary List supported kinds
143+ // @Description Get supported payment kinds
144+ // @Tags payments
145+ // @Produce json
146+ // @Success 200 {array} types.SupportedKind
147+ // @Failure 404 {object} echo.HTTPError
148+ // @Router /supported [get]
149+ func (s * server ) Supported (c echo.Context ) error {
150+ kinds := s .facilitator .Supported ()
151+ if len (kinds ) == 0 {
152+ return echo .NewHTTPError (http .StatusNotFound , "No supported payment kinds found" )
153+ }
154+
155+ return c .JSON (http .StatusOK , kinds )
156+ }
0 commit comments