From 42690c3a849df9b45dfc5d06656eb67b03c7350d Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 5 Nov 2024 22:20:06 +0200 Subject: [PATCH 01/66] init payment service --- api/api.yaml | 106 ++++++ cmd/platform/main.go | 3 +- internal/api/agent.go | 54 ++- internal/api/api.gen.go | 268 ++++++++++++++ internal/api/main_test.go | 3 +- internal/api/payment.go | 66 ++++ internal/api/server.go | 4 +- internal/core/ports/claim_service.go | 4 - internal/core/ports/payment_service.go | 79 ++++ internal/core/services/payment.go | 487 +++++++++++++++++++++++++ internal/network/resolver.go | 17 + 11 files changed, 1075 insertions(+), 16 deletions(-) create mode 100644 internal/api/payment.go create mode 100644 internal/core/ports/payment_service.go create mode 100644 internal/core/services/payment.go diff --git a/api/api.yaml b/api/api.yaml index eebcdd89f..e7a6d3d29 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1292,6 +1292,68 @@ paths: '500': $ref: '#/components/responses/500' + /v2/identities/{identifier}/payment-request: + post: + summary: Create Payment Request + operationId: CreatePaymentRequest + description: Create a payment request for the provided identity. + tags: + - Payment + security: + - basicAuth: [ ] + parameters: + - $ref: '#/components/parameters/pathIdentifier' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePaymentRequest' + responses: + '201': + description: Payment Request Created + content: + application/json: + schema: + $ref: '#/components/schemas/BasicMessage' + '400': + $ref: '#/components/responses/400' + '401': + $ref: '#/components/responses/401' + '422': + $ref: '#/components/responses/422' + '500': + $ref: '#/components/responses/500' + + /v2/payment/verify: + post: + summary: Verify Payment + operationId: VerifyPayment + description: Verify a payment. + tags: + - Payment + security: + - basicAuth: [ ] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BasicMessage' + responses: + '200': + description: Payment verified + content: + application/json: + schema: + type: boolean + '400': + $ref: '#/components/responses/400' + '401': + $ref: '#/components/responses/401' + '500': + $ref: '#/components/responses/500' + components: securitySchemes: basicAuth: @@ -1985,6 +2047,50 @@ components: to: type: string + BasicMessage: + type: object + required: + - body + - id + - typ + - type + - threadID + - from + - to + properties: + id: + type: string + typ: + type: string + type: + type: string + threadID: + type: string + body: + type: null + from: + type: string + to: + type: string + + CreatePaymentRequest: + type: object + required: + - signingKey + - credentialType + - credentialContext + - userDID + properties: + signingKey: + type: string + credentialType: + type: string + credentialContext: + type: string + userDID: + type: string + + TimeUTC: type: string x-go-type: timeapi.Time diff --git a/cmd/platform/main.go b/cmd/platform/main.go index 67943f652..dcb29ea50 100644 --- a/cmd/platform/main.go +++ b/cmd/platform/main.go @@ -151,6 +151,7 @@ func main() { proofService := services.NewProver(circuitsLoaderService) schemaService := services.NewSchema(schemaRepository, schemaLoader) linkService := services.NewLinkService(storage, claimsService, qrService, claimsRepository, linkRepository, schemaRepository, schemaLoader, sessionRepository, ps, identityService, *networkResolver, cfg.UniversalLinks) + paymentService := services.NewPaymentService(*networkResolver) transactionService, err := gateways.NewTransaction(*networkResolver) if err != nil { @@ -193,7 +194,7 @@ func main() { ) api.HandlerWithOptions( api.NewStrictHandlerWithOptions( - api.NewServer(cfg, identityService, accountService, connectionsService, claimsService, qrService, publisher, packageManager, *networkResolver, serverHealth, schemaService, linkService), + api.NewServer(cfg, identityService, accountService, connectionsService, claimsService, qrService, publisher, packageManager, *networkResolver, serverHealth, schemaService, linkService, paymentService), middlewares(ctx, cfg.HTTPBasicAuth), api.StrictHTTPServerOptions{ RequestErrorHandlerFunc: errors.RequestErrorHandlerFunc, diff --git a/internal/api/agent.go b/internal/api/agent.go index b5c01a116..ff31cc5e7 100644 --- a/internal/api/agent.go +++ b/internal/api/agent.go @@ -3,6 +3,8 @@ package api import ( "context" + "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/log" ) @@ -20,17 +22,51 @@ func (s *Server) Agent(ctx context.Context, request AgentRequestObject) (AgentRe return Agent400JSONResponse{N400JSONResponse{"cannot proceed with the given request"}}, nil } - req, err := ports.NewAgentRequest(basicMessage) - if err != nil { - log.Error(ctx, "agent parsing request", "err", err) - return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil - } + var agent *domain.Agent - agent, err := s.claimService.Agent(ctx, req, mediatype) - if err != nil { - log.Error(ctx, "agent error", "err", err) - return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil + switch basicMessage.Type { + case protocol.CredentialFetchRequestMessageType: + case protocol.RevocationStatusRequestMessageType: + req, err := ports.NewAgentRequest(basicMessage) + if err != nil { + log.Error(ctx, "agent parsing request", "err", err) + return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil + } + + agent, err = s.claimService.Agent(ctx, req, mediatype) + if err != nil { + log.Error(ctx, "agent error", "err", err) + return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil + } + break + case protocol.CredentialProposalRequestMessageType: + proposalRequest, err := ports.NewAgentProposalRequest(basicMessage) + if err != nil { + log.Error(ctx, "agent parsing proposal request", "err", err) + return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil + } + paymentRequest, err := s.paymentService.CreatePaymentRequestForProposalRequest(ctx, proposalRequest) + + agent = &domain.Agent{ + ID: paymentRequest.ID, + Type: paymentRequest.Type, + ThreadID: paymentRequest.ThreadID, + Body: paymentRequest.Body, + From: paymentRequest.From, + To: paymentRequest.To, + Typ: paymentRequest.Typ, + } + + if err != nil { + log.Error(ctx, "agent error", "err", err) + return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil + } + break + default: + log.Debug(ctx, "agent not supported type", basicMessage.Type) + return Agent400JSONResponse{N400JSONResponse{"cannot proceed with the given message type"}}, nil } + return Agent200JSONResponse{ Body: agent.Body, From: agent.From, diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index afdba34dc..a73ebcbf1 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -184,6 +184,17 @@ type AuthenticationResponse struct { SessionID UUIDString `json:"sessionID"` } +// BasicMessage defines model for BasicMessage. +type BasicMessage struct { + Body interface{} `json:"body"` + From string `json:"from"` + Id string `json:"id"` + ThreadID string `json:"threadID"` + To string `json:"to"` + Typ string `json:"typ"` + Type string `json:"type"` +} + // ConnectionsPaginated defines model for ConnectionsPaginated. type ConnectionsPaginated struct { Items GetConnectionsResponse `json:"items"` @@ -270,6 +281,14 @@ type CreateLinkRequest struct { SignatureProof bool `json:"signatureProof"` } +// CreatePaymentRequest defines model for CreatePaymentRequest. +type CreatePaymentRequest struct { + CredentialContext string `json:"credentialContext"` + CredentialType string `json:"credentialType"` + SigningKey string `json:"signingKey"` + UserDID string `json:"userDID"` +} + // Credential defines model for Credential. type Credential struct { Id string `json:"id"` @@ -777,9 +796,15 @@ type CreateLinkQrCodeCallbackTextRequestBody = CreateLinkQrCodeCallbackTextBody // ActivateLinkJSONRequestBody defines body for ActivateLink for application/json ContentType. type ActivateLinkJSONRequestBody ActivateLinkJSONBody +// CreatePaymentRequestJSONRequestBody defines body for CreatePaymentRequest for application/json ContentType. +type CreatePaymentRequestJSONRequestBody = CreatePaymentRequest + // ImportSchemaJSONRequestBody defines body for ImportSchema for application/json ContentType. type ImportSchemaJSONRequestBody = ImportSchemaRequest +// VerifyPaymentJSONRequestBody defines body for VerifyPayment for application/json ContentType. +type VerifyPaymentJSONRequestBody = BasicMessage + // ServerInterface represents all server handlers. type ServerInterface interface { // Healthcheck @@ -872,6 +897,9 @@ type ServerInterface interface { // Get Credentials Offer // (GET /v2/identities/{identifier}/credentials/{id}/offer) GetCredentialOffer(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id PathClaim, params GetCredentialOfferParams) + // Create Payment Request + // (POST /v2/identities/{identifier}/payment-request) + CreatePaymentRequest(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) // Get Schemas // (GET /v2/identities/{identifier}/schemas) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) @@ -893,6 +921,9 @@ type ServerInterface interface { // Get Identity State Transactions // (GET /v2/identities/{identifier}/state/transactions) GetStateTransactions(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetStateTransactionsParams) + // Verify Payment + // (POST /v2/payment/verify) + VerifyPayment(w http.ResponseWriter, r *http.Request) // Get QrCode from store // (GET /v2/qr-store) GetQrFromStore(w http.ResponseWriter, r *http.Request, params GetQrFromStoreParams) @@ -1088,6 +1119,12 @@ func (_ Unimplemented) GetCredentialOffer(w http.ResponseWriter, r *http.Request w.WriteHeader(http.StatusNotImplemented) } +// Create Payment Request +// (POST /v2/identities/{identifier}/payment-request) +func (_ Unimplemented) CreatePaymentRequest(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { + w.WriteHeader(http.StatusNotImplemented) +} + // Get Schemas // (GET /v2/identities/{identifier}/schemas) func (_ Unimplemented) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) { @@ -1130,6 +1167,12 @@ func (_ Unimplemented) GetStateTransactions(w http.ResponseWriter, r *http.Reque w.WriteHeader(http.StatusNotImplemented) } +// Verify Payment +// (POST /v2/payment/verify) +func (_ Unimplemented) VerifyPayment(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + // Get QrCode from store // (GET /v2/qr-store) func (_ Unimplemented) GetQrFromStore(w http.ResponseWriter, r *http.Request, params GetQrFromStoreParams) { @@ -2280,6 +2323,37 @@ func (siw *ServerInterfaceWrapper) GetCredentialOffer(w http.ResponseWriter, r * handler.ServeHTTP(w, r) } +// CreatePaymentRequest operation middleware +func (siw *ServerInterfaceWrapper) CreatePaymentRequest(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.CreatePaymentRequest(w, r, identifier) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // GetSchemas operation middleware func (siw *ServerInterfaceWrapper) GetSchemas(w http.ResponseWriter, r *http.Request) { @@ -2552,6 +2626,26 @@ func (siw *ServerInterfaceWrapper) GetStateTransactions(w http.ResponseWriter, r handler.ServeHTTP(w, r) } +// VerifyPayment operation middleware +func (siw *ServerInterfaceWrapper) VerifyPayment(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.VerifyPayment(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // GetQrFromStore operation middleware func (siw *ServerInterfaceWrapper) GetQrFromStore(w http.ResponseWriter, r *http.Request) { @@ -2846,6 +2940,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/identities/{identifier}/credentials/{id}/offer", wrapper.GetCredentialOffer) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/v2/identities/{identifier}/payment-request", wrapper.CreatePaymentRequest) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/identities/{identifier}/schemas", wrapper.GetSchemas) }) @@ -2867,6 +2964,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/identities/{identifier}/state/transactions", wrapper.GetStateTransactions) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/v2/payment/verify", wrapper.VerifyPayment) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/qr-store", wrapper.GetQrFromStore) }) @@ -4156,6 +4256,60 @@ func (response GetCredentialOffer500JSONResponse) VisitGetCredentialOfferRespons return json.NewEncoder(w).Encode(response) } +type CreatePaymentRequestRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Body *CreatePaymentRequestJSONRequestBody +} + +type CreatePaymentRequestResponseObject interface { + VisitCreatePaymentRequestResponse(w http.ResponseWriter) error +} + +type CreatePaymentRequest201JSONResponse BasicMessage + +func (response CreatePaymentRequest201JSONResponse) VisitCreatePaymentRequestResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(201) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentRequest400JSONResponse struct{ N400JSONResponse } + +func (response CreatePaymentRequest400JSONResponse) VisitCreatePaymentRequestResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentRequest401JSONResponse struct{ N401JSONResponse } + +func (response CreatePaymentRequest401JSONResponse) VisitCreatePaymentRequestResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentRequest422JSONResponse struct{ N422JSONResponse } + +func (response CreatePaymentRequest422JSONResponse) VisitCreatePaymentRequestResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(422) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentRequest500JSONResponse struct{ N500JSONResponse } + +func (response CreatePaymentRequest500JSONResponse) VisitCreatePaymentRequestResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type GetSchemasRequestObject struct { Identifier PathIdentifier `json:"identifier"` Params GetSchemasParams @@ -4432,6 +4586,50 @@ func (response GetStateTransactions500JSONResponse) VisitGetStateTransactionsRes return json.NewEncoder(w).Encode(response) } +type VerifyPaymentRequestObject struct { + Body *VerifyPaymentJSONRequestBody +} + +type VerifyPaymentResponseObject interface { + VisitVerifyPaymentResponse(w http.ResponseWriter) error +} + +type VerifyPayment200JSONResponse bool + +func (response VerifyPayment200JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type VerifyPayment400JSONResponse struct{ N400JSONResponse } + +func (response VerifyPayment400JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type VerifyPayment401JSONResponse struct{ N401JSONResponse } + +func (response VerifyPayment401JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type VerifyPayment500JSONResponse struct{ N500JSONResponse } + +func (response VerifyPayment500JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type GetQrFromStoreRequestObject struct { Params GetQrFromStoreParams } @@ -4665,6 +4863,9 @@ type StrictServerInterface interface { // Get Credentials Offer // (GET /v2/identities/{identifier}/credentials/{id}/offer) GetCredentialOffer(ctx context.Context, request GetCredentialOfferRequestObject) (GetCredentialOfferResponseObject, error) + // Create Payment Request + // (POST /v2/identities/{identifier}/payment-request) + CreatePaymentRequest(ctx context.Context, request CreatePaymentRequestRequestObject) (CreatePaymentRequestResponseObject, error) // Get Schemas // (GET /v2/identities/{identifier}/schemas) GetSchemas(ctx context.Context, request GetSchemasRequestObject) (GetSchemasResponseObject, error) @@ -4686,6 +4887,9 @@ type StrictServerInterface interface { // Get Identity State Transactions // (GET /v2/identities/{identifier}/state/transactions) GetStateTransactions(ctx context.Context, request GetStateTransactionsRequestObject) (GetStateTransactionsResponseObject, error) + // Verify Payment + // (POST /v2/payment/verify) + VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) // Get QrCode from store // (GET /v2/qr-store) GetQrFromStore(ctx context.Context, request GetQrFromStoreRequestObject) (GetQrFromStoreResponseObject, error) @@ -5590,6 +5794,39 @@ func (sh *strictHandler) GetCredentialOffer(w http.ResponseWriter, r *http.Reque } } +// CreatePaymentRequest operation middleware +func (sh *strictHandler) CreatePaymentRequest(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { + var request CreatePaymentRequestRequestObject + + request.Identifier = identifier + + var body CreatePaymentRequestJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.CreatePaymentRequest(ctx, request.(CreatePaymentRequestRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "CreatePaymentRequest") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(CreatePaymentRequestResponseObject); ok { + if err := validResponse.VisitCreatePaymentRequestResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // GetSchemas operation middleware func (sh *strictHandler) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) { var request GetSchemasRequestObject @@ -5782,6 +6019,37 @@ func (sh *strictHandler) GetStateTransactions(w http.ResponseWriter, r *http.Req } } +// VerifyPayment operation middleware +func (sh *strictHandler) VerifyPayment(w http.ResponseWriter, r *http.Request) { + var request VerifyPaymentRequestObject + + var body VerifyPaymentJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.VerifyPayment(ctx, request.(VerifyPaymentRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "VerifyPayment") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(VerifyPaymentResponseObject); ok { + if err := validResponse.VisitVerifyPaymentResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // GetQrFromStore operation middleware func (sh *strictHandler) GetQrFromStore(w http.ResponseWriter, r *http.Request, params GetQrFromStoreParams) { var request GetQrFromStoreRequestObject diff --git a/internal/api/main_test.go b/internal/api/main_test.go index 476cbdde8..27e33f594 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -286,6 +286,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { identityService := services.NewIdentity(keyStore, repos.identity, repos.idenMerkleTree, repos.identityState, mtService, qrService, repos.claims, repos.revocation, repos.connection, st, nil, repos.sessions, pubSub, *networkResolver, rhsFactory, revocationStatusResolver) connectionService := services.NewConnection(repos.connection, repos.claims, st) schemaService := services.NewSchema(repos.schemas, schemaLoader) + paymentService := services.NewPaymentService(*networkResolver) mediaTypeManager := services.NewMediaTypeManager( map[iden3comm.ProtocolMessage][]string{ @@ -298,7 +299,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { claimsService := services.NewClaim(repos.claims, identityService, qrService, mtService, repos.identityState, schemaLoader, st, cfg.ServerUrl, pubSub, ipfsGatewayURL, revocationStatusResolver, mediaTypeManager, cfg.UniversalLinks) accountService := services.NewAccountService(*networkResolver) linkService := services.NewLinkService(storage, claimsService, qrService, repos.claims, repos.links, repos.schemas, schemaLoader, repos.sessions, pubSub, identityService, *networkResolver, cfg.UniversalLinks) - server := NewServer(&cfg, identityService, accountService, connectionService, claimsService, qrService, NewPublisherMock(), NewPackageManagerMock(), *networkResolver, nil, schemaService, linkService) + server := NewServer(&cfg, identityService, accountService, connectionService, claimsService, qrService, NewPublisherMock(), NewPackageManagerMock(), *networkResolver, nil, schemaService, linkService, paymentService) return &testServer{ Server: server, diff --git a/internal/api/payment.go b/internal/api/payment.go new file mode 100644 index 000000000..34eb337c8 --- /dev/null +++ b/internal/api/payment.go @@ -0,0 +1,66 @@ +package api + +import ( + "context" + "encoding/json" + + "github.com/iden3/go-iden3-core/v2/w3c" + comm "github.com/iden3/iden3comm/v2" + "github.com/polygonid/sh-id-platform/internal/log" +) + +// CreatePaymentRequest is the controller to get qr bodies +func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePaymentRequestRequestObject) (CreatePaymentRequestResponseObject, error) { + var err error + issuerDID, err := w3c.ParseDID(request.Identifier) + if err != nil { + log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) + return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } + + userDID, err := w3c.ParseDID(request.Body.UserDID) + if err != nil { + log.Error(ctx, "parsing user did", "err", err, "did", request.Identifier) + return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } + + paymentRequest, err := s.paymentService.CreatePaymentRequest(ctx, issuerDID, userDID, request.Body.SigningKey, request.Body.CredentialContext, request.Body.CredentialType) + if err != nil { + log.Error(ctx, "creating payment request", "err", err) + return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "can't create payment-request"}}, nil + } + + basicMessage := BasicMessage{ + From: paymentRequest.From, + To: paymentRequest.To, + ThreadID: paymentRequest.ThreadID, + Id: paymentRequest.ID, + Typ: string(paymentRequest.Typ), + Type: string(paymentRequest.Type), + Body: paymentRequest.Body, + } + return CreatePaymentRequest201JSONResponse(basicMessage), nil +} + +func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) { + bodyBytes, err := json.Marshal(request.Body.Body) + if err != nil { + log.Error(ctx, "marshaling request body", "err", err) + return VerifyPayment400JSONResponse{N400JSONResponse{Message: "can't parse payment"}}, nil + } + basicMessage := comm.BasicMessage{ + From: request.Body.From, + To: request.Body.To, + ThreadID: request.Body.ThreadID, + ID: request.Body.Id, + Typ: comm.MediaType(request.Body.Typ), + Type: comm.ProtocolMessage(""), + Body: bodyBytes, + } + isPaid, err := s.paymentService.VerifyPayment(ctx, basicMessage) + if err != nil { + log.Error(ctx, "can't process payment message", "err", err) + return VerifyPayment400JSONResponse{N400JSONResponse{Message: "can't process payment message"}}, nil + } + return VerifyPayment200JSONResponse(isPaid), nil +} diff --git a/internal/api/server.go b/internal/api/server.go index 96b7de338..f7ca91c07 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -29,10 +29,11 @@ type Server struct { publisherGateway ports.Publisher qrService ports.QrStoreService schemaService ports.SchemaService + paymentService ports.PaymentService } // NewServer is a Server constructor -func NewServer(cfg *config.Configuration, identityService ports.IdentityService, accountService ports.AccountService, connectionsService ports.ConnectionService, claimsService ports.ClaimService, qrService ports.QrStoreService, publisherGateway ports.Publisher, packageManager *iden3comm.PackageManager, networkResolver network.Resolver, health *health.Status, schemaService ports.SchemaService, linkService ports.LinkService) *Server { +func NewServer(cfg *config.Configuration, identityService ports.IdentityService, accountService ports.AccountService, connectionsService ports.ConnectionService, claimsService ports.ClaimService, qrService ports.QrStoreService, publisherGateway ports.Publisher, packageManager *iden3comm.PackageManager, networkResolver network.Resolver, health *health.Status, schemaService ports.SchemaService, linkService ports.LinkService, paymentService ports.PaymentService) *Server { return &Server{ cfg: cfg, accountService: accountService, @@ -46,6 +47,7 @@ func NewServer(cfg *config.Configuration, identityService ports.IdentityService, packageManager: packageManager, qrService: qrService, schemaService: schemaService, + paymentService: paymentService, } } diff --git a/internal/core/ports/claim_service.go b/internal/core/ports/claim_service.go index 73264e40f..15ae339f9 100644 --- a/internal/core/ports/claim_service.go +++ b/internal/core/ports/claim_service.go @@ -186,10 +186,6 @@ func NewAgentRequest(basicMessage *comm.BasicMessage) (*AgentRequest, error) { return nil, fmt.Errorf("invalid type") } - if basicMessage.ID == "" { - return nil, fmt.Errorf("'id' field cannot be empty") - } - return &AgentRequest{ Body: basicMessage.Body, UserDID: fromDID, diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go new file mode 100644 index 000000000..8d65fecd7 --- /dev/null +++ b/internal/core/ports/payment_service.go @@ -0,0 +1,79 @@ +package ports + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/iden3/go-iden3-core/v2/w3c" + comm "github.com/iden3/iden3comm/v2" + "github.com/iden3/iden3comm/v2/protocol" +) + +type Credential struct { + Type string `json:"type"` + Context string `json:"context"` + typss protocol.CredentialIssuanceRequestMessage +} + +// AgentProposalRequest struct +type AgentProposalRequest struct { + Body json.RawMessage + ThreadID string + IssuerDID *w3c.DID + UserDID *w3c.DID + Typ comm.MediaType + Type comm.ProtocolMessage + Credentials []Credential +} + +// NewAgentProposalRequest validates the inputs and returns a new AgentRequest +func NewAgentProposalRequest(basicMessage *comm.BasicMessage) (*protocol.CredentialsProposalRequestMessage, error) { + if basicMessage.To == "" { + return nil, fmt.Errorf("'to' field cannot be empty") + } + + toDID, err := w3c.ParseDID(basicMessage.To) + if err != nil { + return nil, err + } + + if basicMessage.From == "" { + return nil, fmt.Errorf("'from' field cannot be empty") + } + + fromDID, err := w3c.ParseDID(basicMessage.From) + if err != nil { + return nil, err + } + + if basicMessage.ID == "" { + return nil, fmt.Errorf("'id' field cannot be empty") + } + + if basicMessage.Type != protocol.CredentialProposalRequestMessageType { + return nil, fmt.Errorf("invalid type") + } + + var body *protocol.CredentialsProposalRequestBody + if err := json.Unmarshal(basicMessage.Body, body); err != nil { + return nil, err + } + + return &protocol.CredentialsProposalRequestMessage{ + ID: basicMessage.ID, + ThreadID: basicMessage.ThreadID, + Typ: basicMessage.Typ, + Type: basicMessage.Type, + Body: *body, + To: toDID.String(), + From: fromDID.String(), + }, nil +} + +// PaymentService is the interface implemented by the payment service +type PaymentService interface { + CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, senderDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) + CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) + VerifyPayment(ctx context.Context, message comm.BasicMessage) (bool, error) +} diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go new file mode 100644 index 000000000..3dc2956a8 --- /dev/null +++ b/internal/core/services/payment.go @@ -0,0 +1,487 @@ +package services + +import ( + "context" + "crypto/ecdsa" + "crypto/rand" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/google/uuid" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/go-schema-processor/v2/verifiable" + comm "github.com/iden3/iden3comm/v2" + "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/core/ports" + "github.com/polygonid/sh-id-platform/internal/network" +) + +type payment struct { + networkResolver network.Resolver +} + +// NewClaim creates a new claim service +func NewPaymentService(resolver network.Resolver) ports.PaymentService { + return &payment{networkResolver: resolver} +} + +type PaymentRequestMessageBody struct { + Agent string `json:"agent"` + Payments []PaymentRequestInfo `json:"payments"` +} + +type PaymentRequestInfo struct { + Type *string `json:"type,omitempty"` + Credentials []PaymentRequestInfoCredentials `json:"credentials"` + Description string `json:"description"` + Data interface{} `json:"data"` +} + +type PaymentRequestInfoCredentials struct { + Context string `json:"context,omitempty"` + Type string `json:"type,omitempty"` +} + +type EthereumEip712Signature2021 struct { + Type verifiable.ProofType `json:"type"` + ProofPurpose string `json:"proofPurpose"` + ProofValue string `json:"proofValue"` + VerificationMethod string `json:"verificationMethod"` + Created string `json:"created"` + Eip712 Eip712Data `json:"eip712"` +} + +type Eip712Data struct { + Types string `json:"types"` + PrimaryType string `json:"primaryType"` + Domain Eip712Domain `json:"domain"` +} + +type Eip712Domain struct { + Name string `json:"name"` + Version string `json:"version"` + ChainID string `json:"chainId"` + VerifyingContract string `json:"verifyingContract"` +} + +// Iden3PaymentRailsRequestV1 represents the Iden3PaymentRailsRequestV1 payment request data. +type Iden3PaymentRailsRequestV1 struct { + Nonce string `json:"nonce"` + Type string `json:"type"` + Context []string `json:"@context"` + Recipient string `json:"recipient"` + Amount string `json:"amount"` // Not negative number + ExpirationDate string `json:"expirationDate"` + Proof []EthereumEip712Signature2021 `json:"proof"` + Metadata string `json:"metadata"` + Currency string `json:"currency"` +} + +// Iden3PaymentRailsERC20RequestV1 represents the Iden3PaymentRailsERC20RequestV1 payment request data. +type Iden3PaymentRailsERC20RequestV1 struct { + Nonce string `json:"nonce"` + Type string `json:"type"` + Context []string `json:"@context"` + Recipient string `json:"recipient"` + Amount string `json:"amount"` // Not negative number + ExpirationDate string `json:"expirationDate"` + Proof []EthereumEip712Signature2021 `json:"proof"` + Metadata string `json:"metadata"` + Currency string `json:"currency"` + TokenAddress string `json:"tokenAddress"` + Features []string `json:"features,omitempty"` +} + +func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, userDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) { + id := uuid.New().String() + basicMessage := comm.BasicMessage{ + From: issuerDID.String(), + To: userDID.String(), + Typ: "application/iden3comm-plain-json", + Type: "https://iden3-communication.io/credentials/0.1/payment-reques", + ID: id, + ThreadID: id, + } + + var max *big.Int = big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil) + randomBigInt, err := rand.Int(rand.Reader, max) + if err != nil { + return nil, err + } + + now := time.Now() + oneHourLater := now.Add(1 * time.Hour) + + domain := Eip712Domain{ + Name: "MCPayment", + Version: "1.0.0", + ChainID: "80002", + VerifyingContract: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + } + + privateKeyBytes, err := hex.DecodeString(signingKey) + if err != nil { + return nil, err + } + + privateKey, err := crypto.ToECDSA(privateKeyBytes) + if err != nil { + return nil, err + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("Cannot assert type: publicKey is not of type *ecdsa.PublicKey") + } + address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + typedData := apitypes.TypedData{ + Types: apitypes.Types{ + "EIP712Domain": []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, + "Iden3PaymentRailsRequestV1": []apitypes.Type{ + { + Name: "recipient", + Type: "address", + }, + { + Name: "amount", + Type: "uint256", + }, + { + Name: "expirationDate", + Type: "uint256", + }, + { + Name: "nonce", + Type: "uint256", + }, + { + Name: "metadata", + Type: "bytes", + }, + }, + }, + PrimaryType: "Iden3PaymentRailsRequestV1", + Domain: apitypes.TypedDataDomain{ + Name: "MCPayment", + Version: "1.0.0", + ChainId: math.NewHexOrDecimal256(80002), + VerifyingContract: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + }, + Message: apitypes.TypedDataMessage{ + "recipient": address, + "amount": "100", + "expirationDate": fmt.Sprint(oneHourLater.Unix()), + "nonce": randomBigInt.String(), + "metadata": "0x", + }, + } + + typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + return nil, err + } + + signature, err := crypto.Sign(typedDataHash[:], privateKey) + if err != nil { + return nil, err + } + + nativePayments := Iden3PaymentRailsRequestV1{ + Nonce: randomBigInt.String(), + Type: "Iden3PaymentRailsRequestV1", + Context: []string{ + "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", + "https://w3id.org/security/suites/eip712sig-2021/v1", + }, + Recipient: address, + Amount: "100", + ExpirationDate: fmt.Sprint(oneHourLater.Unix()), + Metadata: "0x", + Currency: "ETHWEI", + Proof: []EthereumEip712Signature2021{ + { + Type: "EthereumEip712Signature2021", + ProofPurpose: "assertionMethod", + ProofValue: hex.EncodeToString(signature), + VerificationMethod: "did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a#blockchainAccountId", + Created: now.Format(time.RFC3339), + Eip712: Eip712Data{ + Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", + PrimaryType: "Iden3PaymentRailsRequestV1", + Domain: domain, + }, + }, + }, + } + + paymentRequestMessageBody := PaymentRequestMessageBody{ + Agent: "localhost", + Payments: []PaymentRequestInfo{ + { + Description: "Payment for credential", + Data: nativePayments, + Credentials: []PaymentRequestInfoCredentials{ + { + Context: credContext, + Type: credType, + }, + }, + }, + }, + } + basicMessage.Body, err = json.Marshal(paymentRequestMessageBody) + + return &basicMessage, nil +} + +func (p *payment) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) { + basicMessage := comm.BasicMessage{ + From: proposalRequest.To, + To: proposalRequest.From, + ThreadID: proposalRequest.ThreadID, + ID: proposalRequest.ID, + Typ: proposalRequest.Typ, + } + return &basicMessage, nil +} + +type Iden3PaymentRailsV1 struct { + Nonce string `json:"nonce"` + Type string `json:"type"` + Context []string `json:"@context,omitempty"` + PaymentData struct { + TxID string `json:"txId"` + ChainID string `json:"chainId"` + } `json:"paymentData"` +} + +const contractABI = `[ + { + inputs: [ + { + components: [ + { + internalType: 'address', + name: 'tokenAddress', + type: 'address', + }, + { + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'expirationDate', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'metadata', + type: 'bytes', + }, + ], + internalType: 'struct MCPayment.Iden3PaymentRailsERC20RequestV1', + name: 'paymentData', + type: 'tuple', + }, + { + internalType: 'bytes', + name: 'signature', + type: 'bytes', + }, + ], + name: 'payERC20', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'permitSignature', + type: 'bytes', + }, + { + components: [ + { + internalType: 'address', + name: 'tokenAddress', + type: 'address', + }, + { + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'expirationDate', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'metadata', + type: 'bytes', + }, + ], + internalType: 'struct MCPayment.Iden3PaymentRailsERC20RequestV1', + name: 'paymentData', + type: 'tuple', + }, + { + internalType: 'bytes', + name: 'signature', + type: 'bytes', + }, + ], + name: 'payERC20Permit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'pendingOwner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + ], + name: 'isPaymentDone', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'expirationDate', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'metadata', + type: 'bytes', + }, + ], + internalType: 'struct MCPayment.Iden3PaymentRailsRequestV1', + name: 'paymentData', + type: 'tuple', + }, + { + internalType: 'bytes', + name: 'signature', + type: 'bytes', + }, + ], + name: 'pay', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, +]` + +func (p *payment) VerifyPayment(ctx context.Context, message comm.BasicMessage) (bool, error) { + var paymentRequest Iden3PaymentRailsV1 + err := json.Unmarshal(message.Body, &paymentRequest) + if err != nil { + return false, err + } + + client, err := p.networkResolver.GetEthClient("polygon:amoy") + if err != nil { + return false, err + } + address := common.HexToAddress("0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774") + + if err != nil { + return false, err + } + + recipient := "0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a" + nonce := paymentRequest.Nonce + + var output bool + err = client.GetEthereumClient().Client().Call(output, "isPaymentDone", address, recipient, nonce) + if err != nil { + return false, err + } + return output, nil +} diff --git a/internal/network/resolver.go b/internal/network/resolver.go index 23d723a61..133419af4 100644 --- a/internal/network/resolver.go +++ b/internal/network/resolver.go @@ -54,6 +54,7 @@ type Resolver struct { supportedContracts map[string]*abi.State stateResolvers map[string]pubsignals.StateResolver supportedNetworks []SupportedNetworks + paymentSettings map[resolverPrefix]PaymentSettings } // SupportedNetworks holds the chain and networks supoprted @@ -73,6 +74,22 @@ type RhsSettings struct { SingleIssuer bool } +// PaymentSettings holds the payment settings +type PaymentSettings struct { + MCPaymentContract string `yaml:"mcPaymentContract"` + Recipient string `yaml:"recipient"` + Amount uint `yaml:"amount"` + erc20Tokens []ERC20TokenSettings `yaml:"erc20Tokens"` +} + +// ERC20TokenSettings holds the ERC20 payment settings +type ERC20TokenSettings struct { + tokenAddress string `yaml:"tokenAddress"` + tokenSymbol string `yaml:"tokenSymbol"` + tokenName string `yaml:"tokenName"` + tokenAmount uint `yaml:"tokenAmount"` +} + // ResolverSettings holds the resolver settings type ResolverSettings map[string]map[string]struct { ContractAddress string `yaml:"contractAddress"` From 485c4b8a898aad50d1085e7bc70fa28f89c0397f Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 5 Nov 2024 23:41:03 +0200 Subject: [PATCH 02/66] payment abi gen --- internal/core/services/payment.go | 213 +--- internal/eth/PaymentContract.go | 1543 +++++++++++++++++++++++++++++ internal/eth/payment.abi | 669 +++++++++++++ 3 files changed, 2226 insertions(+), 199 deletions(-) create mode 100644 internal/eth/PaymentContract.go create mode 100644 internal/eth/payment.abi diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 3dc2956a8..c5774a4d8 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -10,9 +10,11 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" @@ -20,6 +22,7 @@ import ( comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/core/ports" + "github.com/polygonid/sh-id-platform/internal/eth" "github.com/polygonid/sh-id-platform/internal/network" ) @@ -268,220 +271,32 @@ type Iden3PaymentRailsV1 struct { } `json:"paymentData"` } -const contractABI = `[ - { - inputs: [ - { - components: [ - { - internalType: 'address', - name: 'tokenAddress', - type: 'address', - }, - { - internalType: 'address', - name: 'recipient', - type: 'address', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'expirationDate', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'nonce', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'metadata', - type: 'bytes', - }, - ], - internalType: 'struct MCPayment.Iden3PaymentRailsERC20RequestV1', - name: 'paymentData', - type: 'tuple', - }, - { - internalType: 'bytes', - name: 'signature', - type: 'bytes', - }, - ], - name: 'payERC20', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes', - name: 'permitSignature', - type: 'bytes', - }, - { - components: [ - { - internalType: 'address', - name: 'tokenAddress', - type: 'address', - }, - { - internalType: 'address', - name: 'recipient', - type: 'address', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'expirationDate', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'nonce', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'metadata', - type: 'bytes', - }, - ], - internalType: 'struct MCPayment.Iden3PaymentRailsERC20RequestV1', - name: 'paymentData', - type: 'tuple', - }, - { - internalType: 'bytes', - name: 'signature', - type: 'bytes', - }, - ], - name: 'payERC20Permit', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'pendingOwner', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'recipient', - type: 'address', - }, - { - internalType: 'uint256', - name: 'nonce', - type: 'uint256', - }, - ], - name: 'isPaymentDone', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - components: [ - { - internalType: 'address', - name: 'recipient', - type: 'address', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'expirationDate', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'nonce', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'metadata', - type: 'bytes', - }, - ], - internalType: 'struct MCPayment.Iden3PaymentRailsRequestV1', - name: 'paymentData', - type: 'tuple', - }, - { - internalType: 'bytes', - name: 'signature', - type: 'bytes', - }, - ], - name: 'pay', - outputs: [], - stateMutability: 'payable', - type: 'function', - }, -]` +type Iden3PaymentRailsV1Body struct { + Payments []Iden3PaymentRailsV1 `json:"payments"` +} func (p *payment) VerifyPayment(ctx context.Context, message comm.BasicMessage) (bool, error) { - var paymentRequest Iden3PaymentRailsV1 + var paymentRequest Iden3PaymentRailsV1Body err := json.Unmarshal(message.Body, &paymentRequest) if err != nil { return false, err } - client, err := p.networkResolver.GetEthClient("polygon:amoy") + client, err := ethclient.Dial("") if err != nil { return false, err } - address := common.HexToAddress("0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774") + contractAddress := common.HexToAddress("0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774") + instance, err := eth.NewPaymentContract(contractAddress, client) if err != nil { return false, err } - - recipient := "0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a" - nonce := paymentRequest.Nonce - - var output bool - err = client.GetEthereumClient().Client().Call(output, "isPaymentDone", address, recipient, nonce) + recipient := common.HexToAddress("0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a") + nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, 10) + isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: context.Background()}, recipient, nonce) if err != nil { return false, err } - return output, nil + return isPaid, nil } diff --git a/internal/eth/PaymentContract.go b/internal/eth/PaymentContract.go new file mode 100644 index 000000000..8a7f925c4 --- /dev/null +++ b/internal/eth/PaymentContract.go @@ -0,0 +1,1543 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package eth + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// MCPaymentIden3PaymentRailsERC20RequestV1 is an auto generated low-level Go binding around an user-defined struct. +type MCPaymentIden3PaymentRailsERC20RequestV1 struct { + TokenAddress common.Address + Recipient common.Address + Amount *big.Int + ExpirationDate *big.Int + Nonce *big.Int + Metadata []byte +} + +// MCPaymentIden3PaymentRailsRequestV1 is an auto generated low-level Go binding around an user-defined struct. +type MCPaymentIden3PaymentRailsRequestV1 struct { + Recipient common.Address + Amount *big.Int + ExpirationDate *big.Int + Nonce *big.Int + Metadata []byte +} + +// PaymentContractMetaData contains all meta data concerning the PaymentContract contract. +var PaymentContractMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"ECDSAInvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidOwnerPercentage\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"PaymentError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"WithdrawError\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"EIP712DomainChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"Payment\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ERC_20_PAYMENT_DATA_TYPE_HASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PAYMENT_DATA_TYPE_HASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VERSION\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"eip712Domain\",\"outputs\":[{\"internalType\":\"bytes1\",\"name\":\"fields\",\"type\":\"bytes1\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"verifyingContract\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"extensions\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getOwnerBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getOwnerPercentage\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"ownerPercentage\",\"type\":\"uint8\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"isPaymentDone\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"issuerWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ownerWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expirationDate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"metadata\",\"type\":\"bytes\"}],\"internalType\":\"structMCPayment.Iden3PaymentRailsRequestV1\",\"name\":\"paymentData\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"pay\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expirationDate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"metadata\",\"type\":\"bytes\"}],\"internalType\":\"structMCPayment.Iden3PaymentRailsERC20RequestV1\",\"name\":\"paymentData\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"payERC20\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"permitSignature\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expirationDate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"metadata\",\"type\":\"bytes\"}],\"internalType\":\"structMCPayment.Iden3PaymentRailsERC20RequestV1\",\"name\":\"paymentData\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"payERC20Permit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ownerPercentage\",\"type\":\"uint8\"}],\"name\":\"updateOwnerPercentage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expirationDate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"metadata\",\"type\":\"bytes\"}],\"internalType\":\"structMCPayment.Iden3PaymentRailsERC20RequestV1\",\"name\":\"paymentData\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"verifyIden3PaymentRailsERC20RequestV1Signature\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expirationDate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"metadata\",\"type\":\"bytes\"}],\"internalType\":\"structMCPayment.Iden3PaymentRailsRequestV1\",\"name\":\"paymentData\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"verifyIden3PaymentRailsRequestV1Signature\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// PaymentContractABI is the input ABI used to generate the binding from. +// Deprecated: Use PaymentContractMetaData.ABI instead. +var PaymentContractABI = PaymentContractMetaData.ABI + +// PaymentContract is an auto generated Go binding around an Ethereum contract. +type PaymentContract struct { + PaymentContractCaller // Read-only binding to the contract + PaymentContractTransactor // Write-only binding to the contract + PaymentContractFilterer // Log filterer for contract events +} + +// PaymentContractCaller is an auto generated read-only Go binding around an Ethereum contract. +type PaymentContractCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PaymentContractTransactor is an auto generated write-only Go binding around an Ethereum contract. +type PaymentContractTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PaymentContractFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type PaymentContractFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PaymentContractSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type PaymentContractSession struct { + Contract *PaymentContract // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PaymentContractCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type PaymentContractCallerSession struct { + Contract *PaymentContractCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// PaymentContractTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type PaymentContractTransactorSession struct { + Contract *PaymentContractTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PaymentContractRaw is an auto generated low-level Go binding around an Ethereum contract. +type PaymentContractRaw struct { + Contract *PaymentContract // Generic contract binding to access the raw methods on +} + +// PaymentContractCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type PaymentContractCallerRaw struct { + Contract *PaymentContractCaller // Generic read-only contract binding to access the raw methods on +} + +// PaymentContractTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type PaymentContractTransactorRaw struct { + Contract *PaymentContractTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewPaymentContract creates a new instance of PaymentContract, bound to a specific deployed contract. +func NewPaymentContract(address common.Address, backend bind.ContractBackend) (*PaymentContract, error) { + contract, err := bindPaymentContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &PaymentContract{PaymentContractCaller: PaymentContractCaller{contract: contract}, PaymentContractTransactor: PaymentContractTransactor{contract: contract}, PaymentContractFilterer: PaymentContractFilterer{contract: contract}}, nil +} + +// NewPaymentContractCaller creates a new read-only instance of PaymentContract, bound to a specific deployed contract. +func NewPaymentContractCaller(address common.Address, caller bind.ContractCaller) (*PaymentContractCaller, error) { + contract, err := bindPaymentContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &PaymentContractCaller{contract: contract}, nil +} + +// NewPaymentContractTransactor creates a new write-only instance of PaymentContract, bound to a specific deployed contract. +func NewPaymentContractTransactor(address common.Address, transactor bind.ContractTransactor) (*PaymentContractTransactor, error) { + contract, err := bindPaymentContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &PaymentContractTransactor{contract: contract}, nil +} + +// NewPaymentContractFilterer creates a new log filterer instance of PaymentContract, bound to a specific deployed contract. +func NewPaymentContractFilterer(address common.Address, filterer bind.ContractFilterer) (*PaymentContractFilterer, error) { + contract, err := bindPaymentContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &PaymentContractFilterer{contract: contract}, nil +} + +// bindPaymentContract binds a generic wrapper to an already deployed contract. +func bindPaymentContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := PaymentContractMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PaymentContract *PaymentContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _PaymentContract.Contract.PaymentContractCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PaymentContract *PaymentContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PaymentContract.Contract.PaymentContractTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PaymentContract *PaymentContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PaymentContract.Contract.PaymentContractTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PaymentContract *PaymentContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _PaymentContract.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PaymentContract *PaymentContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PaymentContract.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PaymentContract *PaymentContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PaymentContract.Contract.contract.Transact(opts, method, params...) +} + +// ERC20PAYMENTDATATYPEHASH is a free data retrieval call binding the contract method 0xc6bfaa3f. +// +// Solidity: function ERC_20_PAYMENT_DATA_TYPE_HASH() view returns(bytes32) +func (_PaymentContract *PaymentContractCaller) ERC20PAYMENTDATATYPEHASH(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "ERC_20_PAYMENT_DATA_TYPE_HASH") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// ERC20PAYMENTDATATYPEHASH is a free data retrieval call binding the contract method 0xc6bfaa3f. +// +// Solidity: function ERC_20_PAYMENT_DATA_TYPE_HASH() view returns(bytes32) +func (_PaymentContract *PaymentContractSession) ERC20PAYMENTDATATYPEHASH() ([32]byte, error) { + return _PaymentContract.Contract.ERC20PAYMENTDATATYPEHASH(&_PaymentContract.CallOpts) +} + +// ERC20PAYMENTDATATYPEHASH is a free data retrieval call binding the contract method 0xc6bfaa3f. +// +// Solidity: function ERC_20_PAYMENT_DATA_TYPE_HASH() view returns(bytes32) +func (_PaymentContract *PaymentContractCallerSession) ERC20PAYMENTDATATYPEHASH() ([32]byte, error) { + return _PaymentContract.Contract.ERC20PAYMENTDATATYPEHASH(&_PaymentContract.CallOpts) +} + +// PAYMENTDATATYPEHASH is a free data retrieval call binding the contract method 0xf0dd6899. +// +// Solidity: function PAYMENT_DATA_TYPE_HASH() view returns(bytes32) +func (_PaymentContract *PaymentContractCaller) PAYMENTDATATYPEHASH(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "PAYMENT_DATA_TYPE_HASH") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// PAYMENTDATATYPEHASH is a free data retrieval call binding the contract method 0xf0dd6899. +// +// Solidity: function PAYMENT_DATA_TYPE_HASH() view returns(bytes32) +func (_PaymentContract *PaymentContractSession) PAYMENTDATATYPEHASH() ([32]byte, error) { + return _PaymentContract.Contract.PAYMENTDATATYPEHASH(&_PaymentContract.CallOpts) +} + +// PAYMENTDATATYPEHASH is a free data retrieval call binding the contract method 0xf0dd6899. +// +// Solidity: function PAYMENT_DATA_TYPE_HASH() view returns(bytes32) +func (_PaymentContract *PaymentContractCallerSession) PAYMENTDATATYPEHASH() ([32]byte, error) { + return _PaymentContract.Contract.PAYMENTDATATYPEHASH(&_PaymentContract.CallOpts) +} + +// VERSION is a free data retrieval call binding the contract method 0xffa1ad74. +// +// Solidity: function VERSION() view returns(string) +func (_PaymentContract *PaymentContractCaller) VERSION(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "VERSION") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// VERSION is a free data retrieval call binding the contract method 0xffa1ad74. +// +// Solidity: function VERSION() view returns(string) +func (_PaymentContract *PaymentContractSession) VERSION() (string, error) { + return _PaymentContract.Contract.VERSION(&_PaymentContract.CallOpts) +} + +// VERSION is a free data retrieval call binding the contract method 0xffa1ad74. +// +// Solidity: function VERSION() view returns(string) +func (_PaymentContract *PaymentContractCallerSession) VERSION() (string, error) { + return _PaymentContract.Contract.VERSION(&_PaymentContract.CallOpts) +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_PaymentContract *PaymentContractCaller) Eip712Domain(opts *bind.CallOpts) (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "eip712Domain") + + outstruct := new(struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int + }) + if err != nil { + return *outstruct, err + } + + outstruct.Fields = *abi.ConvertType(out[0], new([1]byte)).(*[1]byte) + outstruct.Name = *abi.ConvertType(out[1], new(string)).(*string) + outstruct.Version = *abi.ConvertType(out[2], new(string)).(*string) + outstruct.ChainId = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.VerifyingContract = *abi.ConvertType(out[4], new(common.Address)).(*common.Address) + outstruct.Salt = *abi.ConvertType(out[5], new([32]byte)).(*[32]byte) + outstruct.Extensions = *abi.ConvertType(out[6], new([]*big.Int)).(*[]*big.Int) + + return *outstruct, err + +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_PaymentContract *PaymentContractSession) Eip712Domain() (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + return _PaymentContract.Contract.Eip712Domain(&_PaymentContract.CallOpts) +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_PaymentContract *PaymentContractCallerSession) Eip712Domain() (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + return _PaymentContract.Contract.Eip712Domain(&_PaymentContract.CallOpts) +} + +// GetBalance is a free data retrieval call binding the contract method 0xf8b2cb4f. +// +// Solidity: function getBalance(address recipient) view returns(uint256) +func (_PaymentContract *PaymentContractCaller) GetBalance(opts *bind.CallOpts, recipient common.Address) (*big.Int, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "getBalance", recipient) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetBalance is a free data retrieval call binding the contract method 0xf8b2cb4f. +// +// Solidity: function getBalance(address recipient) view returns(uint256) +func (_PaymentContract *PaymentContractSession) GetBalance(recipient common.Address) (*big.Int, error) { + return _PaymentContract.Contract.GetBalance(&_PaymentContract.CallOpts, recipient) +} + +// GetBalance is a free data retrieval call binding the contract method 0xf8b2cb4f. +// +// Solidity: function getBalance(address recipient) view returns(uint256) +func (_PaymentContract *PaymentContractCallerSession) GetBalance(recipient common.Address) (*big.Int, error) { + return _PaymentContract.Contract.GetBalance(&_PaymentContract.CallOpts, recipient) +} + +// GetOwnerBalance is a free data retrieval call binding the contract method 0x590791f2. +// +// Solidity: function getOwnerBalance() view returns(uint256) +func (_PaymentContract *PaymentContractCaller) GetOwnerBalance(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "getOwnerBalance") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetOwnerBalance is a free data retrieval call binding the contract method 0x590791f2. +// +// Solidity: function getOwnerBalance() view returns(uint256) +func (_PaymentContract *PaymentContractSession) GetOwnerBalance() (*big.Int, error) { + return _PaymentContract.Contract.GetOwnerBalance(&_PaymentContract.CallOpts) +} + +// GetOwnerBalance is a free data retrieval call binding the contract method 0x590791f2. +// +// Solidity: function getOwnerBalance() view returns(uint256) +func (_PaymentContract *PaymentContractCallerSession) GetOwnerBalance() (*big.Int, error) { + return _PaymentContract.Contract.GetOwnerBalance(&_PaymentContract.CallOpts) +} + +// GetOwnerPercentage is a free data retrieval call binding the contract method 0x309a042c. +// +// Solidity: function getOwnerPercentage() view returns(uint8) +func (_PaymentContract *PaymentContractCaller) GetOwnerPercentage(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "getOwnerPercentage") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// GetOwnerPercentage is a free data retrieval call binding the contract method 0x309a042c. +// +// Solidity: function getOwnerPercentage() view returns(uint8) +func (_PaymentContract *PaymentContractSession) GetOwnerPercentage() (uint8, error) { + return _PaymentContract.Contract.GetOwnerPercentage(&_PaymentContract.CallOpts) +} + +// GetOwnerPercentage is a free data retrieval call binding the contract method 0x309a042c. +// +// Solidity: function getOwnerPercentage() view returns(uint8) +func (_PaymentContract *PaymentContractCallerSession) GetOwnerPercentage() (uint8, error) { + return _PaymentContract.Contract.GetOwnerPercentage(&_PaymentContract.CallOpts) +} + +// IsPaymentDone is a free data retrieval call binding the contract method 0x9d9c12b7. +// +// Solidity: function isPaymentDone(address recipient, uint256 nonce) view returns(bool) +func (_PaymentContract *PaymentContractCaller) IsPaymentDone(opts *bind.CallOpts, recipient common.Address, nonce *big.Int) (bool, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "isPaymentDone", recipient, nonce) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsPaymentDone is a free data retrieval call binding the contract method 0x9d9c12b7. +// +// Solidity: function isPaymentDone(address recipient, uint256 nonce) view returns(bool) +func (_PaymentContract *PaymentContractSession) IsPaymentDone(recipient common.Address, nonce *big.Int) (bool, error) { + return _PaymentContract.Contract.IsPaymentDone(&_PaymentContract.CallOpts, recipient, nonce) +} + +// IsPaymentDone is a free data retrieval call binding the contract method 0x9d9c12b7. +// +// Solidity: function isPaymentDone(address recipient, uint256 nonce) view returns(bool) +func (_PaymentContract *PaymentContractCallerSession) IsPaymentDone(recipient common.Address, nonce *big.Int) (bool, error) { + return _PaymentContract.Contract.IsPaymentDone(&_PaymentContract.CallOpts, recipient, nonce) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_PaymentContract *PaymentContractCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_PaymentContract *PaymentContractSession) Owner() (common.Address, error) { + return _PaymentContract.Contract.Owner(&_PaymentContract.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_PaymentContract *PaymentContractCallerSession) Owner() (common.Address, error) { + return _PaymentContract.Contract.Owner(&_PaymentContract.CallOpts) +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_PaymentContract *PaymentContractCaller) PendingOwner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "pendingOwner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_PaymentContract *PaymentContractSession) PendingOwner() (common.Address, error) { + return _PaymentContract.Contract.PendingOwner(&_PaymentContract.CallOpts) +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_PaymentContract *PaymentContractCallerSession) PendingOwner() (common.Address, error) { + return _PaymentContract.Contract.PendingOwner(&_PaymentContract.CallOpts) +} + +// VerifyIden3PaymentRailsERC20RequestV1Signature is a free data retrieval call binding the contract method 0x3039009d. +// +// Solidity: function verifyIden3PaymentRailsERC20RequestV1Signature((address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) view returns() +func (_PaymentContract *PaymentContractCaller) VerifyIden3PaymentRailsERC20RequestV1Signature(opts *bind.CallOpts, paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) error { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "verifyIden3PaymentRailsERC20RequestV1Signature", paymentData, signature) + + if err != nil { + return err + } + + return err + +} + +// VerifyIden3PaymentRailsERC20RequestV1Signature is a free data retrieval call binding the contract method 0x3039009d. +// +// Solidity: function verifyIden3PaymentRailsERC20RequestV1Signature((address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) view returns() +func (_PaymentContract *PaymentContractSession) VerifyIden3PaymentRailsERC20RequestV1Signature(paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) error { + return _PaymentContract.Contract.VerifyIden3PaymentRailsERC20RequestV1Signature(&_PaymentContract.CallOpts, paymentData, signature) +} + +// VerifyIden3PaymentRailsERC20RequestV1Signature is a free data retrieval call binding the contract method 0x3039009d. +// +// Solidity: function verifyIden3PaymentRailsERC20RequestV1Signature((address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) view returns() +func (_PaymentContract *PaymentContractCallerSession) VerifyIden3PaymentRailsERC20RequestV1Signature(paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) error { + return _PaymentContract.Contract.VerifyIden3PaymentRailsERC20RequestV1Signature(&_PaymentContract.CallOpts, paymentData, signature) +} + +// VerifyIden3PaymentRailsRequestV1Signature is a free data retrieval call binding the contract method 0x955317f6. +// +// Solidity: function verifyIden3PaymentRailsRequestV1Signature((address,uint256,uint256,uint256,bytes) paymentData, bytes signature) view returns() +func (_PaymentContract *PaymentContractCaller) VerifyIden3PaymentRailsRequestV1Signature(opts *bind.CallOpts, paymentData MCPaymentIden3PaymentRailsRequestV1, signature []byte) error { + var out []interface{} + err := _PaymentContract.contract.Call(opts, &out, "verifyIden3PaymentRailsRequestV1Signature", paymentData, signature) + + if err != nil { + return err + } + + return err + +} + +// VerifyIden3PaymentRailsRequestV1Signature is a free data retrieval call binding the contract method 0x955317f6. +// +// Solidity: function verifyIden3PaymentRailsRequestV1Signature((address,uint256,uint256,uint256,bytes) paymentData, bytes signature) view returns() +func (_PaymentContract *PaymentContractSession) VerifyIden3PaymentRailsRequestV1Signature(paymentData MCPaymentIden3PaymentRailsRequestV1, signature []byte) error { + return _PaymentContract.Contract.VerifyIden3PaymentRailsRequestV1Signature(&_PaymentContract.CallOpts, paymentData, signature) +} + +// VerifyIden3PaymentRailsRequestV1Signature is a free data retrieval call binding the contract method 0x955317f6. +// +// Solidity: function verifyIden3PaymentRailsRequestV1Signature((address,uint256,uint256,uint256,bytes) paymentData, bytes signature) view returns() +func (_PaymentContract *PaymentContractCallerSession) VerifyIden3PaymentRailsRequestV1Signature(paymentData MCPaymentIden3PaymentRailsRequestV1, signature []byte) error { + return _PaymentContract.Contract.VerifyIden3PaymentRailsRequestV1Signature(&_PaymentContract.CallOpts, paymentData, signature) +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_PaymentContract *PaymentContractTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "acceptOwnership") +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_PaymentContract *PaymentContractSession) AcceptOwnership() (*types.Transaction, error) { + return _PaymentContract.Contract.AcceptOwnership(&_PaymentContract.TransactOpts) +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_PaymentContract *PaymentContractTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _PaymentContract.Contract.AcceptOwnership(&_PaymentContract.TransactOpts) +} + +// Initialize is a paid mutator transaction binding the contract method 0x943b24b2. +// +// Solidity: function initialize(address owner, uint8 ownerPercentage) returns() +func (_PaymentContract *PaymentContractTransactor) Initialize(opts *bind.TransactOpts, owner common.Address, ownerPercentage uint8) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "initialize", owner, ownerPercentage) +} + +// Initialize is a paid mutator transaction binding the contract method 0x943b24b2. +// +// Solidity: function initialize(address owner, uint8 ownerPercentage) returns() +func (_PaymentContract *PaymentContractSession) Initialize(owner common.Address, ownerPercentage uint8) (*types.Transaction, error) { + return _PaymentContract.Contract.Initialize(&_PaymentContract.TransactOpts, owner, ownerPercentage) +} + +// Initialize is a paid mutator transaction binding the contract method 0x943b24b2. +// +// Solidity: function initialize(address owner, uint8 ownerPercentage) returns() +func (_PaymentContract *PaymentContractTransactorSession) Initialize(owner common.Address, ownerPercentage uint8) (*types.Transaction, error) { + return _PaymentContract.Contract.Initialize(&_PaymentContract.TransactOpts, owner, ownerPercentage) +} + +// IssuerWithdraw is a paid mutator transaction binding the contract method 0xcc9cd961. +// +// Solidity: function issuerWithdraw() returns() +func (_PaymentContract *PaymentContractTransactor) IssuerWithdraw(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "issuerWithdraw") +} + +// IssuerWithdraw is a paid mutator transaction binding the contract method 0xcc9cd961. +// +// Solidity: function issuerWithdraw() returns() +func (_PaymentContract *PaymentContractSession) IssuerWithdraw() (*types.Transaction, error) { + return _PaymentContract.Contract.IssuerWithdraw(&_PaymentContract.TransactOpts) +} + +// IssuerWithdraw is a paid mutator transaction binding the contract method 0xcc9cd961. +// +// Solidity: function issuerWithdraw() returns() +func (_PaymentContract *PaymentContractTransactorSession) IssuerWithdraw() (*types.Transaction, error) { + return _PaymentContract.Contract.IssuerWithdraw(&_PaymentContract.TransactOpts) +} + +// OwnerWithdraw is a paid mutator transaction binding the contract method 0x4311de8f. +// +// Solidity: function ownerWithdraw() returns() +func (_PaymentContract *PaymentContractTransactor) OwnerWithdraw(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "ownerWithdraw") +} + +// OwnerWithdraw is a paid mutator transaction binding the contract method 0x4311de8f. +// +// Solidity: function ownerWithdraw() returns() +func (_PaymentContract *PaymentContractSession) OwnerWithdraw() (*types.Transaction, error) { + return _PaymentContract.Contract.OwnerWithdraw(&_PaymentContract.TransactOpts) +} + +// OwnerWithdraw is a paid mutator transaction binding the contract method 0x4311de8f. +// +// Solidity: function ownerWithdraw() returns() +func (_PaymentContract *PaymentContractTransactorSession) OwnerWithdraw() (*types.Transaction, error) { + return _PaymentContract.Contract.OwnerWithdraw(&_PaymentContract.TransactOpts) +} + +// Pay is a paid mutator transaction binding the contract method 0xaa021669. +// +// Solidity: function pay((address,uint256,uint256,uint256,bytes) paymentData, bytes signature) payable returns() +func (_PaymentContract *PaymentContractTransactor) Pay(opts *bind.TransactOpts, paymentData MCPaymentIden3PaymentRailsRequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "pay", paymentData, signature) +} + +// Pay is a paid mutator transaction binding the contract method 0xaa021669. +// +// Solidity: function pay((address,uint256,uint256,uint256,bytes) paymentData, bytes signature) payable returns() +func (_PaymentContract *PaymentContractSession) Pay(paymentData MCPaymentIden3PaymentRailsRequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.Contract.Pay(&_PaymentContract.TransactOpts, paymentData, signature) +} + +// Pay is a paid mutator transaction binding the contract method 0xaa021669. +// +// Solidity: function pay((address,uint256,uint256,uint256,bytes) paymentData, bytes signature) payable returns() +func (_PaymentContract *PaymentContractTransactorSession) Pay(paymentData MCPaymentIden3PaymentRailsRequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.Contract.Pay(&_PaymentContract.TransactOpts, paymentData, signature) +} + +// PayERC20 is a paid mutator transaction binding the contract method 0x57615a3a. +// +// Solidity: function payERC20((address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) returns() +func (_PaymentContract *PaymentContractTransactor) PayERC20(opts *bind.TransactOpts, paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "payERC20", paymentData, signature) +} + +// PayERC20 is a paid mutator transaction binding the contract method 0x57615a3a. +// +// Solidity: function payERC20((address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) returns() +func (_PaymentContract *PaymentContractSession) PayERC20(paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.Contract.PayERC20(&_PaymentContract.TransactOpts, paymentData, signature) +} + +// PayERC20 is a paid mutator transaction binding the contract method 0x57615a3a. +// +// Solidity: function payERC20((address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) returns() +func (_PaymentContract *PaymentContractTransactorSession) PayERC20(paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.Contract.PayERC20(&_PaymentContract.TransactOpts, paymentData, signature) +} + +// PayERC20Permit is a paid mutator transaction binding the contract method 0x3dbbdbe5. +// +// Solidity: function payERC20Permit(bytes permitSignature, (address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) returns() +func (_PaymentContract *PaymentContractTransactor) PayERC20Permit(opts *bind.TransactOpts, permitSignature []byte, paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "payERC20Permit", permitSignature, paymentData, signature) +} + +// PayERC20Permit is a paid mutator transaction binding the contract method 0x3dbbdbe5. +// +// Solidity: function payERC20Permit(bytes permitSignature, (address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) returns() +func (_PaymentContract *PaymentContractSession) PayERC20Permit(permitSignature []byte, paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.Contract.PayERC20Permit(&_PaymentContract.TransactOpts, permitSignature, paymentData, signature) +} + +// PayERC20Permit is a paid mutator transaction binding the contract method 0x3dbbdbe5. +// +// Solidity: function payERC20Permit(bytes permitSignature, (address,address,uint256,uint256,uint256,bytes) paymentData, bytes signature) returns() +func (_PaymentContract *PaymentContractTransactorSession) PayERC20Permit(permitSignature []byte, paymentData MCPaymentIden3PaymentRailsERC20RequestV1, signature []byte) (*types.Transaction, error) { + return _PaymentContract.Contract.PayERC20Permit(&_PaymentContract.TransactOpts, permitSignature, paymentData, signature) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_PaymentContract *PaymentContractTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_PaymentContract *PaymentContractSession) RenounceOwnership() (*types.Transaction, error) { + return _PaymentContract.Contract.RenounceOwnership(&_PaymentContract.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_PaymentContract *PaymentContractTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _PaymentContract.Contract.RenounceOwnership(&_PaymentContract.TransactOpts) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_PaymentContract *PaymentContractTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_PaymentContract *PaymentContractSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _PaymentContract.Contract.TransferOwnership(&_PaymentContract.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_PaymentContract *PaymentContractTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _PaymentContract.Contract.TransferOwnership(&_PaymentContract.TransactOpts, newOwner) +} + +// UpdateOwnerPercentage is a paid mutator transaction binding the contract method 0x0cea58aa. +// +// Solidity: function updateOwnerPercentage(uint8 ownerPercentage) returns() +func (_PaymentContract *PaymentContractTransactor) UpdateOwnerPercentage(opts *bind.TransactOpts, ownerPercentage uint8) (*types.Transaction, error) { + return _PaymentContract.contract.Transact(opts, "updateOwnerPercentage", ownerPercentage) +} + +// UpdateOwnerPercentage is a paid mutator transaction binding the contract method 0x0cea58aa. +// +// Solidity: function updateOwnerPercentage(uint8 ownerPercentage) returns() +func (_PaymentContract *PaymentContractSession) UpdateOwnerPercentage(ownerPercentage uint8) (*types.Transaction, error) { + return _PaymentContract.Contract.UpdateOwnerPercentage(&_PaymentContract.TransactOpts, ownerPercentage) +} + +// UpdateOwnerPercentage is a paid mutator transaction binding the contract method 0x0cea58aa. +// +// Solidity: function updateOwnerPercentage(uint8 ownerPercentage) returns() +func (_PaymentContract *PaymentContractTransactorSession) UpdateOwnerPercentage(ownerPercentage uint8) (*types.Transaction, error) { + return _PaymentContract.Contract.UpdateOwnerPercentage(&_PaymentContract.TransactOpts, ownerPercentage) +} + +// PaymentContractEIP712DomainChangedIterator is returned from FilterEIP712DomainChanged and is used to iterate over the raw logs and unpacked data for EIP712DomainChanged events raised by the PaymentContract contract. +type PaymentContractEIP712DomainChangedIterator struct { + Event *PaymentContractEIP712DomainChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PaymentContractEIP712DomainChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PaymentContractEIP712DomainChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PaymentContractEIP712DomainChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PaymentContractEIP712DomainChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PaymentContractEIP712DomainChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PaymentContractEIP712DomainChanged represents a EIP712DomainChanged event raised by the PaymentContract contract. +type PaymentContractEIP712DomainChanged struct { + Raw types.Log // Blockchain specific contextual infos +} + +// FilterEIP712DomainChanged is a free log retrieval operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_PaymentContract *PaymentContractFilterer) FilterEIP712DomainChanged(opts *bind.FilterOpts) (*PaymentContractEIP712DomainChangedIterator, error) { + + logs, sub, err := _PaymentContract.contract.FilterLogs(opts, "EIP712DomainChanged") + if err != nil { + return nil, err + } + return &PaymentContractEIP712DomainChangedIterator{contract: _PaymentContract.contract, event: "EIP712DomainChanged", logs: logs, sub: sub}, nil +} + +// WatchEIP712DomainChanged is a free log subscription operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_PaymentContract *PaymentContractFilterer) WatchEIP712DomainChanged(opts *bind.WatchOpts, sink chan<- *PaymentContractEIP712DomainChanged) (event.Subscription, error) { + + logs, sub, err := _PaymentContract.contract.WatchLogs(opts, "EIP712DomainChanged") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PaymentContractEIP712DomainChanged) + if err := _PaymentContract.contract.UnpackLog(event, "EIP712DomainChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseEIP712DomainChanged is a log parse operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_PaymentContract *PaymentContractFilterer) ParseEIP712DomainChanged(log types.Log) (*PaymentContractEIP712DomainChanged, error) { + event := new(PaymentContractEIP712DomainChanged) + if err := _PaymentContract.contract.UnpackLog(event, "EIP712DomainChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PaymentContractInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the PaymentContract contract. +type PaymentContractInitializedIterator struct { + Event *PaymentContractInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PaymentContractInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PaymentContractInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PaymentContractInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PaymentContractInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PaymentContractInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PaymentContractInitialized represents a Initialized event raised by the PaymentContract contract. +type PaymentContractInitialized struct { + Version uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_PaymentContract *PaymentContractFilterer) FilterInitialized(opts *bind.FilterOpts) (*PaymentContractInitializedIterator, error) { + + logs, sub, err := _PaymentContract.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &PaymentContractInitializedIterator{contract: _PaymentContract.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_PaymentContract *PaymentContractFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *PaymentContractInitialized) (event.Subscription, error) { + + logs, sub, err := _PaymentContract.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PaymentContractInitialized) + if err := _PaymentContract.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_PaymentContract *PaymentContractFilterer) ParseInitialized(log types.Log) (*PaymentContractInitialized, error) { + event := new(PaymentContractInitialized) + if err := _PaymentContract.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PaymentContractOwnershipTransferStartedIterator is returned from FilterOwnershipTransferStarted and is used to iterate over the raw logs and unpacked data for OwnershipTransferStarted events raised by the PaymentContract contract. +type PaymentContractOwnershipTransferStartedIterator struct { + Event *PaymentContractOwnershipTransferStarted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PaymentContractOwnershipTransferStartedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PaymentContractOwnershipTransferStarted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PaymentContractOwnershipTransferStarted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PaymentContractOwnershipTransferStartedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PaymentContractOwnershipTransferStartedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PaymentContractOwnershipTransferStarted represents a OwnershipTransferStarted event raised by the PaymentContract contract. +type PaymentContractOwnershipTransferStarted struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferStarted is a free log retrieval operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_PaymentContract *PaymentContractFilterer) FilterOwnershipTransferStarted(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*PaymentContractOwnershipTransferStartedIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _PaymentContract.contract.FilterLogs(opts, "OwnershipTransferStarted", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &PaymentContractOwnershipTransferStartedIterator{contract: _PaymentContract.contract, event: "OwnershipTransferStarted", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferStarted is a free log subscription operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_PaymentContract *PaymentContractFilterer) WatchOwnershipTransferStarted(opts *bind.WatchOpts, sink chan<- *PaymentContractOwnershipTransferStarted, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _PaymentContract.contract.WatchLogs(opts, "OwnershipTransferStarted", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PaymentContractOwnershipTransferStarted) + if err := _PaymentContract.contract.UnpackLog(event, "OwnershipTransferStarted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferStarted is a log parse operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_PaymentContract *PaymentContractFilterer) ParseOwnershipTransferStarted(log types.Log) (*PaymentContractOwnershipTransferStarted, error) { + event := new(PaymentContractOwnershipTransferStarted) + if err := _PaymentContract.contract.UnpackLog(event, "OwnershipTransferStarted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PaymentContractOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the PaymentContract contract. +type PaymentContractOwnershipTransferredIterator struct { + Event *PaymentContractOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PaymentContractOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PaymentContractOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PaymentContractOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PaymentContractOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PaymentContractOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PaymentContractOwnershipTransferred represents a OwnershipTransferred event raised by the PaymentContract contract. +type PaymentContractOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_PaymentContract *PaymentContractFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*PaymentContractOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _PaymentContract.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &PaymentContractOwnershipTransferredIterator{contract: _PaymentContract.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_PaymentContract *PaymentContractFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *PaymentContractOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _PaymentContract.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PaymentContractOwnershipTransferred) + if err := _PaymentContract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_PaymentContract *PaymentContractFilterer) ParseOwnershipTransferred(log types.Log) (*PaymentContractOwnershipTransferred, error) { + event := new(PaymentContractOwnershipTransferred) + if err := _PaymentContract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PaymentContractPaymentIterator is returned from FilterPayment and is used to iterate over the raw logs and unpacked data for Payment events raised by the PaymentContract contract. +type PaymentContractPaymentIterator struct { + Event *PaymentContractPayment // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PaymentContractPaymentIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PaymentContractPayment) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PaymentContractPayment) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PaymentContractPaymentIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PaymentContractPaymentIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PaymentContractPayment represents a Payment event raised by the PaymentContract contract. +type PaymentContractPayment struct { + Recipient common.Address + Nonce *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterPayment is a free log retrieval operation binding the contract event 0xd4f43975feb89f48dd30cabbb32011045be187d1e11c8ea9faa43efc35282519. +// +// Solidity: event Payment(address indexed recipient, uint256 indexed nonce) +func (_PaymentContract *PaymentContractFilterer) FilterPayment(opts *bind.FilterOpts, recipient []common.Address, nonce []*big.Int) (*PaymentContractPaymentIterator, error) { + + var recipientRule []interface{} + for _, recipientItem := range recipient { + recipientRule = append(recipientRule, recipientItem) + } + var nonceRule []interface{} + for _, nonceItem := range nonce { + nonceRule = append(nonceRule, nonceItem) + } + + logs, sub, err := _PaymentContract.contract.FilterLogs(opts, "Payment", recipientRule, nonceRule) + if err != nil { + return nil, err + } + return &PaymentContractPaymentIterator{contract: _PaymentContract.contract, event: "Payment", logs: logs, sub: sub}, nil +} + +// WatchPayment is a free log subscription operation binding the contract event 0xd4f43975feb89f48dd30cabbb32011045be187d1e11c8ea9faa43efc35282519. +// +// Solidity: event Payment(address indexed recipient, uint256 indexed nonce) +func (_PaymentContract *PaymentContractFilterer) WatchPayment(opts *bind.WatchOpts, sink chan<- *PaymentContractPayment, recipient []common.Address, nonce []*big.Int) (event.Subscription, error) { + + var recipientRule []interface{} + for _, recipientItem := range recipient { + recipientRule = append(recipientRule, recipientItem) + } + var nonceRule []interface{} + for _, nonceItem := range nonce { + nonceRule = append(nonceRule, nonceItem) + } + + logs, sub, err := _PaymentContract.contract.WatchLogs(opts, "Payment", recipientRule, nonceRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PaymentContractPayment) + if err := _PaymentContract.contract.UnpackLog(event, "Payment", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParsePayment is a log parse operation binding the contract event 0xd4f43975feb89f48dd30cabbb32011045be187d1e11c8ea9faa43efc35282519. +// +// Solidity: event Payment(address indexed recipient, uint256 indexed nonce) +func (_PaymentContract *PaymentContractFilterer) ParsePayment(log types.Log) (*PaymentContractPayment, error) { + event := new(PaymentContractPayment) + if err := _PaymentContract.contract.UnpackLog(event, "Payment", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/internal/eth/payment.abi b/internal/eth/payment.abi new file mode 100644 index 000000000..56a528ff2 --- /dev/null +++ b/internal/eth/payment.abi @@ -0,0 +1,669 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "ECDSAInvalidSignatureLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "InvalidOwnerPercentage", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "PaymentError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "WithdrawError", + "type": "error" + }, + { + "anonymous": false, + "inputs": [], + "name": "EIP712DomainChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "Payment", + "type": "event" + }, + { + "inputs": [], + "name": "ERC_20_PAYMENT_DATA_TYPE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAYMENT_DATA_TYPE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "eip712Domain", + "outputs": [ + { + "internalType": "bytes1", + "name": "fields", + "type": "bytes1" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "verifyingContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256[]", + "name": "extensions", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOwnerBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOwnerPercentage", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint8", + "name": "ownerPercentage", + "type": "uint8" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "isPaymentDone", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "issuerWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ownerWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expirationDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "metadata", + "type": "bytes" + } + ], + "internalType": "struct MCPayment.Iden3PaymentRailsRequestV1", + "name": "paymentData", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "pay", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expirationDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "metadata", + "type": "bytes" + } + ], + "internalType": "struct MCPayment.Iden3PaymentRailsERC20RequestV1", + "name": "paymentData", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "payERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "permitSignature", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expirationDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "metadata", + "type": "bytes" + } + ], + "internalType": "struct MCPayment.Iden3PaymentRailsERC20RequestV1", + "name": "paymentData", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "payERC20Permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "ownerPercentage", + "type": "uint8" + } + ], + "name": "updateOwnerPercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expirationDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "metadata", + "type": "bytes" + } + ], + "internalType": "struct MCPayment.Iden3PaymentRailsERC20RequestV1", + "name": "paymentData", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "verifyIden3PaymentRailsERC20RequestV1Signature", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expirationDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "metadata", + "type": "bytes" + } + ], + "internalType": "struct MCPayment.Iden3PaymentRailsRequestV1", + "name": "paymentData", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "verifyIden3PaymentRailsRequestV1Signature", + "outputs": [], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file From 2009e0c470ae30cb8116442837698f70f5b0db62 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Thu, 7 Nov 2024 12:40:34 +0200 Subject: [PATCH 03/66] add recipient --- api/api.yaml | 12 ++++++++- internal/api/api.gen.go | 35 +++++++++++++++++++------- internal/api/payment.go | 6 ++++- internal/core/ports/payment_service.go | 3 ++- internal/core/services/payment.go | 14 +++++------ 5 files changed, 51 insertions(+), 19 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index e7a6d3d29..e8c7b52f0 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1325,7 +1325,7 @@ paths: '500': $ref: '#/components/responses/500' - /v2/payment/verify: + /v2/payment/verify/{recipient}: post: summary: Verify Payment operationId: VerifyPayment @@ -1334,6 +1334,8 @@ paths: - Payment security: - basicAuth: [ ] + parameters: + - $ref: '#/components/parameters/pathRecipient' requestBody: required: true content: @@ -2379,6 +2381,14 @@ components: description: Issuer identifier schema: type: string + + pathRecipient: + name: recipient + in: path + required: true + description: Payment recipient address + schema: + type: string pathClaim: name: id diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index a73ebcbf1..f8ae838be 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -575,6 +575,9 @@ type PathIdentifier = string // PathNonce defines model for pathNonce. type PathNonce = int64 +// PathRecipient defines model for pathRecipient. +type PathRecipient = string + // SessionID defines model for sessionID. type SessionID = uuid.UUID @@ -922,8 +925,8 @@ type ServerInterface interface { // (GET /v2/identities/{identifier}/state/transactions) GetStateTransactions(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetStateTransactionsParams) // Verify Payment - // (POST /v2/payment/verify) - VerifyPayment(w http.ResponseWriter, r *http.Request) + // (POST /v2/payment/verify/{recipient}) + VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) // Get QrCode from store // (GET /v2/qr-store) GetQrFromStore(w http.ResponseWriter, r *http.Request, params GetQrFromStoreParams) @@ -1168,8 +1171,8 @@ func (_ Unimplemented) GetStateTransactions(w http.ResponseWriter, r *http.Reque } // Verify Payment -// (POST /v2/payment/verify) -func (_ Unimplemented) VerifyPayment(w http.ResponseWriter, r *http.Request) { +// (POST /v2/payment/verify/{recipient}) +func (_ Unimplemented) VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) { w.WriteHeader(http.StatusNotImplemented) } @@ -2629,6 +2632,17 @@ func (siw *ServerInterfaceWrapper) GetStateTransactions(w http.ResponseWriter, r // VerifyPayment operation middleware func (siw *ServerInterfaceWrapper) VerifyPayment(w http.ResponseWriter, r *http.Request) { + var err error + + // ------------- Path parameter "recipient" ------------- + var recipient PathRecipient + + err = runtime.BindStyledParameterWithOptions("simple", "recipient", chi.URLParam(r, "recipient"), &recipient, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "recipient", Err: err}) + return + } + ctx := r.Context() ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) @@ -2636,7 +2650,7 @@ func (siw *ServerInterfaceWrapper) VerifyPayment(w http.ResponseWriter, r *http. r = r.WithContext(ctx) handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.VerifyPayment(w, r) + siw.Handler.VerifyPayment(w, r, recipient) })) for _, middleware := range siw.HandlerMiddlewares { @@ -2965,7 +2979,7 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Get(options.BaseURL+"/v2/identities/{identifier}/state/transactions", wrapper.GetStateTransactions) }) r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/v2/payment/verify", wrapper.VerifyPayment) + r.Post(options.BaseURL+"/v2/payment/verify/{recipient}", wrapper.VerifyPayment) }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/qr-store", wrapper.GetQrFromStore) @@ -4587,7 +4601,8 @@ func (response GetStateTransactions500JSONResponse) VisitGetStateTransactionsRes } type VerifyPaymentRequestObject struct { - Body *VerifyPaymentJSONRequestBody + Recipient PathRecipient `json:"recipient"` + Body *VerifyPaymentJSONRequestBody } type VerifyPaymentResponseObject interface { @@ -4888,7 +4903,7 @@ type StrictServerInterface interface { // (GET /v2/identities/{identifier}/state/transactions) GetStateTransactions(ctx context.Context, request GetStateTransactionsRequestObject) (GetStateTransactionsResponseObject, error) // Verify Payment - // (POST /v2/payment/verify) + // (POST /v2/payment/verify/{recipient}) VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) // Get QrCode from store // (GET /v2/qr-store) @@ -6020,9 +6035,11 @@ func (sh *strictHandler) GetStateTransactions(w http.ResponseWriter, r *http.Req } // VerifyPayment operation middleware -func (sh *strictHandler) VerifyPayment(w http.ResponseWriter, r *http.Request) { +func (sh *strictHandler) VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) { var request VerifyPaymentRequestObject + request.Recipient = recipient + var body VerifyPaymentJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&body); err != nil { sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) diff --git a/internal/api/payment.go b/internal/api/payment.go index 34eb337c8..6dd46bf61 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" + "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" "github.com/polygonid/sh-id-platform/internal/log" @@ -57,7 +58,10 @@ func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequest Type: comm.ProtocolMessage(""), Body: bodyBytes, } - isPaid, err := s.paymentService.VerifyPayment(ctx, basicMessage) + + recipient := common.HexToAddress(request.Recipient) + + isPaid, err := s.paymentService.VerifyPayment(ctx, recipient, basicMessage) if err != nil { log.Error(ctx, "can't process payment message", "err", err) return VerifyPayment400JSONResponse{N400JSONResponse{Message: "can't process payment message"}}, nil diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index 8d65fecd7..3fd180355 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" @@ -75,5 +76,5 @@ func NewAgentProposalRequest(basicMessage *comm.BasicMessage) (*protocol.Credent type PaymentService interface { CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, senderDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) - VerifyPayment(ctx context.Context, message comm.BasicMessage) (bool, error) + VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) } diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index c5774a4d8..33f488153 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -180,12 +180,12 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, Domain: apitypes.TypedDataDomain{ Name: "MCPayment", Version: "1.0.0", - ChainId: math.NewHexOrDecimal256(80002), - VerifyingContract: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + ChainId: math.NewHexOrDecimal256(80002), // 1. config + VerifyingContract: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", // 2. config }, Message: apitypes.TypedDataMessage{ - "recipient": address, - "amount": "100", + "recipient": address, // 3. derive from PK + "amount": "100", // 4. config per credential "expirationDate": fmt.Sprint(oneHourLater.Unix()), "nonce": randomBigInt.String(), "metadata": "0x", @@ -275,14 +275,14 @@ type Iden3PaymentRailsV1Body struct { Payments []Iden3PaymentRailsV1 `json:"payments"` } -func (p *payment) VerifyPayment(ctx context.Context, message comm.BasicMessage) (bool, error) { +func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) { var paymentRequest Iden3PaymentRailsV1Body err := json.Unmarshal(message.Body, &paymentRequest) if err != nil { return false, err } - client, err := ethclient.Dial("") + client, err := ethclient.Dial("https://polygon-amoy.g.alchemy.com/v2/DHvucvBBzrBhaHzmjrMp24PGbl7vwee6") if err != nil { return false, err } @@ -292,7 +292,7 @@ func (p *payment) VerifyPayment(ctx context.Context, message comm.BasicMessage) if err != nil { return false, err } - recipient := common.HexToAddress("0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a") + nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, 10) isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: context.Background()}, recipient, nonce) if err != nil { From e4c62ca8c37d647784ea44f85c28928270e832f9 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Fri, 8 Nov 2024 17:29:15 +0100 Subject: [PATCH 04/66] chore: linter --- internal/api/agent.go | 6 ++---- internal/api/payment.go | 2 ++ internal/core/ports/payment_service.go | 3 ++- internal/core/services/payment.go | 29 +++++++++++++++++++------- internal/network/resolver.go | 12 +++++------ 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/internal/api/agent.go b/internal/api/agent.go index ff31cc5e7..522175f11 100644 --- a/internal/api/agent.go +++ b/internal/api/agent.go @@ -4,6 +4,7 @@ import ( "context" "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/log" @@ -25,8 +26,7 @@ func (s *Server) Agent(ctx context.Context, request AgentRequestObject) (AgentRe var agent *domain.Agent switch basicMessage.Type { - case protocol.CredentialFetchRequestMessageType: - case protocol.RevocationStatusRequestMessageType: + case protocol.CredentialFetchRequestMessageType, protocol.RevocationStatusRequestMessageType: req, err := ports.NewAgentRequest(basicMessage) if err != nil { log.Error(ctx, "agent parsing request", "err", err) @@ -38,7 +38,6 @@ func (s *Server) Agent(ctx context.Context, request AgentRequestObject) (AgentRe log.Error(ctx, "agent error", "err", err) return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil } - break case protocol.CredentialProposalRequestMessageType: proposalRequest, err := ports.NewAgentProposalRequest(basicMessage) if err != nil { @@ -61,7 +60,6 @@ func (s *Server) Agent(ctx context.Context, request AgentRequestObject) (AgentRe log.Error(ctx, "agent error", "err", err) return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil } - break default: log.Debug(ctx, "agent not supported type", basicMessage.Type) return Agent400JSONResponse{N400JSONResponse{"cannot proceed with the given message type"}}, nil diff --git a/internal/api/payment.go b/internal/api/payment.go index 6dd46bf61..1589f1a6d 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" + "github.com/polygonid/sh-id-platform/internal/log" ) @@ -43,6 +44,7 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment return CreatePaymentRequest201JSONResponse(basicMessage), nil } +// VerifyPayment is the controller to verify payment func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) { bodyBytes, err := json.Marshal(request.Body.Body) if err != nil { diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index 3fd180355..498988b3b 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -11,10 +11,11 @@ import ( "github.com/iden3/iden3comm/v2/protocol" ) +// Credential struct: TODO: This shouldn't be here with this name type Credential struct { Type string `json:"type"` Context string `json:"context"` - typss protocol.CredentialIssuanceRequestMessage + // typss protocol.CredentialIssuanceRequestMessage } // AgentProposalRequest struct diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 33f488153..914e76a3c 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -21,6 +21,7 @@ import ( "github.com/iden3/go-schema-processor/v2/verifiable" comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/eth" "github.com/polygonid/sh-id-platform/internal/network" @@ -30,16 +31,18 @@ type payment struct { networkResolver network.Resolver } -// NewClaim creates a new claim service +// NewPaymentService creates a new payment service func NewPaymentService(resolver network.Resolver) ports.PaymentService { return &payment{networkResolver: resolver} } +// PaymentRequestMessageBody TODO: use ones from iden3comm type PaymentRequestMessageBody struct { Agent string `json:"agent"` Payments []PaymentRequestInfo `json:"payments"` } +// PaymentRequestInfo TODO: use ones from iden3comm type PaymentRequestInfo struct { Type *string `json:"type,omitempty"` Credentials []PaymentRequestInfoCredentials `json:"credentials"` @@ -47,11 +50,13 @@ type PaymentRequestInfo struct { Data interface{} `json:"data"` } +// PaymentRequestInfoCredentials TODO: use ones from iden3comm type PaymentRequestInfoCredentials struct { Context string `json:"context,omitempty"` Type string `json:"type,omitempty"` } +// EthereumEip712Signature2021 TODO: use ones from iden3comm type EthereumEip712Signature2021 struct { Type verifiable.ProofType `json:"type"` ProofPurpose string `json:"proofPurpose"` @@ -61,12 +66,14 @@ type EthereumEip712Signature2021 struct { Eip712 Eip712Data `json:"eip712"` } +// Eip712Data TODO: use ones from iden3comm type Eip712Data struct { Types string `json:"types"` PrimaryType string `json:"primaryType"` Domain Eip712Domain `json:"domain"` } +// Eip712Domain TODO: use ones from iden3comm type Eip712Domain struct { Name string `json:"name"` Version string `json:"version"` @@ -74,7 +81,7 @@ type Eip712Domain struct { VerifyingContract string `json:"verifyingContract"` } -// Iden3PaymentRailsRequestV1 represents the Iden3PaymentRailsRequestV1 payment request data. +// Iden3PaymentRailsRequestV1 TODO: use ones from iden3comm type Iden3PaymentRailsRequestV1 struct { Nonce string `json:"nonce"` Type string `json:"type"` @@ -87,7 +94,7 @@ type Iden3PaymentRailsRequestV1 struct { Currency string `json:"currency"` } -// Iden3PaymentRailsERC20RequestV1 represents the Iden3PaymentRailsERC20RequestV1 payment request data. +// Iden3PaymentRailsERC20RequestV1 TODO: use ones from iden3comm type Iden3PaymentRailsERC20RequestV1 struct { Nonce string `json:"nonce"` Type string `json:"type"` @@ -102,6 +109,7 @@ type Iden3PaymentRailsERC20RequestV1 struct { Features []string `json:"features,omitempty"` } +// CreatePaymentRequest creates a payment request func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, userDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) { id := uuid.New().String() basicMessage := comm.BasicMessage{ @@ -113,8 +121,8 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, ThreadID: id, } - var max *big.Int = big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil) - randomBigInt, err := rand.Int(rand.Reader, max) + //nolint:mnd + randomBigInt, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) if err != nil { return nil, err } @@ -180,7 +188,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, Domain: apitypes.TypedDataDomain{ Name: "MCPayment", Version: "1.0.0", - ChainId: math.NewHexOrDecimal256(80002), // 1. config + ChainId: math.NewHexOrDecimal256(80002), // nolint:mnd VerifyingContract: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", // 2. config }, Message: apitypes.TypedDataMessage{ @@ -246,10 +254,14 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, }, } basicMessage.Body, err = json.Marshal(paymentRequestMessageBody) + if err != nil { + return nil, err + } return &basicMessage, nil } +// CreatePaymentRequestForProposalRequest creates a payment request for a proposal request func (p *payment) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) { basicMessage := comm.BasicMessage{ From: proposalRequest.To, @@ -261,6 +273,7 @@ func (p *payment) CreatePaymentRequestForProposalRequest(ctx context.Context, pr return &basicMessage, nil } +// Iden3PaymentRailsV1 TODO: Use ones from iden3comm type Iden3PaymentRailsV1 struct { Nonce string `json:"nonce"` Type string `json:"type"` @@ -271,11 +284,13 @@ type Iden3PaymentRailsV1 struct { } `json:"paymentData"` } +// Iden3PaymentRailsV1Body TODO: Use ones from iden3comm type Iden3PaymentRailsV1Body struct { Payments []Iden3PaymentRailsV1 `json:"payments"` } func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) { + const base10 = 10 var paymentRequest Iden3PaymentRailsV1Body err := json.Unmarshal(message.Body, &paymentRequest) if err != nil { @@ -293,7 +308,7 @@ func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, m return false, err } - nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, 10) + nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, base10) isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: context.Background()}, recipient, nonce) if err != nil { return false, err diff --git a/internal/network/resolver.go b/internal/network/resolver.go index fa03ac469..ba548ebfd 100644 --- a/internal/network/resolver.go +++ b/internal/network/resolver.go @@ -54,7 +54,7 @@ type Resolver struct { supportedContracts map[string]*abi.State stateResolvers map[string]pubsignals.StateResolver supportedNetworks []SupportedNetworks - paymentSettings map[resolverPrefix]PaymentSettings + // paymentSettings map[resolverPrefix]PaymentSettings } // SupportedNetworks holds the chain and networks supoprted @@ -79,15 +79,15 @@ type PaymentSettings struct { MCPaymentContract string `yaml:"mcPaymentContract"` Recipient string `yaml:"recipient"` Amount uint `yaml:"amount"` - erc20Tokens []ERC20TokenSettings `yaml:"erc20Tokens"` + Erc20Tokens []ERC20TokenSettings `yaml:"Erc20Tokens"` } // ERC20TokenSettings holds the ERC20 payment settings type ERC20TokenSettings struct { - tokenAddress string `yaml:"tokenAddress"` - tokenSymbol string `yaml:"tokenSymbol"` - tokenName string `yaml:"tokenName"` - tokenAmount uint `yaml:"tokenAmount"` + TokenAddress string `yaml:"TokenAddress"` + TokenSymbol string `yaml:"TokenSymbol"` + TokenName string `yaml:"TokenName"` + TokenAmount uint `yaml:"TokenAmount"` } // ResolverSettings holds the resolver settings From 8d02268f3ac31dc83795ac7e8e79c8774ba315b0 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 12 Nov 2024 10:03:15 +0100 Subject: [PATCH 05/66] chore: Use protocol messages --- go.mod | 17 ++-- go.sum | 38 ++++---- internal/core/services/payment.go | 153 ++++++++++-------------------- 3 files changed, 77 insertions(+), 131 deletions(-) diff --git a/go.mod b/go.mod index 7d2b99346..8af2f0029 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/polygonid/sh-id-platform -go 1.22.1 +go 1.22.6 require ( github.com/alicebob/miniredis/v2 v2.33.0 @@ -9,8 +9,8 @@ require ( github.com/aws/aws-sdk-go-v2/credentials v1.17.30 github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 github.com/caarlos0/env/v11 v11.2.2 - github.com/ethereum/go-ethereum v1.14.8 - github.com/getkin/kin-openapi v0.127.0 + github.com/ethereum/go-ethereum v1.14.11 + github.com/getkin/kin-openapi v0.128.0 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-redis/cache/v8 v8.4.4 @@ -23,7 +23,7 @@ require ( github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi v0.0.0-20230911113809-c58b7e7a69b0 github.com/iden3/contracts-abi/state/go/abi v1.0.2-0.20230911112726-4533c5701af1 github.com/iden3/go-circuits/v2 v2.4.0 - github.com/iden3/go-iden3-auth/v2 v2.5.0 + github.com/iden3/go-iden3-auth/v2 v2.5.1-0.20241111153205-5fe88a55e393 github.com/iden3/go-iden3-core/v2 v2.3.1 github.com/iden3/go-iden3-crypto v0.0.17 github.com/iden3/go-jwz/v2 v2.2.0 @@ -35,7 +35,7 @@ require ( github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20240914111027-9588ce2d7e1b github.com/iden3/go-schema-processor v1.3.1 github.com/iden3/go-schema-processor/v2 v2.5.0 - github.com/iden3/iden3comm/v2 v2.6.0 + github.com/iden3/iden3comm/v2 v2.8.1 github.com/iden3/merkletree-proof v0.3.0 github.com/ipfs/go-ipfs-api v0.7.0 github.com/jackc/pgconn v1.14.3 @@ -118,7 +118,7 @@ require ( github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v1.1.1 // indirect + github.com/cockroachdb/pebble v1.1.2 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect @@ -135,7 +135,7 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 // indirect github.com/ethereum/c-kzg-4844 v1.0.1 // indirect - github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.17.0 // indirect github.com/fatih/structtag v1.2.0 // indirect @@ -190,6 +190,7 @@ require ( github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.3.1 // indirect github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48 // indirect + github.com/iden3/driver-did-iden3 v0.0.5 // indirect github.com/iden3/go-iden3-core v1.0.2 // indirect github.com/iden3/go-rapidsnark/verifier v0.0.5 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -307,7 +308,7 @@ require ( github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/supranational/blst v0.3.11 // indirect + github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tdakkota/asciicheck v0.2.0 // indirect github.com/tetafro/godot v1.4.16 // indirect diff --git a/go.sum b/go.sum index 4609cd0ab..aa24cac79 100644 --- a/go.sum +++ b/go.sum @@ -126,8 +126,8 @@ github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= +github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -151,8 +151,8 @@ github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/e github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= -github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= @@ -199,10 +199,10 @@ github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJL github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8= github.com/ethereum/c-kzg-4844 v1.0.1 h1:pGixCbGizcVKSwoV70ge48+PrbB+iSKs2rjgfE4yJmQ= github.com/ethereum/c-kzg-4844 v1.0.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.14.8 h1:NgOWvXS+lauK+zFukEvi85UmmsS/OkV0N23UZ1VTIig= -github.com/ethereum/go-ethereum v1.14.8/go.mod h1:TJhyuDq0JDppAkFXgqjwpdlQApywnu/m10kFPxh8vvs= -github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= -github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= +github.com/ethereum/go-ethereum v1.14.11 h1:8nFDCUUE67rPc6AKxFj7JKaOa2W/W1Rse3oS6LvvxEY= +github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= @@ -219,10 +219,12 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY= github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= +github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk= @@ -415,10 +417,14 @@ github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48/go.mod h1:kJmVPMk4HfWyl2kcta34aad/K4TAfCwB29xX9PsR7LQ= github.com/iden3/contracts-abi/state/go/abi v1.0.2-0.20230911112726-4533c5701af1 h1:67302Oni3SpT2sbz7HXXEOR3zK3/usEC69W9ULILR1o= github.com/iden3/contracts-abi/state/go/abi v1.0.2-0.20230911112726-4533c5701af1/go.mod h1:TxgIrXCvxms3sbOdsy8kTvffUCIpEEifNy0fSXdkU4w= +github.com/iden3/driver-did-iden3 v0.0.5 h1:N3B9S3CBZHqWIZWspAiq2JFaLyq0eLZf26sN3ZEMAhQ= +github.com/iden3/driver-did-iden3 v0.0.5/go.mod h1:TcEG6fkExW6hgafjrU4ObOQ/HZqIRPQoL3TMU+URbS0= github.com/iden3/go-circuits/v2 v2.4.0 h1:m+7uYtrvJKuc+gVhbXDXl1BJQyK7sWdW7OWttM3R/8I= github.com/iden3/go-circuits/v2 v2.4.0/go.mod h1:k0uYx/ZdZPiDEIy7kI3MAixnREKcc7NdCKDRw8Q+iFA= github.com/iden3/go-iden3-auth/v2 v2.5.0 h1:vvleEbb9WvZ5dH9FSLnE9pYCsdKh+jgIBLfr1tgP+7o= github.com/iden3/go-iden3-auth/v2 v2.5.0/go.mod h1:z+7+loGUSX2loVVyyipwySvxAizrecz8sbDqdGwqKiI= +github.com/iden3/go-iden3-auth/v2 v2.5.1-0.20241111153205-5fe88a55e393 h1:OtKSqF+UjvyeRPuaJqwc7tSJxg3DzE9va7ro/oZZQBI= +github.com/iden3/go-iden3-auth/v2 v2.5.1-0.20241111153205-5fe88a55e393/go.mod h1:8DYjax0IdfzN+LUvCuGUJkjMPgq7o0byZIrZGdhIxjM= github.com/iden3/go-iden3-core v1.0.2 h1:HwNDFeqcUv4ybZj5tH+58JKWKarn/qqBpNCqTLxGP0Y= github.com/iden3/go-iden3-core v1.0.2/go.mod h1:X4PjlJG8OsEQEsSbzzYqqAk2olYGZ2nuGqiUPyEYjOo= github.com/iden3/go-iden3-core/v2 v2.3.1 h1:ytQqiclnVAIWyRKR2LF31hfz4DGRBD6nMjiPILXGSKk= @@ -439,16 +445,14 @@ github.com/iden3/go-rapidsnark/verifier v0.0.5 h1:J7y0ovrEjDQoWtZmlrp4tgGng1A9fa github.com/iden3/go-rapidsnark/verifier v0.0.5/go.mod h1:KgL3Yr9NehlFDI4EIWVLE3UDUi8ulyjbp7HcXSBfiGI= github.com/iden3/go-rapidsnark/witness/v2 v2.0.0 h1:mkY6VDfwKVJc83QGKmwVXY2LYepidPrFAxskrjr8UCs= github.com/iden3/go-rapidsnark/witness/v2 v2.0.0/go.mod h1:3JRjqUfW1hgI9hzLDO0v8z/DUkR0ZUehhYLlnIfRxnA= -github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20230524142950-0986cf057d4e h1:WeiFCrpj5pLRtSA4Mg03yTrSZhHHqN/k5b6bwxd9/tY= -github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20230524142950-0986cf057d4e/go.mod h1:UEBifEzw62T6VzIHJeHuUgeLg2U/J9ttf7hOwQEqnYk= github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20240914111027-9588ce2d7e1b h1:uoiSTMXT7NOVhyhpuztjGZeA8hP0u7IsfTvOQKLlvjs= github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20240914111027-9588ce2d7e1b/go.mod h1:8qNTFId1o6wftaLnvdiprtJ9MoaKO7g7Xh64vnFcnQc= github.com/iden3/go-schema-processor v1.3.1 h1:LJfFInfYGMOp0bTKKC17R8q4XI+VtqhFLPTEqnOIvlM= github.com/iden3/go-schema-processor v1.3.1/go.mod h1:NwJ1nuGdRlCFaN1/V6mS0AOAdvpLcGf4KKq0mluLG7U= github.com/iden3/go-schema-processor/v2 v2.5.0 h1:MX84oFb9kYq0ntKiU4DrPPaRgCDUCKlurtHB6nvPPAs= github.com/iden3/go-schema-processor/v2 v2.5.0/go.mod h1:hMqYi4lKOzEGkmCRks/r4Crj8H4G8YaTt8H4jZHzX9Y= -github.com/iden3/iden3comm/v2 v2.6.0 h1:6cu0N2b9oluJGDMvM2C0r2IH1GtrMUmmHOcJTn3O0mQ= -github.com/iden3/iden3comm/v2 v2.6.0/go.mod h1:ZRnfFg4geX336Bp9+29ZQdSqchbsBoVcd7ARn6JXH5Y= +github.com/iden3/iden3comm/v2 v2.8.1 h1:SjDXYS2O9jfpXNUzSdOBRTs+EjFKPkKOz4/IFkaOfXQ= +github.com/iden3/iden3comm/v2 v2.8.1/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= github.com/iden3/merkletree-proof v0.3.0 h1:NVlvtUBEgn4Etxxn+RsOmXP/qlI+85BdN8oUDTf3mxI= github.com/iden3/merkletree-proof v0.3.0/go.mod h1:+E2sBxMqhcn/fcu0LDGjmk3us+Vr+fxQUiZMxdpbgUE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -857,8 +861,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM= @@ -869,8 +873,6 @@ github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpR github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= -github.com/tetratelabs/wazero v1.7.0 h1:jg5qPydno59wqjpGrHph81lbtHzTrWzwwtD4cD88+hQ= -github.com/tetratelabs/wazero v1.7.0/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y= github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g= github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 914e76a3c..08274e351 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -18,12 +18,12 @@ import ( "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" - "github.com/iden3/go-schema-processor/v2/verifiable" comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/eth" + "github.com/polygonid/sh-id-platform/internal/log" "github.com/polygonid/sh-id-platform/internal/network" ) @@ -36,79 +36,6 @@ func NewPaymentService(resolver network.Resolver) ports.PaymentService { return &payment{networkResolver: resolver} } -// PaymentRequestMessageBody TODO: use ones from iden3comm -type PaymentRequestMessageBody struct { - Agent string `json:"agent"` - Payments []PaymentRequestInfo `json:"payments"` -} - -// PaymentRequestInfo TODO: use ones from iden3comm -type PaymentRequestInfo struct { - Type *string `json:"type,omitempty"` - Credentials []PaymentRequestInfoCredentials `json:"credentials"` - Description string `json:"description"` - Data interface{} `json:"data"` -} - -// PaymentRequestInfoCredentials TODO: use ones from iden3comm -type PaymentRequestInfoCredentials struct { - Context string `json:"context,omitempty"` - Type string `json:"type,omitempty"` -} - -// EthereumEip712Signature2021 TODO: use ones from iden3comm -type EthereumEip712Signature2021 struct { - Type verifiable.ProofType `json:"type"` - ProofPurpose string `json:"proofPurpose"` - ProofValue string `json:"proofValue"` - VerificationMethod string `json:"verificationMethod"` - Created string `json:"created"` - Eip712 Eip712Data `json:"eip712"` -} - -// Eip712Data TODO: use ones from iden3comm -type Eip712Data struct { - Types string `json:"types"` - PrimaryType string `json:"primaryType"` - Domain Eip712Domain `json:"domain"` -} - -// Eip712Domain TODO: use ones from iden3comm -type Eip712Domain struct { - Name string `json:"name"` - Version string `json:"version"` - ChainID string `json:"chainId"` - VerifyingContract string `json:"verifyingContract"` -} - -// Iden3PaymentRailsRequestV1 TODO: use ones from iden3comm -type Iden3PaymentRailsRequestV1 struct { - Nonce string `json:"nonce"` - Type string `json:"type"` - Context []string `json:"@context"` - Recipient string `json:"recipient"` - Amount string `json:"amount"` // Not negative number - ExpirationDate string `json:"expirationDate"` - Proof []EthereumEip712Signature2021 `json:"proof"` - Metadata string `json:"metadata"` - Currency string `json:"currency"` -} - -// Iden3PaymentRailsERC20RequestV1 TODO: use ones from iden3comm -type Iden3PaymentRailsERC20RequestV1 struct { - Nonce string `json:"nonce"` - Type string `json:"type"` - Context []string `json:"@context"` - Recipient string `json:"recipient"` - Amount string `json:"amount"` // Not negative number - ExpirationDate string `json:"expirationDate"` - Proof []EthereumEip712Signature2021 `json:"proof"` - Metadata string `json:"metadata"` - Currency string `json:"currency"` - TokenAddress string `json:"tokenAddress"` - Features []string `json:"features,omitempty"` -} - // CreatePaymentRequest creates a payment request func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, userDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) { id := uuid.New().String() @@ -130,7 +57,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, now := time.Now() oneHourLater := now.Add(1 * time.Hour) - domain := Eip712Domain{ + domain := protocol.Eip712Domain{ Name: "MCPayment", Version: "1.0.0", ChainID: "80002", @@ -150,7 +77,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) if !ok { - return nil, fmt.Errorf("Cannot assert type: publicKey is not of type *ecdsa.PublicKey") + return nil, fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") } address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() typedData := apitypes.TypedData{ @@ -210,41 +137,41 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, return nil, err } - nativePayments := Iden3PaymentRailsRequestV1{ + nativePayments := protocol.Iden3PaymentRailsRequestV1{ Nonce: randomBigInt.String(), Type: "Iden3PaymentRailsRequestV1", - Context: []string{ + Context: protocol.NewPaymentContextStringCol([]string{ "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", - }, + }), Recipient: address, Amount: "100", ExpirationDate: fmt.Sprint(oneHourLater.Unix()), Metadata: "0x", Currency: "ETHWEI", - Proof: []EthereumEip712Signature2021{ + Proof: protocol.NewPaymentProofEip712Signature([]protocol.EthereumEip712Signature2021{ { Type: "EthereumEip712Signature2021", ProofPurpose: "assertionMethod", ProofValue: hex.EncodeToString(signature), VerificationMethod: "did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a#blockchainAccountId", Created: now.Format(time.RFC3339), - Eip712: Eip712Data{ + Eip712: protocol.Eip712Data{ Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", PrimaryType: "Iden3PaymentRailsRequestV1", Domain: domain, }, }, - }, + }), } - paymentRequestMessageBody := PaymentRequestMessageBody{ + paymentRequestMessageBody := protocol.PaymentRequestMessageBody{ Agent: "localhost", - Payments: []PaymentRequestInfo{ + Payments: []protocol.PaymentRequestInfo{ { Description: "Payment for credential", - Data: nativePayments, - Credentials: []PaymentRequestInfoCredentials{ + Data: protocol.NewPaymentRequestInfoDataRails(nativePayments), + Credentials: []protocol.PaymentRequestInfoCredentials{ { Context: credContext, Type: credType, @@ -273,25 +200,8 @@ func (p *payment) CreatePaymentRequestForProposalRequest(ctx context.Context, pr return &basicMessage, nil } -// Iden3PaymentRailsV1 TODO: Use ones from iden3comm -type Iden3PaymentRailsV1 struct { - Nonce string `json:"nonce"` - Type string `json:"type"` - Context []string `json:"@context,omitempty"` - PaymentData struct { - TxID string `json:"txId"` - ChainID string `json:"chainId"` - } `json:"paymentData"` -} - -// Iden3PaymentRailsV1Body TODO: Use ones from iden3comm -type Iden3PaymentRailsV1Body struct { - Payments []Iden3PaymentRailsV1 `json:"payments"` -} - func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) { - const base10 = 10 - var paymentRequest Iden3PaymentRailsV1Body + var paymentRequest protocol.PaymentRequestMessageBody err := json.Unmarshal(message.Body, &paymentRequest) if err != nil { return false, err @@ -308,10 +218,43 @@ func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, m return false, err } - nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, base10) + // nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, base10) + nonce, err := nonceFromPaymentRequestInfoData(paymentRequest.Payments[0].Data) + if err != nil { + log.Error(ctx, "failed to get nonce from payment request info data", "err", err) + return false, err + } isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: context.Background()}, recipient, nonce) if err != nil { return false, err } return isPaid, nil } + +func nonceFromPaymentRequestInfoData(data protocol.PaymentRequestInfoData) (*big.Int, error) { + const base10 = 10 + var nonce string + switch data.Type() { + case protocol.Iden3PaymentRequestCryptoV1Type: + nonce = "" + case protocol.Iden3PaymentRailsRequestV1Type: + t, ok := data.Data().(protocol.Iden3PaymentRailsRequestV1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") + } + nonce = t.Nonce + case protocol.Iden3PaymentRailsERC20RequestV1Type: + t, ok := data.Data().(protocol.Iden3PaymentRailsERC20RequestV1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") + } + nonce = t.Nonce + default: + return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) + } + bigIntNonce, ok := new(big.Int).SetString(nonce, base10) + if !ok { + return nil, fmt.Errorf("failed to parse nonce creating big int: %s", nonce) + } + return bigIntNonce, nil +} From de4ab31551dc7d7be64527e6402ee779cbef310d Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 12 Nov 2024 12:30:51 +0100 Subject: [PATCH 06/66] feat: Payment settings --- .gitignore | 1 + Dockerfile | 3 +- README.md | 4 + infrastructure/local/docker-compose-full.yml | 1 + internal/config/config.go | 25 +++- internal/config/config_test.go | 1 + internal/payments/settings.go | 107 +++++++++++++++ internal/payments/settings_test.go | 124 ++++++++++++++++++ .../testdata/payment_settings.test.yaml | 14 ++ payment_settings.sample.yaml | 14 ++ 10 files changed, 287 insertions(+), 7 deletions(-) create mode 100644 internal/payments/settings.go create mode 100644 internal/payments/settings_test.go create mode 100644 internal/payments/testdata/payment_settings.test.yaml create mode 100644 payment_settings.sample.yaml diff --git a/.gitignore b/.gitignore index 81510db82..50b6e2372 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ bin/ .env-issuer .env-ui resolvers_settings.yaml +payments.yaml infrastructure/local/.vault/data infrastructure/local/.vault/plugins diff --git a/Dockerfile b/Dockerfile index d3d3095a7..88cd9fa81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,4 +26,5 @@ RUN apk add curl COPY --from=base ./service/api ./api COPY --from=base ./service/bin/* ./ COPY --from=base ./service/pkg/credentials ./pkg/credentials -COPY --from=base ./service/resolvers_settings.* ./ \ No newline at end of file +COPY --from=base ./service/resolvers_settings.* ./ +COPY --from=base ./service/payment.* ./ \ No newline at end of file diff --git a/README.md b/README.md index 8c306409f..d91013b3b 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,8 @@ a4a1d3ec9159 redis:6-alpine "docker-entrypoint.s…" 38 sec make private_key= import-private-key-to-kms ``` +**_TODO_**: Add section to configure payments + then visit: * https://localhost:8088/ to access the UI (default username / password are: user-ui, password-ui). You can set them using env [vars](.env-ui.sample). * :3001/ to access the API. (default username / password are: user-issuer, password-issuer) @@ -139,6 +141,8 @@ then modify the file with the proper values. The most important fields to run th In this file you can define customizations for each type of blockchain and network. For this example, we only need to define the RPCs. that will use. +**_TODO_**: Add section to configure payments**** + 4. Copy .env-ui sample file and fill the needed env variables: ```bash diff --git a/infrastructure/local/docker-compose-full.yml b/infrastructure/local/docker-compose-full.yml index 26ac40b5d..a52ff6433 100644 --- a/infrastructure/local/docker-compose-full.yml +++ b/infrastructure/local/docker-compose-full.yml @@ -81,6 +81,7 @@ services: volumes: - ../../localstoragekeys:/localstoragekeys:rw - ../../resolvers_settings.yaml:/resolvers_settings.yaml + - ../../payment_settings.test.yaml:/payment_settings.test.yaml healthcheck: test: ["CMD", "curl", "-f", "api:3001/status"] interval: 10s diff --git a/internal/config/config.go b/internal/config/config.go index 2e8943e66..519518b8e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -62,6 +62,13 @@ type Configuration struct { MediaTypeManager MediaTypeManager UniversalLinks UniversalLinks UniversalDIDResolver UniversalDIDResolver + Payments Payments +} + +// Payments configurations +type Payments struct { + SettingsPath string `env:"ISSUER_PAYMENTS_SETTINGS_PATH"` + SettingsFile *string `env:"ISSUER_PAYMENTS_SETTINGS_FILE"` } // Database has the database configuration @@ -255,6 +262,8 @@ func lookupVaultTokenFromFile(pathVaultConfig string) (string, error) { // nolint:gocyclo,gocognit func checkEnvVars(ctx context.Context, cfg *Configuration) error { + const defResolverPath = "./resolvers_settings.yaml" + const defPaymentPath = "./payment_settings.yaml" if cfg.IPFS.GatewayURL == "" { log.Warn(ctx, "ISSUER_IPFS_GATEWAY_URL value is missing, using default value: "+ipfsGateway) cfg.IPFS.GatewayURL = ipfsGateway @@ -318,15 +327,19 @@ func checkEnvVars(ctx context.Context, cfg *Configuration) error { if cfg.NetworkResolverPath == "" { log.Info(ctx, "ISSUER_RESOLVER_PATH value is missing. Trying to use ISSUER_RESOLVER_FILE") if cfg.NetworkResolverFile == nil || *cfg.NetworkResolverFile == "" { - log.Info(ctx, "ISSUER_RESOLVER_FILE value is missing") - } else { - log.Info(ctx, "ISSUER_RESOLVER_FILE value is present") + log.Info(ctx, "ISSUER_RESOLVER_PATH and ISSUER_RESOLVER_FILE value is missing. Using default value", "path", "/resolvers_settings.yaml") + cfg.NetworkResolverPath = defResolverPath } + log.Info(ctx, "ISSUER_RESOLVER_FILE value is present") } - if cfg.NetworkResolverPath == "" && (cfg.NetworkResolverFile == nil || *cfg.NetworkResolverFile == "") { - log.Info(ctx, "ISSUER_RESOLVER_PATH and ISSUER_RESOLVER_FILE value is missing. Using default value: ./resolvers_settings.yaml") - cfg.NetworkResolverPath = "./resolvers_settings.yaml" + if cfg.Payments.SettingsPath == "" { + log.Info(ctx, "ISSUER_PAYMENTS_SETTINGS_PATH value is missing. Trying to use ISSUER_PAYMENTS_SETTINGS_FILE") + if cfg.Payments.SettingsFile == nil || *cfg.Payments.SettingsFile == "" { + log.Info(ctx, "ISSUER_PAYMENTS_SETTINGS_FILE value is missing. Using default value:", "path", defPaymentPath) + cfg.Payments.SettingsPath = defPaymentPath + } + log.Info(ctx, "ISSUER_PAYMENTS_SETTINGS_FILE value is present") } if cfg.KeyStore.BJJProvider == "" { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index c9af7ff07..2132c7c72 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -141,6 +141,7 @@ func TestLoad(t *testing.T) { assert.Equal(t, "123HHUBUuO5", cfg.KeyStore.AWSSecretKey) assert.Equal(t, "eu-west-1", cfg.KeyStore.AWSRegion) assert.Equal(t, "./resolvers_settings.yaml", cfg.NetworkResolverPath) + assert.Equal(t, "./payment_settings.yaml", cfg.Payments.SettingsPath) assert.Equal(t, "hvs.NK8jrOU4XNY", cfg.KeyStore.Token) assert.Equal(t, "123", *cfg.NetworkResolverFile) assert.Equal(t, "./pkg/credentials/circuits", cfg.Circuit.Path) diff --git a/internal/payments/settings.go b/internal/payments/settings.go new file mode 100644 index 000000000..6c059bbf0 --- /dev/null +++ b/internal/payments/settings.go @@ -0,0 +1,107 @@ +package payments + +import ( + "context" + "encoding/base64" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "gopkg.in/yaml.v3" + + "github.com/polygonid/sh-id-platform/internal/config" + "github.com/polygonid/sh-id-platform/internal/log" +) + +// Settings holds the payments settings for the different chains +type Settings map[int]ChainSettings + +// ChainSettings holds the settings for a chain +type ChainSettings struct { + MCPayment string `yaml:"MCPayment"` + ERC20 *ERC20 `yaml:"ERC20,omitempty"` +} + +// ERC20 holds the settings for the ERC20 tokens +type ERC20 struct { + USDT Token `yaml:"USDT"` + USDC Token `yaml:"USDC"` +} + +// Token holds the settings for a token +type Token struct { + ContractAddress string `yaml:"ContractAddress"` + Features []string `yaml:"Features"` +} + +// SettingsFromConfig returns the settings from the configuration +// It reads the settings from the file if the path is provided or from the base64 encoded file injected +// into the configuration via an environment variable +func SettingsFromConfig(ctx context.Context, cfg *config.Payments) (*Settings, error) { + var reader io.Reader + var err error + if cfg.SettingsPath != "" { + reader, err = readFileFromPath(ctx, cfg.SettingsPath) + if err != nil { + log.Error(ctx, "cannot read settings file", "err", err) + return nil, err + } + return SettingsFromReader(reader) + } + if settingsFileHasContent(cfg) { + reader, err = readBase64FileContent(ctx, *cfg.SettingsFile) + if err != nil { + log.Error(ctx, "cannot read settings file", "err", err) + return nil, err + } + return SettingsFromReader(reader) + } + return nil, errors.New("no payment settings file or payment file path provided") +} + +func settingsFileHasContent(cfg *config.Payments) bool { + return cfg.SettingsFile != nil && *cfg.SettingsFile != "" +} + +// SettingsFromReader reads the settings from a reader +func SettingsFromReader(reader io.Reader) (*Settings, error) { + var settings Settings + decoder := yaml.NewDecoder(reader) + if err := decoder.Decode(&settings); err != nil { + return nil, err + } + return &settings, nil +} + +// ReadFileFromPath is a function that returns a reader for the resolver settings file +func readFileFromPath(ctx context.Context, path string) (io.Reader, error) { + if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { + log.Info(ctx, "payment settings file not found", "path", path) + return nil, fmt.Errorf("payment settings file not found: %w", err) + } + + if info, _ := os.Stat(path); info.Size() == 0 { + log.Info(ctx, "payment settings file is empty") + return nil, fmt.Errorf("payment settings file is empty: %s", path) + } + + f, err := os.Open(filepath.Clean(path)) + if err != nil { + return nil, err + } + + return f, nil +} + +// readBase64FileContent is a function that returns a reader for the encoded (base64) file +func readBase64FileContent(ctx context.Context, payload string) (io.Reader, error) { + decodedBytes, err := base64.StdEncoding.DecodeString(payload) + if err != nil { + log.Error(ctx, "cannot decode base64 encoded file", "err", err) + return nil, err + } + return strings.NewReader(string(decodedBytes)), nil +} diff --git a/internal/payments/settings_test.go b/internal/payments/settings_test.go new file mode 100644 index 000000000..83ee5afd8 --- /dev/null +++ b/internal/payments/settings_test.go @@ -0,0 +1,124 @@ +package payments + +import ( + "context" + "encoding/base64" + "errors" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/polygonid/sh-id-platform/internal/common" + "github.com/polygonid/sh-id-platform/internal/config" +) + +func TestSettingsFromConfig(t *testing.T) { + ctx := context.Background() + filePath := "testdata/payment_settings.test.yaml" + fileContent, err := os.ReadFile(filePath) + require.NoError(t, err) + fileContentUrlBase64 := base64.StdEncoding.EncodeToString(fileContent) + + expectedSettings := Settings{ + 137: { + MCPayment: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + ERC20: &ERC20{ + USDT: Token{ + ContractAddress: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", + Features: []string{}, + }, + USDC: Token{ + ContractAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", + Features: []string{"EIP-2612"}, + }, + }, + }, + 80002: { + MCPayment: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + }, + 1101: { + MCPayment: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + }, + } + + type expected struct { + err error + settings Settings + } + + for _, tc := range []struct { + name string + cfg config.Payments + expected expected + }{ + { + name: "Config from file content base64 encoded", + cfg: config.Payments{ + SettingsPath: "", + SettingsFile: common.ToPointer(fileContentUrlBase64), + }, + expected: expected{ + settings: expectedSettings, + }, + }, + { + name: "Config from file path", + cfg: config.Payments{ + SettingsPath: filePath, + SettingsFile: nil, + }, + expected: expected{ + settings: expectedSettings, + }, + }, + { + name: "Config from file has preference", + cfg: config.Payments{ + SettingsPath: filePath, + SettingsFile: common.ToPointer("irrelevant wrong content"), + }, + expected: expected{ + settings: expectedSettings, + }, + }, + { + name: "No file or path configured", + cfg: config.Payments{ + SettingsPath: "", + SettingsFile: nil, + }, + expected: expected{ + err: errors.New("no payment settings file or payment file path provided"), + }, + }, + { + name: "Wrong file content. Not base64 encoded", + cfg: config.Payments{ + SettingsPath: "", + SettingsFile: common.ToPointer(string(fileContent)), + }, + expected: expected{ + err: errors.New("illegal base64 data at input byte 3"), + }, + }, + { + name: "Wrong file path", + cfg: config.Payments{ + SettingsPath: "/wrong/path", + }, + expected: expected{ + err: errors.New("payment settings file not found: stat /wrong/path: no such file or directory"), + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + settings, err := SettingsFromConfig(ctx, &tc.cfg) + if tc.expected.err == nil { + require.EqualValues(t, &tc.expected.settings, settings) + } else { + require.Equal(t, tc.expected.err.Error(), err.Error()) + } + }) + } +} diff --git a/internal/payments/testdata/payment_settings.test.yaml b/internal/payments/testdata/payment_settings.test.yaml new file mode 100644 index 000000000..5f4fd985f --- /dev/null +++ b/internal/payments/testdata/payment_settings.test.yaml @@ -0,0 +1,14 @@ +137: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 + ERC20: + USDT: + ContractAddress: 0xc2132D05D31c914a87C6611C10748AEb04B58e8F + Features: [] + USDC: + ContractAddress: 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 + Features: + - EIP-2612 +80002: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 +1101: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 \ No newline at end of file diff --git a/payment_settings.sample.yaml b/payment_settings.sample.yaml new file mode 100644 index 000000000..5f4fd985f --- /dev/null +++ b/payment_settings.sample.yaml @@ -0,0 +1,14 @@ +137: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 + ERC20: + USDT: + ContractAddress: 0xc2132D05D31c914a87C6611C10748AEb04B58e8F + Features: [] + USDC: + ContractAddress: 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 + Features: + - EIP-2612 +80002: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 +1101: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 \ No newline at end of file From bec717b267bbdd22559f6836eec854dd7b1cb2e0 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 12 Nov 2024 19:00:22 +0100 Subject: [PATCH 07/66] feat: Payment settings injected in service and endpoint to return it --- api/api.yaml | 30 +++++++++- cmd/platform/main.go | 9 ++- go.sum | 16 ++---- internal/api/api.gen.go | 79 ++++++++++++++++++++++++++ internal/api/main_test.go | 21 ++++++- internal/api/payment.go | 5 ++ internal/core/ports/payment_service.go | 3 + internal/core/services/payment.go | 9 ++- internal/payments/settings.go | 12 ++-- 9 files changed, 163 insertions(+), 21 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 4f0067ac5..138c64442 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1325,6 +1325,25 @@ paths: '500': $ref: '#/components/responses/500' + /v2/payment/settings: + get: + summary: Return payment settings + operationId: GetPaymentSettings + description: Get the payment settings + tags: + - Payment + security: + - basicAuth: [ ] + responses: + '200': + description: Payment settings + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentSettings' + + + /v2/payment/verify/{recipient}: post: summary: Verify Payment @@ -1922,8 +1941,8 @@ components: type: object x-go-type: verifiable.W3CCredential x-go-type-import: - name: verifiable - path: "github.com/iden3/go-schema-processor/v2/verifiable" + name: verifiable + path: "github.com/iden3/go-schema-processor/v2/verifiable" AuthenticationResponse: type: object @@ -2049,6 +2068,13 @@ components: to: type: string + PaymentSettings: + type: object + x-go-type: payments.Settings + x-go-type-import: + name: payments + path: "github.com/polygonid/sh-id-platform/internal/payments" + BasicMessage: type: object required: diff --git a/cmd/platform/main.go b/cmd/platform/main.go index dcb29ea50..8876aa14a 100644 --- a/cmd/platform/main.go +++ b/cmd/platform/main.go @@ -30,6 +30,7 @@ import ( "github.com/polygonid/sh-id-platform/internal/log" "github.com/polygonid/sh-id-platform/internal/network" "github.com/polygonid/sh-id-platform/internal/packagemanager" + "github.com/polygonid/sh-id-platform/internal/payments" "github.com/polygonid/sh-id-platform/internal/providers" "github.com/polygonid/sh-id-platform/internal/pubsub" "github.com/polygonid/sh-id-platform/internal/repositories" @@ -145,13 +146,19 @@ func main() { return } + paymentSettings, err := payments.SettingsFromConfig(context.Background(), &cfg.Payments) + if err != nil { + log.Error(ctx, "failed to load payment settings", "err", err) + return + } + revocationStatusResolver := revocationstatus.NewRevocationStatusResolver(*networkResolver) identityService := services.NewIdentity(keyStore, identityRepository, mtRepository, identityStateRepository, mtService, qrService, claimsRepository, revocationRepository, connectionsRepository, storage, verifier, sessionRepository, ps, *networkResolver, rhsFactory, revocationStatusResolver) claimsService := services.NewClaim(claimsRepository, identityService, qrService, mtService, identityStateRepository, schemaLoader, storage, cfg.ServerUrl, ps, cfg.IPFS.GatewayURL, revocationStatusResolver, mediaTypeManager, cfg.UniversalLinks) proofService := services.NewProver(circuitsLoaderService) schemaService := services.NewSchema(schemaRepository, schemaLoader) linkService := services.NewLinkService(storage, claimsService, qrService, claimsRepository, linkRepository, schemaRepository, schemaLoader, sessionRepository, ps, identityService, *networkResolver, cfg.UniversalLinks) - paymentService := services.NewPaymentService(*networkResolver) + paymentService := services.NewPaymentService(*networkResolver, *paymentSettings) transactionService, err := gateways.NewTransaction(*networkResolver) if err != nil { diff --git a/go.sum b/go.sum index aa24cac79..7f238c4d7 100644 --- a/go.sum +++ b/go.sum @@ -106,14 +106,14 @@ github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U= -github.com/btcsuite/btcd v0.23.3 h1:4KH/JKy9WiCd+iUS9Mu0Zp7Dnj17TGdKrg9xc/FGj24= -github.com/btcsuite/btcd v0.23.3/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= +github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= -github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= +github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0= github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA= github.com/butuzov/mirror v1.2.0 h1:9YVK1qIjNspaqWutSv8gsge2e/Xpq1eqEkslEUHy5cs= @@ -221,8 +221,6 @@ github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY= -github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= @@ -421,8 +419,6 @@ github.com/iden3/driver-did-iden3 v0.0.5 h1:N3B9S3CBZHqWIZWspAiq2JFaLyq0eLZf26sN github.com/iden3/driver-did-iden3 v0.0.5/go.mod h1:TcEG6fkExW6hgafjrU4ObOQ/HZqIRPQoL3TMU+URbS0= github.com/iden3/go-circuits/v2 v2.4.0 h1:m+7uYtrvJKuc+gVhbXDXl1BJQyK7sWdW7OWttM3R/8I= github.com/iden3/go-circuits/v2 v2.4.0/go.mod h1:k0uYx/ZdZPiDEIy7kI3MAixnREKcc7NdCKDRw8Q+iFA= -github.com/iden3/go-iden3-auth/v2 v2.5.0 h1:vvleEbb9WvZ5dH9FSLnE9pYCsdKh+jgIBLfr1tgP+7o= -github.com/iden3/go-iden3-auth/v2 v2.5.0/go.mod h1:z+7+loGUSX2loVVyyipwySvxAizrecz8sbDqdGwqKiI= github.com/iden3/go-iden3-auth/v2 v2.5.1-0.20241111153205-5fe88a55e393 h1:OtKSqF+UjvyeRPuaJqwc7tSJxg3DzE9va7ro/oZZQBI= github.com/iden3/go-iden3-auth/v2 v2.5.1-0.20241111153205-5fe88a55e393/go.mod h1:8DYjax0IdfzN+LUvCuGUJkjMPgq7o0byZIrZGdhIxjM= github.com/iden3/go-iden3-core v1.0.2 h1:HwNDFeqcUv4ybZj5tH+58JKWKarn/qqBpNCqTLxGP0Y= diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index ba9eb44f3..bb73d9b2c 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -17,6 +17,7 @@ import ( protocol "github.com/iden3/iden3comm/v2/protocol" "github.com/oapi-codegen/runtime" strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" + payments "github.com/polygonid/sh-id-platform/internal/payments" timeapi "github.com/polygonid/sh-id-platform/internal/timeapi" ) @@ -469,6 +470,9 @@ type PaginatedMetadata struct { Total uint `json:"total"` } +// PaymentSettings defines model for PaymentSettings. +type PaymentSettings = payments.Settings + // PublishIdentityStateResponse defines model for PublishIdentityStateResponse. type PublishIdentityStateResponse struct { ClaimsTreeRoot *string `json:"claimsTreeRoot,omitempty"` @@ -930,6 +934,9 @@ type ServerInterface interface { // Get Identity State Transactions // (GET /v2/identities/{identifier}/state/transactions) GetStateTransactions(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetStateTransactionsParams) + // Return payment settings + // (GET /v2/payment/settings) + GetPaymentSettings(w http.ResponseWriter, r *http.Request) // Verify Payment // (POST /v2/payment/verify/{recipient}) VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) @@ -1176,6 +1183,12 @@ func (_ Unimplemented) GetStateTransactions(w http.ResponseWriter, r *http.Reque w.WriteHeader(http.StatusNotImplemented) } +// Return payment settings +// (GET /v2/payment/settings) +func (_ Unimplemented) GetPaymentSettings(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + // Verify Payment // (POST /v2/payment/verify/{recipient}) func (_ Unimplemented) VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) { @@ -2635,6 +2648,26 @@ func (siw *ServerInterfaceWrapper) GetStateTransactions(w http.ResponseWriter, r handler.ServeHTTP(w, r) } +// GetPaymentSettings operation middleware +func (siw *ServerInterfaceWrapper) GetPaymentSettings(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetPaymentSettings(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // VerifyPayment operation middleware func (siw *ServerInterfaceWrapper) VerifyPayment(w http.ResponseWriter, r *http.Request) { @@ -2984,6 +3017,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/identities/{identifier}/state/transactions", wrapper.GetStateTransactions) }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v2/payment/settings", wrapper.GetPaymentSettings) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/v2/payment/verify/{recipient}", wrapper.VerifyPayment) }) @@ -4606,6 +4642,22 @@ func (response GetStateTransactions500JSONResponse) VisitGetStateTransactionsRes return json.NewEncoder(w).Encode(response) } +type GetPaymentSettingsRequestObject struct { +} + +type GetPaymentSettingsResponseObject interface { + VisitGetPaymentSettingsResponse(w http.ResponseWriter) error +} + +type GetPaymentSettings200JSONResponse PaymentSettings + +func (response GetPaymentSettings200JSONResponse) VisitGetPaymentSettingsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + type VerifyPaymentRequestObject struct { Recipient PathRecipient `json:"recipient"` Body *VerifyPaymentJSONRequestBody @@ -4908,6 +4960,9 @@ type StrictServerInterface interface { // Get Identity State Transactions // (GET /v2/identities/{identifier}/state/transactions) GetStateTransactions(ctx context.Context, request GetStateTransactionsRequestObject) (GetStateTransactionsResponseObject, error) + // Return payment settings + // (GET /v2/payment/settings) + GetPaymentSettings(ctx context.Context, request GetPaymentSettingsRequestObject) (GetPaymentSettingsResponseObject, error) // Verify Payment // (POST /v2/payment/verify/{recipient}) VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) @@ -6040,6 +6095,30 @@ func (sh *strictHandler) GetStateTransactions(w http.ResponseWriter, r *http.Req } } +// GetPaymentSettings operation middleware +func (sh *strictHandler) GetPaymentSettings(w http.ResponseWriter, r *http.Request) { + var request GetPaymentSettingsRequestObject + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetPaymentSettings(ctx, request.(GetPaymentSettingsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetPaymentSettings") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetPaymentSettingsResponseObject); ok { + if err := validResponse.VisitGetPaymentSettingsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // VerifyPayment operation middleware func (sh *strictHandler) VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) { var request VerifyPaymentRequestObject diff --git a/internal/api/main_test.go b/internal/api/main_test.go index 27e33f594..ffad2603a 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -27,6 +27,7 @@ import ( "github.com/polygonid/sh-id-platform/internal/loader" "github.com/polygonid/sh-id-platform/internal/log" "github.com/polygonid/sh-id-platform/internal/network" + "github.com/polygonid/sh-id-platform/internal/payments" "github.com/polygonid/sh-id-platform/internal/providers" "github.com/polygonid/sh-id-platform/internal/pubsub" "github.com/polygonid/sh-id-platform/internal/repositories" @@ -280,13 +281,31 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { require.NoError(t, err) revocationStatusResolver := revocationstatus.NewRevocationStatusResolver(*networkResolver) + paymentSettings, err := payments.SettingsFromReader(common.NewMyYAMLReader([]byte(` +137: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 + ERC20: + USDT: + ContractAddress: 0xc2132D05D31c914a87C6611C10748AEb04B58e8F + Features: [] + USDC: + ContractAddress: 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 + Features: + - EIP-2612 +80002: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 +1101: + MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774`, + ))) + require.NoError(t, err) + mtService := services.NewIdentityMerkleTrees(repos.idenMerkleTree) qrService := services.NewQrStoreService(cachex) rhsFactory := reversehash.NewFactory(*networkResolver, reversehash.DefaultRHSTimeOut) identityService := services.NewIdentity(keyStore, repos.identity, repos.idenMerkleTree, repos.identityState, mtService, qrService, repos.claims, repos.revocation, repos.connection, st, nil, repos.sessions, pubSub, *networkResolver, rhsFactory, revocationStatusResolver) connectionService := services.NewConnection(repos.connection, repos.claims, st) schemaService := services.NewSchema(repos.schemas, schemaLoader) - paymentService := services.NewPaymentService(*networkResolver) + paymentService := services.NewPaymentService(*networkResolver, *paymentSettings) mediaTypeManager := services.NewMediaTypeManager( map[iden3comm.ProtocolMessage][]string{ diff --git a/internal/api/payment.go b/internal/api/payment.go index 1589f1a6d..be6ef3a52 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -44,6 +44,11 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment return CreatePaymentRequest201JSONResponse(basicMessage), nil } +// GetPaymentSettings is the controller to get payment settings +func (s *Server) GetPaymentSettings(_ context.Context, _ GetPaymentSettingsRequestObject) (GetPaymentSettingsResponseObject, error) { + return GetPaymentSettings200JSONResponse(s.paymentService.GetSettings()), nil +} + // VerifyPayment is the controller to verify payment func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) { bodyBytes, err := json.Marshal(request.Body.Body) diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index 498988b3b..0602350be 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -9,6 +9,8 @@ import ( "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" + + "github.com/polygonid/sh-id-platform/internal/payments" ) // Credential struct: TODO: This shouldn't be here with this name @@ -77,5 +79,6 @@ func NewAgentProposalRequest(basicMessage *comm.BasicMessage) (*protocol.Credent type PaymentService interface { CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, senderDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) + GetSettings() payments.Settings VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) } diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 08274e351..570701bf9 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -25,14 +25,16 @@ import ( "github.com/polygonid/sh-id-platform/internal/eth" "github.com/polygonid/sh-id-platform/internal/log" "github.com/polygonid/sh-id-platform/internal/network" + "github.com/polygonid/sh-id-platform/internal/payments" ) type payment struct { networkResolver network.Resolver + settings payments.Settings } // NewPaymentService creates a new payment service -func NewPaymentService(resolver network.Resolver) ports.PaymentService { +func NewPaymentService(resolver network.Resolver, settings payments.Settings) ports.PaymentService { return &payment{networkResolver: resolver} } @@ -200,6 +202,11 @@ func (p *payment) CreatePaymentRequestForProposalRequest(ctx context.Context, pr return &basicMessage, nil } +// GetSettings returns the current payment settings +func (p *payment) GetSettings() payments.Settings { + return p.settings +} + func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) { var paymentRequest protocol.PaymentRequestMessageBody err := json.Unmarshal(message.Body, &paymentRequest) diff --git a/internal/payments/settings.go b/internal/payments/settings.go index 6c059bbf0..2c6304c6b 100644 --- a/internal/payments/settings.go +++ b/internal/payments/settings.go @@ -21,20 +21,20 @@ type Settings map[int]ChainSettings // ChainSettings holds the settings for a chain type ChainSettings struct { - MCPayment string `yaml:"MCPayment"` - ERC20 *ERC20 `yaml:"ERC20,omitempty"` + MCPayment string `yaml:"MCPayment" json:"MCPayment"` + ERC20 *ERC20 `yaml:"ERC20,omitempty" json:"ERC20,omitempty"` } // ERC20 holds the settings for the ERC20 tokens type ERC20 struct { - USDT Token `yaml:"USDT"` - USDC Token `yaml:"USDC"` + USDT Token `yaml:"USDT" json:"USDT"` + USDC Token `yaml:"USDC" json:"USDC"` } // Token holds the settings for a token type Token struct { - ContractAddress string `yaml:"ContractAddress"` - Features []string `yaml:"Features"` + ContractAddress string `yaml:"ContractAddress" json:"contractAddress"` + Features []string `yaml:"Features" json:"features"` } // SettingsFromConfig returns the settings from the configuration From 7e4c0ac19ebd100b84110bedaf9afe92bcc02fba Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 13 Nov 2024 16:20:02 +0100 Subject: [PATCH 08/66] feat: Test get payment settings --- go.mod | 2 +- go.sum | 4 ++-- internal/api/identity_test.go | 2 +- internal/api/payment_test.go | 29 +++++++++++++++++++++++++++++ internal/core/services/payment.go | 5 ++++- 5 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 internal/api/payment_test.go diff --git a/go.mod b/go.mod index 8af2f0029..d03258144 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi v0.0.0-20230911113809-c58b7e7a69b0 github.com/iden3/contracts-abi/state/go/abi v1.0.2-0.20230911112726-4533c5701af1 github.com/iden3/go-circuits/v2 v2.4.0 - github.com/iden3/go-iden3-auth/v2 v2.5.1-0.20241111153205-5fe88a55e393 + github.com/iden3/go-iden3-auth/v2 v2.5.1 github.com/iden3/go-iden3-core/v2 v2.3.1 github.com/iden3/go-iden3-crypto v0.0.17 github.com/iden3/go-jwz/v2 v2.2.0 diff --git a/go.sum b/go.sum index 7f238c4d7..966b5811f 100644 --- a/go.sum +++ b/go.sum @@ -419,8 +419,8 @@ github.com/iden3/driver-did-iden3 v0.0.5 h1:N3B9S3CBZHqWIZWspAiq2JFaLyq0eLZf26sN github.com/iden3/driver-did-iden3 v0.0.5/go.mod h1:TcEG6fkExW6hgafjrU4ObOQ/HZqIRPQoL3TMU+URbS0= github.com/iden3/go-circuits/v2 v2.4.0 h1:m+7uYtrvJKuc+gVhbXDXl1BJQyK7sWdW7OWttM3R/8I= github.com/iden3/go-circuits/v2 v2.4.0/go.mod h1:k0uYx/ZdZPiDEIy7kI3MAixnREKcc7NdCKDRw8Q+iFA= -github.com/iden3/go-iden3-auth/v2 v2.5.1-0.20241111153205-5fe88a55e393 h1:OtKSqF+UjvyeRPuaJqwc7tSJxg3DzE9va7ro/oZZQBI= -github.com/iden3/go-iden3-auth/v2 v2.5.1-0.20241111153205-5fe88a55e393/go.mod h1:8DYjax0IdfzN+LUvCuGUJkjMPgq7o0byZIrZGdhIxjM= +github.com/iden3/go-iden3-auth/v2 v2.5.1 h1:2+YPVsYpbTl+eKzVDqEeiiVoAD+c6s/TYPo/oWbVjwg= +github.com/iden3/go-iden3-auth/v2 v2.5.1/go.mod h1:8DYjax0IdfzN+LUvCuGUJkjMPgq7o0byZIrZGdhIxjM= github.com/iden3/go-iden3-core v1.0.2 h1:HwNDFeqcUv4ybZj5tH+58JKWKarn/qqBpNCqTLxGP0Y= github.com/iden3/go-iden3-core v1.0.2/go.mod h1:X4PjlJG8OsEQEsSbzzYqqAk2olYGZ2nuGqiUPyEYjOo= github.com/iden3/go-iden3-core/v2 v2.3.1 h1:ytQqiclnVAIWyRKR2LF31hfz4DGRBD6nMjiPILXGSKk= diff --git a/internal/api/identity_test.go b/internal/api/identity_test.go index 592e7943f..a8b390a8c 100644 --- a/internal/api/identity_test.go +++ b/internal/api/identity_test.go @@ -310,8 +310,8 @@ func TestServer_GetIdentities(t *testing.T) { t.Run(tc.name, func(t *testing.T) { rr := httptest.NewRecorder() req, err := http.NewRequest("GET", "/v2/identities", nil) - req.SetBasicAuth(tc.auth()) require.NoError(t, err) + req.SetBasicAuth(tc.auth()) handler.ServeHTTP(rr, req) require.Equal(t, tc.expected.httpCode, rr.Code) diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go new file mode 100644 index 000000000..a8d389542 --- /dev/null +++ b/internal/api/payment_test.go @@ -0,0 +1,29 @@ +package api + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestServer_GetPaymentSettings(t *testing.T) { + ctx := context.Background() + + server := newTestServer(t, nil) + handler := getHandler(ctx, server) + + rr := httptest.NewRecorder() + req, err := http.NewRequest(http.MethodGet, "/v2/payment/settings", nil) + assert.NoError(t, err) + req.SetBasicAuth(authOk()) + + handler.ServeHTTP(rr, req) + require.Equal(t, http.StatusOK, rr.Code) + var response GetPaymentSettings200JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) +} diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 570701bf9..a89d5d6ed 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -35,7 +35,10 @@ type payment struct { // NewPaymentService creates a new payment service func NewPaymentService(resolver network.Resolver, settings payments.Settings) ports.PaymentService { - return &payment{networkResolver: resolver} + return &payment{ + networkResolver: resolver, + settings: settings, + } } // CreatePaymentRequest creates a payment request From 8b3b041227690b6ea4ef4de1b28bd109f26ff130 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Thu, 14 Nov 2024 12:25:02 +0100 Subject: [PATCH 09/66] feat: Payment options repo redone --- internal/core/domain/payment.go | 32 +++++ .../202411131724000_payment_options.sql | 21 +++ internal/repositories/payment.go | 109 +++++++++++++++ internal/repositories/payment_test.go | 129 ++++++++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 internal/core/domain/payment.go create mode 100644 internal/db/schema/migrations/202411131724000_payment_options.sql create mode 100644 internal/repositories/payment.go create mode 100644 internal/repositories/payment_test.go diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go new file mode 100644 index 000000000..560a136b5 --- /dev/null +++ b/internal/core/domain/payment.go @@ -0,0 +1,32 @@ +package domain + +import ( + "time" + + "github.com/google/uuid" + "github.com/iden3/go-iden3-core/v2/w3c" +) + +// PaymentOption represents a payment option +type PaymentOption struct { + ID uuid.UUID + IssuerDID w3c.DID + Name string + Description string + Config any + CreatedAt time.Time + UpdatedAt time.Time +} + +// NewPaymentOption creates a new PaymentOption +func NewPaymentOption(issuerDID w3c.DID, name string, description string, config any) *PaymentOption { + return &PaymentOption{ + ID: uuid.New(), + IssuerDID: issuerDID, + Name: name, + Description: description, + Config: config, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } +} diff --git a/internal/db/schema/migrations/202411131724000_payment_options.sql b/internal/db/schema/migrations/202411131724000_payment_options.sql new file mode 100644 index 000000000..3a4b30603 --- /dev/null +++ b/internal/db/schema/migrations/202411131724000_payment_options.sql @@ -0,0 +1,21 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE payment_options +( + id UUID PRIMARY KEY NOT NULL, + issuer_did text NOT NULL REFERENCES identities (identifier), + name text, + description text, + configuration jsonb NOT NULL, + created_at timestamptz NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamptz NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX payment_options_id_issuer_did_index ON payment_options (id, issuer_did); +CREATE INDEX payment_options_issuer_did_created_at_index ON payment_options (issuer_did, created_at); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS payment_options; +-- +goose StatementEnd \ No newline at end of file diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go new file mode 100644 index 000000000..06a913f84 --- /dev/null +++ b/internal/repositories/payment.go @@ -0,0 +1,109 @@ +package repositories + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/google/uuid" + "github.com/iden3/go-iden3-core/v2/w3c" + + "github.com/polygonid/sh-id-platform/internal/core/domain" + "github.com/polygonid/sh-id-platform/internal/db" +) + +// PaymentOptionNotFound error +var PaymentOptionNotFound = errors.New("payment option not found") + +// Payment repository +type Payment struct { + conn db.Storage +} + +// NewPayment creates a new Payment repository +func NewPayment(conn db.Storage) *Payment { + return &Payment{conn} +} + +// SavePaymentOption saves a payment option +func (p *Payment) SavePaymentOption(ctx context.Context, opt *domain.PaymentOption) (uuid.UUID, error) { + const query = ` +INSERT INTO payment_options (id, issuer_did, name, description, configuration, created_at, updated_at) +VALUES ($1, $2, $3, $4, $5, $6, $7); +` + + _, err := p.conn.Pgx.Exec(ctx, query, opt.ID, opt.IssuerDID.String(), opt.Name, opt.Description, opt.Config, opt.CreatedAt, opt.UpdatedAt) + if err != nil { + return uuid.Nil, err + } + return opt.ID, nil +} + +// GetAllPaymentOptions returns all payment options +func (p *Payment) GetAllPaymentOptions(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentOption, error) { + const query = ` +SELECT id, issuer_did, name, description, configuration, created_at, updated_at +FROM payment_options +WHERE issuer_did=$1 +ORDER BY created_at DESC;` + + rows, err := p.conn.Pgx.Query(ctx, query, issuerDID.String()) + if err != nil { + return nil, err + } + defer rows.Close() + + var opts []domain.PaymentOption + for rows.Next() { + var opt domain.PaymentOption + var strIssuerDID string + err := rows.Scan(&opt.ID, &strIssuerDID, &opt.Name, &opt.Description, &opt.Config, &opt.CreatedAt, &opt.UpdatedAt) + if err != nil { + return nil, err + } + did, err := w3c.ParseDID(strIssuerDID) + if err != nil { + return nil, fmt.Errorf("could not parse issuer DID: %w", err) + } + opt.IssuerDID = *did + opts = append(opts, opt) + } + return opts, nil +} + +// GetPaymentOptionByID returns a payment option by ID +func (p *Payment) GetPaymentOptionByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) { + const query = ` +SELECT id, issuer_did, name, description, configuration, created_at, updated_at +FROM payment_options +WHERE id = $1 +AND issuer_did = $2;` + + var opt domain.PaymentOption + var strIssuerDID string + err := p.conn.Pgx.QueryRow(ctx, query, id, issuerDID.String()).Scan(&opt.ID, &strIssuerDID, &opt.Name, &opt.Description, &opt.Config, &opt.CreatedAt, &opt.UpdatedAt) + if err != nil { + if strings.Contains(err.Error(), "no rows in result set") { + return nil, PaymentOptionNotFound + } + return nil, err + } + did, err := w3c.ParseDID(strIssuerDID) + if err != nil { + return nil, fmt.Errorf("could not parse issuer DID: %w", err) + } + opt.IssuerDID = *did + return &opt, nil +} + +// DeletePaymentOption deletes a payment option +func (p *Payment) DeletePaymentOption(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) error { + const query = `DELETE FROM payment_options WHERE id = $1 and issuer_did = $2;` + + _, err := p.conn.Pgx.Exec(ctx, query, id, issuerDID.String()) + if err != nil { + return err + } + return nil +} diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go new file mode 100644 index 000000000..8e98dd073 --- /dev/null +++ b/internal/repositories/payment_test.go @@ -0,0 +1,129 @@ +package repositories + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/google/uuid" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/polygonid/sh-id-platform/internal/core/domain" +) + +func TestPayment_SavePaymentOption(t *testing.T) { + ctx := context.Background() + fixture := NewFixture(storage) + issuerID, err := w3c.ParseDID("did:iden3:privado:main:2Sh93vMXNar5fP5ifutHerL9bdUkocB464n3TG6BWV") + require.NoError(t, err) + issuerDIDOther, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qYQdd1yqFyrM9ZPqYTE4WHAQH2PX5Rjtj7YDYPppj") + require.NoError(t, err) + + fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) + + repo := NewPayment(*storage) + t.Run("Save payment option", func(t *testing.T) { + id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", struct{}{})) + assert.NoError(t, err) + assert.NotEqual(t, uuid.Nil, id) + }) + t.Run("Save payment option linked to non existing issuer", func(t *testing.T) { + id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerDIDOther, "name 2", "description 2", struct{}{})) + require.Error(t, err) + assert.Equal(t, uuid.Nil, id) + }) +} + +func TestPayment_GetAllPaymentOptions(t *testing.T) { + ctx := context.Background() + fixture := NewFixture(storage) + issuerID, err := w3c.ParseDID("did:iden3:privado:main:2SbDGSG2TTN1N1UuFaFq7EoFK3RY5wfcotuD8rDCn2") + require.NoError(t, err) + fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) + issuerDIDOther, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qYQdd1yqFyrM9ZPqYTE4WHAQH2PX5Rjtj7YDYPppj") + require.NoError(t, err) + + repo := NewPayment(*storage) + ids := make([]uuid.UUID, 0) + now := time.Now() + for i := 0; i < 10; i++ { + id, err := repo.SavePaymentOption(ctx, &domain.PaymentOption{ + ID: uuid.New(), + IssuerDID: *issuerID, + Name: fmt.Sprintf("name %d", i), + Description: fmt.Sprintf("description %d", i), + Config: struct{}{}, + CreatedAt: now, + UpdatedAt: now, + }) + now = now.Add(1 * time.Second) + + require.NoError(t, err) + ids = append([]uuid.UUID{id}, ids...) + } + t.Run("Get all payment options", func(t *testing.T) { + opts, err := repo.GetAllPaymentOptions(ctx, *issuerID) + assert.NoError(t, err) + assert.Len(t, opts, 10) + for i, opt := range opts { + assert.Equal(t, ids[i], opt.ID) + } + }) + t.Run("Get all payment options linked to non existing issuer", func(t *testing.T) { + opts, err := repo.GetAllPaymentOptions(ctx, *issuerDIDOther) + require.NoError(t, err) + assert.Len(t, opts, 0) + }) +} + +func TestPayment_GetPaymentOptionByID(t *testing.T) { + ctx := context.Background() + fixture := NewFixture(storage) + issuerID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qWxoum8UEJzbUL1Ej9UWjGYHL8oL31BBLJ4ob8bmM") + require.NoError(t, err) + issuerDIDOther, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qYQdd1yqFyrM9ZPqYTE4WHAQH2PX5Rjtj7YDYPppj") + require.NoError(t, err) + + fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) + repo := NewPayment(*storage) + id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", struct{}{})) + assert.NoError(t, err) + assert.NotEqual(t, uuid.Nil, id) + + t.Run("Get payment option", func(t *testing.T) { + opt, err := repo.GetPaymentOptionByID(ctx, *issuerID, id) + assert.NoError(t, err) + assert.Equal(t, id, opt.ID) + }) + t.Run("Get payment option linked to non existing issuer", func(t *testing.T) { + opt, err := repo.GetPaymentOptionByID(ctx, *issuerDIDOther, id) + require.Error(t, err) + assert.Nil(t, opt) + }) +} + +func TestPayment_DeletePaymentOption(t *testing.T) { + ctx := context.Background() + fixture := NewFixture(storage) + issuerID, err := w3c.ParseDID("did:iden3:privado:main:2Se8ZgrJDWycoKfH9JkBsCuEF127n3nk4G4YW7Dxjo") + require.NoError(t, err) + + fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) + repo := NewPayment(*storage) + id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", struct{}{})) + assert.NoError(t, err) + assert.NotEqual(t, uuid.Nil, id) + + opt, err := repo.GetPaymentOptionByID(ctx, *issuerID, id) + assert.NoError(t, err) + assert.Equal(t, id, opt.ID) + + require.NoError(t, repo.DeletePaymentOption(ctx, *issuerID, id)) + + opt, err = repo.GetPaymentOptionByID(ctx, *issuerID, id) + assert.Error(t, err) + assert.Nil(t, opt) +} From a5655a02d96cb9fa109741f652b525000d521c33 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Fri, 15 Nov 2024 13:19:02 +0100 Subject: [PATCH 10/66] feat: Payment options API. --- api/api.yaml | 165 +++++++ cmd/platform/main.go | 3 +- internal/api/api.gen.go | 524 +++++++++++++++++++++ internal/api/main_test.go | 4 +- internal/api/payment.go | 79 ++++ internal/api/responses.go | 33 ++ internal/core/ports/payment_service.go | 7 + internal/core/ports/payments_repository.go | 18 + internal/core/services/payment.go | 46 +- 9 files changed, 876 insertions(+), 3 deletions(-) create mode 100644 internal/core/ports/payments_repository.go diff --git a/api/api.yaml b/api/api.yaml index 138c64442..426a462c7 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1342,6 +1342,109 @@ paths: schema: $ref: '#/components/schemas/PaymentSettings' + /v2/identities/{identifier}/payment/options: + get: + summary: Get Payment Options + operationId: GetPaymentOptions + description: Get the payment options for the provided identity. + tags: + - Payment + security: + - basicAuth: [ ] + parameters: + - $ref: '#/components/parameters/pathIdentifier' + responses: + '200': + description: Payment options + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentOptionsPaginated' + '400': + $ref: '#/components/responses/400' + '401': + $ref: '#/components/responses/401' + '500': + $ref: '#/components/responses/500' + post: + summary: Create Payment Option + operationId: CreatePaymentOption + description: Create a payment option for the provided identity. + tags: + - Payment + security: + - basicAuth: [ ] + parameters: + - $ref: '#/components/parameters/pathIdentifier' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentOptionRequest' + responses: + '201': + description: Payment Option Created + content: + application/json: + schema: + $ref: '#/components/schemas/UUIDResponse' + '400': + $ref: '#/components/responses/400' + '401': + $ref: '#/components/responses/401' + '422': + $ref: '#/components/responses/422' + '500': + $ref: '#/components/responses/500' + + /v2/identities/{identifier}/payment/options/{id}: + get: + summary: Get Payment Option + operationId: GetPaymentOption + description: Get a specific payment option for the provided identity. + tags: + - Payment + security: + - basicAuth: [ ] + parameters: + - $ref: '#/components/parameters/pathIdentifier' + - $ref: '#/components/parameters/id' + responses: + '200': + description: Payment option + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentOption' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + '500': + $ref: '#/components/responses/500' + delete: + summary: Delete Payment Option + operationId: DeletePaymentOption + description: Remove a specific payment option for the provided identity. + tags: + - Payment + security: + - basicAuth: [ ] + parameters: + - $ref: '#/components/parameters/pathIdentifier' + - $ref: '#/components/parameters/id' + responses: + '200': + description: Payment option deleted + content: + application/json: + schema: + $ref: '#/components/schemas/GenericMessage' + '400': + $ref: '#/components/responses/400' + '500': + $ref: '#/components/responses/500' /v2/payment/verify/{recipient}: @@ -2075,6 +2178,68 @@ components: name: payments path: "github.com/polygonid/sh-id-platform/internal/payments" + PaymentOptionsPaginated: + type: object + required: [ items, meta ] + properties: + items: + $ref: '#/components/schemas/PaymentOptions' + meta: + $ref: '#/components/schemas/PaginatedMetadata' + + PaymentOptions: + type: array + items: + $ref: '#/components/schemas/PaymentOption' + + PaymentOption: + type: object + required: + - id + - issuerID + - name + - description + - config + - modifiedAt + - createdAt + properties: + id: + type: string + x-go-type: uuid.UUID + x-go-type-import: + name: uuid + path: github.com/google/uuid + issuerID: + type: string + name: + type: string + description: + type: string + config: + type: object + modifiedAt: + $ref: '#/components/schemas/TimeUTC' + createdAt: + $ref: '#/components/schemas/TimeUTC' + + PaymentOptionRequest: + type: object + required: + - id + - issuerID + - name + - description + - config + - modifiedAt + - createdAt + properties: + name: + type: string + description: + type: string + config: + type: object + BasicMessage: type: object required: diff --git a/cmd/platform/main.go b/cmd/platform/main.go index 8876aa14a..9acd09d53 100644 --- a/cmd/platform/main.go +++ b/cmd/platform/main.go @@ -113,6 +113,7 @@ func main() { schemaRepository := repositories.NewSchema(*storage) linkRepository := repositories.NewLink(*storage) sessionRepository := repositories.NewSessionCached(cachex) + paymentsRepo := repositories.NewPayment(*storage) // services initialization mtService := services.NewIdentityMerkleTrees(mtRepository) @@ -158,7 +159,7 @@ func main() { proofService := services.NewProver(circuitsLoaderService) schemaService := services.NewSchema(schemaRepository, schemaLoader) linkService := services.NewLinkService(storage, claimsService, qrService, claimsRepository, linkRepository, schemaRepository, schemaLoader, sessionRepository, ps, identityService, *networkResolver, cfg.UniversalLinks) - paymentService := services.NewPaymentService(*networkResolver, *paymentSettings) + paymentService := services.NewPaymentService(paymentsRepo, *networkResolver, *paymentSettings) transactionService, err := gateways.NewTransaction(*networkResolver) if err != nil { diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index bb73d9b2c..25792520e 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -470,6 +470,33 @@ type PaginatedMetadata struct { Total uint `json:"total"` } +// PaymentOption defines model for PaymentOption. +type PaymentOption struct { + Config map[string]interface{} `json:"config"` + CreatedAt TimeUTC `json:"createdAt"` + Description string `json:"description"` + Id uuid.UUID `json:"id"` + IssuerID string `json:"issuerID"` + ModifiedAt TimeUTC `json:"modifiedAt"` + Name string `json:"name"` +} + +// PaymentOptionRequest defines model for PaymentOptionRequest. +type PaymentOptionRequest struct { + Config map[string]interface{} `json:"config"` + Description string `json:"description"` + Name string `json:"name"` +} + +// PaymentOptions defines model for PaymentOptions. +type PaymentOptions = []PaymentOption + +// PaymentOptionsPaginated defines model for PaymentOptionsPaginated. +type PaymentOptionsPaginated struct { + Items PaymentOptions `json:"items"` + Meta PaginatedMetadata `json:"meta"` +} + // PaymentSettings defines model for PaymentSettings. type PaymentSettings = payments.Settings @@ -812,6 +839,9 @@ type ActivateLinkJSONRequestBody ActivateLinkJSONBody // CreatePaymentRequestJSONRequestBody defines body for CreatePaymentRequest for application/json ContentType. type CreatePaymentRequestJSONRequestBody = CreatePaymentRequest +// CreatePaymentOptionJSONRequestBody defines body for CreatePaymentOption for application/json ContentType. +type CreatePaymentOptionJSONRequestBody = PaymentOptionRequest + // ImportSchemaJSONRequestBody defines body for ImportSchema for application/json ContentType. type ImportSchemaJSONRequestBody = ImportSchemaRequest @@ -913,6 +943,18 @@ type ServerInterface interface { // Create Payment Request // (POST /v2/identities/{identifier}/payment-request) CreatePaymentRequest(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) + // Get Payment Options + // (GET /v2/identities/{identifier}/payment/options) + GetPaymentOptions(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) + // Create Payment Option + // (POST /v2/identities/{identifier}/payment/options) + CreatePaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) + // Delete Payment Option + // (DELETE /v2/identities/{identifier}/payment/options/{id}) + DeletePaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) + // Get Payment Option + // (GET /v2/identities/{identifier}/payment/options/{id}) + GetPaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) // Get Schemas // (GET /v2/identities/{identifier}/schemas) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) @@ -1141,6 +1183,30 @@ func (_ Unimplemented) CreatePaymentRequest(w http.ResponseWriter, r *http.Reque w.WriteHeader(http.StatusNotImplemented) } +// Get Payment Options +// (GET /v2/identities/{identifier}/payment/options) +func (_ Unimplemented) GetPaymentOptions(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Create Payment Option +// (POST /v2/identities/{identifier}/payment/options) +func (_ Unimplemented) CreatePaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Delete Payment Option +// (DELETE /v2/identities/{identifier}/payment/options/{id}) +func (_ Unimplemented) DeletePaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Get Payment Option +// (GET /v2/identities/{identifier}/payment/options/{id}) +func (_ Unimplemented) GetPaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + w.WriteHeader(http.StatusNotImplemented) +} + // Get Schemas // (GET /v2/identities/{identifier}/schemas) func (_ Unimplemented) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) { @@ -2376,6 +2442,148 @@ func (siw *ServerInterfaceWrapper) CreatePaymentRequest(w http.ResponseWriter, r handler.ServeHTTP(w, r) } +// GetPaymentOptions operation middleware +func (siw *ServerInterfaceWrapper) GetPaymentOptions(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetPaymentOptions(w, r, identifier) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// CreatePaymentOption operation middleware +func (siw *ServerInterfaceWrapper) CreatePaymentOption(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.CreatePaymentOption(w, r, identifier) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// DeletePaymentOption operation middleware +func (siw *ServerInterfaceWrapper) DeletePaymentOption(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + // ------------- Path parameter "id" ------------- + var id Id + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DeletePaymentOption(w, r, identifier, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetPaymentOption operation middleware +func (siw *ServerInterfaceWrapper) GetPaymentOption(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + // ------------- Path parameter "id" ------------- + var id Id + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetPaymentOption(w, r, identifier, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // GetSchemas operation middleware func (siw *ServerInterfaceWrapper) GetSchemas(w http.ResponseWriter, r *http.Request) { @@ -2996,6 +3204,18 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/v2/identities/{identifier}/payment-request", wrapper.CreatePaymentRequest) }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v2/identities/{identifier}/payment/options", wrapper.GetPaymentOptions) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/v2/identities/{identifier}/payment/options", wrapper.CreatePaymentOption) + }) + r.Group(func(r chi.Router) { + r.Delete(options.BaseURL+"/v2/identities/{identifier}/payment/options/{id}", wrapper.DeletePaymentOption) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v2/identities/{identifier}/payment/options/{id}", wrapper.GetPaymentOption) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/identities/{identifier}/schemas", wrapper.GetSchemas) }) @@ -4366,6 +4586,185 @@ func (response CreatePaymentRequest500JSONResponse) VisitCreatePaymentRequestRes return json.NewEncoder(w).Encode(response) } +type GetPaymentOptionsRequestObject struct { + Identifier PathIdentifier `json:"identifier"` +} + +type GetPaymentOptionsResponseObject interface { + VisitGetPaymentOptionsResponse(w http.ResponseWriter) error +} + +type GetPaymentOptions200JSONResponse PaymentOptionsPaginated + +func (response GetPaymentOptions200JSONResponse) VisitGetPaymentOptionsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetPaymentOptions400JSONResponse struct{ N400JSONResponse } + +func (response GetPaymentOptions400JSONResponse) VisitGetPaymentOptionsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetPaymentOptions401JSONResponse struct{ N401JSONResponse } + +func (response GetPaymentOptions401JSONResponse) VisitGetPaymentOptionsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type GetPaymentOptions500JSONResponse struct{ N500JSONResponse } + +func (response GetPaymentOptions500JSONResponse) VisitGetPaymentOptionsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentOptionRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Body *CreatePaymentOptionJSONRequestBody +} + +type CreatePaymentOptionResponseObject interface { + VisitCreatePaymentOptionResponse(w http.ResponseWriter) error +} + +type CreatePaymentOption201JSONResponse UUIDResponse + +func (response CreatePaymentOption201JSONResponse) VisitCreatePaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(201) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentOption400JSONResponse struct{ N400JSONResponse } + +func (response CreatePaymentOption400JSONResponse) VisitCreatePaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentOption401JSONResponse struct{ N401JSONResponse } + +func (response CreatePaymentOption401JSONResponse) VisitCreatePaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentOption422JSONResponse struct{ N422JSONResponse } + +func (response CreatePaymentOption422JSONResponse) VisitCreatePaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(422) + + return json.NewEncoder(w).Encode(response) +} + +type CreatePaymentOption500JSONResponse struct{ N500JSONResponse } + +func (response CreatePaymentOption500JSONResponse) VisitCreatePaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type DeletePaymentOptionRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Id Id `json:"id"` +} + +type DeletePaymentOptionResponseObject interface { + VisitDeletePaymentOptionResponse(w http.ResponseWriter) error +} + +type DeletePaymentOption200JSONResponse GenericMessage + +func (response DeletePaymentOption200JSONResponse) VisitDeletePaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeletePaymentOption400JSONResponse struct{ N400JSONResponse } + +func (response DeletePaymentOption400JSONResponse) VisitDeletePaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type DeletePaymentOption500JSONResponse struct{ N500JSONResponse } + +func (response DeletePaymentOption500JSONResponse) VisitDeletePaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type GetPaymentOptionRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Id Id `json:"id"` +} + +type GetPaymentOptionResponseObject interface { + VisitGetPaymentOptionResponse(w http.ResponseWriter) error +} + +type GetPaymentOption200JSONResponse PaymentOption + +func (response GetPaymentOption200JSONResponse) VisitGetPaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetPaymentOption400JSONResponse struct{ N400JSONResponse } + +func (response GetPaymentOption400JSONResponse) VisitGetPaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetPaymentOption404JSONResponse struct{ N404JSONResponse } + +func (response GetPaymentOption404JSONResponse) VisitGetPaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type GetPaymentOption500JSONResponse struct{ N500JSONResponse } + +func (response GetPaymentOption500JSONResponse) VisitGetPaymentOptionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type GetSchemasRequestObject struct { Identifier PathIdentifier `json:"identifier"` Params GetSchemasParams @@ -4939,6 +5338,18 @@ type StrictServerInterface interface { // Create Payment Request // (POST /v2/identities/{identifier}/payment-request) CreatePaymentRequest(ctx context.Context, request CreatePaymentRequestRequestObject) (CreatePaymentRequestResponseObject, error) + // Get Payment Options + // (GET /v2/identities/{identifier}/payment/options) + GetPaymentOptions(ctx context.Context, request GetPaymentOptionsRequestObject) (GetPaymentOptionsResponseObject, error) + // Create Payment Option + // (POST /v2/identities/{identifier}/payment/options) + CreatePaymentOption(ctx context.Context, request CreatePaymentOptionRequestObject) (CreatePaymentOptionResponseObject, error) + // Delete Payment Option + // (DELETE /v2/identities/{identifier}/payment/options/{id}) + DeletePaymentOption(ctx context.Context, request DeletePaymentOptionRequestObject) (DeletePaymentOptionResponseObject, error) + // Get Payment Option + // (GET /v2/identities/{identifier}/payment/options/{id}) + GetPaymentOption(ctx context.Context, request GetPaymentOptionRequestObject) (GetPaymentOptionResponseObject, error) // Get Schemas // (GET /v2/identities/{identifier}/schemas) GetSchemas(ctx context.Context, request GetSchemasRequestObject) (GetSchemasResponseObject, error) @@ -5903,6 +6314,119 @@ func (sh *strictHandler) CreatePaymentRequest(w http.ResponseWriter, r *http.Req } } +// GetPaymentOptions operation middleware +func (sh *strictHandler) GetPaymentOptions(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { + var request GetPaymentOptionsRequestObject + + request.Identifier = identifier + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetPaymentOptions(ctx, request.(GetPaymentOptionsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetPaymentOptions") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetPaymentOptionsResponseObject); ok { + if err := validResponse.VisitGetPaymentOptionsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// CreatePaymentOption operation middleware +func (sh *strictHandler) CreatePaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { + var request CreatePaymentOptionRequestObject + + request.Identifier = identifier + + var body CreatePaymentOptionJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.CreatePaymentOption(ctx, request.(CreatePaymentOptionRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "CreatePaymentOption") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(CreatePaymentOptionResponseObject); ok { + if err := validResponse.VisitCreatePaymentOptionResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeletePaymentOption operation middleware +func (sh *strictHandler) DeletePaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + var request DeletePaymentOptionRequestObject + + request.Identifier = identifier + request.Id = id + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.DeletePaymentOption(ctx, request.(DeletePaymentOptionRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeletePaymentOption") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(DeletePaymentOptionResponseObject); ok { + if err := validResponse.VisitDeletePaymentOptionResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetPaymentOption operation middleware +func (sh *strictHandler) GetPaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + var request GetPaymentOptionRequestObject + + request.Identifier = identifier + request.Id = id + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetPaymentOption(ctx, request.(GetPaymentOptionRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetPaymentOption") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetPaymentOptionResponseObject); ok { + if err := validResponse.VisitGetPaymentOptionResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // GetSchemas operation middleware func (sh *strictHandler) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) { var request GetSchemasRequestObject diff --git a/internal/api/main_test.go b/internal/api/main_test.go index ffad2603a..b1a48ff8d 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -233,6 +233,7 @@ type repos struct { idenMerkleTree ports.IdentityMerkleTreeRepository identityState ports.IdentityStateRepository links ports.LinkRepository + payments ports.PaymentRepository schemas ports.SchemaRepository sessions ports.SessionRepository revocation ports.RevocationRepository @@ -270,6 +271,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { idenMerkleTree: repositories.NewIdentityMerkleTreeRepository(), identityState: repositories.NewIdentityState(), links: repositories.NewLink(*st), + payments: repositories.NewPayment(*st), sessions: repositories.NewSessionCached(cachex), schemas: repositories.NewSchema(*st), revocation: repositories.NewRevocation(), @@ -305,7 +307,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { identityService := services.NewIdentity(keyStore, repos.identity, repos.idenMerkleTree, repos.identityState, mtService, qrService, repos.claims, repos.revocation, repos.connection, st, nil, repos.sessions, pubSub, *networkResolver, rhsFactory, revocationStatusResolver) connectionService := services.NewConnection(repos.connection, repos.claims, st) schemaService := services.NewSchema(repos.schemas, schemaLoader) - paymentService := services.NewPaymentService(*networkResolver, *paymentSettings) + paymentService := services.NewPaymentService(repos.payments, *networkResolver, *paymentSettings) mediaTypeManager := services.NewMediaTypeManager( map[iden3comm.ProtocolMessage][]string{ diff --git a/internal/api/payment.go b/internal/api/payment.go index be6ef3a52..b9ee7f854 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "fmt" "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" @@ -11,6 +12,84 @@ import ( "github.com/polygonid/sh-id-platform/internal/log" ) +// GetPaymentOptions is the controller to get payment options +func (s *Server) GetPaymentOptions(ctx context.Context, request GetPaymentOptionsRequestObject) (GetPaymentOptionsResponseObject, error) { + issuerDID, err := w3c.ParseDID(request.Identifier) + if err != nil { + log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) + return GetPaymentOptions400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } + paymentOptions, err := s.paymentService.GetPaymentOptions(ctx, issuerDID) + if err != nil { + log.Error(ctx, "getting payment options", "err", err, "issuer", issuerDID) + return GetPaymentOptions500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't get payment-options: <%s>", err.Error())}}, nil + } + items, err := toGetPaymentOptionsResponse(paymentOptions) + if err != nil { + log.Error(ctx, "creating payment options response", "err", err) + return GetPaymentOptions500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't convert payment-options: <%s>", err.Error())}}, nil + } + return GetPaymentOptions200JSONResponse( + PaymentOptionsPaginated{ + Items: items, + Meta: PaginatedMetadata{ // No pagination by now, just return all + MaxResults: uint(len(paymentOptions)), + Page: 1, + Total: uint(len(paymentOptions)), + }, + }), nil +} + +// CreatePaymentOption is the controller to create a payment option +func (s *Server) CreatePaymentOption(ctx context.Context, request CreatePaymentOptionRequestObject) (CreatePaymentOptionResponseObject, error) { + issuerDID, err := w3c.ParseDID(request.Identifier) + if err != nil { + log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) + return CreatePaymentOption400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } + id, err := s.paymentService.CreatePaymentOption(ctx, issuerDID, request.Body.Name, request.Body.Description, request.Body.Config) + if err != nil { + log.Error(ctx, "creating payment option", "err", err, "issuer", issuerDID, "request", request.Body) + return CreatePaymentOption500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't create payment-option: <%s>", err.Error())}}, nil + } + return CreatePaymentOption201JSONResponse{Id: id.String()}, nil +} + +// DeletePaymentOption is the controller to delete a payment option +func (s *Server) DeletePaymentOption(ctx context.Context, request DeletePaymentOptionRequestObject) (DeletePaymentOptionResponseObject, error) { + issuerID, err := w3c.ParseDID(request.Identifier) + if err != nil { + log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) + return DeletePaymentOption400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } + if err := s.paymentService.DeletePaymentOption(ctx, issuerID, request.Id); err != nil { + log.Error(ctx, "deleting payment option", "err", err, "issuer", issuerID, "id", request.Id) + return DeletePaymentOption500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't delete payment-option: <%s>", err.Error())}}, nil + } + return DeletePaymentOption200JSONResponse{Message: "deleted"}, nil +} + +// GetPaymentOption is the controller to get a payment option +func (s *Server) GetPaymentOption(ctx context.Context, request GetPaymentOptionRequestObject) (GetPaymentOptionResponseObject, error) { + issuerID, err := w3c.ParseDID(request.Identifier) + if err != nil { + log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) + return GetPaymentOption400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } + paymentOption, err := s.paymentService.GetPaymentOptionByID(ctx, issuerID, request.Id) + if err != nil { + log.Error(ctx, "getting payment option", "err", err, "issuer", issuerID, "id", request.Id) + return GetPaymentOption500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't get payment-option: <%s>", err.Error())}}, nil + } + + option, err := toPaymentOption(paymentOption) + if err != nil { + log.Error(ctx, "creating payment option response", "err", err) + return GetPaymentOption500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't convert payment-option: <%s>", err.Error())}}, nil + } + return GetPaymentOption200JSONResponse(option), nil +} + // CreatePaymentRequest is the controller to get qr bodies func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePaymentRequestRequestObject) (CreatePaymentRequestResponseObject, error) { var err error diff --git a/internal/api/responses.go b/internal/api/responses.go index 4be127ff3..18de52bd5 100644 --- a/internal/api/responses.go +++ b/internal/api/responses.go @@ -1,6 +1,7 @@ package api import ( + "encoding/json" "net/http" "github.com/iden3/go-schema-processor/v2/verifiable" @@ -280,3 +281,35 @@ func getTransactionStatus(status domain.IdentityStatus) StateTransactionStatus { return "failed" } } + +func toGetPaymentOptionsResponse(opts []domain.PaymentOption) (PaymentOptions, error) { + var err error + res := make([]PaymentOption, len(opts)) + for i, opt := range opts { + res[i], err = toPaymentOption(&opt) + if err != nil { + return PaymentOptions{}, err + } + } + return res, nil +} + +func toPaymentOption(opt *domain.PaymentOption) (PaymentOption, error) { + var config map[string]interface{} + raw, err := json.Marshal(opt.Config) + if err != nil { + return PaymentOption{}, err + } + if err := json.Unmarshal(raw, &config); err != nil { + return PaymentOption{}, err + } + return PaymentOption{ + Id: opt.ID, + IssuerID: opt.IssuerDID.String(), + Name: opt.Name, + Description: opt.Description, + Config: config, + CreatedAt: TimeUTC(opt.CreatedAt), + ModifiedAt: TimeUTC(opt.UpdatedAt), + }, nil +} diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index 0602350be..de493eb0f 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -6,10 +6,12 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/payments" ) @@ -81,4 +83,9 @@ type PaymentService interface { CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) GetSettings() payments.Settings VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) + + CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config any) (uuid.UUID, error) + GetPaymentOptions(ctx context.Context, issuerDID *w3c.DID) ([]domain.PaymentOption, error) + GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) + DeletePaymentOption(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) error } diff --git a/internal/core/ports/payments_repository.go b/internal/core/ports/payments_repository.go new file mode 100644 index 000000000..3456a25b6 --- /dev/null +++ b/internal/core/ports/payments_repository.go @@ -0,0 +1,18 @@ +package ports + +import ( + "context" + + "github.com/google/uuid" + "github.com/iden3/go-iden3-core/v2/w3c" + + "github.com/polygonid/sh-id-platform/internal/core/domain" +) + +// PaymentRepository is the interface that defines the available methods for the Payment repository +type PaymentRepository interface { + SavePaymentOption(ctx context.Context, opt *domain.PaymentOption) (uuid.UUID, error) + GetAllPaymentOptions(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentOption, error) + GetPaymentOptionByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) + DeletePaymentOption(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) error +} diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index a89d5d6ed..852ca3458 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -21,6 +21,7 @@ import ( comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/eth" "github.com/polygonid/sh-id-platform/internal/log" @@ -31,16 +32,59 @@ import ( type payment struct { networkResolver network.Resolver settings payments.Settings + paymentsStore ports.PaymentRepository } // NewPaymentService creates a new payment service -func NewPaymentService(resolver network.Resolver, settings payments.Settings) ports.PaymentService { +func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, settings payments.Settings) ports.PaymentService { return &payment{ networkResolver: resolver, settings: settings, + paymentsStore: payOptsRepo, } } +// CreatePaymentOption creates a payment option for a specific issuer +func (p *payment) CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config any) (uuid.UUID, error) { + paymentOption := domain.NewPaymentOption(*issuerDID, name, description, config) + id, err := p.paymentsStore.SavePaymentOption(ctx, paymentOption) + if err != nil { + log.Error(ctx, "failed to save payment option", "err", err, "issuerDID", issuerDID, "name", name, "description", description, "config", config) + return uuid.Nil, err + } + return id, nil +} + +// GetPaymentOptions returns all payment options of a issuer +func (p *payment) GetPaymentOptions(ctx context.Context, issuerDID *w3c.DID) ([]domain.PaymentOption, error) { + opts, err := p.paymentsStore.GetAllPaymentOptions(ctx, *issuerDID) + if err != nil { + log.Error(ctx, "failed to get payment options", "err", err, "issuerDID", issuerDID) + return nil, err + } + return opts, nil +} + +// GetPaymentOptionByID returns a payment option by its ID +func (p *payment) GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) { + opt, err := p.paymentsStore.GetPaymentOptionByID(ctx, *issuerDID, id) + if err != nil { + log.Error(ctx, "failed to get payment option", "err", err, "issuerDID", issuerDID, "id", id) + return nil, err + } + return opt, nil +} + +// DeletePaymentOption deletes a payment option +func (p *payment) DeletePaymentOption(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) error { + err := p.paymentsStore.DeletePaymentOption(ctx, *issuerDID, id) + if err != nil { + log.Error(ctx, "failed to delete payment option", "err", err, "issuerDID", issuerDID, "id", id) + return err + } + return nil +} + // CreatePaymentRequest creates a payment request func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, userDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) { id := uuid.New().String() From ee7833e0ccf63d78e199981c5e1329d392e90ff5 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Fri, 15 Nov 2024 18:10:21 +0100 Subject: [PATCH 11/66] feat: Create Payment Option test --- internal/api/connections_test.go | 2 +- internal/api/payment.go | 5 ++ internal/api/payment_test.go | 140 +++++++++++++++++++++++++++++++ internal/repositories/payment.go | 10 ++- 4 files changed, 152 insertions(+), 5 deletions(-) diff --git a/internal/api/connections_test.go b/internal/api/connections_test.go index 87d949419..f8ac170d7 100644 --- a/internal/api/connections_test.go +++ b/internal/api/connections_test.go @@ -182,8 +182,8 @@ func TestServer_CreateConnection(t *testing.T) { reqBytes, err := json.Marshal(tc.body) require.NoError(t, err) req, err := http.NewRequest(http.MethodPost, parsedURL.String(), bytes.NewBuffer(reqBytes)) - req.SetBasicAuth(tc.auth()) require.NoError(t, err) + req.SetBasicAuth(tc.auth()) handler.ServeHTTP(rr, req) diff --git a/internal/api/payment.go b/internal/api/payment.go index b9ee7f854..163a7be03 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "errors" "fmt" "github.com/ethereum/go-ethereum/common" @@ -10,6 +11,7 @@ import ( comm "github.com/iden3/iden3comm/v2" "github.com/polygonid/sh-id-platform/internal/log" + "github.com/polygonid/sh-id-platform/internal/repositories" ) // GetPaymentOptions is the controller to get payment options @@ -50,6 +52,9 @@ func (s *Server) CreatePaymentOption(ctx context.Context, request CreatePaymentO id, err := s.paymentService.CreatePaymentOption(ctx, issuerDID, request.Body.Name, request.Body.Description, request.Body.Config) if err != nil { log.Error(ctx, "creating payment option", "err", err, "issuer", issuerDID, "request", request.Body) + if errors.Is(err, repositories.ErrIdentityNotFound) { + return CreatePaymentOption400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } return CreatePaymentOption500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't create payment-option: <%s>", err.Error())}}, nil } return CreatePaymentOption201JSONResponse{Id: id.String()}, nil diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index a8d389542..be6a18f45 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -1,16 +1,54 @@ package api import ( + "bytes" "context" "encoding/json" + "fmt" "net/http" "net/http/httptest" "testing" + "github.com/iden3/go-iden3-core/v2/w3c" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/polygonid/sh-id-platform/internal/core/ports" ) +const paymentOptionConfigurationTesting = ` +{ + "Chains": [ + { + "ChainId": 137, + "Recipient": "0x..", + "SigningKeyId": "", + "Iden3PaymentRailsRequestV1": { + "Amount": "0.01", + "Currency": "POL" + }, + "Iden3PaymentRailsERC20RequestV1": { + "USDT": { + "Amount": "3" + }, + "USDC": { + "Amount": "3" + } + } + }, + { + "ChainId": 1101, + "Recipient": "0x..", + "SigningKeyId": "", + "Iden3PaymentRailsRequestV1": { + "Amount": "0.5", + "Currency": "ETH" + } + } + ] +} +` + func TestServer_GetPaymentSettings(t *testing.T) { ctx := context.Background() @@ -27,3 +65,105 @@ func TestServer_GetPaymentSettings(t *testing.T) { var response GetPaymentSettings200JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) } + +func TestServer_CreatePaymentOption(t *testing.T) { + const ( + method = "polygonid" + blockchain = "polygon" + network = "amoy" + BJJ = "BJJ" + ) + + var config map[string]interface{} + ctx := context.Background() + + server := newTestServer(t, nil) + handler := getHandler(ctx, server) + + iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) + require.NoError(t, err) + issuerDID, err := w3c.ParseDID(iden.Identifier) + require.NoError(t, err) + + otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") + require.NoError(t, err) + + require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) + + type expected struct { + httpCode int + msg string + } + + for _, tc := range []struct { + name string + issuerDID w3c.DID + auth func() (string, string) + body CreatePaymentOptionJSONRequestBody + expected expected + }{ + { + name: "no auth header", + auth: authWrong, + issuerDID: *issuerDID, + body: CreatePaymentOptionJSONRequestBody{ + Config: config, + Description: "Payment Option explanation", + Name: "1 POL Payment", + }, + expected: expected{ + httpCode: http.StatusUnauthorized, + msg: "Unauthorized", + }, + }, + { + name: "Happy Path", + auth: authOk, + issuerDID: *issuerDID, + body: CreatePaymentOptionJSONRequestBody{ + Config: config, + Description: "Payment Option explanation", + Name: "1 POL Payment", + }, + expected: expected{ + httpCode: http.StatusCreated, + }, + }, + { + name: "Not existing issuerDID", + auth: authOk, + issuerDID: *otherDID, + body: CreatePaymentOptionJSONRequestBody{ + Config: config, + Description: "Payment Option explanation", + Name: "1 POL Payment", + }, + expected: expected{ + httpCode: http.StatusBadRequest, + msg: "invalid issuer did", + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + rr := httptest.NewRecorder() + payload, err := json.Marshal(tc.body) + require.NoError(t, err) + url := fmt.Sprintf("/v2/identities/%s/payment/options", tc.issuerDID.String()) + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payload)) + assert.NoError(t, err) + req.SetBasicAuth(tc.auth()) + + handler.ServeHTTP(rr, req) + require.Equal(t, tc.expected.httpCode, rr.Code) + switch tc.expected.httpCode { + case http.StatusCreated: + var response CreatePaymentOption201JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + case http.StatusBadRequest: + var response CreatePaymentOption400JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + } + }) + } +} diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go index 06a913f84..5155dc47e 100644 --- a/internal/repositories/payment.go +++ b/internal/repositories/payment.go @@ -13,8 +13,8 @@ import ( "github.com/polygonid/sh-id-platform/internal/db" ) -// PaymentOptionNotFound error -var PaymentOptionNotFound = errors.New("payment option not found") +// ErrPaymentOptionNotFound error +var ErrPaymentOptionNotFound = errors.New("payment option not found") // Payment repository type Payment struct { @@ -35,7 +35,9 @@ VALUES ($1, $2, $3, $4, $5, $6, $7); _, err := p.conn.Pgx.Exec(ctx, query, opt.ID, opt.IssuerDID.String(), opt.Name, opt.Description, opt.Config, opt.CreatedAt, opt.UpdatedAt) if err != nil { - return uuid.Nil, err + if strings.Contains(err.Error(), "violates foreign key constraint") { + return uuid.Nil, ErrIdentityNotFound + } } return opt.ID, nil } @@ -85,7 +87,7 @@ AND issuer_did = $2;` err := p.conn.Pgx.QueryRow(ctx, query, id, issuerDID.String()).Scan(&opt.ID, &strIssuerDID, &opt.Name, &opt.Description, &opt.Config, &opt.CreatedAt, &opt.UpdatedAt) if err != nil { if strings.Contains(err.Error(), "no rows in result set") { - return nil, PaymentOptionNotFound + return nil, ErrPaymentOptionNotFound } return nil, err } From c428691a709b58d4a0c6f90789428a0ca278f736 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 18 Nov 2024 10:34:37 +0100 Subject: [PATCH 12/66] fix: API running via docker --- .env-issuer.sample | 8 +++++++- .gitignore | 2 +- Dockerfile | 2 +- infrastructure/local/docker-compose-full.yml | 2 +- infrastructure/local/docker-compose.yml | 1 + 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.env-issuer.sample b/.env-issuer.sample index 89c655eaf..5ce960794 100644 --- a/.env-issuer.sample +++ b/.env-issuer.sample @@ -68,4 +68,10 @@ ISSUER_RESOLVER_PATH=./resolvers_settings.yaml # if you want, you can specify the content of the resolvers encoded in base64. In this case ISSUER_RESOLVER_PATH have to be empty ISSUER_RESOLVER_FILE= -ISSUER_UNIVERSAL_LINKS_BASE_URL=https://wallet.privado.id \ No newline at end of file +ISSUER_UNIVERSAL_LINKS_BASE_URL=https://wallet.privado.id + +#Payments configuration +# ISSUER_PAYMENTS_SETTINGS_PATH is the configutation file for payments. +# You can use another file by specifying the path. Be Sure to the file is mounted in the container (docker compose files) +ISSUER_PAYMENTS_SETTINGS_PATH=./payment_settings.yaml +# \ No newline at end of file diff --git a/.gitignore b/.gitignore index 50b6e2372..8690a5e32 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ bin/ .env-issuer .env-ui resolvers_settings.yaml -payments.yaml +payment_settings.yaml infrastructure/local/.vault/data infrastructure/local/.vault/plugins diff --git a/Dockerfile b/Dockerfile index 88cd9fa81..e2ec1c282 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,4 +27,4 @@ COPY --from=base ./service/api ./api COPY --from=base ./service/bin/* ./ COPY --from=base ./service/pkg/credentials ./pkg/credentials COPY --from=base ./service/resolvers_settings.* ./ -COPY --from=base ./service/payment.* ./ \ No newline at end of file +COPY --from=base ./service/payment_settings.* ./ \ No newline at end of file diff --git a/infrastructure/local/docker-compose-full.yml b/infrastructure/local/docker-compose-full.yml index a52ff6433..43da41a58 100644 --- a/infrastructure/local/docker-compose-full.yml +++ b/infrastructure/local/docker-compose-full.yml @@ -81,7 +81,7 @@ services: volumes: - ../../localstoragekeys:/localstoragekeys:rw - ../../resolvers_settings.yaml:/resolvers_settings.yaml - - ../../payment_settings.test.yaml:/payment_settings.test.yaml + - ../../payment_settings.yaml:/payment_settings.yaml healthcheck: test: ["CMD", "curl", "-f", "api:3001/status"] interval: 10s diff --git a/infrastructure/local/docker-compose.yml b/infrastructure/local/docker-compose.yml index bf5d2dc07..75e92f7ec 100644 --- a/infrastructure/local/docker-compose.yml +++ b/infrastructure/local/docker-compose.yml @@ -21,6 +21,7 @@ services: volumes: - ../../localstoragekeys:/localstoragekeys:rw - ../../resolvers_settings.yaml:/resolvers_settings.yaml + - ../../payment_settings.yaml:/payment_settings.yaml healthcheck: test: ["CMD", "curl", "-f", "localhost:3001/status"] interval: 10s From d30d1ae977a909e00a7b0aa506c0291680ca338f Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 18 Nov 2024 17:45:29 +0100 Subject: [PATCH 13/66] chore: Test get payment option --- api/api.yaml | 2 - internal/api/api.gen.go | 9 --- internal/api/main_test.go | 2 + internal/api/payment.go | 3 + internal/api/payment_test.go | 104 +++++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 11 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 426a462c7..e5e2daf1f 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1393,8 +1393,6 @@ paths: $ref: '#/components/responses/400' '401': $ref: '#/components/responses/401' - '422': - $ref: '#/components/responses/422' '500': $ref: '#/components/responses/500' diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 25792520e..59d5cd55b 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -4666,15 +4666,6 @@ func (response CreatePaymentOption401JSONResponse) VisitCreatePaymentOptionRespo return json.NewEncoder(w).Encode(response) } -type CreatePaymentOption422JSONResponse struct{ N422JSONResponse } - -func (response CreatePaymentOption422JSONResponse) VisitCreatePaymentOptionResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(422) - - return json.NewEncoder(w).Encode(response) -} - type CreatePaymentOption500JSONResponse struct{ N500JSONResponse } func (response CreatePaymentOption500JSONResponse) VisitCreatePaymentOptionResponse(w http.ResponseWriter) error { diff --git a/internal/api/main_test.go b/internal/api/main_test.go index b1a48ff8d..d4fa247de 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -244,6 +244,7 @@ type servicex struct { identity ports.IdentityService schema ports.SchemaService links ports.LinkService + payments ports.PaymentService qrs ports.QrStoreService } @@ -329,6 +330,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { credentials: claimsService, identity: identityService, links: linkService, + payments: paymentService, qrs: qrService, schema: schemaService, }, diff --git a/internal/api/payment.go b/internal/api/payment.go index 163a7be03..30a6cc516 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -83,6 +83,9 @@ func (s *Server) GetPaymentOption(ctx context.Context, request GetPaymentOptionR } paymentOption, err := s.paymentService.GetPaymentOptionByID(ctx, issuerID, request.Id) if err != nil { + if errors.Is(err, repositories.ErrPaymentOptionNotFound) { + return GetPaymentOption404JSONResponse{N404JSONResponse{Message: "payment option not found"}}, nil + } log.Error(ctx, "getting payment option", "err", err, "issuer", issuerID, "id", request.Id) return GetPaymentOption500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't get payment-option: <%s>", err.Error())}}, nil } diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index be6a18f45..38f826958 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -9,6 +9,7 @@ import ( "net/http/httptest" "testing" + "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -163,6 +164,109 @@ func TestServer_CreatePaymentOption(t *testing.T) { var response CreatePaymentOption400JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) assert.Equal(t, tc.expected.msg, response.Message) + + } + }) + } +} + +func TestServer_GetPaymentOption(t *testing.T) { + const ( + method = "polygonid" + blockchain = "polygon" + network = "amoy" + BJJ = "BJJ" + ) + + var config map[string]interface{} + ctx := context.Background() + + server := newTestServer(t, nil) + handler := getHandler(ctx, server) + + iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) + require.NoError(t, err) + issuerDID, err := w3c.ParseDID(iden.Identifier) + require.NoError(t, err) + + otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") + require.NoError(t, err) + + optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", config) + require.NoError(t, err) + + require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) + + type expected struct { + httpCode int + msg string + } + + for _, tc := range []struct { + name string + issuerDID w3c.DID + optionID uuid.UUID + auth func() (string, string) + expected expected + }{ + { + name: "no auth header", + auth: authWrong, + issuerDID: *issuerDID, + optionID: optionID, + expected: expected{ + httpCode: http.StatusUnauthorized, + }, + }, + { + name: "Happy Path", + auth: authOk, + issuerDID: *issuerDID, + optionID: optionID, + expected: expected{ + httpCode: http.StatusOK, + }, + }, + { + name: "Not existing issuerDID", + auth: authOk, + issuerDID: *otherDID, + optionID: optionID, + expected: expected{ + httpCode: http.StatusNotFound, + msg: "payment option not found", + }, + }, + { + name: "Not existing Payment option", + auth: authOk, + issuerDID: *issuerDID, + optionID: uuid.New(), + expected: expected{ + httpCode: http.StatusNotFound, + msg: "payment option not found", + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + rr := httptest.NewRecorder() + url := fmt.Sprintf("/v2/identities/%s/payment/options/%s", tc.issuerDID.String(), tc.optionID.String()) + req, err := http.NewRequest(http.MethodGet, url, nil) + assert.NoError(t, err) + req.SetBasicAuth(tc.auth()) + + handler.ServeHTTP(rr, req) + require.Equal(t, tc.expected.httpCode, rr.Code) + + switch tc.expected.httpCode { + case http.StatusOK: + var response GetPaymentOption200JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.optionID, response.Id) + case http.StatusNotFound: + var response GetPaymentOption404JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) } }) } From a6b8d1d800908cb153d0a26a1d621350829199b0 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 18 Nov 2024 18:05:10 +0100 Subject: [PATCH 14/66] feat: Test get payment options --- internal/api/payment_test.go | 94 ++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 38f826958..a3ae34930 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -271,3 +271,97 @@ func TestServer_GetPaymentOption(t *testing.T) { }) } } + +func TestServer_GetPaymentOptions(t *testing.T) { + const ( + method = "polygonid" + blockchain = "polygon" + network = "amoy" + BJJ = "BJJ" + ) + + var config map[string]interface{} + ctx := context.Background() + + server := newTestServer(t, nil) + handler := getHandler(ctx, server) + + iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) + require.NoError(t, err) + issuerDID, err := w3c.ParseDID(iden.Identifier) + require.NoError(t, err) + + otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") + require.NoError(t, err) + + require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) + + for i := 0; i < 10; i++ { + _, err = server.Services.payments.CreatePaymentOption(ctx, issuerDID, fmt.Sprintf("Payment Option %d", i+1), "Payment Option explanation", config) + require.NoError(t, err) + } + + type expected struct { + httpCode int + msg string + count int + } + + for _, tc := range []struct { + name string + issuerDID w3c.DID + auth func() (string, string) + expected expected + }{ + { + name: "no auth header", + auth: authWrong, + issuerDID: *issuerDID, + expected: expected{ + httpCode: http.StatusUnauthorized, + }, + }, + { + name: "Happy Path", + auth: authOk, + issuerDID: *issuerDID, + expected: expected{ + httpCode: http.StatusOK, + count: 10, + }, + }, + { + name: "Other issuer DID with no payment options. Should return empty string", + auth: authOk, + issuerDID: *otherDID, + expected: expected{ + httpCode: http.StatusOK, + count: 0, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + rr := httptest.NewRecorder() + url := fmt.Sprintf("/v2/identities/%s/payment/options", tc.issuerDID.String()) + req, err := http.NewRequest(http.MethodGet, url, nil) + assert.NoError(t, err) + req.SetBasicAuth(tc.auth()) + + handler.ServeHTTP(rr, req) + require.Equal(t, tc.expected.httpCode, rr.Code) + + switch tc.expected.httpCode { + case http.StatusOK: + var response GetPaymentOptions200JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.count, len(response.Items)) // Check that 10 items are returned + assert.Equal(t, 1, int(response.Meta.Page)) + assert.Equal(t, tc.expected.count, int(response.Meta.Total)) + case http.StatusBadRequest: + var response GetPaymentOptions400JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + } + }) + } +} From d76f784e20f03b21b2900bc01f5327a8950a9a61 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 19 Nov 2024 11:30:04 +0100 Subject: [PATCH 15/66] feat: Test remove payment options --- internal/api/payment.go | 5 +- internal/api/payment_test.go | 105 +++++++++++++++++++++++++++++++ internal/repositories/payment.go | 11 ++-- 3 files changed, 116 insertions(+), 5 deletions(-) diff --git a/internal/api/payment.go b/internal/api/payment.go index 30a6cc516..7b7a9e65c 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -69,6 +69,9 @@ func (s *Server) DeletePaymentOption(ctx context.Context, request DeletePaymentO } if err := s.paymentService.DeletePaymentOption(ctx, issuerID, request.Id); err != nil { log.Error(ctx, "deleting payment option", "err", err, "issuer", issuerID, "id", request.Id) + if errors.Is(err, repositories.ErrPaymentOptionDoesNotExists) { + return DeletePaymentOption400JSONResponse{N400JSONResponse{Message: "payment option not found"}}, nil + } return DeletePaymentOption500JSONResponse{N500JSONResponse{Message: fmt.Sprintf("can't delete payment-option: <%s>", err.Error())}}, nil } return DeletePaymentOption200JSONResponse{Message: "deleted"}, nil @@ -83,7 +86,7 @@ func (s *Server) GetPaymentOption(ctx context.Context, request GetPaymentOptionR } paymentOption, err := s.paymentService.GetPaymentOptionByID(ctx, issuerID, request.Id) if err != nil { - if errors.Is(err, repositories.ErrPaymentOptionNotFound) { + if errors.Is(err, repositories.ErrPaymentOptionDoesNotExists) { return GetPaymentOption404JSONResponse{N404JSONResponse{Message: "payment option not found"}}, nil } log.Error(ctx, "getting payment option", "err", err, "issuer", issuerID, "id", request.Id) diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index a3ae34930..b350f35dc 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -365,3 +365,108 @@ func TestServer_GetPaymentOptions(t *testing.T) { }) } } + +func TestServer_DeletePaymentOption(t *testing.T) { + const ( + method = "polygonid" + blockchain = "polygon" + network = "amoy" + BJJ = "BJJ" + ) + + var config map[string]interface{} + ctx := context.Background() + + server := newTestServer(t, nil) + handler := getHandler(ctx, server) + + iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) + require.NoError(t, err) + issuerDID, err := w3c.ParseDID(iden.Identifier) + require.NoError(t, err) + + otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") + require.NoError(t, err) + + optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", config) + require.NoError(t, err) + + type expected struct { + httpCode int + msg string + } + + for _, tc := range []struct { + name string + issuerDID w3c.DID + optionID uuid.UUID + auth func() (string, string) + expected expected + }{ + { + name: "no auth header", + auth: authWrong, + issuerDID: *issuerDID, + optionID: optionID, + expected: expected{ + httpCode: http.StatusUnauthorized, + }, + }, + { + name: "Happy Path", + auth: authOk, + issuerDID: *issuerDID, + optionID: optionID, + expected: expected{ + httpCode: http.StatusOK, + msg: "deleted", + }, + }, + { + name: "Not existing issuerDID", + auth: authOk, + issuerDID: *otherDID, + optionID: optionID, + expected: expected{ + httpCode: http.StatusBadRequest, + msg: "payment option not found", + }, + }, + { + name: "Not existing Payment option", + auth: authOk, + issuerDID: *issuerDID, + optionID: uuid.New(), + expected: expected{ + httpCode: http.StatusBadRequest, + msg: "payment option not found", + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + rr := httptest.NewRecorder() + url := fmt.Sprintf("/v2/identities/%s/payment/options/%s", tc.issuerDID.String(), tc.optionID.String()) + req, err := http.NewRequest(http.MethodDelete, url, nil) + assert.NoError(t, err) + req.SetBasicAuth(tc.auth()) + + handler.ServeHTTP(rr, req) + require.Equal(t, tc.expected.httpCode, rr.Code) + + switch tc.expected.httpCode { + case http.StatusOK: + var response DeletePaymentOption200JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + case http.StatusBadRequest: + var response DeletePaymentOption400JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + case http.StatusInternalServerError: + var response DeletePaymentOption500JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + } + }) + } +} diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go index 5155dc47e..9f75dcbe5 100644 --- a/internal/repositories/payment.go +++ b/internal/repositories/payment.go @@ -13,8 +13,8 @@ import ( "github.com/polygonid/sh-id-platform/internal/db" ) -// ErrPaymentOptionNotFound error -var ErrPaymentOptionNotFound = errors.New("payment option not found") +// ErrPaymentOptionDoesNotExists error +var ErrPaymentOptionDoesNotExists = errors.New("payment option not found") // Payment repository type Payment struct { @@ -87,7 +87,7 @@ AND issuer_did = $2;` err := p.conn.Pgx.QueryRow(ctx, query, id, issuerDID.String()).Scan(&opt.ID, &strIssuerDID, &opt.Name, &opt.Description, &opt.Config, &opt.CreatedAt, &opt.UpdatedAt) if err != nil { if strings.Contains(err.Error(), "no rows in result set") { - return nil, ErrPaymentOptionNotFound + return nil, ErrPaymentOptionDoesNotExists } return nil, err } @@ -103,9 +103,12 @@ AND issuer_did = $2;` func (p *Payment) DeletePaymentOption(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) error { const query = `DELETE FROM payment_options WHERE id = $1 and issuer_did = $2;` - _, err := p.conn.Pgx.Exec(ctx, query, id, issuerDID.String()) + cmd, err := p.conn.Pgx.Exec(ctx, query, id, issuerDID.String()) if err != nil { return err } + if cmd.RowsAffected() == 0 { + return ErrPaymentOptionDoesNotExists + } return nil } From b79059dc56956acbe17446b7dad93bf4a1ffbf03 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 20 Nov 2024 09:45:42 +0100 Subject: [PATCH 16/66] feat: Define structure for paymentOption Config --- api/api.yaml | 10 +++++ internal/api/api.gen.go | 21 ++++----- internal/api/payment.go | 2 +- internal/api/payment_test.go | 31 +++++++++---- internal/api/responses.go | 2 +- internal/core/domain/payment.go | 28 +++++++++++- internal/core/ports/payment_service.go | 2 +- internal/core/services/payment.go | 2 +- internal/repositories/payment_test.go | 60 +++++++++++++++++++++++--- 9 files changed, 128 insertions(+), 30 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index e5e2daf1f..e51ecb5b0 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -2213,8 +2213,13 @@ components: type: string description: type: string + # TODO: Convert config into a DTO object when format is more clear. config: type: object + x-go-type: domain.PaymentOptionConfig + x-go-type-import: + name: domain + path: "github.com/polygonid/sh-id-platform/internal/core/domain" modifiedAt: $ref: '#/components/schemas/TimeUTC' createdAt: @@ -2235,8 +2240,13 @@ components: type: string description: type: string + # TODO: Convert config into a DTO object when format is more clear. config: type: object + x-go-type: domain.PaymentOptionConfig + x-go-type-import: + name: domain + path: "github.com/polygonid/sh-id-platform/internal/core/domain" BasicMessage: type: object diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 59d5cd55b..2a39aa8f7 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -17,6 +17,7 @@ import ( protocol "github.com/iden3/iden3comm/v2/protocol" "github.com/oapi-codegen/runtime" strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" + domain "github.com/polygonid/sh-id-platform/internal/core/domain" payments "github.com/polygonid/sh-id-platform/internal/payments" timeapi "github.com/polygonid/sh-id-platform/internal/timeapi" ) @@ -472,20 +473,20 @@ type PaginatedMetadata struct { // PaymentOption defines model for PaymentOption. type PaymentOption struct { - Config map[string]interface{} `json:"config"` - CreatedAt TimeUTC `json:"createdAt"` - Description string `json:"description"` - Id uuid.UUID `json:"id"` - IssuerID string `json:"issuerID"` - ModifiedAt TimeUTC `json:"modifiedAt"` - Name string `json:"name"` + Config domain.PaymentOptionConfig `json:"config"` + CreatedAt TimeUTC `json:"createdAt"` + Description string `json:"description"` + Id uuid.UUID `json:"id"` + IssuerID string `json:"issuerID"` + ModifiedAt TimeUTC `json:"modifiedAt"` + Name string `json:"name"` } // PaymentOptionRequest defines model for PaymentOptionRequest. type PaymentOptionRequest struct { - Config map[string]interface{} `json:"config"` - Description string `json:"description"` - Name string `json:"name"` + Config domain.PaymentOptionConfig `json:"config"` + Description string `json:"description"` + Name string `json:"name"` } // PaymentOptions defines model for PaymentOptions. diff --git a/internal/api/payment.go b/internal/api/payment.go index 7b7a9e65c..3d9f36507 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -49,7 +49,7 @@ func (s *Server) CreatePaymentOption(ctx context.Context, request CreatePaymentO log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) return CreatePaymentOption400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil } - id, err := s.paymentService.CreatePaymentOption(ctx, issuerDID, request.Body.Name, request.Body.Description, request.Body.Config) + id, err := s.paymentService.CreatePaymentOption(ctx, issuerDID, request.Body.Name, request.Body.Description, &request.Body.Config) if err != nil { log.Error(ctx, "creating payment option", "err", err, "issuer", issuerDID, "request", request.Body) if errors.Is(err, repositories.ErrIdentityNotFound) { diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index b350f35dc..56d3e52fb 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" ) @@ -75,7 +76,7 @@ func TestServer_CreatePaymentOption(t *testing.T) { BJJ = "BJJ" ) - var config map[string]interface{} + var config domain.PaymentOptionConfig ctx := context.Background() server := newTestServer(t, nil) @@ -178,7 +179,7 @@ func TestServer_GetPaymentOption(t *testing.T) { BJJ = "BJJ" ) - var config map[string]interface{} + var config domain.PaymentOptionConfig ctx := context.Background() server := newTestServer(t, nil) @@ -192,14 +193,15 @@ func TestServer_GetPaymentOption(t *testing.T) { otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") require.NoError(t, err) - optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", config) - require.NoError(t, err) - require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) + optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", &config) + require.NoError(t, err) + type expected struct { httpCode int msg string + option domain.PaymentOption } for _, tc := range []struct { @@ -225,6 +227,13 @@ func TestServer_GetPaymentOption(t *testing.T) { optionID: optionID, expected: expected{ httpCode: http.StatusOK, + option: domain.PaymentOption{ + ID: optionID, + IssuerDID: *issuerDID, + Name: "1 POL Payment", + Description: "Payment Option explanation", + Config: &config, + }, }, }, { @@ -263,6 +272,10 @@ func TestServer_GetPaymentOption(t *testing.T) { var response GetPaymentOption200JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) assert.Equal(t, tc.optionID, response.Id) + assert.Equal(t, tc.expected.option.Name, response.Name) + assert.Equal(t, tc.expected.option.Description, response.Description) + assert.Equal(t, tc.expected.option.Config, &response.Config) + case http.StatusNotFound: var response GetPaymentOption404JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) @@ -280,7 +293,7 @@ func TestServer_GetPaymentOptions(t *testing.T) { BJJ = "BJJ" ) - var config map[string]interface{} + var config domain.PaymentOptionConfig ctx := context.Background() server := newTestServer(t, nil) @@ -297,7 +310,7 @@ func TestServer_GetPaymentOptions(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) for i := 0; i < 10; i++ { - _, err = server.Services.payments.CreatePaymentOption(ctx, issuerDID, fmt.Sprintf("Payment Option %d", i+1), "Payment Option explanation", config) + _, err = server.Services.payments.CreatePaymentOption(ctx, issuerDID, fmt.Sprintf("Payment Option %d", i+1), "Payment Option explanation", &config) require.NoError(t, err) } @@ -374,7 +387,7 @@ func TestServer_DeletePaymentOption(t *testing.T) { BJJ = "BJJ" ) - var config map[string]interface{} + var config domain.PaymentOptionConfig ctx := context.Background() server := newTestServer(t, nil) @@ -388,7 +401,7 @@ func TestServer_DeletePaymentOption(t *testing.T) { otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") require.NoError(t, err) - optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", config) + optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", &config) require.NoError(t, err) type expected struct { diff --git a/internal/api/responses.go b/internal/api/responses.go index 18de52bd5..e647e39c0 100644 --- a/internal/api/responses.go +++ b/internal/api/responses.go @@ -308,7 +308,7 @@ func toPaymentOption(opt *domain.PaymentOption) (PaymentOption, error) { IssuerID: opt.IssuerDID.String(), Name: opt.Name, Description: opt.Description, - Config: config, + Config: *opt.Config, CreatedAt: TimeUTC(opt.CreatedAt), ModifiedAt: TimeUTC(opt.UpdatedAt), }, nil diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 560a136b5..66d485eba 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -13,13 +13,13 @@ type PaymentOption struct { IssuerDID w3c.DID Name string Description string - Config any + Config *PaymentOptionConfig CreatedAt time.Time UpdatedAt time.Time } // NewPaymentOption creates a new PaymentOption -func NewPaymentOption(issuerDID w3c.DID, name string, description string, config any) *PaymentOption { +func NewPaymentOption(issuerDID w3c.DID, name string, description string, config *PaymentOptionConfig) *PaymentOption { return &PaymentOption{ ID: uuid.New(), IssuerDID: issuerDID, @@ -30,3 +30,27 @@ func NewPaymentOption(issuerDID w3c.DID, name string, description string, config UpdatedAt: time.Now(), } } + +// PaymentOptionConfig represents the configuration of a payment option +type PaymentOptionConfig struct { + Chains []PaymentOptionConfigChain `json:"Chains"` +} + +// PaymentOptionConfigChain represents the configuration of a payment option chain +type PaymentOptionConfigChain struct { + ChainId int `json:"ChainId"` + Recipient string `json:"Recipient"` + SigningKeyId string `json:"SigningKeyId"` + Iden3PaymentRailsRequestV1 struct { + Amount string `json:"Amount"` + Currency string `json:"Currency"` + } `json:"Iden3PaymentRailsRequestV1"` + Iden3PaymentRailsERC20RequestV1 struct { + USDT struct { + Amount string `json:"Amount"` + } `json:"USDT"` + USDC struct { + Amount string `json:"Amount"` + } `json:"USDC"` + } +} diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index de493eb0f..d509948af 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -84,7 +84,7 @@ type PaymentService interface { GetSettings() payments.Settings VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) - CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config any) (uuid.UUID, error) + CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config *domain.PaymentOptionConfig) (uuid.UUID, error) GetPaymentOptions(ctx context.Context, issuerDID *w3c.DID) ([]domain.PaymentOption, error) GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) DeletePaymentOption(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) error diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 852ca3458..fb2ac9ea5 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -45,7 +45,7 @@ func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Res } // CreatePaymentOption creates a payment option for a specific issuer -func (p *payment) CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config any) (uuid.UUID, error) { +func (p *payment) CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config *domain.PaymentOptionConfig) (uuid.UUID, error) { paymentOption := domain.NewPaymentOption(*issuerDID, name, description, config) id, err := p.paymentsStore.SavePaymentOption(ctx, paymentOption) if err != nil { diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go index 8e98dd073..41ea4e358 100644 --- a/internal/repositories/payment_test.go +++ b/internal/repositories/payment_test.go @@ -2,6 +2,7 @@ package repositories import ( "context" + "encoding/json" "fmt" "testing" "time" @@ -14,8 +15,43 @@ import ( "github.com/polygonid/sh-id-platform/internal/core/domain" ) +const paymentOptionTest = ` +{ + "Chains": [ + { + "ChainId": 137, + "Recipient": "0x..", + "SigningKeyId": "", + "Iden3PaymentRailsRequestV1": { + "Amount": "0.01", + "Currency": "POL" + }, + "Iden3PaymentRailsERC20RequestV1": { + "USDT": { + "Amount": "3" + }, + "USDC": { + "Amount": "3" + } + } + }, + { + "ChainId": 1101, + "Recipient": "0x..", + "SigningKeyId": "", + "Iden3PaymentRailsRequestV1": { + "Amount": "0.5", + "Currency": "ETH" + } + } + ] + } +` + func TestPayment_SavePaymentOption(t *testing.T) { + var paymentOptionConfig domain.PaymentOptionConfig ctx := context.Background() + fixture := NewFixture(storage) issuerID, err := w3c.ParseDID("did:iden3:privado:main:2Sh93vMXNar5fP5ifutHerL9bdUkocB464n3TG6BWV") require.NoError(t, err) @@ -24,20 +60,23 @@ func TestPayment_SavePaymentOption(t *testing.T) { fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) + require.NoError(t, json.Unmarshal([]byte(paymentOptionTest), &paymentOptionConfig)) + repo := NewPayment(*storage) t.Run("Save payment option", func(t *testing.T) { - id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", struct{}{})) + id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", &paymentOptionConfig)) assert.NoError(t, err) assert.NotEqual(t, uuid.Nil, id) }) t.Run("Save payment option linked to non existing issuer", func(t *testing.T) { - id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerDIDOther, "name 2", "description 2", struct{}{})) + id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerDIDOther, "name 2", "description 2", &paymentOptionConfig)) require.Error(t, err) assert.Equal(t, uuid.Nil, id) }) } func TestPayment_GetAllPaymentOptions(t *testing.T) { + var paymentOptionConfig domain.PaymentOptionConfig ctx := context.Background() fixture := NewFixture(storage) issuerID, err := w3c.ParseDID("did:iden3:privado:main:2SbDGSG2TTN1N1UuFaFq7EoFK3RY5wfcotuD8rDCn2") @@ -46,6 +85,8 @@ func TestPayment_GetAllPaymentOptions(t *testing.T) { issuerDIDOther, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qYQdd1yqFyrM9ZPqYTE4WHAQH2PX5Rjtj7YDYPppj") require.NoError(t, err) + require.NoError(t, json.Unmarshal([]byte(paymentOptionTest), &paymentOptionConfig)) + repo := NewPayment(*storage) ids := make([]uuid.UUID, 0) now := time.Now() @@ -55,7 +96,7 @@ func TestPayment_GetAllPaymentOptions(t *testing.T) { IssuerDID: *issuerID, Name: fmt.Sprintf("name %d", i), Description: fmt.Sprintf("description %d", i), - Config: struct{}{}, + Config: &paymentOptionConfig, CreatedAt: now, UpdatedAt: now, }) @@ -70,6 +111,9 @@ func TestPayment_GetAllPaymentOptions(t *testing.T) { assert.Len(t, opts, 10) for i, opt := range opts { assert.Equal(t, ids[i], opt.ID) + assert.Equal(t, fmt.Sprintf("name %d", len(opts)-i-1), opt.Name) + assert.Equal(t, fmt.Sprintf("description %d", len(opts)-i-1), opt.Description) + assert.Equal(t, paymentOptionConfig, *opt.Config) } }) t.Run("Get all payment options linked to non existing issuer", func(t *testing.T) { @@ -80,6 +124,7 @@ func TestPayment_GetAllPaymentOptions(t *testing.T) { } func TestPayment_GetPaymentOptionByID(t *testing.T) { + var paymentOptionConfig domain.PaymentOptionConfig ctx := context.Background() fixture := NewFixture(storage) issuerID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qWxoum8UEJzbUL1Ej9UWjGYHL8oL31BBLJ4ob8bmM") @@ -87,9 +132,11 @@ func TestPayment_GetPaymentOptionByID(t *testing.T) { issuerDIDOther, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qYQdd1yqFyrM9ZPqYTE4WHAQH2PX5Rjtj7YDYPppj") require.NoError(t, err) + require.NoError(t, json.Unmarshal([]byte(paymentOptionTest), &paymentOptionConfig)) + fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) repo := NewPayment(*storage) - id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", struct{}{})) + id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", &paymentOptionConfig)) assert.NoError(t, err) assert.NotEqual(t, uuid.Nil, id) @@ -97,6 +144,9 @@ func TestPayment_GetPaymentOptionByID(t *testing.T) { opt, err := repo.GetPaymentOptionByID(ctx, *issuerID, id) assert.NoError(t, err) assert.Equal(t, id, opt.ID) + assert.Equal(t, "name", opt.Name) + assert.Equal(t, "description", opt.Description) + assert.Equal(t, paymentOptionConfig, *opt.Config) }) t.Run("Get payment option linked to non existing issuer", func(t *testing.T) { opt, err := repo.GetPaymentOptionByID(ctx, *issuerDIDOther, id) @@ -113,7 +163,7 @@ func TestPayment_DeletePaymentOption(t *testing.T) { fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) repo := NewPayment(*storage) - id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", struct{}{})) + id, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", &domain.PaymentOptionConfig{})) assert.NoError(t, err) assert.NotEqual(t, uuid.Nil, id) From 40bae14ff89bdacd9ebfa1752483d6b5437a78c3 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Thu, 21 Nov 2024 18:30:12 +0100 Subject: [PATCH 17/66] wip: Payment request endpoint uses payment options --- api/api.yaml | 29 ++- internal/api/api.gen.go | 10 +- internal/api/payment.go | 18 +- internal/core/domain/payment.go | 38 ++-- internal/core/ports/payment_service.go | 10 +- internal/core/services/payment.go | 271 ++++++++++++++++--------- 6 files changed, 249 insertions(+), 127 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index e51ecb5b0..8e139054e 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -2277,20 +2277,31 @@ components: CreatePaymentRequest: type: object required: - - signingKey - - credentialType - - credentialContext + - option + - credentials - userDID properties: - signingKey: - type: string - credentialType: - type: string - credentialContext: + option: type: string + x-go-type: uuid.UUID + x-go-type-import: + name: uuid + path: github.com/google/uuid + credentials: + type: array + items: + type: object + required: [type, context] + properties: + type: + type: string + example: "AML" + context: + type: string + example: "" userDID: type: string - + example: "" TimeUTC: type: string diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 2a39aa8f7..e104da454 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -285,10 +285,12 @@ type CreateLinkRequest struct { // CreatePaymentRequest defines model for CreatePaymentRequest. type CreatePaymentRequest struct { - CredentialContext string `json:"credentialContext"` - CredentialType string `json:"credentialType"` - SigningKey string `json:"signingKey"` - UserDID string `json:"userDID"` + Credentials []struct { + Context string `json:"context"` + Type string `json:"type"` + } `json:"credentials"` + Option uuid.UUID `json:"option"` + UserDID string `json:"userDID"` } // Credential defines model for Credential. diff --git a/internal/api/payment.go b/internal/api/payment.go index 3d9f36507..5f7611de1 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -9,7 +9,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" + "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/log" "github.com/polygonid/sh-id-platform/internal/repositories" ) @@ -115,8 +117,22 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment log.Error(ctx, "parsing user did", "err", err, "did", request.Identifier) return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil } + if len(request.Body.Credentials) == 0 { + log.Error(ctx, "create payment request: empty credentials") + return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "empty credentials"}}, nil + } + + req := &ports.CreatePaymentRequestReq{ + IssuerDID: *issuerDID, + UserDID: *userDID, + OptionID: request.Body.Option, + } + req.Creds = make([]protocol.PaymentRequestInfoCredentials, 0, len(request.Body.Credentials)) + for i, cred := range request.Body.Credentials { + req.Creds[i] = protocol.PaymentRequestInfoCredentials{Type: cred.Type, Context: cred.Context} + } - paymentRequest, err := s.paymentService.CreatePaymentRequest(ctx, issuerDID, userDID, request.Body.SigningKey, request.Body.CredentialContext, request.Body.CredentialType) + paymentRequest, err := s.paymentService.CreatePaymentRequest(ctx, req) if err != nil { log.Error(ctx, "creating payment request", "err", err) return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "can't create payment-request"}}, nil diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 66d485eba..70893dd09 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -38,19 +38,27 @@ type PaymentOptionConfig struct { // PaymentOptionConfigChain represents the configuration of a payment option chain type PaymentOptionConfigChain struct { - ChainId int `json:"ChainId"` - Recipient string `json:"Recipient"` - SigningKeyId string `json:"SigningKeyId"` - Iden3PaymentRailsRequestV1 struct { - Amount string `json:"Amount"` - Currency string `json:"Currency"` - } `json:"Iden3PaymentRailsRequestV1"` - Iden3PaymentRailsERC20RequestV1 struct { - USDT struct { - Amount string `json:"Amount"` - } `json:"USDT"` - USDC struct { - Amount string `json:"Amount"` - } `json:"USDC"` - } + ChainId int `json:"ChainId"` + Recipient string `json:"Recipient"` + SigningKeyId string `json:"SigningKeyId"` + Iden3PaymentRailsRequestV1 *PaymentOptionConfigChainIden3PaymentRailsRequestV1 `json:"Iden3PaymentRailsRequestV1"` + Iden3PaymentRailsERC20RequestV1 *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 `json:"Iden3PaymentRailsERC20RequestV1"` +} + +// PaymentOptionConfigChainIden3PaymentRailsRequestV1 represents the configuration of a payment option rails +// TODO: Add validation for the amount and currency when creating it +type PaymentOptionConfigChainIden3PaymentRailsRequestV1 struct { + Amount string `json:"Amount"` + Currency string `json:"Currency"` +} + +// PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 represents the configuration of a rails ERC20 payment option +// TODO: Add validation for the amounts when creating it +type PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 struct { + USDT struct { + Amount string `json:"Amount"` + } `json:"USDT"` + USDC struct { + Amount string `json:"Amount"` + } `json:"USDC"` } diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index d509948af..5fddd3ad0 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -77,9 +77,17 @@ func NewAgentProposalRequest(basicMessage *comm.BasicMessage) (*protocol.Credent }, nil } +// CreatePaymentRequestReq is the request for PaymentService.CreatePaymentRequest +type CreatePaymentRequestReq struct { + IssuerDID w3c.DID + UserDID w3c.DID + OptionID uuid.UUID + Creds []protocol.PaymentRequestInfoCredentials +} + // PaymentService is the interface implemented by the payment service type PaymentService interface { - CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, senderDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) + CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq) (*protocol.PaymentRequestMessage, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) GetSettings() payments.Settings VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index fb2ac9ea5..d621ff3cf 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -2,23 +2,23 @@ package services import ( "context" - "crypto/ecdsa" "crypto/rand" "encoding/hex" "encoding/json" "fmt" "math/big" + "strconv" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" + "github.com/iden3/iden3comm/v2/packers" "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/core/domain" @@ -29,6 +29,11 @@ import ( "github.com/polygonid/sh-id-platform/internal/payments" ) +const ( + iden3PaymentRailsRequestV1Type = "Iden3PaymentRailsRequestV1" + iden3PaymentRailsERC20RequestV1Type = "Iden3PaymentRailsERC20RequestV1" +) + type payment struct { networkResolver network.Resolver settings payments.Settings @@ -86,50 +91,174 @@ func (p *payment) DeletePaymentOption(ctx context.Context, issuerDID *w3c.DID, i } // CreatePaymentRequest creates a payment request -func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, userDID *w3c.DID, signingKey string, credContext string, credType string) (*comm.BasicMessage, error) { - id := uuid.New().String() - basicMessage := comm.BasicMessage{ - From: issuerDID.String(), - To: userDID.String(), - Typ: "application/iden3comm-plain-json", - Type: "https://iden3-communication.io/credentials/0.1/payment-reques", - ID: id, - ThreadID: id, - } +func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePaymentRequestReq) (*protocol.PaymentRequestMessage, error) { + const defaultExpirationDate = 1 * time.Hour - //nolint:mnd - randomBigInt, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) + option, err := p.paymentsStore.GetPaymentOptionByID(ctx, req.IssuerDID, req.OptionID) if err != nil { + log.Error(ctx, "failed to get payment option", "err", err, "issuerDID", req.IssuerDID, "optionID", req.OptionID) return nil, err } - now := time.Now() - oneHourLater := now.Add(1 * time.Hour) + paymentsList := make([]protocol.PaymentRequestInfo, 0) + for _, chainConfig := range option.Config.Chains { + setting, found := p.settings[chainConfig.ChainId] + if !found { + log.Error(ctx, "chain not found in settings", "chainId", chainConfig.ChainId) + return nil, fmt.Errorf("chain not <%d> not found in payment settings", chainConfig.ChainId) + } + + //nolint: mnd + nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) + if err != nil { + return nil, err + } + expirationTime := time.Now().Add(defaultExpirationDate) + if chainConfig.Iden3PaymentRailsRequestV1 != nil { + data, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, nonce) + if err != nil { + log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) + return nil, err + } + + paymentsList = append(paymentsList, protocol.PaymentRequestInfo{ + Description: option.Description, + Credentials: req.Creds, + Data: *data, + }) + } + if chainConfig.Iden3PaymentRailsERC20RequestV1 != nil { + data, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce) + if err != nil { + log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) + return nil, err + } + + paymentsList = append(paymentsList, protocol.PaymentRequestInfo{ + Description: option.Description, + Credentials: req.Creds, + Data: *data, + }) + + } + + } - domain := protocol.Eip712Domain{ - Name: "MCPayment", - Version: "1.0.0", - ChainID: "80002", - VerifyingContract: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + msgID := uuid.New() + message := &protocol.PaymentRequestMessage{ + From: req.IssuerDID.String(), + To: req.UserDID.String(), + Typ: packers.MediaTypePlainMessage, + Type: protocol.PaymentRequestMessageType, + ID: msgID.String(), + ThreadID: msgID.String(), + Body: protocol.PaymentRequestMessageBody{ + Agent: "localhost", // TODO!!!, + Payments: paymentsList, + }, } - privateKeyBytes, err := hex.DecodeString(signingKey) + return message, nil +} + +// TODO: something +func (p *payment) newIden3PaymentRailsERC20RequestV1(ctx context.Context, chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int) (*protocol.PaymentRequestInfoData, error) { + data := protocol.NewPaymentRequestInfoDataRailsERC20(protocol.Iden3PaymentRailsERC20RequestV1{}) + return &data, nil +} + +func (p *payment) newIden3PaymentRailsRequestV1(ctx context.Context, chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int) (*protocol.PaymentRequestInfoData, error) { + metadata := "0x" + signature, address, err := p.paymentRequestSignature(chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, chainConfig.SigningKeyId) if err != nil { + log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err } - privateKey, err := crypto.ToECDSA(privateKeyBytes) + paymentInfo := protocol.NewPaymentRequestInfoDataRails(protocol.Iden3PaymentRailsRequestV1{ + Nonce: nonce.String(), + Type: iden3PaymentRailsRequestV1Type, + Context: protocol.NewPaymentContextStringCol([]string{ + "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", + "https://w3id.org/security/suites/eip712sig-2021/v1", + }), + Amount: chainConfig.Iden3PaymentRailsRequestV1.Amount, + ExpirationDate: fmt.Sprint(expirationTime.Unix()), + Metadata: metadata, + Currency: chainConfig.Iden3PaymentRailsRequestV1.Currency, + Recipient: address.String(), + Proof: protocol.NewPaymentProofEip712Signature([]protocol.EthereumEip712Signature2021{ + { + Type: "EthereumEip712Signature2021", + ProofPurpose: "assertionMethod", + ProofValue: hex.EncodeToString(signature), + VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", chainConfig.ChainId, address), + Created: time.Now().Format(time.RFC3339), + Eip712: protocol.Eip712Data{ + Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", + PrimaryType: string(protocol.Iden3PaymentRailsRequestV1Type), + Domain: protocol.Eip712Domain{ + Name: "MCPayment", + Version: "1.0.0", + ChainID: strconv.Itoa(chainConfig.ChainId), + VerifyingContract: setting.MCPayment, + }, + }, + }, + }), + }) + return &paymentInfo, nil +} + +func (p *payment) paymentRequestSignature(chainID int, verifContract string, amount string, expTime time.Time, nonce *big.Int, metadata string, signingKeyId string) ([]byte, common.Address, error) { + // TODO: Sign message with SigningKeyId. Get the "recipient"? address from private key + /* + signingKey := "0x" + option.Config.Chains[0].SigningKeyId + privateKeyBytes, err := hex.DecodeString(signingKey) + if err != nil { + return nil, common.Address{}, err + } + privateKey, err := crypto.ToECDSA(privateKeyBytes) + if err != nil { + return nil, common.Address{}, err + } + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, common.Address{}, errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + } + + address := crypto.PubkeyToAddress(*publicKeyECDSA) + */ + address := common.Address{} // Get from private key + typedData, err := typedDataForHashing(iden3PaymentRailsRequestV1Type, chainID, verifContract, address, amount, expTime, nonce, metadata) if err != nil { - return nil, err + return nil, common.Address{}, err } - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return nil, fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + return nil, common.Address{}, err + } + + // TODO: Sign the hash with the private key + /* + signature, err := crypto.Sign(typedDataHash[:], privateKey) + if err != nil { + return nil, common.Address{}, err + } + */ + _ = typedDataHash + signature := []byte{} // Sign with private key + return signature, address, nil +} + +func typedDataForHashing(paymentType string, chainID int, verifyContract string, address common.Address, amount string, expTime time.Time, nonce *big.Int, metadata string) (*apitypes.TypedData, error) { + if paymentType != iden3PaymentRailsRequestV1Type && paymentType != iden3PaymentRailsERC20RequestV1Type { + return nil, fmt.Errorf("unsupported payment type: %s", paymentType) } - address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() - typedData := apitypes.TypedData{ + + data := apitypes.TypedData{ Types: apitypes.Types{ "EIP712Domain": []apitypes.Type{ {Name: "name", Type: "string"}, @@ -137,7 +266,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, {Name: "chainId", Type: "uint256"}, {Name: "verifyingContract", Type: "address"}, }, - "Iden3PaymentRailsRequestV1": []apitypes.Type{ + paymentType: []apitypes.Type{ { Name: "recipient", Type: "address", @@ -164,77 +293,25 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, issuerDID *w3c.DID, Domain: apitypes.TypedDataDomain{ Name: "MCPayment", Version: "1.0.0", - ChainId: math.NewHexOrDecimal256(80002), // nolint:mnd - VerifyingContract: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", // 2. config + ChainId: math.NewHexOrDecimal256(int64(chainID)), + VerifyingContract: verifyContract, // 2. config }, Message: apitypes.TypedDataMessage{ - "recipient": address, // 3. derive from PK - "amount": "100", // 4. config per credential - "expirationDate": fmt.Sprint(oneHourLater.Unix()), - "nonce": randomBigInt.String(), - "metadata": "0x", + "recipient": address, + "amount": amount, + "expirationDate": fmt.Sprint(expTime.Unix()), + "nonce": nonce.String(), + "metadata": metadata, }, } - - typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - return nil, err - } - - signature, err := crypto.Sign(typedDataHash[:], privateKey) - if err != nil { - return nil, err + if paymentType == iden3PaymentRailsERC20RequestV1Type { + data.Types[paymentType] = append(data.Types[paymentType], apitypes.Type{ + Name: "tokenAddress", + Type: "address", + }) + data.Message["tokenAddress"] = "" // TODO: What is this? } - - nativePayments := protocol.Iden3PaymentRailsRequestV1{ - Nonce: randomBigInt.String(), - Type: "Iden3PaymentRailsRequestV1", - Context: protocol.NewPaymentContextStringCol([]string{ - "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", - "https://w3id.org/security/suites/eip712sig-2021/v1", - }), - Recipient: address, - Amount: "100", - ExpirationDate: fmt.Sprint(oneHourLater.Unix()), - Metadata: "0x", - Currency: "ETHWEI", - Proof: protocol.NewPaymentProofEip712Signature([]protocol.EthereumEip712Signature2021{ - { - Type: "EthereumEip712Signature2021", - ProofPurpose: "assertionMethod", - ProofValue: hex.EncodeToString(signature), - VerificationMethod: "did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a#blockchainAccountId", - Created: now.Format(time.RFC3339), - Eip712: protocol.Eip712Data{ - Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", - PrimaryType: "Iden3PaymentRailsRequestV1", - Domain: domain, - }, - }, - }), - } - - paymentRequestMessageBody := protocol.PaymentRequestMessageBody{ - Agent: "localhost", - Payments: []protocol.PaymentRequestInfo{ - { - Description: "Payment for credential", - Data: protocol.NewPaymentRequestInfoDataRails(nativePayments), - Credentials: []protocol.PaymentRequestInfoCredentials{ - { - Context: credContext, - Type: credType, - }, - }, - }, - }, - } - basicMessage.Body, err = json.Marshal(paymentRequestMessageBody) - if err != nil { - return nil, err - } - - return &basicMessage, nil + return &data, nil } // CreatePaymentRequestForProposalRequest creates a payment request for a proposal request From cb95423299ca944f7894254dab610a6070da44ac Mon Sep 17 00:00:00 2001 From: x1m3 Date: Fri, 22 Nov 2024 12:46:07 +0100 Subject: [PATCH 18/66] feat: Payload signed with kms using key --- api/api.yaml | 33 +----- cmd/platform/main.go | 2 +- internal/api/api.gen.go | 10 +- internal/api/main_test.go | 2 +- internal/api/payment.go | 2 +- internal/core/ports/payment_service.go | 20 +--- internal/core/services/identity.go | 36 +----- internal/core/services/payment.go | 147 ++++++++++++++++--------- internal/kms/vault_eth_key_provider.go | 39 +++++++ 9 files changed, 144 insertions(+), 147 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 8e139054e..27c3658f7 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -2142,8 +2142,8 @@ components: value: type: string - #Agent - AgentResponse: + + BasicMessage: type: object required: - body @@ -2169,6 +2169,9 @@ components: to: type: string + AgentResponse: + $ref: '#/components/schemas/BasicMessage' + PaymentSettings: type: object x-go-type: payments.Settings @@ -2248,32 +2251,6 @@ components: name: domain path: "github.com/polygonid/sh-id-platform/internal/core/domain" - BasicMessage: - type: object - required: - - body - - id - - typ - - type - - threadID - - from - - to - properties: - id: - type: string - typ: - type: string - type: - type: string - threadID: - type: string - body: - type: null - from: - type: string - to: - type: string - CreatePaymentRequest: type: object required: diff --git a/cmd/platform/main.go b/cmd/platform/main.go index 9acd09d53..05fb23231 100644 --- a/cmd/platform/main.go +++ b/cmd/platform/main.go @@ -159,7 +159,7 @@ func main() { proofService := services.NewProver(circuitsLoaderService) schemaService := services.NewSchema(schemaRepository, schemaLoader) linkService := services.NewLinkService(storage, claimsService, qrService, claimsRepository, linkRepository, schemaRepository, schemaLoader, sessionRepository, ps, identityService, *networkResolver, cfg.UniversalLinks) - paymentService := services.NewPaymentService(paymentsRepo, *networkResolver, *paymentSettings) + paymentService := services.NewPaymentService(paymentsRepo, *networkResolver, *paymentSettings, keyStore) transactionService, err := gateways.NewTransaction(*networkResolver) if err != nil { diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index e104da454..c9ff1c9b5 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -161,15 +161,7 @@ const ( ) // AgentResponse defines model for AgentResponse. -type AgentResponse struct { - Body interface{} `json:"body"` - From string `json:"from"` - Id string `json:"id"` - ThreadID string `json:"threadID"` - To string `json:"to"` - Typ string `json:"typ"` - Type string `json:"type"` -} +type AgentResponse = BasicMessage // AuthenticationConnection defines model for AuthenticationConnection. type AuthenticationConnection struct { diff --git a/internal/api/main_test.go b/internal/api/main_test.go index d4fa247de..76a31d3fe 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -308,7 +308,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { identityService := services.NewIdentity(keyStore, repos.identity, repos.idenMerkleTree, repos.identityState, mtService, qrService, repos.claims, repos.revocation, repos.connection, st, nil, repos.sessions, pubSub, *networkResolver, rhsFactory, revocationStatusResolver) connectionService := services.NewConnection(repos.connection, repos.claims, st) schemaService := services.NewSchema(repos.schemas, schemaLoader) - paymentService := services.NewPaymentService(repos.payments, *networkResolver, *paymentSettings) + paymentService := services.NewPaymentService(repos.payments, *networkResolver, *paymentSettings, keyStore) mediaTypeManager := services.NewMediaTypeManager( map[iden3comm.ProtocolMessage][]string{ diff --git a/internal/api/payment.go b/internal/api/payment.go index 5f7611de1..8d699ff6d 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -132,7 +132,7 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment req.Creds[i] = protocol.PaymentRequestInfoCredentials{Type: cred.Type, Context: cred.Context} } - paymentRequest, err := s.paymentService.CreatePaymentRequest(ctx, req) + paymentRequest, err := s.paymentService.CreatePaymentRequest(ctx, req, s.cfg.ServerUrl) if err != nil { log.Error(ctx, "creating payment request", "err", err) return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "can't create payment-request"}}, nil diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index 5fddd3ad0..246ff91ee 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -15,24 +15,6 @@ import ( "github.com/polygonid/sh-id-platform/internal/payments" ) -// Credential struct: TODO: This shouldn't be here with this name -type Credential struct { - Type string `json:"type"` - Context string `json:"context"` - // typss protocol.CredentialIssuanceRequestMessage -} - -// AgentProposalRequest struct -type AgentProposalRequest struct { - Body json.RawMessage - ThreadID string - IssuerDID *w3c.DID - UserDID *w3c.DID - Typ comm.MediaType - Type comm.ProtocolMessage - Credentials []Credential -} - // NewAgentProposalRequest validates the inputs and returns a new AgentRequest func NewAgentProposalRequest(basicMessage *comm.BasicMessage) (*protocol.CredentialsProposalRequestMessage, error) { if basicMessage.To == "" { @@ -87,7 +69,7 @@ type CreatePaymentRequestReq struct { // PaymentService is the interface implemented by the payment service type PaymentService interface { - CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq) (*protocol.PaymentRequestMessage, error) + CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq, baseURL string) (*protocol.PaymentRequestMessage, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) GetSettings() payments.Settings VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) diff --git a/internal/core/services/identity.go b/internal/core/services/identity.go index 53219d811..bec7f234e 100644 --- a/internal/core/services/identity.go +++ b/internal/core/services/identity.go @@ -3,7 +3,6 @@ package services import ( "bytes" "context" - "crypto/ecdsa" "encoding/hex" "encoding/json" "errors" @@ -852,7 +851,7 @@ func (i *identity) createIdentity(ctx context.Context, tx db.Querier, hostURL st } func (i *identity) createEthIdentityFromKeyID(ctx context.Context, mts *domain.IdentityMerkleTrees, key *kms.KeyID, didOptions *ports.DIDCreationOptions, tx db.Querier) (*domain.Identity, *w3c.DID, error) { - pubKey, err := ethPubKey(ctx, i.kms, *key) + pubKey, err := kms.EthPubKey(ctx, i.kms, *key) if err != nil { return nil, nil, err } @@ -1258,36 +1257,3 @@ func sanitizeIssuerDoc(issDoc []byte) []byte { str := strings.Replace(string(issDoc), "\\u0000", "", -1) return []byte(str) } - -// ethPubKey returns the public key from the key manager service. -// the public key is either uncompressed or compressed, so we need to handle both cases. -func ethPubKey(ctx context.Context, keyMS kms.KMSType, keyID kms.KeyID) (*ecdsa.PublicKey, error) { - const ( - uncompressedKeyLength = 65 - awsKeyLength = 88 - defaultKeyLength = 33 - ) - - keyBytes, err := keyMS.PublicKey(keyID) - if err != nil { - log.Error(ctx, "can't get bytes from public key", "err", err) - return nil, err - } - - // public key is uncompressed. It's 65 bytes long. - if len(keyBytes) == uncompressedKeyLength { - return crypto.UnmarshalPubkey(keyBytes) - } - - // public key is AWS format. It's 88 bytes long. - if len(keyBytes) == awsKeyLength { - return kms.DecodeAWSETHPubKey(ctx, keyBytes) - } - - // public key is compressed. It's 33 bytes long. - if len(keyBytes) == defaultKeyLength { - return kms.DecodeETHPubKey(keyBytes) - } - - return nil, errors.New("unsupported public key format") -} diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index d621ff3cf..af9db7729 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" @@ -24,28 +25,37 @@ import ( "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/eth" + "github.com/polygonid/sh-id-platform/internal/kms" "github.com/polygonid/sh-id-platform/internal/log" "github.com/polygonid/sh-id-platform/internal/network" "github.com/polygonid/sh-id-platform/internal/payments" ) const ( - iden3PaymentRailsRequestV1Type = "Iden3PaymentRailsRequestV1" - iden3PaymentRailsERC20RequestV1Type = "Iden3PaymentRailsERC20RequestV1" + iden3PaymentRailsRequestV1Type paymentRequestType = "Iden3PaymentRailsRequestV1" + iden3PaymentRailsERC20RequestV1Type paymentRequestType = "Iden3PaymentRailsERC20RequestV1" ) +type paymentRequestType string + +func (t paymentRequestType) Valid() bool { + return t == iden3PaymentRailsRequestV1Type || t == iden3PaymentRailsERC20RequestV1Type +} + type payment struct { networkResolver network.Resolver settings payments.Settings paymentsStore ports.PaymentRepository + kms kms.KMSType } // NewPaymentService creates a new payment service -func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, settings payments.Settings) ports.PaymentService { +func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, settings payments.Settings, kms kms.KMSType) ports.PaymentService { return &payment{ networkResolver: resolver, settings: settings, paymentsStore: payOptsRepo, + kms: kms, } } @@ -91,7 +101,7 @@ func (p *payment) DeletePaymentOption(ctx context.Context, issuerDID *w3c.DID, i } // CreatePaymentRequest creates a payment request -func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePaymentRequestReq) (*protocol.PaymentRequestMessage, error) { +func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePaymentRequestReq, baseURL string) (*protocol.PaymentRequestMessage, error) { const defaultExpirationDate = 1 * time.Hour option, err := p.paymentsStore.GetPaymentOptionByID(ctx, req.IssuerDID, req.OptionID) @@ -114,8 +124,16 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay return nil, err } expirationTime := time.Now().Add(defaultExpirationDate) + + pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyId, Type: kms.KeyTypeEthereum}) + if err != nil { + log.Error(ctx, "failed to get ethereum public key", "err", err, "keyId", chainConfig.SigningKeyId) + return nil, fmt.Errorf("cannot get ethAddr creaing payment request: %w", err) + } + address := crypto.PubkeyToAddress(*pubKey) + if chainConfig.Iden3PaymentRailsRequestV1 != nil { - data, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, nonce) + data, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, nonce, address) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err @@ -128,7 +146,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay }) } if chainConfig.Iden3PaymentRailsERC20RequestV1 != nil { - data, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce) + data, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err @@ -139,9 +157,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay Credentials: req.Creds, Data: *data, }) - } - } msgID := uuid.New() @@ -153,7 +169,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay ID: msgID.String(), ThreadID: msgID.String(), Body: protocol.PaymentRequestMessageBody{ - Agent: "localhost", // TODO!!!, + Agent: fmt.Sprintf(ports.AgentUrl, baseURL), Payments: paymentsList, }, } @@ -161,15 +177,54 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay return message, nil } -// TODO: something -func (p *payment) newIden3PaymentRailsERC20RequestV1(ctx context.Context, chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int) (*protocol.PaymentRequestInfoData, error) { - data := protocol.NewPaymentRequestInfoDataRailsERC20(protocol.Iden3PaymentRailsERC20RequestV1{}) - return &data, nil +// newIden3PaymentRailsERC20RequestV1 creates a new Iden3PaymentRailsERC20RequestV1 +// TODO: Check implementation correctness!!!! +func (p *payment) newIden3PaymentRailsERC20RequestV1(ctx context.Context, chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int, address common.Address) (*protocol.PaymentRequestInfoData, error) { + metadata := "0x" + signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId) + if err != nil { + log.Error(ctx, "failed to create payment request signature", "err", err) + return nil, err + } + + paymentInfo := protocol.NewPaymentRequestInfoDataRailsERC20(protocol.Iden3PaymentRailsERC20RequestV1{ + Nonce: nonce.String(), + Type: protocol.PaymentRequestType(iden3PaymentRailsERC20RequestV1Type), + Context: protocol.NewPaymentContextStringCol([]string{ + "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", + "https://w3id.org/security/suites/eip712sig-2021/v1", + }), + Amount: chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, + ExpirationDate: fmt.Sprint(expirationTime.Unix()), + Metadata: metadata, + Currency: "USDT", + Recipient: address.String(), + Proof: protocol.NewPaymentProofEip712Signature([]protocol.EthereumEip712Signature2021{ + { + Type: "EthereumEip712Signature2021", + ProofPurpose: "assertionMethod", + ProofValue: hex.EncodeToString(signature), + VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", chainConfig.ChainId, address), + Created: time.Now().Format(time.RFC3339), + Eip712: protocol.Eip712Data{ + Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", + PrimaryType: string(protocol.Iden3PaymentRailsERC20RequestV1Type), + Domain: protocol.Eip712Domain{ + Name: "MCPayment", + Version: "1.0.0", + ChainID: strconv.Itoa(chainConfig.ChainId), + VerifyingContract: setting.MCPayment, + }, + }, + }, + }), + }) + return &paymentInfo, nil } -func (p *payment) newIden3PaymentRailsRequestV1(ctx context.Context, chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int) (*protocol.PaymentRequestInfoData, error) { +func (p *payment) newIden3PaymentRailsRequestV1(ctx context.Context, chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int, address common.Address) (*protocol.PaymentRequestInfoData, error) { metadata := "0x" - signature, address, err := p.paymentRequestSignature(chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, chainConfig.SigningKeyId) + signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId) if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err @@ -177,7 +232,7 @@ func (p *payment) newIden3PaymentRailsRequestV1(ctx context.Context, chainConfig paymentInfo := protocol.NewPaymentRequestInfoDataRails(protocol.Iden3PaymentRailsRequestV1{ Nonce: nonce.String(), - Type: iden3PaymentRailsRequestV1Type, + Type: protocol.PaymentRequestType(iden3PaymentRailsRequestV1Type), Context: protocol.NewPaymentContextStringCol([]string{ "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", @@ -210,51 +265,37 @@ func (p *payment) newIden3PaymentRailsRequestV1(ctx context.Context, chainConfig return &paymentInfo, nil } -func (p *payment) paymentRequestSignature(chainID int, verifContract string, amount string, expTime time.Time, nonce *big.Int, metadata string, signingKeyId string) ([]byte, common.Address, error) { - // TODO: Sign message with SigningKeyId. Get the "recipient"? address from private key - /* - signingKey := "0x" + option.Config.Chains[0].SigningKeyId - privateKeyBytes, err := hex.DecodeString(signingKey) - if err != nil { - return nil, common.Address{}, err - } - privateKey, err := crypto.ToECDSA(privateKeyBytes) - if err != nil { - return nil, common.Address{}, err - } - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return nil, common.Address{}, errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey") - } +func (p *payment) paymentRequestSignature(ctx context.Context, paymentType paymentRequestType, chainID int, verifContract string, amount string, expTime time.Time, nonce *big.Int, metadata string, address common.Address, signingKeyId string) ([]byte, error) { + if !paymentType.Valid() { + return nil, fmt.Errorf("unsupported payment type: %s", paymentType) + } + + keyID := kms.KeyID{ + Type: kms.KeyTypeEthereum, + ID: signingKeyId, + } - address := crypto.PubkeyToAddress(*publicKeyECDSA) - */ - address := common.Address{} // Get from private key - typedData, err := typedDataForHashing(iden3PaymentRailsRequestV1Type, chainID, verifContract, address, amount, expTime, nonce, metadata) + typedData, err := typedDataForHashing(paymentType, chainID, verifContract, address, amount, expTime, nonce, metadata) if err != nil { - return nil, common.Address{}, err + return nil, err } typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) if err != nil { - return nil, common.Address{}, err + return nil, err } - // TODO: Sign the hash with the private key - /* - signature, err := crypto.Sign(typedDataHash[:], privateKey) - if err != nil { - return nil, common.Address{}, err - } - */ - _ = typedDataHash - signature := []byte{} // Sign with private key - return signature, address, nil + signature, err := p.kms.Sign(ctx, keyID, typedDataHash[:]) + if err != nil { + log.Error(ctx, "failed to sign typed data hash", "err", err, "keyId", signingKeyId) + return nil, err + } + + return signature, nil } -func typedDataForHashing(paymentType string, chainID int, verifyContract string, address common.Address, amount string, expTime time.Time, nonce *big.Int, metadata string) (*apitypes.TypedData, error) { - if paymentType != iden3PaymentRailsRequestV1Type && paymentType != iden3PaymentRailsERC20RequestV1Type { +func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyContract string, address common.Address, amount string, expTime time.Time, nonce *big.Int, metadata string) (*apitypes.TypedData, error) { + if !paymentType.Valid() { return nil, fmt.Errorf("unsupported payment type: %s", paymentType) } @@ -266,7 +307,7 @@ func typedDataForHashing(paymentType string, chainID int, verifyContract string, {Name: "chainId", Type: "uint256"}, {Name: "verifyingContract", Type: "address"}, }, - paymentType: []apitypes.Type{ + string(paymentType): []apitypes.Type{ { Name: "recipient", Type: "address", @@ -305,7 +346,7 @@ func typedDataForHashing(paymentType string, chainID int, verifyContract string, }, } if paymentType == iden3PaymentRailsERC20RequestV1Type { - data.Types[paymentType] = append(data.Types[paymentType], apitypes.Type{ + data.Types[string(paymentType)] = append(data.Types[string(paymentType)], apitypes.Type{ Name: "tokenAddress", Type: "address", }) diff --git a/internal/kms/vault_eth_key_provider.go b/internal/kms/vault_eth_key_provider.go index aedc61abc..2e8b876a4 100644 --- a/internal/kms/vault_eth_key_provider.go +++ b/internal/kms/vault_eth_key_provider.go @@ -10,6 +10,8 @@ import ( "github.com/hashicorp/vault/api" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/pkg/errors" + + "github.com/polygonid/sh-id-platform/internal/log" ) type vaultETHKeyProvider struct { @@ -203,3 +205,40 @@ func DecodeETHPubKey(key []byte) (*ecdsa.PublicKey, error) { pubKey, err := crypto.DecompressPubkey(key) return pubKey, errors.WithStack(err) } + +// EthPubKey returns the ethereum public key from the key manager service. +// the public key is either uncompressed or compressed, so we need to handle both cases. +func EthPubKey(ctx context.Context, keyMS KMSType, keyID KeyID) (*ecdsa.PublicKey, error) { + const ( + uncompressedKeyLength = 65 + awsKeyLength = 88 + defaultKeyLength = 33 + ) + + if keyID.Type != KeyTypeEthereum { + return nil, errors.New("key type is not ethereum") + } + + keyBytes, err := keyMS.PublicKey(keyID) + if err != nil { + log.Error(ctx, "can't get bytes from public key", "err", err) + return nil, err + } + + // public key is uncompressed. It's 65 bytes long. + if len(keyBytes) == uncompressedKeyLength { + return crypto.UnmarshalPubkey(keyBytes) + } + + // public key is AWS format. It's 88 bytes long. + if len(keyBytes) == awsKeyLength { + return DecodeAWSETHPubKey(ctx, keyBytes) + } + + // public key is compressed. It's 33 bytes long. + if len(keyBytes) == defaultKeyLength { + return DecodeETHPubKey(keyBytes) + } + + return nil, errors.New("unsupported public key format") +} From 726ba7a2bdee7715da1ef39d9afeabea669cae62 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 25 Nov 2024 09:18:50 +0100 Subject: [PATCH 19/66] fix: Remove not used status response --- api/api.yaml | 2 -- internal/api/api.gen.go | 9 --------- 2 files changed, 11 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 27c3658f7..7e0a03ec1 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1320,8 +1320,6 @@ paths: $ref: '#/components/responses/400' '401': $ref: '#/components/responses/401' - '422': - $ref: '#/components/responses/422' '500': $ref: '#/components/responses/500' diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index c9ff1c9b5..8f5dbccb3 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -4563,15 +4563,6 @@ func (response CreatePaymentRequest401JSONResponse) VisitCreatePaymentRequestRes return json.NewEncoder(w).Encode(response) } -type CreatePaymentRequest422JSONResponse struct{ N422JSONResponse } - -func (response CreatePaymentRequest422JSONResponse) VisitCreatePaymentRequestResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(422) - - return json.NewEncoder(w).Encode(response) -} - type CreatePaymentRequest500JSONResponse struct{ N500JSONResponse } func (response CreatePaymentRequest500JSONResponse) VisitCreatePaymentRequestResponse(w http.ResponseWriter) error { From 1602ac913e37a47ed873c592208d939a58f8399c Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 25 Nov 2024 11:31:12 +0100 Subject: [PATCH 20/66] feat: One paymentRequest object for each stable coin --- internal/core/domain/payment.go | 2 - internal/core/services/payment.go | 244 +++++++++++++++++------------- internal/payments/currency.go | 13 ++ 3 files changed, 154 insertions(+), 105 deletions(-) create mode 100644 internal/payments/currency.go diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 70893dd09..3d40438c9 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -46,14 +46,12 @@ type PaymentOptionConfigChain struct { } // PaymentOptionConfigChainIden3PaymentRailsRequestV1 represents the configuration of a payment option rails -// TODO: Add validation for the amount and currency when creating it type PaymentOptionConfigChainIden3PaymentRailsRequestV1 struct { Amount string `json:"Amount"` Currency string `json:"Currency"` } // PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 represents the configuration of a rails ERC20 payment option -// TODO: Add validation for the amounts when creating it type PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 struct { USDT struct { Amount string `json:"Amount"` diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index af9db7729..9e070009d 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -146,17 +146,29 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay }) } if chainConfig.Iden3PaymentRailsERC20RequestV1 != nil { - data, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address) + reqUSDT, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, payments.USDT, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, setting.ERC20.USDT.ContractAddress) + if err != nil { + log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) + return nil, err + } + reqUSDC, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, payments.USDC, chainConfig.Iden3PaymentRailsERC20RequestV1.USDC.Amount, setting.ERC20.USDC.ContractAddress) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err } - paymentsList = append(paymentsList, protocol.PaymentRequestInfo{ - Description: option.Description, - Credentials: req.Creds, - Data: *data, - }) + paymentsList = append(paymentsList, + protocol.PaymentRequestInfo{ + Description: option.Description, + Credentials: req.Creds, + Data: *reqUSDT, + }, + protocol.PaymentRequestInfo{ + Description: option.Description, + Credentials: req.Creds, + Data: *reqUSDC, + }, + ) } } @@ -177,27 +189,108 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay return message, nil } -// newIden3PaymentRailsERC20RequestV1 creates a new Iden3PaymentRailsERC20RequestV1 -// TODO: Check implementation correctness!!!! -func (p *payment) newIden3PaymentRailsERC20RequestV1(ctx context.Context, chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int, address common.Address) (*protocol.PaymentRequestInfoData, error) { +// CreatePaymentRequestForProposalRequest creates a payment request for a proposal request +func (p *payment) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) { + basicMessage := comm.BasicMessage{ + From: proposalRequest.To, + To: proposalRequest.From, + ThreadID: proposalRequest.ThreadID, + ID: proposalRequest.ID, + Typ: proposalRequest.Typ, + } + return &basicMessage, nil +} + +// GetSettings returns the current payment settings +func (p *payment) GetSettings() payments.Settings { + return p.settings +} + +func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) { + var paymentRequest protocol.PaymentRequestMessageBody + err := json.Unmarshal(message.Body, &paymentRequest) + if err != nil { + return false, err + } + + client, err := ethclient.Dial("https://polygon-amoy.g.alchemy.com/v2/DHvucvBBzrBhaHzmjrMp24PGbl7vwee6") + if err != nil { + return false, err + } + + contractAddress := common.HexToAddress("0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774") + instance, err := eth.NewPaymentContract(contractAddress, client) + if err != nil { + return false, err + } + + // nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, base10) + nonce, err := nonceFromPaymentRequestInfoData(paymentRequest.Payments[0].Data) + if err != nil { + log.Error(ctx, "failed to get nonce from payment request info data", "err", err) + return false, err + } + isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: context.Background()}, recipient, nonce) + if err != nil { + return false, err + } + return isPaid, nil +} + +func nonceFromPaymentRequestInfoData(data protocol.PaymentRequestInfoData) (*big.Int, error) { + const base10 = 10 + var nonce string + switch data.Type() { + case protocol.Iden3PaymentRequestCryptoV1Type: + nonce = "" + case protocol.Iden3PaymentRailsRequestV1Type: + t, ok := data.Data().(protocol.Iden3PaymentRailsRequestV1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") + } + nonce = t.Nonce + case protocol.Iden3PaymentRailsERC20RequestV1Type: + t, ok := data.Data().(protocol.Iden3PaymentRailsERC20RequestV1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") + } + nonce = t.Nonce + default: + return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) + } + bigIntNonce, ok := new(big.Int).SetString(nonce, base10) + if !ok { + return nil, fmt.Errorf("failed to parse nonce creating big int: %s", nonce) + } + return bigIntNonce, nil +} + +func (p *payment) newIden3PaymentRailsRequestV1( + ctx context.Context, + chainConfig domain.PaymentOptionConfigChain, + setting payments.ChainSettings, + expirationTime time.Time, + nonce *big.Int, + address common.Address) (*protocol.PaymentRequestInfoData, error) { + metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId) + signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, "") if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err } - paymentInfo := protocol.NewPaymentRequestInfoDataRailsERC20(protocol.Iden3PaymentRailsERC20RequestV1{ + paymentInfo := protocol.NewPaymentRequestInfoDataRails(protocol.Iden3PaymentRailsRequestV1{ Nonce: nonce.String(), - Type: protocol.PaymentRequestType(iden3PaymentRailsERC20RequestV1Type), + Type: protocol.PaymentRequestType(iden3PaymentRailsRequestV1Type), Context: protocol.NewPaymentContextStringCol([]string{ "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", }), - Amount: chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, + Amount: chainConfig.Iden3PaymentRailsRequestV1.Amount, ExpirationDate: fmt.Sprint(expirationTime.Unix()), Metadata: metadata, - Currency: "USDT", + Currency: chainConfig.Iden3PaymentRailsRequestV1.Currency, Recipient: address.String(), Proof: protocol.NewPaymentProofEip712Signature([]protocol.EthereumEip712Signature2021{ { @@ -208,7 +301,7 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1(ctx context.Context, chainC Created: time.Now().Format(time.RFC3339), Eip712: protocol.Eip712Data{ Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", - PrimaryType: string(protocol.Iden3PaymentRailsERC20RequestV1Type), + PrimaryType: string(protocol.Iden3PaymentRailsRequestV1Type), Domain: protocol.Eip712Domain{ Name: "MCPayment", Version: "1.0.0", @@ -222,25 +315,35 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1(ctx context.Context, chainC return &paymentInfo, nil } -func (p *payment) newIden3PaymentRailsRequestV1(ctx context.Context, chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int, address common.Address) (*protocol.PaymentRequestInfoData, error) { +// newIden3PaymentRailsERC20RequestV1 creates a new Iden3PaymentRailsERC20RequestV1 +func (p *payment) newIden3PaymentRailsERC20RequestV1( + ctx context.Context, + chainConfig domain.PaymentOptionConfigChain, + setting payments.ChainSettings, + expirationTime time.Time, + nonce *big.Int, + address common.Address, + currency payments.Coin, + amount string, tokenAddress string) (*protocol.PaymentRequestInfoData, error) { + metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId) + signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, tokenAddress) if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err } - paymentInfo := protocol.NewPaymentRequestInfoDataRails(protocol.Iden3PaymentRailsRequestV1{ + paymentInfo := protocol.NewPaymentRequestInfoDataRailsERC20(protocol.Iden3PaymentRailsERC20RequestV1{ Nonce: nonce.String(), - Type: protocol.PaymentRequestType(iden3PaymentRailsRequestV1Type), + Type: protocol.PaymentRequestType(iden3PaymentRailsERC20RequestV1Type), Context: protocol.NewPaymentContextStringCol([]string{ "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", }), - Amount: chainConfig.Iden3PaymentRailsRequestV1.Amount, + Amount: amount, ExpirationDate: fmt.Sprint(expirationTime.Unix()), Metadata: metadata, - Currency: chainConfig.Iden3PaymentRailsRequestV1.Currency, + Currency: string(currency), Recipient: address.String(), Proof: protocol.NewPaymentProofEip712Signature([]protocol.EthereumEip712Signature2021{ { @@ -251,7 +354,7 @@ func (p *payment) newIden3PaymentRailsRequestV1(ctx context.Context, chainConfig Created: time.Now().Format(time.RFC3339), Eip712: protocol.Eip712Data{ Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", - PrimaryType: string(protocol.Iden3PaymentRailsRequestV1Type), + PrimaryType: string(protocol.Iden3PaymentRailsERC20RequestV1Type), Domain: protocol.Eip712Domain{ Name: "MCPayment", Version: "1.0.0", @@ -265,7 +368,18 @@ func (p *payment) newIden3PaymentRailsRequestV1(ctx context.Context, chainConfig return &paymentInfo, nil } -func (p *payment) paymentRequestSignature(ctx context.Context, paymentType paymentRequestType, chainID int, verifContract string, amount string, expTime time.Time, nonce *big.Int, metadata string, address common.Address, signingKeyId string) ([]byte, error) { +func (p *payment) paymentRequestSignature( + ctx context.Context, + paymentType paymentRequestType, + chainID int, + verifContract string, + amount string, + expTime time.Time, + nonce *big.Int, + metadata string, + addr common.Address, + signingKeyId string, + tokenAddr string) ([]byte, error) { if !paymentType.Valid() { return nil, fmt.Errorf("unsupported payment type: %s", paymentType) } @@ -275,7 +389,7 @@ func (p *payment) paymentRequestSignature(ctx context.Context, paymentType payme ID: signingKeyId, } - typedData, err := typedDataForHashing(paymentType, chainID, verifContract, address, amount, expTime, nonce, metadata) + typedData, err := typedDataForHashing(paymentType, chainID, verifContract, addr, amount, expTime, nonce, metadata, tokenAddr) if err != nil { return nil, err } @@ -294,7 +408,7 @@ func (p *payment) paymentRequestSignature(ctx context.Context, paymentType payme return signature, nil } -func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyContract string, address common.Address, amount string, expTime time.Time, nonce *big.Int, metadata string) (*apitypes.TypedData, error) { +func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyContract string, address common.Address, amount string, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { if !paymentType.Valid() { return nil, fmt.Errorf("unsupported payment type: %s", paymentType) } @@ -335,7 +449,7 @@ func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyCont Name: "MCPayment", Version: "1.0.0", ChainId: math.NewHexOrDecimal256(int64(chainID)), - VerifyingContract: verifyContract, // 2. config + VerifyingContract: verifyContract, }, Message: apitypes.TypedDataMessage{ "recipient": address, @@ -350,83 +464,7 @@ func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyCont Name: "tokenAddress", Type: "address", }) - data.Message["tokenAddress"] = "" // TODO: What is this? + data.Message["tokenAddress"] = tokenAddress } return &data, nil } - -// CreatePaymentRequestForProposalRequest creates a payment request for a proposal request -func (p *payment) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) { - basicMessage := comm.BasicMessage{ - From: proposalRequest.To, - To: proposalRequest.From, - ThreadID: proposalRequest.ThreadID, - ID: proposalRequest.ID, - Typ: proposalRequest.Typ, - } - return &basicMessage, nil -} - -// GetSettings returns the current payment settings -func (p *payment) GetSettings() payments.Settings { - return p.settings -} - -func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) { - var paymentRequest protocol.PaymentRequestMessageBody - err := json.Unmarshal(message.Body, &paymentRequest) - if err != nil { - return false, err - } - - client, err := ethclient.Dial("https://polygon-amoy.g.alchemy.com/v2/DHvucvBBzrBhaHzmjrMp24PGbl7vwee6") - if err != nil { - return false, err - } - - contractAddress := common.HexToAddress("0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774") - instance, err := eth.NewPaymentContract(contractAddress, client) - if err != nil { - return false, err - } - - // nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, base10) - nonce, err := nonceFromPaymentRequestInfoData(paymentRequest.Payments[0].Data) - if err != nil { - log.Error(ctx, "failed to get nonce from payment request info data", "err", err) - return false, err - } - isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: context.Background()}, recipient, nonce) - if err != nil { - return false, err - } - return isPaid, nil -} - -func nonceFromPaymentRequestInfoData(data protocol.PaymentRequestInfoData) (*big.Int, error) { - const base10 = 10 - var nonce string - switch data.Type() { - case protocol.Iden3PaymentRequestCryptoV1Type: - nonce = "" - case protocol.Iden3PaymentRailsRequestV1Type: - t, ok := data.Data().(protocol.Iden3PaymentRailsRequestV1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") - } - nonce = t.Nonce - case protocol.Iden3PaymentRailsERC20RequestV1Type: - t, ok := data.Data().(protocol.Iden3PaymentRailsERC20RequestV1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") - } - nonce = t.Nonce - default: - return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) - } - bigIntNonce, ok := new(big.Int).SetString(nonce, base10) - if !ok { - return nil, fmt.Errorf("failed to parse nonce creating big int: %s", nonce) - } - return bigIntNonce, nil -} diff --git a/internal/payments/currency.go b/internal/payments/currency.go new file mode 100644 index 000000000..799c153f3 --- /dev/null +++ b/internal/payments/currency.go @@ -0,0 +1,13 @@ +package payments + +const ( + USDT Coin = "USDT" + USDC Coin = "USDC" +) + +// Coin represents a coin +type Coin string + +func (c Coin) IsStableCoin() bool { + return c == USDT || c == USDC +} From 6d8471e997bcc02ac5bb6e33e2e05b4fcc1a073f Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 25 Nov 2024 11:35:50 +0100 Subject: [PATCH 21/66] fix: Remove unwanted commented code --- internal/network/resolver.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/network/resolver.go b/internal/network/resolver.go index ba548ebfd..2f12e4337 100644 --- a/internal/network/resolver.go +++ b/internal/network/resolver.go @@ -54,7 +54,6 @@ type Resolver struct { supportedContracts map[string]*abi.State stateResolvers map[string]pubsignals.StateResolver supportedNetworks []SupportedNetworks - // paymentSettings map[resolverPrefix]PaymentSettings } // SupportedNetworks holds the chain and networks supoprted From 35268d8a90a1e2da7b4617cc866c044bff8cd92f Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 25 Nov 2024 17:00:38 +0100 Subject: [PATCH 22/66] fix: request parsing in CreatePaymentRequest chore: Some tests WIP --- internal/api/payment.go | 8 +-- internal/api/payment_test.go | 135 +++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 4 deletions(-) diff --git a/internal/api/payment.go b/internal/api/payment.go index 8d699ff6d..a0e7de63d 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -114,8 +114,8 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment userDID, err := w3c.ParseDID(request.Body.UserDID) if err != nil { - log.Error(ctx, "parsing user did", "err", err, "did", request.Identifier) - return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + log.Error(ctx, "parsing user did", "err", err, "did", request.Body.UserDID) + return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "invalid userDID"}}, nil } if len(request.Body.Credentials) == 0 { log.Error(ctx, "create payment request: empty credentials") @@ -127,7 +127,7 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment UserDID: *userDID, OptionID: request.Body.Option, } - req.Creds = make([]protocol.PaymentRequestInfoCredentials, 0, len(request.Body.Credentials)) + req.Creds = make([]protocol.PaymentRequestInfoCredentials, len(request.Body.Credentials)) for i, cred := range request.Body.Credentials { req.Creds[i] = protocol.PaymentRequestInfoCredentials{Type: cred.Type, Context: cred.Context} } @@ -135,7 +135,7 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment paymentRequest, err := s.paymentService.CreatePaymentRequest(ctx, req, s.cfg.ServerUrl) if err != nil { log.Error(ctx, "creating payment request", "err", err) - return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "can't create payment-request"}}, nil + return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("can't create payment-request: %s", err)}}, nil } basicMessage := BasicMessage{ diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 56d3e52fb..64edcd7aa 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -483,3 +483,138 @@ func TestServer_DeletePaymentOption(t *testing.T) { }) } } + +func TestServer_CreatePaymentRequest(t *testing.T) { + const ( + method = "polygonid" + blockchain = "polygon" + network = "amoy" + BJJ = "BJJ" + ) + ctx := context.Background() + server := newTestServer(t, nil) + handler := getHandler(ctx, server) + + iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) + require.NoError(t, err) + issuerDID, err := w3c.ParseDID(iden.Identifier) + require.NoError(t, err) + + otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") + require.NoError(t, err) + + var config domain.PaymentOptionConfig + require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) + + paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) + require.NoError(t, err) + + _ = otherDID + + type expected struct { + httpCode int + msg string + count int + } + + for _, tc := range []struct { + name string + issuerDID w3c.DID + auth func() (string, string) + body CreatePaymentRequestJSONRequestBody + expected expected + }{ + { + name: "no auth header", + auth: authWrong, + issuerDID: *issuerDID, + expected: expected{ + httpCode: http.StatusUnauthorized, + }, + }, + { + name: "Empty body", + auth: authOk, + issuerDID: *issuerDID, + body: CreatePaymentRequestJSONRequestBody{}, + expected: expected{ + httpCode: http.StatusBadRequest, + msg: "invalid userDID", + }, + }, + { + name: "Not existing payment option", + auth: authOk, + issuerDID: *issuerDID, + body: CreatePaymentRequestJSONRequestBody{ + UserDID: otherDID.String(), + Option: uuid.New(), + Credentials: []struct { + Context string `json:"context"` + Type string `json:"type"` + }{ + { + Context: "context", + Type: "type", + }, + }, + }, + expected: expected{ + httpCode: http.StatusBadRequest, + msg: "can't create payment-request: payment option not found", + }, + }, + + { + name: "Happy Path", + auth: authOk, + issuerDID: *issuerDID, + body: CreatePaymentRequestJSONRequestBody{ + UserDID: otherDID.String(), + Option: paymentOptionID, + Credentials: []struct { + Context string `json:"context"` + Type string `json:"type"` + }{ + { + Context: "context", + Type: "type", + }, + }, + }, + expected: expected{ + httpCode: http.StatusOK, + count: 10, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + rr := httptest.NewRecorder() + payload, err := json.Marshal(tc.body) + require.NoError(t, err) + url := fmt.Sprintf("/v2/identities/%s/payment-request", tc.issuerDID.String()) + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payload)) + assert.NoError(t, err) + req.SetBasicAuth(tc.auth()) + + handler.ServeHTTP(rr, req) + require.Equal(t, tc.expected.httpCode, rr.Code) + + switch tc.expected.httpCode { + case http.StatusOK: + var response CreatePaymentRequest201JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + case http.StatusBadRequest: + var response CreatePaymentRequest400JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + case http.StatusInternalServerError: + var response CreatePaymentRequest500JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + } + + }) + } + +} From a5b2acd3f79cc58f4501226b5efe16dddec7aff4 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 26 Nov 2024 13:41:32 +0100 Subject: [PATCH 23/66] fix: Amounts management. Other tests. --- internal/api/payment_test.go | 72 +++++++++++++++++++++++----- internal/core/domain/payment.go | 79 +++++++++++++++++++++++++++++-- internal/core/services/payment.go | 34 +++++++------ internal/eth/util.go | 19 ++++++++ internal/payments/currency.go | 5 +- 5 files changed, 175 insertions(+), 34 deletions(-) create mode 100644 internal/eth/util.go diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 64edcd7aa..66b9da855 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -11,11 +11,13 @@ import ( "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/iden3comm/v2/protocol" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" + "github.com/polygonid/sh-id-platform/internal/kms" ) const paymentOptionConfigurationTesting = ` @@ -24,7 +26,7 @@ const paymentOptionConfigurationTesting = ` { "ChainId": 137, "Recipient": "0x..", - "SigningKeyId": "", + "SigningKeyId": "testSigninKey1", "Iden3PaymentRailsRequestV1": { "Amount": "0.01", "Currency": "POL" @@ -41,7 +43,7 @@ const paymentOptionConfigurationTesting = ` { "ChainId": 1101, "Recipient": "0x..", - "SigningKeyId": "", + "SigningKeyId": "testSigninKey2", "Iden3PaymentRailsRequestV1": { "Amount": "0.5", "Currency": "ETH" @@ -500,16 +502,53 @@ func TestServer_CreatePaymentRequest(t *testing.T) { issuerDID, err := w3c.ParseDID(iden.Identifier) require.NoError(t, err) - otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") + receiverDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") require.NoError(t, err) - var config domain.PaymentOptionConfig - require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) + _ = receiverDID - paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) + // Creating an ethereum key + signingKeyID, err := keyStore.CreateKey(kms.KeyTypeEthereum, issuerDID) require.NoError(t, err) - _ = otherDID + // Creating a payment config using previously created key + config := domain.PaymentOptionConfig{ + Chains: []domain.PaymentOptionConfigChain{ + { + ChainId: 1101, + Recipient: "0x..", + SigningKeyId: signingKeyID.ID, + Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ + Amount: 0.5, + Currency: "ETH", + }, + Iden3PaymentRailsERC20RequestV1: nil, + }, + { + ChainId: 137, + Recipient: "0x..", + SigningKeyId: signingKeyID.ID, + Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ + Amount: 0.01, + Currency: "POL", + }, + Iden3PaymentRailsERC20RequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1{ + USDT: struct { + Amount float64 `json:"Amount"` + }{ + Amount: 5.2, + }, + USDC: struct { + Amount float64 `json:"Amount"` + }{ + Amount: 4.3, + }, + }, + }, + }, + } + paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) + require.NoError(t, err) type expected struct { httpCode int @@ -547,7 +586,7 @@ func TestServer_CreatePaymentRequest(t *testing.T) { auth: authOk, issuerDID: *issuerDID, body: CreatePaymentRequestJSONRequestBody{ - UserDID: otherDID.String(), + UserDID: receiverDID.String(), Option: uuid.New(), Credentials: []struct { Context string `json:"context"` @@ -570,7 +609,7 @@ func TestServer_CreatePaymentRequest(t *testing.T) { auth: authOk, issuerDID: *issuerDID, body: CreatePaymentRequestJSONRequestBody{ - UserDID: otherDID.String(), + UserDID: receiverDID.String(), Option: paymentOptionID, Credentials: []struct { Context string `json:"context"` @@ -583,7 +622,7 @@ func TestServer_CreatePaymentRequest(t *testing.T) { }, }, expected: expected{ - httpCode: http.StatusOK, + httpCode: http.StatusCreated, count: 10, }, }, @@ -601,9 +640,18 @@ func TestServer_CreatePaymentRequest(t *testing.T) { require.Equal(t, tc.expected.httpCode, rr.Code) switch tc.expected.httpCode { - case http.StatusOK: + case http.StatusCreated: var response CreatePaymentRequest201JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + raw, err := json.Marshal(response) + require.NoError(t, err) + + var requestMessage protocol.PaymentRequestMessage + require.NoError(t, json.Unmarshal(raw, &requestMessage)) + assert.Equal(t, issuerDID.String(), requestMessage.From) + assert.Equal(t, receiverDID.String(), requestMessage.To) + assert.Len(t, requestMessage.Body.Payments, 4) + case http.StatusBadRequest: var response CreatePaymentRequest400JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) @@ -613,8 +661,6 @@ func TestServer_CreatePaymentRequest(t *testing.T) { require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) assert.Equal(t, tc.expected.msg, response.Message) } - }) } - } diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 3d40438c9..2e4bebcf5 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -1,6 +1,9 @@ package domain import ( + "encoding/json" + "errors" + "strconv" "time" "github.com/google/uuid" @@ -47,16 +50,84 @@ type PaymentOptionConfigChain struct { // PaymentOptionConfigChainIden3PaymentRailsRequestV1 represents the configuration of a payment option rails type PaymentOptionConfigChainIden3PaymentRailsRequestV1 struct { - Amount string `json:"Amount"` - Currency string `json:"Currency"` + Amount float64 `json:"Amount"` + Currency string `json:"Currency"` +} + +// UnmarshalJSON unmarshals a PaymentOptionConfigChainIden3PaymentRailsRequestV1 +// so we can accept amounts with string or float64 types +func (p *PaymentOptionConfigChainIden3PaymentRailsRequestV1) UnmarshalJSON(data []byte) error { + var temp struct { + Amount any `json:"Amount"` + Currency string `json:"Currency"` + } + err := json.Unmarshal(data, &temp) + if err != nil { + return err + } + + switch temp.Amount.(type) { + case float64: + p.Amount = temp.Amount.(float64) + case string: + amountFloat, err := strconv.ParseFloat(temp.Amount.(string), 64) + if err != nil { + return err + } + p.Amount = amountFloat + default: + return errors.New("unexpected format of amount") + } + + p.Currency = temp.Currency + + return nil } // PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 represents the configuration of a rails ERC20 payment option type PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 struct { USDT struct { - Amount string `json:"Amount"` + Amount float64 `json:"Amount"` } `json:"USDT"` USDC struct { - Amount string `json:"Amount"` + Amount float64 `json:"Amount"` } `json:"USDC"` } + +// UnmarshalJSON unmarshals a PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 +// so we can accept amounts with string or float64 types +func (p *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1) UnmarshalJSON(data []byte) error { + var temp struct { + USDT struct { + Amount any `json:"Amount"` + } + USDC struct { + Amount any `json:"Amount"` + } + } + err := json.Unmarshal(data, &temp) + if err != nil { + return err + } + switch temp.USDT.Amount.(type) { + case float64: + p.USDT.Amount = temp.USDT.Amount.(float64) + case string: + amountFloat, err := strconv.ParseFloat(temp.USDT.Amount.(string), 64) + if err != nil { + return err + } + p.USDT.Amount = amountFloat + } + switch temp.USDC.Amount.(type) { + case float64: + p.USDC.Amount = temp.USDC.Amount.(float64) + case string: + amountFloat, err := strconv.ParseFloat(temp.USDC.Amount.(string), 64) + if err != nil { + return err + } + p.USDC.Amount = amountFloat + } + return nil +} diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 9e070009d..6ff9c2198 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -127,8 +127,8 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyId, Type: kms.KeyTypeEthereum}) if err != nil { - log.Error(ctx, "failed to get ethereum public key", "err", err, "keyId", chainConfig.SigningKeyId) - return nil, fmt.Errorf("cannot get ethAddr creaing payment request: %w", err) + log.Error(ctx, "failed to get kms signing key", "err", err, "keyId", chainConfig.SigningKeyId) + return nil, fmt.Errorf("kms signing key not found: %w", err) } address := crypto.PubkeyToAddress(*pubKey) @@ -140,6 +140,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay } paymentsList = append(paymentsList, protocol.PaymentRequestInfo{ + Type: string(iden3PaymentRailsRequestV1Type), Description: option.Description, Credentials: req.Creds, Data: *data, @@ -159,11 +160,13 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay paymentsList = append(paymentsList, protocol.PaymentRequestInfo{ + Type: string(iden3PaymentRailsERC20RequestV1Type), Description: option.Description, Credentials: req.Creds, Data: *reqUSDT, }, protocol.PaymentRequestInfo{ + Type: string(iden3PaymentRailsERC20RequestV1Type), Description: option.Description, Credentials: req.Creds, Data: *reqUSDC, @@ -224,7 +227,6 @@ func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, m return false, err } - // nonce, _ := new(big.Int).SetString(paymentRequest.Payments[0].Nonce, base10) nonce, err := nonceFromPaymentRequestInfoData(paymentRequest.Payments[0].Data) if err != nil { log.Error(ctx, "failed to get nonce from payment request info data", "err", err) @@ -271,8 +273,8 @@ func (p *payment) newIden3PaymentRailsRequestV1( setting payments.ChainSettings, expirationTime time.Time, nonce *big.Int, - address common.Address) (*protocol.PaymentRequestInfoData, error) { - + address common.Address, +) (*protocol.PaymentRequestInfoData, error) { metadata := "0x" signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, "") if err != nil { @@ -287,7 +289,7 @@ func (p *payment) newIden3PaymentRailsRequestV1( "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", }), - Amount: chainConfig.Iden3PaymentRailsRequestV1.Amount, + Amount: fmt.Sprintf("%f", chainConfig.Iden3PaymentRailsRequestV1.Amount), ExpirationDate: fmt.Sprint(expirationTime.Unix()), Metadata: metadata, Currency: chainConfig.Iden3PaymentRailsRequestV1.Currency, @@ -324,8 +326,9 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( nonce *big.Int, address common.Address, currency payments.Coin, - amount string, tokenAddress string) (*protocol.PaymentRequestInfoData, error) { - + amount float64, + tokenAddress string, +) (*protocol.PaymentRequestInfoData, error) { metadata := "0x" signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, tokenAddress) if err != nil { @@ -340,7 +343,7 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", }), - Amount: amount, + Amount: fmt.Sprintf("%f", amount), ExpirationDate: fmt.Sprint(expirationTime.Unix()), Metadata: metadata, Currency: string(currency), @@ -373,13 +376,14 @@ func (p *payment) paymentRequestSignature( paymentType paymentRequestType, chainID int, verifContract string, - amount string, + amount float64, expTime time.Time, nonce *big.Int, metadata string, addr common.Address, signingKeyId string, - tokenAddr string) ([]byte, error) { + tokenAddr string, +) ([]byte, error) { if !paymentType.Valid() { return nil, fmt.Errorf("unsupported payment type: %s", paymentType) } @@ -408,7 +412,7 @@ func (p *payment) paymentRequestSignature( return signature, nil } -func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyContract string, address common.Address, amount string, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { +func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyContract string, address common.Address, amount float64, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { if !paymentType.Valid() { return nil, fmt.Errorf("unsupported payment type: %s", paymentType) } @@ -444,7 +448,7 @@ func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyCont }, }, }, - PrimaryType: "Iden3PaymentRailsRequestV1", + PrimaryType: string(paymentType), Domain: apitypes.TypedDataDomain{ Name: "MCPayment", Version: "1.0.0", @@ -452,8 +456,8 @@ func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyCont VerifyingContract: verifyContract, }, Message: apitypes.TypedDataMessage{ - "recipient": address, - "amount": amount, + "recipient": address.String(), + "amount": eth.ToWei(amount), "expirationDate": fmt.Sprint(expTime.Unix()), "nonce": nonce.String(), "metadata": metadata, diff --git a/internal/eth/util.go b/internal/eth/util.go new file mode 100644 index 000000000..18ae3ccc3 --- /dev/null +++ b/internal/eth/util.go @@ -0,0 +1,19 @@ +package eth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) + +// ToWei converts an ETH amount (float64) tg wei (*big.Int). +func ToWei(amount float64) *big.Int { + ethValue := new(big.Float).SetFloat64(amount) + weiMultiplier := new(big.Float).SetInt64(params.Ether) + weiValue := new(big.Float).Mul(ethValue, weiMultiplier) + + weiInt := new(big.Int) + weiValue.Int(weiInt) + + return weiInt +} diff --git a/internal/payments/currency.go b/internal/payments/currency.go index 799c153f3..077099c11 100644 --- a/internal/payments/currency.go +++ b/internal/payments/currency.go @@ -1,13 +1,14 @@ package payments const ( - USDT Coin = "USDT" - USDC Coin = "USDC" + USDT Coin = "USDT" // USDT Coin + USDC Coin = "USDC" // USDC Coin ) // Coin represents a coin type Coin string +// IsStableCoin returns true if the coin is a stable coin func (c Coin) IsStableCoin() bool { return c == USDT || c == USDC } From 11bb1625e390b847fbc2feb2a7a184daacf6c88f Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 26 Nov 2024 13:42:59 +0100 Subject: [PATCH 24/66] chore: lint --- internal/core/domain/payment.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 2e4bebcf5..e786a7e9f 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -68,7 +68,7 @@ func (p *PaymentOptionConfigChainIden3PaymentRailsRequestV1) UnmarshalJSON(data switch temp.Amount.(type) { case float64: - p.Amount = temp.Amount.(float64) + p.Amount = temp.Amount.(float64) // nolint: forcetypeassert case string: amountFloat, err := strconv.ParseFloat(temp.Amount.(string), 64) if err != nil { @@ -111,7 +111,7 @@ func (p *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1) UnmarshalJSON( } switch temp.USDT.Amount.(type) { case float64: - p.USDT.Amount = temp.USDT.Amount.(float64) + p.USDT.Amount = temp.USDT.Amount.(float64) // nolint: forcetypeassert case string: amountFloat, err := strconv.ParseFloat(temp.USDT.Amount.(string), 64) if err != nil { @@ -121,7 +121,7 @@ func (p *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1) UnmarshalJSON( } switch temp.USDC.Amount.(type) { case float64: - p.USDC.Amount = temp.USDC.Amount.(float64) + p.USDC.Amount = temp.USDC.Amount.(float64) // nolint: forcetypeassert case string: amountFloat, err := strconv.ParseFloat(temp.USDC.Amount.(string), 64) if err != nil { From 3f4231bee6ebd8729448c30f806ae787a7526db4 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 27 Nov 2024 10:15:01 +0100 Subject: [PATCH 25/66] feat: If present, use signin key in payment option config --- internal/api/payment_test.go | 66 +++++++++++++++++++++++++++++++ internal/core/domain/payment.go | 13 ++++-- internal/core/services/payment.go | 65 ++++++++++++++++++++++-------- 3 files changed, 124 insertions(+), 20 deletions(-) diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 66b9da855..83bcb81ae 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -12,6 +12,7 @@ import ( "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -547,9 +548,52 @@ func TestServer_CreatePaymentRequest(t *testing.T) { }, }, } + + // Payment option with private key inside payment for temporary solution + configUnsecure := domain.PaymentOptionConfig{ + Chains: []domain.PaymentOptionConfigChain{ + { + ChainId: 1101, + Recipient: "0x..", + SigningKeyId: signingKeyID.ID, + SigningKeyOpt: common.ToPointer("d1f13a3d23b8a3c5b76fbd62c29e86a3b1cd0e3ef564d11a5e55aab544ce33a4"), + Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ + Amount: 0.5, + Currency: "ETH", + }, + Iden3PaymentRailsERC20RequestV1: nil, + }, + { + ChainId: 137, + Recipient: "0x..", + SigningKeyId: signingKeyID.ID, + SigningKeyOpt: common.ToPointer("d1f13a3d23b8a3c5b76fbd62c29e86a3b1cd0e3ef564d11a5e55aab544ce33a4"), + Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ + Amount: 0.01, + Currency: "POL", + }, + Iden3PaymentRailsERC20RequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1{ + USDT: struct { + Amount float64 `json:"Amount"` + }{ + Amount: 5.2, + }, + USDC: struct { + Amount float64 `json:"Amount"` + }{ + Amount: 4.3, + }, + }, + }, + }, + } + paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) require.NoError(t, err) + paymentOptionIDUnsecure, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &configUnsecure) + require.NoError(t, err) + type expected struct { httpCode int msg string @@ -626,6 +670,28 @@ func TestServer_CreatePaymentRequest(t *testing.T) { count: 10, }, }, + { + name: "Happy Path with with unsecure payment option including private key", + auth: authOk, + issuerDID: *issuerDID, + body: CreatePaymentRequestJSONRequestBody{ + UserDID: receiverDID.String(), + Option: paymentOptionIDUnsecure, + Credentials: []struct { + Context string `json:"context"` + Type string `json:"type"` + }{ + { + Context: "context", + Type: "type", + }, + }, + }, + expected: expected{ + httpCode: http.StatusCreated, + count: 10, + }, + }, } { t.Run(tc.name, func(t *testing.T) { rr := httptest.NewRecorder() diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index e786a7e9f..eb1519c69 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -41,9 +41,16 @@ type PaymentOptionConfig struct { // PaymentOptionConfigChain represents the configuration of a payment option chain type PaymentOptionConfigChain struct { - ChainId int `json:"ChainId"` - Recipient string `json:"Recipient"` - SigningKeyId string `json:"SigningKeyId"` + ChainId int `json:"ChainId"` + Recipient string `json:"Recipient"` + SigningKeyId string `json:"SigningKeyId"` + + // TODO: remove field SigningKeyOpt once we have key management. + // Temporary solution until we have key management + // WARNING: Only for testing purposes. This is not secure as SigningKey will be stored in the database + // USE AT YOUR OWN RISK + SigningKeyOpt *string `json:"SigningKeyOpt"` + Iden3PaymentRailsRequestV1 *PaymentOptionConfigChainIden3PaymentRailsRequestV1 `json:"Iden3PaymentRailsRequestV1"` Iden3PaymentRailsERC20RequestV1 *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 `json:"Iden3PaymentRailsERC20RequestV1"` } diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 6ff9c2198..05e2b27f0 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -2,6 +2,7 @@ package services import ( "context" + "crypto/ecdsa" "crypto/rand" "encoding/hex" "encoding/json" @@ -125,15 +126,34 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay } expirationTime := time.Now().Add(defaultExpirationDate) - pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyId, Type: kms.KeyTypeEthereum}) - if err != nil { - log.Error(ctx, "failed to get kms signing key", "err", err, "keyId", chainConfig.SigningKeyId) - return nil, fmt.Errorf("kms signing key not found: %w", err) + var address common.Address + var privateKey *ecdsa.PrivateKey + if chainConfig.SigningKeyOpt == nil { + pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyId, Type: kms.KeyTypeEthereum}) + if err != nil { + log.Error(ctx, "failed to get kms signing key", "err", err, "keyId", chainConfig.SigningKeyId) + return nil, fmt.Errorf("kms signing key not found: %w", err) + } + address = crypto.PubkeyToAddress(*pubKey) + } else { // Temporary solution until we have key management. We use SigningKeyOpt as a private key if present + privateKeyBytes, err := hex.DecodeString(*chainConfig.SigningKeyOpt) + if err != nil { + return nil, err + } + privateKey, err = crypto.ToECDSA(privateKeyBytes) + if err != nil { + return nil, err + } + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + } + address = crypto.PubkeyToAddress(*publicKeyECDSA) } - address := crypto.PubkeyToAddress(*pubKey) if chainConfig.Iden3PaymentRailsRequestV1 != nil { - data, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, nonce, address) + data, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, privateKey) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err @@ -147,12 +167,12 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay }) } if chainConfig.Iden3PaymentRailsERC20RequestV1 != nil { - reqUSDT, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, payments.USDT, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, setting.ERC20.USDT.ContractAddress) + reqUSDT, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, payments.USDT, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, setting.ERC20.USDT.ContractAddress, privateKey) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err } - reqUSDC, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, payments.USDC, chainConfig.Iden3PaymentRailsERC20RequestV1.USDC.Amount, setting.ERC20.USDC.ContractAddress) + reqUSDC, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, payments.USDC, chainConfig.Iden3PaymentRailsERC20RequestV1.USDC.Amount, setting.ERC20.USDC.ContractAddress, privateKey) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err @@ -193,7 +213,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay } // CreatePaymentRequestForProposalRequest creates a payment request for a proposal request -func (p *payment) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) { +func (p *payment) CreatePaymentRequestForProposalRequest(_ context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) { basicMessage := comm.BasicMessage{ From: proposalRequest.To, To: proposalRequest.From, @@ -274,9 +294,10 @@ func (p *payment) newIden3PaymentRailsRequestV1( expirationTime time.Time, nonce *big.Int, address common.Address, + signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management ) (*protocol.PaymentRequestInfoData, error) { metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, "") + signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, "") if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err @@ -328,9 +349,10 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( currency payments.Coin, amount float64, tokenAddress string, + signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management ) (*protocol.PaymentRequestInfoData, error) { metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, tokenAddress) + signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, tokenAddress) if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err @@ -382,6 +404,7 @@ func (p *payment) paymentRequestSignature( metadata string, addr common.Address, signingKeyId string, + signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management tokenAddr string, ) ([]byte, error) { if !paymentType.Valid() { @@ -403,13 +426,21 @@ func (p *payment) paymentRequestSignature( return nil, err } - signature, err := p.kms.Sign(ctx, keyID, typedDataHash[:]) - if err != nil { - log.Error(ctx, "failed to sign typed data hash", "err", err, "keyId", signingKeyId) - return nil, err - } + if signingKeyOpt == nil { + signature, err := p.kms.Sign(ctx, keyID, typedDataHash[:]) + if err != nil { + log.Error(ctx, "failed to sign typed data hash", "err", err, "keyId", signingKeyId) + return nil, err + } - return signature, nil + return signature, nil + } else { // Temporary solution until we have key management. We use SigningKeyOpt as a private key if present + signature, err := crypto.Sign(typedDataHash[:], signingKeyOpt) + if err != nil { + return nil, err + } + return signature, nil + } } func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyContract string, address common.Address, amount float64, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { From 06ce29ac5da5fd257e9edae7ad9d5df5ee1bb519 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 27 Nov 2024 11:15:18 +0100 Subject: [PATCH 26/66] fix: linter --- internal/api/payment_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 83bcb81ae..918e818ae 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -12,10 +12,10 @@ import ( "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/iden3comm/v2/protocol" - "github.com/polygonid/sh-id-platform/internal/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/polygonid/sh-id-platform/internal/common" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/kms" From e3bd7bb826550dc389c27fbde7d3fe46182a1d36 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Thu, 28 Nov 2024 10:30:29 +0100 Subject: [PATCH 27/66] chore: verifyPayment endpoint now accepts and optionid and returns object with status --- api/api.yaml | 38 +++++++----- internal/api/api.gen.go | 64 ++++++++++++-------- internal/api/payment.go | 27 ++------- internal/core/ports/payment_service.go | 3 +- internal/core/ports/payments_repository.go | 2 +- internal/core/services/payment.go | 68 +++++++++++++++++----- internal/repositories/payment.go | 12 ++-- internal/repositories/payment_test.go | 8 +-- 8 files changed, 139 insertions(+), 83 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 7e0a03ec1..0cd7dc7c8 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1443,7 +1443,7 @@ paths: $ref: '#/components/responses/500' - /v2/payment/verify/{recipient}: + /v2/payment/verify/{paymentOptionID}: post: summary: Verify Payment operationId: VerifyPayment @@ -1453,20 +1453,25 @@ paths: security: - basicAuth: [ ] parameters: - - $ref: '#/components/parameters/pathRecipient' + - name: paymentOptionID + schema: + type: string + x-go-type: uuid.UUID + required: true + in: path requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/BasicMessage' + $ref: '#/components/schemas/PaymentMessage' responses: '200': description: Payment verified content: application/json: schema: - type: boolean + $ref: '#/components/schemas/PaymentStatus' '400': $ref: '#/components/responses/400' '401': @@ -2140,6 +2145,12 @@ components: value: type: string + PaymentMessage: + type: object + x-go-type: protocol.PaymentMessage + x-go-type-import: + name: protocol + path: "github.com/iden3/iden3comm/v2/protocol" BasicMessage: type: object @@ -2191,6 +2202,15 @@ components: items: $ref: '#/components/schemas/PaymentOption' + PaymentStatus: + type: object + required: + - status + properties: + status: + type: string + enum: [ success, failed, pending, canceled ] + PaymentOption: type: object required: @@ -2266,7 +2286,7 @@ components: type: array items: type: object - required: [type, context] + required: [ type, context ] properties: type: type: string @@ -2580,14 +2600,6 @@ components: description: Issuer identifier schema: type: string - - pathRecipient: - name: recipient - in: path - required: true - description: Payment recipient address - schema: - type: string pathClaim: name: id diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 8f5dbccb3..6064e5fbf 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -85,6 +85,14 @@ const ( LinkStatusInactive LinkStatus = "inactive" ) +// Defines values for PaymentStatusStatus. +const ( + PaymentStatusStatusCanceled PaymentStatusStatus = "canceled" + PaymentStatusStatusFailed PaymentStatusStatus = "failed" + PaymentStatusStatusPending PaymentStatusStatus = "pending" + PaymentStatusStatusSuccess PaymentStatusStatus = "success" +) + // Defines values for RefreshServiceType. const ( Iden3RefreshService2023 RefreshServiceType = "Iden3RefreshService2023" @@ -92,10 +100,10 @@ const ( // Defines values for StateTransactionStatus. const ( - Created StateTransactionStatus = "created" - Failed StateTransactionStatus = "failed" - Pending StateTransactionStatus = "pending" - Published StateTransactionStatus = "published" + StateTransactionStatusCreated StateTransactionStatus = "created" + StateTransactionStatusFailed StateTransactionStatus = "failed" + StateTransactionStatusPending StateTransactionStatus = "pending" + StateTransactionStatusPublished StateTransactionStatus = "published" ) // Defines values for GetConnectionsParamsSort. @@ -465,6 +473,9 @@ type PaginatedMetadata struct { Total uint `json:"total"` } +// PaymentMessage defines model for PaymentMessage. +type PaymentMessage = protocol.PaymentMessage + // PaymentOption defines model for PaymentOption. type PaymentOption struct { Config domain.PaymentOptionConfig `json:"config"` @@ -495,6 +506,14 @@ type PaymentOptionsPaginated struct { // PaymentSettings defines model for PaymentSettings. type PaymentSettings = payments.Settings +// PaymentStatus defines model for PaymentStatus. +type PaymentStatus struct { + Status PaymentStatusStatus `json:"status"` +} + +// PaymentStatusStatus defines model for PaymentStatus.Status. +type PaymentStatusStatus string + // PublishIdentityStateResponse defines model for PublishIdentityStateResponse. type PublishIdentityStateResponse struct { ClaimsTreeRoot *string `json:"claimsTreeRoot,omitempty"` @@ -607,9 +626,6 @@ type PathIdentifier = string // PathNonce defines model for pathNonce. type PathNonce = int64 -// PathRecipient defines model for pathRecipient. -type PathRecipient = string - // SessionID defines model for sessionID. type SessionID = uuid.UUID @@ -841,7 +857,7 @@ type CreatePaymentOptionJSONRequestBody = PaymentOptionRequest type ImportSchemaJSONRequestBody = ImportSchemaRequest // VerifyPaymentJSONRequestBody defines body for VerifyPayment for application/json ContentType. -type VerifyPaymentJSONRequestBody = BasicMessage +type VerifyPaymentJSONRequestBody = PaymentMessage // ServerInterface represents all server handlers. type ServerInterface interface { @@ -975,8 +991,8 @@ type ServerInterface interface { // (GET /v2/payment/settings) GetPaymentSettings(w http.ResponseWriter, r *http.Request) // Verify Payment - // (POST /v2/payment/verify/{recipient}) - VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) + // (POST /v2/payment/verify/{paymentOptionID}) + VerifyPayment(w http.ResponseWriter, r *http.Request, paymentOptionID uuid.UUID) // Get QrCode from store // (GET /v2/qr-store) GetQrFromStore(w http.ResponseWriter, r *http.Request, params GetQrFromStoreParams) @@ -1251,8 +1267,8 @@ func (_ Unimplemented) GetPaymentSettings(w http.ResponseWriter, r *http.Request } // Verify Payment -// (POST /v2/payment/verify/{recipient}) -func (_ Unimplemented) VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) { +// (POST /v2/payment/verify/{paymentOptionID}) +func (_ Unimplemented) VerifyPayment(w http.ResponseWriter, r *http.Request, paymentOptionID uuid.UUID) { w.WriteHeader(http.StatusNotImplemented) } @@ -2876,12 +2892,12 @@ func (siw *ServerInterfaceWrapper) VerifyPayment(w http.ResponseWriter, r *http. var err error - // ------------- Path parameter "recipient" ------------- - var recipient PathRecipient + // ------------- Path parameter "paymentOptionID" ------------- + var paymentOptionID uuid.UUID - err = runtime.BindStyledParameterWithOptions("simple", "recipient", chi.URLParam(r, "recipient"), &recipient, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithOptions("simple", "paymentOptionID", chi.URLParam(r, "paymentOptionID"), &paymentOptionID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "recipient", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "paymentOptionID", Err: err}) return } @@ -2892,7 +2908,7 @@ func (siw *ServerInterfaceWrapper) VerifyPayment(w http.ResponseWriter, r *http. r = r.WithContext(ctx) handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.VerifyPayment(w, r, recipient) + siw.Handler.VerifyPayment(w, r, paymentOptionID) })) for _, middleware := range siw.HandlerMiddlewares { @@ -3236,7 +3252,7 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Get(options.BaseURL+"/v2/payment/settings", wrapper.GetPaymentSettings) }) r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/v2/payment/verify/{recipient}", wrapper.VerifyPayment) + r.Post(options.BaseURL+"/v2/payment/verify/{paymentOptionID}", wrapper.VerifyPayment) }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/qr-store", wrapper.GetQrFromStore) @@ -5035,15 +5051,15 @@ func (response GetPaymentSettings200JSONResponse) VisitGetPaymentSettingsRespons } type VerifyPaymentRequestObject struct { - Recipient PathRecipient `json:"recipient"` - Body *VerifyPaymentJSONRequestBody + PaymentOptionID uuid.UUID `json:"paymentOptionID"` + Body *VerifyPaymentJSONRequestBody } type VerifyPaymentResponseObject interface { VisitVerifyPaymentResponse(w http.ResponseWriter) error } -type VerifyPayment200JSONResponse bool +type VerifyPayment200JSONResponse PaymentStatus func (response VerifyPayment200JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") @@ -5352,7 +5368,7 @@ type StrictServerInterface interface { // (GET /v2/payment/settings) GetPaymentSettings(ctx context.Context, request GetPaymentSettingsRequestObject) (GetPaymentSettingsResponseObject, error) // Verify Payment - // (POST /v2/payment/verify/{recipient}) + // (POST /v2/payment/verify/{paymentOptionID}) VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) // Get QrCode from store // (GET /v2/qr-store) @@ -6621,10 +6637,10 @@ func (sh *strictHandler) GetPaymentSettings(w http.ResponseWriter, r *http.Reque } // VerifyPayment operation middleware -func (sh *strictHandler) VerifyPayment(w http.ResponseWriter, r *http.Request, recipient PathRecipient) { +func (sh *strictHandler) VerifyPayment(w http.ResponseWriter, r *http.Request, paymentOptionID uuid.UUID) { var request VerifyPaymentRequestObject - request.Recipient = recipient + request.PaymentOptionID = paymentOptionID var body VerifyPaymentJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&body); err != nil { diff --git a/internal/api/payment.go b/internal/api/payment.go index a0e7de63d..be7531b86 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -2,13 +2,10 @@ package api import ( "context" - "encoding/json" "errors" "fmt" - "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" - comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/core/ports" @@ -157,27 +154,13 @@ func (s *Server) GetPaymentSettings(_ context.Context, _ GetPaymentSettingsReque // VerifyPayment is the controller to verify payment func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) { - bodyBytes, err := json.Marshal(request.Body.Body) - if err != nil { - log.Error(ctx, "marshaling request body", "err", err) - return VerifyPayment400JSONResponse{N400JSONResponse{Message: "can't parse payment"}}, nil - } - basicMessage := comm.BasicMessage{ - From: request.Body.From, - To: request.Body.To, - ThreadID: request.Body.ThreadID, - ID: request.Body.Id, - Typ: comm.MediaType(request.Body.Typ), - Type: comm.ProtocolMessage(""), - Body: bodyBytes, - } - - recipient := common.HexToAddress(request.Recipient) - - isPaid, err := s.paymentService.VerifyPayment(ctx, recipient, basicMessage) + isPaid, err := s.paymentService.VerifyPayment(ctx, request.PaymentOptionID, request.Body) if err != nil { log.Error(ctx, "can't process payment message", "err", err) return VerifyPayment400JSONResponse{N400JSONResponse{Message: "can't process payment message"}}, nil } - return VerifyPayment200JSONResponse(isPaid), nil + if !isPaid { + return VerifyPayment200JSONResponse{Status: PaymentStatusStatusPending}, nil + } + return VerifyPayment200JSONResponse{Status: PaymentStatusStatusSuccess}, nil } diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index 246ff91ee..ded160d5c 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" - "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" @@ -72,7 +71,7 @@ type PaymentService interface { CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq, baseURL string) (*protocol.PaymentRequestMessage, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) GetSettings() payments.Settings - VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) + VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, message *protocol.PaymentMessage) (bool, error) CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config *domain.PaymentOptionConfig) (uuid.UUID, error) GetPaymentOptions(ctx context.Context, issuerDID *w3c.DID) ([]domain.PaymentOption, error) diff --git a/internal/core/ports/payments_repository.go b/internal/core/ports/payments_repository.go index 3456a25b6..52f61a3dd 100644 --- a/internal/core/ports/payments_repository.go +++ b/internal/core/ports/payments_repository.go @@ -13,6 +13,6 @@ import ( type PaymentRepository interface { SavePaymentOption(ctx context.Context, opt *domain.PaymentOption) (uuid.UUID, error) GetAllPaymentOptions(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentOption, error) - GetPaymentOptionByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) + GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) DeletePaymentOption(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) error } diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 05e2b27f0..92748d75a 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -5,7 +5,6 @@ import ( "crypto/ecdsa" "crypto/rand" "encoding/hex" - "encoding/json" "fmt" "math/big" "strconv" @@ -83,7 +82,7 @@ func (p *payment) GetPaymentOptions(ctx context.Context, issuerDID *w3c.DID) ([] // GetPaymentOptionByID returns a payment option by its ID func (p *payment) GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) { - opt, err := p.paymentsStore.GetPaymentOptionByID(ctx, *issuerDID, id) + opt, err := p.paymentsStore.GetPaymentOptionByID(ctx, issuerDID, id) if err != nil { log.Error(ctx, "failed to get payment option", "err", err, "issuerDID", issuerDID, "id", id) return nil, err @@ -105,7 +104,7 @@ func (p *payment) DeletePaymentOption(ctx context.Context, issuerDID *w3c.DID, i func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePaymentRequestReq, baseURL string) (*protocol.PaymentRequestMessage, error) { const defaultExpirationDate = 1 * time.Hour - option, err := p.paymentsStore.GetPaymentOptionByID(ctx, req.IssuerDID, req.OptionID) + option, err := p.paymentsStore.GetPaymentOptionByID(ctx, &req.IssuerDID, req.OptionID) if err != nil { log.Error(ctx, "failed to get payment option", "err", err, "issuerDID", req.IssuerDID, "optionID", req.OptionID) return nil, err @@ -229,49 +228,92 @@ func (p *payment) GetSettings() payments.Settings { return p.settings } -func (p *payment) VerifyPayment(ctx context.Context, recipient common.Address, message comm.BasicMessage) (bool, error) { - var paymentRequest protocol.PaymentRequestMessageBody - err := json.Unmarshal(message.Body, &paymentRequest) +func (p *payment) VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, message *protocol.PaymentMessage) (bool, error) { + option, err := p.paymentsStore.GetPaymentOptionByID(ctx, nil, paymentOptionID) if err != nil { return false, err } + // TODO: Load rpc from network resolvers client, err := ethclient.Dial("https://polygon-amoy.g.alchemy.com/v2/DHvucvBBzrBhaHzmjrMp24PGbl7vwee6") if err != nil { return false, err } - contractAddress := common.HexToAddress("0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774") + // TODO: Load contract from payment config + contractAddress := common.HexToAddress("0xF8E49b922D5Fb00d3EdD12bd14064f275726D339") instance, err := eth.NewPaymentContract(contractAddress, client) if err != nil { return false, err } - nonce, err := nonceFromPaymentRequestInfoData(paymentRequest.Payments[0].Data) + // TODO: Iterate over all payments? + nonce, err := nonceFromPayment(&message.Body.Payments[0]) if err != nil { log.Error(ctx, "failed to get nonce from payment request info data", "err", err) return false, err } - isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: context.Background()}, recipient, nonce) + + address, err := recipientAddressFromPayment(&message.Body.Payments[0], option) + if err != nil { + log.Error(ctx, "failed to get recipient address from payment", "err", err) + return false, err + } + + // TODO: pending, canceled, success, failed + isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, address, nonce) if err != nil { return false, err } return isPaid, nil } -func nonceFromPaymentRequestInfoData(data protocol.PaymentRequestInfoData) (*big.Int, error) { +func recipientAddressFromPayment(data *protocol.Payment, option *domain.PaymentOption) (common.Address, error) { + var address common.Address + switch data.Type() { + case protocol.Iden3PaymentCryptoV1Type: + address = common.Address{} + case protocol.Iden3PaymentRailsV1Type: + t, ok := data.Data().(protocol.Iden3PaymentRailsV1) + if !ok { + return common.Address{}, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") + } + for _, chain := range option.Config.Chains { + if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { + address = common.HexToAddress(chain.Recipient) + break + } + } + case protocol.Iden3PaymentRailsERC20V1Type: + t, ok := data.Data().(protocol.Iden3PaymentRailsERC20V1) + if !ok { + return common.Address{}, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") + } + for _, chain := range option.Config.Chains { + if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { + address = common.HexToAddress(chain.Recipient) + break + } + } + default: + return common.Address{}, fmt.Errorf("unsupported payment request data type: %s", data.Type()) + } + return address, nil +} + +func nonceFromPayment(data *protocol.Payment) (*big.Int, error) { const base10 = 10 var nonce string switch data.Type() { - case protocol.Iden3PaymentRequestCryptoV1Type: + case protocol.Iden3PaymentCryptoV1Type: nonce = "" - case protocol.Iden3PaymentRailsRequestV1Type: + case protocol.Iden3PaymentRailsV1Type: t, ok := data.Data().(protocol.Iden3PaymentRailsRequestV1) if !ok { return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") } nonce = t.Nonce - case protocol.Iden3PaymentRailsERC20RequestV1Type: + case protocol.Iden3PaymentRailsERC20V1Type: t, ok := data.Data().(protocol.Iden3PaymentRailsERC20RequestV1) if !ok { return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go index 9f75dcbe5..969d36c08 100644 --- a/internal/repositories/payment.go +++ b/internal/repositories/payment.go @@ -75,15 +75,19 @@ ORDER BY created_at DESC;` } // GetPaymentOptionByID returns a payment option by ID -func (p *Payment) GetPaymentOptionByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) { - const query = ` +func (p *Payment) GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) { + const baseQuery = ` SELECT id, issuer_did, name, description, configuration, created_at, updated_at FROM payment_options WHERE id = $1 -AND issuer_did = $2;` - +` var opt domain.PaymentOption var strIssuerDID string + + query := baseQuery + if issuerDID != nil { + query += ` AND issuer_did = $2` + } err := p.conn.Pgx.QueryRow(ctx, query, id, issuerDID.String()).Scan(&opt.ID, &strIssuerDID, &opt.Name, &opt.Description, &opt.Config, &opt.CreatedAt, &opt.UpdatedAt) if err != nil { if strings.Contains(err.Error(), "no rows in result set") { diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go index 41ea4e358..f24cc246d 100644 --- a/internal/repositories/payment_test.go +++ b/internal/repositories/payment_test.go @@ -141,7 +141,7 @@ func TestPayment_GetPaymentOptionByID(t *testing.T) { assert.NotEqual(t, uuid.Nil, id) t.Run("Get payment option", func(t *testing.T) { - opt, err := repo.GetPaymentOptionByID(ctx, *issuerID, id) + opt, err := repo.GetPaymentOptionByID(ctx, issuerID, id) assert.NoError(t, err) assert.Equal(t, id, opt.ID) assert.Equal(t, "name", opt.Name) @@ -149,7 +149,7 @@ func TestPayment_GetPaymentOptionByID(t *testing.T) { assert.Equal(t, paymentOptionConfig, *opt.Config) }) t.Run("Get payment option linked to non existing issuer", func(t *testing.T) { - opt, err := repo.GetPaymentOptionByID(ctx, *issuerDIDOther, id) + opt, err := repo.GetPaymentOptionByID(ctx, issuerDIDOther, id) require.Error(t, err) assert.Nil(t, opt) }) @@ -167,13 +167,13 @@ func TestPayment_DeletePaymentOption(t *testing.T) { assert.NoError(t, err) assert.NotEqual(t, uuid.Nil, id) - opt, err := repo.GetPaymentOptionByID(ctx, *issuerID, id) + opt, err := repo.GetPaymentOptionByID(ctx, issuerID, id) assert.NoError(t, err) assert.Equal(t, id, opt.ID) require.NoError(t, repo.DeletePaymentOption(ctx, *issuerID, id)) - opt, err = repo.GetPaymentOptionByID(ctx, *issuerID, id) + opt, err = repo.GetPaymentOptionByID(ctx, issuerID, id) assert.Error(t, err) assert.Nil(t, opt) } From 2a04d8ea23852fb6465fc838518e012e398af3f4 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Thu, 28 Nov 2024 10:37:15 +0100 Subject: [PATCH 28/66] feat: improve example --- api/api.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/api.yaml b/api/api.yaml index 0cd7dc7c8..7155f27bf 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -2151,6 +2151,9 @@ components: x-go-type-import: name: protocol path: "github.com/iden3/iden3comm/v2/protocol" + example: | + // protocol.PaymentMessage + // https://iden3-communication.io/credentials/0.1/payment/ BasicMessage: type: object From a61300e465f0eb42fecf839e11b4a4065e0c60d1 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Thu, 28 Nov 2024 14:49:25 +0100 Subject: [PATCH 29/66] feat: Load contract from configuration and test it --- api/api.yaml | 2 +- internal/api/api.gen.go | 3 +- internal/api/payment_test.go | 141 ++++++++++++++++++++++++++++++ internal/core/services/payment.go | 74 ++++++++++++---- internal/repositories/payment.go | 5 +- payment_settings.sample.yaml | 32 +++++-- 6 files changed, 229 insertions(+), 28 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 7155f27bf..1fe14a6b1 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -2151,7 +2151,7 @@ components: x-go-type-import: name: protocol path: "github.com/iden3/iden3comm/v2/protocol" - example: | + description: | // protocol.PaymentMessage // https://iden3-communication.io/credentials/0.1/payment/ diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 6064e5fbf..62b80df0d 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -473,7 +473,8 @@ type PaginatedMetadata struct { Total uint `json:"total"` } -// PaymentMessage defines model for PaymentMessage. +// PaymentMessage // protocol.PaymentMessage +// // https://iden3-communication.io/credentials/0.1/payment/ type PaymentMessage = protocol.PaymentMessage // PaymentOption defines model for PaymentOption. diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 918e818ae..0e3007107 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -730,3 +730,144 @@ func TestServer_CreatePaymentRequest(t *testing.T) { }) } } + +// TODO: Review this test!! +func TestServer_VerifyPayment(t *testing.T) { + const ( + method = "polygonid" + blockchain = "polygon" + network = "amoy" + BJJ = "BJJ" + ) + ctx := context.Background() + server := newTestServer(t, nil) + handler := getHandler(ctx, server) + + iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) + require.NoError(t, err) + issuerDID, err := w3c.ParseDID(iden.Identifier) + require.NoError(t, err) + + receiverDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") + require.NoError(t, err) + + _ = receiverDID + + // Creating an ethereum key + signingKeyID, err := keyStore.CreateKey(kms.KeyTypeEthereum, issuerDID) + require.NoError(t, err) + + // Creating a payment config using previously created key + config := domain.PaymentOptionConfig{ + Chains: []domain.PaymentOptionConfigChain{ + { + ChainId: 1101, + Recipient: "0x1101...", + SigningKeyId: signingKeyID.ID, + Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ + Amount: 0.5, + Currency: "ETH", + }, + Iden3PaymentRailsERC20RequestV1: nil, + }, + { + ChainId: 137, + Recipient: "0x137...", + SigningKeyId: signingKeyID.ID, + Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ + Amount: 0.01, + Currency: "POL", + }, + Iden3PaymentRailsERC20RequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1{ + USDT: struct { + Amount float64 `json:"Amount"` + }{ + Amount: 5.2, + }, + USDC: struct { + Amount float64 `json:"Amount"` + }{ + Amount: 4.3, + }, + }, + }, + }, + } + + paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) + require.NoError(t, err) + + type expected struct { + httpCode int + msg string + } + + for _, tc := range []struct { + name string + issuerDID w3c.DID + auth func() (string, string) + PaymentOptionID uuid.UUID + body protocol.PaymentMessage + expected expected + }{ + { + name: "Happy Path", + auth: authOk, + issuerDID: *issuerDID, + PaymentOptionID: paymentOptionID, + body: protocol.PaymentMessage{ + ID: uuid.New().String(), + Typ: "application/iden3comm-plain-json", + Type: "https://iden3-communication.io/credentials/0.1/payment", + ThreadID: uuid.New().String(), + From: "did:iden3:polygon:mumbai:x3HstHLj2rTp6HHXk2WczYP7w3rpCsRbwCMeaQ2H2", + To: "did:polygonid:polygon:mumbai:2qJUZDSCFtpR8QvHyBC4eFm6ab9sJo5rqPbcaeyGC4", + Body: protocol.PaymentMessageBody{ + Payments: []protocol.Payment{protocol.NewPaymentRails(protocol.Iden3PaymentRailsV1{ + Nonce: "123", + Type: "Iden3PaymentRailsV1", + Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), + PaymentData: struct { + TxID string `json:"txId"` + ChainID string `json:"chainId"` + }{ + TxID: "0x123", + ChainID: "137", + }, + })}, + }, + }, + expected: expected{ + httpCode: http.StatusOK, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + rr := httptest.NewRecorder() + payload, err := json.Marshal(tc.body) + require.NoError(t, err) + url := fmt.Sprintf("/v2/payment/verify/%s", tc.PaymentOptionID) + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payload)) + assert.NoError(t, err) + req.SetBasicAuth(tc.auth()) + + handler.ServeHTTP(rr, req) + require.Equal(t, tc.expected.httpCode, rr.Code) + + switch tc.expected.httpCode { + case http.StatusCreated: + var response VerifyPayment200JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + + case http.StatusBadRequest: + var response VerifyPayment400JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + case http.StatusInternalServerError: + var response VerifyPayment500JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + } + }) + } +} diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 92748d75a..bfd46de0d 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -229,54 +229,94 @@ func (p *payment) GetSettings() payments.Settings { } func (p *payment) VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, message *protocol.PaymentMessage) (bool, error) { + if len(message.Body.Payments) != 1 { + return false, fmt.Errorf("expected one payment, got %d", len(message.Body.Payments)) + } + option, err := p.paymentsStore.GetPaymentOptionByID(ctx, nil, paymentOptionID) if err != nil { - return false, err + return false, fmt.Errorf("failed to get payment option: %w", err) } // TODO: Load rpc from network resolvers client, err := ethclient.Dial("https://polygon-amoy.g.alchemy.com/v2/DHvucvBBzrBhaHzmjrMp24PGbl7vwee6") if err != nil { - return false, err + return false, fmt.Errorf("failed to connect to ethereum client: %w", err) } - // TODO: Load contract from payment config - contractAddress := common.HexToAddress("0xF8E49b922D5Fb00d3EdD12bd14064f275726D339") - instance, err := eth.NewPaymentContract(contractAddress, client) + // contractAddress := common.HexToAddress("0xF8E49b922D5Fb00d3EdD12bd14064f275726D339") + contractAddress, err := contractAddressFromPayment(&message.Body.Payments[0], p.settings) + if err != nil { + return false, fmt.Errorf("failed to get contract address from payment: %w", err) + } + instance, err := eth.NewPaymentContract(*contractAddress, client) if err != nil { return false, err } - // TODO: Iterate over all payments? + // TODO: Iterate over all payments? Right now we only support one payment nonce, err := nonceFromPayment(&message.Body.Payments[0]) if err != nil { log.Error(ctx, "failed to get nonce from payment request info data", "err", err) return false, err } - address, err := recipientAddressFromPayment(&message.Body.Payments[0], option) + recipientAddr, err := recipientAddressFromPayment(&message.Body.Payments[0], option) if err != nil { log.Error(ctx, "failed to get recipient address from payment", "err", err) return false, err } // TODO: pending, canceled, success, failed - isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, address, nonce) + isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, *recipientAddr, nonce) if err != nil { return false, err } return isPaid, nil } -func recipientAddressFromPayment(data *protocol.Payment, option *domain.PaymentOption) (common.Address, error) { +func contractAddressFromPayment(data *protocol.Payment, config payments.Settings) (*common.Address, error) { + var sChainID string + switch data.Type() { + case protocol.Iden3PaymentCryptoV1Type: + return nil, nil + case protocol.Iden3PaymentRailsV1Type: + d := data.Data() + t, ok := d.(*protocol.Iden3PaymentRailsV1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") + } + sChainID = t.PaymentData.ChainID + case protocol.Iden3PaymentRailsERC20V1Type: + t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") + } + sChainID = t.PaymentData.ChainID + default: + return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) + } + chainID, err := strconv.Atoi(sChainID) + if err != nil { + return nil, fmt.Errorf("failed to parse chain id: %w", err) + } + c, found := config[chainID] + if !found { + return nil, fmt.Errorf("chain id not found in settings: %d", chainID) + } + addr := common.HexToAddress(c.MCPayment) + return &addr, nil +} + +func recipientAddressFromPayment(data *protocol.Payment, option *domain.PaymentOption) (*common.Address, error) { var address common.Address switch data.Type() { case protocol.Iden3PaymentCryptoV1Type: address = common.Address{} case protocol.Iden3PaymentRailsV1Type: - t, ok := data.Data().(protocol.Iden3PaymentRailsV1) + t, ok := data.Data().(*protocol.Iden3PaymentRailsV1) if !ok { - return common.Address{}, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") } for _, chain := range option.Config.Chains { if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { @@ -285,9 +325,9 @@ func recipientAddressFromPayment(data *protocol.Payment, option *domain.PaymentO } } case protocol.Iden3PaymentRailsERC20V1Type: - t, ok := data.Data().(protocol.Iden3PaymentRailsERC20V1) + t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) if !ok { - return common.Address{}, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") } for _, chain := range option.Config.Chains { if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { @@ -296,9 +336,9 @@ func recipientAddressFromPayment(data *protocol.Payment, option *domain.PaymentO } } default: - return common.Address{}, fmt.Errorf("unsupported payment request data type: %s", data.Type()) + return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) } - return address, nil + return &address, nil } func nonceFromPayment(data *protocol.Payment) (*big.Int, error) { @@ -308,13 +348,13 @@ func nonceFromPayment(data *protocol.Payment) (*big.Int, error) { case protocol.Iden3PaymentCryptoV1Type: nonce = "" case protocol.Iden3PaymentRailsV1Type: - t, ok := data.Data().(protocol.Iden3PaymentRailsRequestV1) + t, ok := data.Data().(*protocol.Iden3PaymentRailsV1) if !ok { return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") } nonce = t.Nonce case protocol.Iden3PaymentRailsERC20V1Type: - t, ok := data.Data().(protocol.Iden3PaymentRailsERC20RequestV1) + t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) if !ok { return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") } diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go index 969d36c08..6775dba4a 100644 --- a/internal/repositories/payment.go +++ b/internal/repositories/payment.go @@ -85,10 +85,13 @@ WHERE id = $1 var strIssuerDID string query := baseQuery + queryParams := []interface{}{id} if issuerDID != nil { query += ` AND issuer_did = $2` + queryParams = append(queryParams, issuerDID.String()) } - err := p.conn.Pgx.QueryRow(ctx, query, id, issuerDID.String()).Scan(&opt.ID, &strIssuerDID, &opt.Name, &opt.Description, &opt.Config, &opt.CreatedAt, &opt.UpdatedAt) + + err := p.conn.Pgx.QueryRow(ctx, query, queryParams...).Scan(&opt.ID, &strIssuerDID, &opt.Name, &opt.Description, &opt.Config, &opt.CreatedAt, &opt.UpdatedAt) if err != nil { if strings.Contains(err.Error(), "no rows in result set") { return nil, ErrPaymentOptionDoesNotExists diff --git a/payment_settings.sample.yaml b/payment_settings.sample.yaml index 5f4fd985f..38b3787a5 100644 --- a/payment_settings.sample.yaml +++ b/payment_settings.sample.yaml @@ -1,14 +1,30 @@ -137: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 +80002: + MCPayment: 0xF8E49b922D5Fb00d3EdD12bd14064f275726D339 ERC20: USDT: - ContractAddress: 0xc2132D05D31c914a87C6611C10748AEb04B58e8F + ContractAddress: 0x2FE40749812FAC39a0F380649eF59E01bccf3a1A Features: [] USDC: - ContractAddress: 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 + ContractAddress: 0x2FE40749812FAC39a0F380649eF59E01bccf3a1A Features: - EIP-2612 -80002: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 -1101: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 \ No newline at end of file +59141: + MCPayment: 0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3 + ERC20: + USDT: + ContractAddress: 0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C + Features: [] + USDC: + ContractAddress: 0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C + Features: + - EIP-2612 +2442: + MCPayment: 0x09c269e74d8B47c98537Acd6CbEe8056806F4c70 + ERC20: + USDT: + ContractAddress: 0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db + Features: [] + USDC: + ContractAddress: 0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db + Features: + - EIP-2612 \ No newline at end of file From db3ada841ba7a35807878281cf14029a6e4b2c3a Mon Sep 17 00:00:00 2001 From: x1m3 Date: Fri, 29 Nov 2024 12:23:50 +0100 Subject: [PATCH 30/66] chore: Reuse protocol constants for payment request type defined in iden3comm --- internal/core/services/payment.go | 37 +++++++++---------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index bfd46de0d..0c8c29bb0 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -31,17 +31,6 @@ import ( "github.com/polygonid/sh-id-platform/internal/payments" ) -const ( - iden3PaymentRailsRequestV1Type paymentRequestType = "Iden3PaymentRailsRequestV1" - iden3PaymentRailsERC20RequestV1Type paymentRequestType = "Iden3PaymentRailsERC20RequestV1" -) - -type paymentRequestType string - -func (t paymentRequestType) Valid() bool { - return t == iden3PaymentRailsRequestV1Type || t == iden3PaymentRailsERC20RequestV1Type -} - type payment struct { networkResolver network.Resolver settings payments.Settings @@ -159,7 +148,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay } paymentsList = append(paymentsList, protocol.PaymentRequestInfo{ - Type: string(iden3PaymentRailsRequestV1Type), + Type: string(protocol.Iden3PaymentRailsRequestV1Type), Description: option.Description, Credentials: req.Creds, Data: *data, @@ -179,13 +168,13 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay paymentsList = append(paymentsList, protocol.PaymentRequestInfo{ - Type: string(iden3PaymentRailsERC20RequestV1Type), + Type: string(protocol.Iden3PaymentRailsERC20RequestV1Type), Description: option.Description, Credentials: req.Creds, Data: *reqUSDT, }, protocol.PaymentRequestInfo{ - Type: string(iden3PaymentRailsERC20RequestV1Type), + Type: string(protocol.Iden3PaymentRailsERC20RequestV1Type), Description: option.Description, Credentials: req.Creds, Data: *reqUSDC, @@ -379,7 +368,7 @@ func (p *payment) newIden3PaymentRailsRequestV1( signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management ) (*protocol.PaymentRequestInfoData, error) { metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, "") + signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, "") if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err @@ -387,7 +376,7 @@ func (p *payment) newIden3PaymentRailsRequestV1( paymentInfo := protocol.NewPaymentRequestInfoDataRails(protocol.Iden3PaymentRailsRequestV1{ Nonce: nonce.String(), - Type: protocol.PaymentRequestType(iden3PaymentRailsRequestV1Type), + Type: protocol.Iden3PaymentRailsRequestV1Type, Context: protocol.NewPaymentContextStringCol([]string{ "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", @@ -434,7 +423,7 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management ) (*protocol.PaymentRequestInfoData, error) { metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, tokenAddress) + signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, tokenAddress) if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err @@ -442,7 +431,7 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( paymentInfo := protocol.NewPaymentRequestInfoDataRailsERC20(protocol.Iden3PaymentRailsERC20RequestV1{ Nonce: nonce.String(), - Type: protocol.PaymentRequestType(iden3PaymentRailsERC20RequestV1Type), + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, Context: protocol.NewPaymentContextStringCol([]string{ "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", @@ -477,7 +466,7 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( func (p *payment) paymentRequestSignature( ctx context.Context, - paymentType paymentRequestType, + paymentType protocol.PaymentRequestType, chainID int, verifContract string, amount float64, @@ -489,7 +478,7 @@ func (p *payment) paymentRequestSignature( signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management tokenAddr string, ) ([]byte, error) { - if !paymentType.Valid() { + if paymentType != protocol.Iden3PaymentRailsRequestV1Type && paymentType != protocol.Iden3PaymentRailsERC20RequestV1Type { return nil, fmt.Errorf("unsupported payment type: %s", paymentType) } @@ -525,11 +514,7 @@ func (p *payment) paymentRequestSignature( } } -func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyContract string, address common.Address, amount float64, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { - if !paymentType.Valid() { - return nil, fmt.Errorf("unsupported payment type: %s", paymentType) - } - +func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, verifyContract string, address common.Address, amount float64, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { data := apitypes.TypedData{ Types: apitypes.Types{ "EIP712Domain": []apitypes.Type{ @@ -576,7 +561,7 @@ func typedDataForHashing(paymentType paymentRequestType, chainID int, verifyCont "metadata": metadata, }, } - if paymentType == iden3PaymentRailsERC20RequestV1Type { + if paymentType == protocol.Iden3PaymentRailsERC20RequestV1Type { data.Types[string(paymentType)] = append(data.Types[string(paymentType)], apitypes.Type{ Name: "tokenAddress", Type: "address", From 2c7dd00df26e112522663326a3c4ff2df01fda33 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 4 Dec 2024 11:52:08 +0100 Subject: [PATCH 31/66] feat: Payment request repository --- internal/core/domain/payment.go | 21 +++ internal/core/ports/payments_repository.go | 6 + .../202411131724000_payment_options.sql | 30 +++- internal/repositories/fixture.go | 4 +- internal/repositories/payment.go | 128 ++++++++++++++++-- .../repositories/payment_request_fixture.go | 57 ++++++++ internal/repositories/payment_test.go | 110 +++++++++++++++ 7 files changed, 344 insertions(+), 12 deletions(-) create mode 100644 internal/repositories/payment_request_fixture.go diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index eb1519c69..3e4f1607d 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -3,13 +3,34 @@ package domain import ( "encoding/json" "errors" + "math/big" "strconv" "time" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/iden3comm/v2/protocol" ) +// PaymentRequest represents a payment request +type PaymentRequest struct { + ID uuid.UUID + IssuerDID w3c.DID + RecipientDID w3c.DID + ThreadID string + PaymentOptionID uuid.UUID + Payments []PaymentRequestItem + CreatedAt time.Time +} + +// PaymentRequestItem represents a payment request item +type PaymentRequestItem struct { + ID uuid.UUID + Nonce big.Int + PaymentRequestID uuid.UUID + Payment protocol.Payment +} + // PaymentOption represents a payment option type PaymentOption struct { ID uuid.UUID diff --git a/internal/core/ports/payments_repository.go b/internal/core/ports/payments_repository.go index 52f61a3dd..1986de6a7 100644 --- a/internal/core/ports/payments_repository.go +++ b/internal/core/ports/payments_repository.go @@ -2,6 +2,7 @@ package ports import ( "context" + "math/big" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" @@ -15,4 +16,9 @@ type PaymentRepository interface { GetAllPaymentOptions(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentOption, error) GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) DeletePaymentOption(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) error + + SavePaymentRequest(ctx context.Context, req *domain.PaymentRequest) (uuid.UUID, error) + GetPaymentRequestByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentRequest, error) + GetAllPaymentRequests(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentRequest, error) + GetPaymentRequestItem(ctx context.Context, issuerDID w3c.DID, nonce *big.Int) (*domain.PaymentRequestItem, error) } diff --git a/internal/db/schema/migrations/202411131724000_payment_options.sql b/internal/db/schema/migrations/202411131724000_payment_options.sql index 3a4b30603..824fb51e6 100644 --- a/internal/db/schema/migrations/202411131724000_payment_options.sql +++ b/internal/db/schema/migrations/202411131724000_payment_options.sql @@ -4,8 +4,8 @@ CREATE TABLE payment_options ( id UUID PRIMARY KEY NOT NULL, issuer_did text NOT NULL REFERENCES identities (identifier), - name text, - description text, + name text NOT NULL, + description text NOT NULL, configuration jsonb NOT NULL, created_at timestamptz NULL DEFAULT CURRENT_TIMESTAMP, updated_at timestamptz NULL DEFAULT CURRENT_TIMESTAMP @@ -13,9 +13,35 @@ CREATE TABLE payment_options CREATE INDEX payment_options_id_issuer_did_index ON payment_options (id, issuer_did); CREATE INDEX payment_options_issuer_did_created_at_index ON payment_options (issuer_did, created_at); +CREATE INDEX payment_options_issuer_did ON payment_options (issuer_did); + +CREATE TABLE payment_requests +( + id UUID PRIMARY KEY NOT NULL, + issuer_did text NOT NULL REFERENCES identities (identifier), + recipient_did text NOT NULL, + thread_id text NOT NULL, + payment_option_id UUID REFERENCES payment_options (id), + created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE INDEX payment_requests_from_nonce_created_at_idx ON payment_requests (issuer_did, created_at); + +CREATE TABLE payment_request_items +( + id UUID PRIMARY KEY NOT NULL, + nonce numeric NOT NULL, + payment_request_id UUID NOT NULL REFERENCES payment_requests (id), + payment_request_info jsonb NOT NULL /* protocol.PaymentRequestInfo */ +); + +CREATE UNIQUE INDEX payment_request_items_nonce_idx ON payment_request_items (nonce); + + -- +goose StatementEnd -- +goose Down -- +goose StatementBegin +DROP TABLE IF EXISTS payment_requests_items; +DROP TABLE IF EXISTS payment_requests; DROP TABLE IF EXISTS payment_options; -- +goose StatementEnd \ No newline at end of file diff --git a/internal/repositories/fixture.go b/internal/repositories/fixture.go index b8e88a5fc..26a223b98 100644 --- a/internal/repositories/fixture.go +++ b/internal/repositories/fixture.go @@ -18,6 +18,7 @@ type Fixture struct { connectionsRepository ports.ConnectionRepository schemaRepository ports.SchemaRepository identityStateRepository ports.IdentityStateRepository + paymentRepository ports.PaymentRepository } // NewFixture - constructor @@ -29,10 +30,11 @@ func NewFixture(storage *db.Storage) *Fixture { connectionsRepository: NewConnection(), schemaRepository: NewSchema(*storage), identityStateRepository: NewIdentityState(), + paymentRepository: NewPayment(*storage), } } -// ExecQueryParams - handle the query and the argumens for that query. +// ExecQueryParams - handle the query and the arguments for that query. type ExecQueryParams struct { Query string Arguments []interface{} diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go index 6775dba4a..a804bd177 100644 --- a/internal/repositories/payment.go +++ b/internal/repositories/payment.go @@ -4,30 +4,140 @@ import ( "context" "errors" "fmt" + "math/big" "strings" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/polygonid/sh-id-platform/internal/core/domain" + "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/db" ) // ErrPaymentOptionDoesNotExists error var ErrPaymentOptionDoesNotExists = errors.New("payment option not found") -// Payment repository -type Payment struct { +// payment repository +type payment struct { conn db.Storage } -// NewPayment creates a new Payment repository -func NewPayment(conn db.Storage) *Payment { - return &Payment{conn} +// NewPayment creates a new payment repository +func NewPayment(conn db.Storage) ports.PaymentRepository { + return &payment{conn} +} + +// SavePaymentRequest saves a payment request +func (p *payment) SavePaymentRequest(ctx context.Context, req *domain.PaymentRequest) (uuid.UUID, error) { + const ( + insertPaymentRequest = ` +INSERT +INTO payment_requests (id, issuer_did, recipient_did, thread_id, payment_option_id, created_at) +VALUES ($1, $2, $3, $4, $5, $6);` + insertPaymentRequestItem = ` +INSERT +INTO payment_request_items (id, nonce, payment_request_id, payment_request_info) +VALUES ($1, $2, $3, $4);` + ) + + tx, err := p.conn.Pgx.Begin(ctx) + if err != nil { + return uuid.Nil, fmt.Errorf("could not start transaction: %w", err) + } + defer func() { _ = tx.Rollback(ctx) }() + + _, err = tx.Exec(ctx, insertPaymentRequest, req.ID, req.IssuerDID.String(), req.RecipientDID.String(), req.ThreadID, req.PaymentOptionID, req.CreatedAt) + if err != nil { + return uuid.Nil, fmt.Errorf("could not insert payment request: %w", err) + } + for _, item := range req.Payments { + _, err = tx.Exec(ctx, insertPaymentRequestItem, item.ID, item.Nonce.String(), item.PaymentRequestID, item.Payment) + if err != nil { + return uuid.Nil, fmt.Errorf("could not insert payment request item: %w", err) + } + } + if err := tx.Commit(ctx); err != nil { + return uuid.Nil, fmt.Errorf("could not commit transaction: %w", err) + } + return req.ID, nil +} + +// GetPaymentRequestByID returns a payment request by ID +func (p *payment) GetPaymentRequestByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentRequest, error) { + const query = ` +SELECT pr.id, pr.issuer_did, pr.recipient_did, pr.thread_id, pr.payment_option_id, pr.created_at, pri.id, pri.nonce, pri.payment_request_id, pri.payment_request_info +FROM payment_requests pr +LEFT JOIN payment_request_items pri ON pr.id = pri.payment_request_id +WHERE pr.issuer_did = $1 AND pr.id = $2;` + rows, err := p.conn.Pgx.Query(ctx, query, issuerDID.String(), id) + if err != nil { + return nil, err + } + defer rows.Close() + var pr domain.PaymentRequest + for rows.Next() { + var item domain.PaymentRequestItem + var strIssuerDID, strRecipientDID string + var sNonce string + var did *w3c.DID + if err := rows.Scan( + &pr.ID, + &strIssuerDID, + &strRecipientDID, + &pr.ThreadID, + &pr.PaymentOptionID, + &pr.CreatedAt, + &item.ID, + &sNonce, + &item.PaymentRequestID, + &item.Payment, + ); err != nil { + return nil, fmt.Errorf("could not scan payment request: %w", err) + } + const base10 = 10 + nonce, ok := new(big.Int).SetString(sNonce, base10) + if !ok { + return nil, fmt.Errorf("could not parse nonce: %w", err) + } + item.Nonce = *nonce + if did, err = w3c.ParseDID(strIssuerDID); err != nil { + return nil, fmt.Errorf("could not parse issuer DID: %w", err) + } + pr.IssuerDID = *did + if did, err = w3c.ParseDID(strRecipientDID); err != nil { + return nil, fmt.Errorf("could not parse recipient DID: %w", err) + } + pr.RecipientDID = *did + pr.Payments = append(pr.Payments, item) + } + return &pr, nil +} + +// GetAllPaymentRequests returns all payment requests +// TODO: Pagination? +func (p *payment) GetAllPaymentRequests(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentRequest, error) { + // TODO implement me + panic("implement me") +} + +// GetPaymentRequestItem returns a payment request item +func (p *payment) GetPaymentRequestItem(ctx context.Context, issuerDID w3c.DID, nonce *big.Int) (*domain.PaymentRequestItem, error) { + const query = ` +SELECT id, nonce, payment_request_id, payment_request_info +FROM payment_request_items +LEFT JOIN payment_requests ON payment_requests.id = payment_request_items.payment_request_id +WHERE payment_requests.issuer_did = $1 AND nonce = $2;` + var item domain.PaymentRequestItem + err := p.conn.Pgx.QueryRow(ctx, query, issuerDID.String(), nonce).Scan(&item.ID, &item.Nonce, &item.PaymentRequestID, &item.Payment) + if err != nil { + return nil, fmt.Errorf("could not get payment request item: %w", err) + } + return &item, nil } // SavePaymentOption saves a payment option -func (p *Payment) SavePaymentOption(ctx context.Context, opt *domain.PaymentOption) (uuid.UUID, error) { +func (p *payment) SavePaymentOption(ctx context.Context, opt *domain.PaymentOption) (uuid.UUID, error) { const query = ` INSERT INTO payment_options (id, issuer_did, name, description, configuration, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7); @@ -43,7 +153,7 @@ VALUES ($1, $2, $3, $4, $5, $6, $7); } // GetAllPaymentOptions returns all payment options -func (p *Payment) GetAllPaymentOptions(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentOption, error) { +func (p *payment) GetAllPaymentOptions(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentOption, error) { const query = ` SELECT id, issuer_did, name, description, configuration, created_at, updated_at FROM payment_options @@ -75,7 +185,7 @@ ORDER BY created_at DESC;` } // GetPaymentOptionByID returns a payment option by ID -func (p *Payment) GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) { +func (p *payment) GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) { const baseQuery = ` SELECT id, issuer_did, name, description, configuration, created_at, updated_at FROM payment_options @@ -107,7 +217,7 @@ WHERE id = $1 } // DeletePaymentOption deletes a payment option -func (p *Payment) DeletePaymentOption(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) error { +func (p *payment) DeletePaymentOption(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) error { const query = `DELETE FROM payment_options WHERE id = $1 and issuer_did = $2;` cmd, err := p.conn.Pgx.Exec(ctx, query, id, issuerDID.String()) diff --git a/internal/repositories/payment_request_fixture.go b/internal/repositories/payment_request_fixture.go new file mode 100644 index 000000000..59d42d858 --- /dev/null +++ b/internal/repositories/payment_request_fixture.go @@ -0,0 +1,57 @@ +package repositories + +import ( + "context" + "math/big" + "math/rand" + "testing" + "time" + + "github.com/google/uuid" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/iden3comm/v2/protocol" + "github.com/stretchr/testify/require" + + "github.com/polygonid/sh-id-platform/internal/core/domain" +) + +// CreatePaymentRequest creates a new payment request fixture to be used on tests +func (f *Fixture) CreatePaymentRequest(t *testing.T, issuerDID, recipientDID w3c.DID, paymentOptionID uuid.UUID, nPayments int) *domain.PaymentRequest { + t.Helper() + + var payments []domain.PaymentRequestItem + paymentRequestID := uuid.New() + for i := 0; i < nPayments; i++ { + nonce := big.NewInt(rand.Int63()) + payments = append(payments, domain.PaymentRequestItem{ + ID: uuid.New(), + Nonce: *nonce, + PaymentRequestID: paymentRequestID, + Payment: protocol.NewPaymentRails(protocol.Iden3PaymentRailsV1{ + Nonce: nonce.String(), + Type: "Iden3PaymentRailsV1", + Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), + PaymentData: struct { + TxID string `json:"txId"` + ChainID string `json:"chainId"` + }{ + TxID: "0x123", + ChainID: "137", + }, + }), + }) + } + paymentRequest := &domain.PaymentRequest{ + ID: paymentRequestID, + IssuerDID: issuerDID, + RecipientDID: recipientDID, + ThreadID: "thread-id", + PaymentOptionID: paymentOptionID, + Payments: payments, + CreatedAt: time.Now(), + } + + _, err := f.paymentRepository.SavePaymentRequest(context.Background(), paymentRequest) + require.NoError(t, err) + return paymentRequest +} diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go index f24cc246d..65b3a2039 100644 --- a/internal/repositories/payment_test.go +++ b/internal/repositories/payment_test.go @@ -4,11 +4,14 @@ import ( "context" "encoding/json" "fmt" + "math/big" + "math/rand" "testing" "time" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/iden3comm/v2/protocol" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -177,3 +180,110 @@ func TestPayment_DeletePaymentOption(t *testing.T) { assert.Error(t, err) assert.Nil(t, opt) } + +func TestPayment_SavePaymentRequest(t *testing.T) { + ctx := context.Background() + fixture := NewFixture(storage) + issuerID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qQHtVzfwjywqosK24XT7um3R1Ym5L1GJTbijjcxMq") + require.NoError(t, err) + + fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) + repo := NewPayment(*storage) + + payymentOptionID, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", &domain.PaymentOptionConfig{})) + require.NoError(t, err) + + t.Run("Save payment to not existing payment option id", func(t *testing.T) { + id, err := repo.SavePaymentRequest(ctx, &domain.PaymentRequest{ + ID: uuid.New(), + IssuerDID: *issuerID, + RecipientDID: *issuerID, + ThreadID: "thread-id", + PaymentOptionID: uuid.New(), + Payments: []domain.PaymentRequestItem{}, + CreatedAt: time.Now(), + }) + assert.Error(t, err) + assert.Equal(t, uuid.Nil, id) + }) + t.Run("Save payment with empty Payments", func(t *testing.T) { + id, err := repo.SavePaymentRequest(ctx, &domain.PaymentRequest{ + ID: uuid.New(), + IssuerDID: *issuerID, + RecipientDID: *issuerID, + ThreadID: "thread-id", + PaymentOptionID: payymentOptionID, + Payments: nil, + CreatedAt: time.Now(), + }) + assert.NoError(t, err) + assert.NotEqual(t, uuid.Nil, id) + }) + t.Run("Save payment Happy path", func(t *testing.T) { + var payments []domain.PaymentRequestItem + paymentRequestID := uuid.New() + for i := 0; i < 10; i++ { + nonce := big.NewInt(rand.Int63()) + payments = append(payments, domain.PaymentRequestItem{ + ID: uuid.New(), + Nonce: *nonce, + PaymentRequestID: paymentRequestID, + Payment: protocol.NewPaymentRails(protocol.Iden3PaymentRailsV1{ + Nonce: nonce.String(), + Type: "Iden3PaymentRailsV1", + Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), + PaymentData: struct { + TxID string `json:"txId"` + ChainID string `json:"chainId"` + }{ + TxID: "0x123", + ChainID: "137", + }, + }), + }) + } + id, err := repo.SavePaymentRequest(ctx, &domain.PaymentRequest{ + ID: paymentRequestID, + IssuerDID: *issuerID, + RecipientDID: *issuerID, + ThreadID: "thread-id", + PaymentOptionID: payymentOptionID, + Payments: payments, + CreatedAt: time.Now(), + }) + assert.NoError(t, err) + assert.NotEqual(t, uuid.Nil, id) + }) +} + +// TestPayment_GetPaymentRequestByID tests the GetPaymentRequestByID method +func TestPayment_GetPaymentRequestByID(t *testing.T) { + ctx := context.Background() + fixture := NewFixture(storage) + repo := NewPayment(*storage) + issuerID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qSrFwSvp8rLicBhUz4D21nZavGsZufBpjazwQHKmS") + require.NoError(t, err) + + fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) + paymentOptionID, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", &domain.PaymentOptionConfig{})) + require.NoError(t, err) + expected := fixture.CreatePaymentRequest(t, *issuerID, *issuerID, paymentOptionID, 10) + + t.Run("Get payment request", func(t *testing.T) { + paymentRequest, err := repo.GetPaymentRequestByID(ctx, expected.IssuerDID, expected.ID) + require.NoError(t, err) + assert.Equal(t, expected.ID, paymentRequest.ID) + assert.Equal(t, expected.IssuerDID, paymentRequest.IssuerDID) + assert.Equal(t, expected.RecipientDID, paymentRequest.RecipientDID) + assert.Equal(t, expected.ThreadID, paymentRequest.ThreadID) + assert.Equal(t, expected.PaymentOptionID, paymentRequest.PaymentOptionID) + assert.InDelta(t, expected.CreatedAt.UnixMilli(), paymentRequest.CreatedAt.UnixMilli(), 1) + assert.Len(t, expected.Payments, len(paymentRequest.Payments)) + for i, expectedPayment := range expected.Payments { + assert.Equal(t, expectedPayment.ID, paymentRequest.Payments[i].ID) + assert.Equal(t, expectedPayment.Nonce, paymentRequest.Payments[i].Nonce) + assert.Equal(t, expectedPayment.PaymentRequestID, paymentRequest.Payments[i].PaymentRequestID) + assert.Equal(t, expectedPayment.Payment, paymentRequest.Payments[i].Payment) + } + }) +} From 3580d7e282078b6a14ed9dc7891f2d5ff7b094c1 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Wed, 4 Dec 2024 14:38:51 +0200 Subject: [PATCH 32/66] fix payment-request signature --- go.mod | 2 +- go.sum | 2 + internal/core/services/payment.go | 145 ++++++++++++++++-------------- 3 files changed, 81 insertions(+), 68 deletions(-) diff --git a/go.mod b/go.mod index d03258144..7724a5de1 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20240914111027-9588ce2d7e1b github.com/iden3/go-schema-processor v1.3.1 github.com/iden3/go-schema-processor/v2 v2.5.0 - github.com/iden3/iden3comm/v2 v2.8.1 + github.com/iden3/iden3comm/v2 v2.8.3-0.20241203145634-5c082c18773e github.com/iden3/merkletree-proof v0.3.0 github.com/ipfs/go-ipfs-api v0.7.0 github.com/jackc/pgconn v1.14.3 diff --git a/go.sum b/go.sum index 966b5811f..993c606ad 100644 --- a/go.sum +++ b/go.sum @@ -449,6 +449,8 @@ github.com/iden3/go-schema-processor/v2 v2.5.0 h1:MX84oFb9kYq0ntKiU4DrPPaRgCDUCK github.com/iden3/go-schema-processor/v2 v2.5.0/go.mod h1:hMqYi4lKOzEGkmCRks/r4Crj8H4G8YaTt8H4jZHzX9Y= github.com/iden3/iden3comm/v2 v2.8.1 h1:SjDXYS2O9jfpXNUzSdOBRTs+EjFKPkKOz4/IFkaOfXQ= github.com/iden3/iden3comm/v2 v2.8.1/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= +github.com/iden3/iden3comm/v2 v2.8.3-0.20241203145634-5c082c18773e h1:kKVW1Y3hHyBKp3ZCOzHQxOGsXfJP5x3CgT6LeL24HPY= +github.com/iden3/iden3comm/v2 v2.8.3-0.20241203145634-5c082c18773e/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= github.com/iden3/merkletree-proof v0.3.0 h1:NVlvtUBEgn4Etxxn+RsOmXP/qlI+85BdN8oUDTf3mxI= github.com/iden3/merkletree-proof v0.3.0/go.mod h1:+E2sBxMqhcn/fcu0LDGjmk3us+Vr+fxQUiZMxdpbgUE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 0c8c29bb0..49007a497 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -98,8 +98,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay log.Error(ctx, "failed to get payment option", "err", err, "issuerDID", req.IssuerDID, "optionID", req.OptionID) return nil, err } - - paymentsList := make([]protocol.PaymentRequestInfo, 0) + var dataArr protocol.PaymentRequestInfoData for _, chainConfig := range option.Config.Chains { setting, found := p.settings[chainConfig.ChainId] if !found { @@ -107,15 +106,10 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay return nil, fmt.Errorf("chain not <%d> not found in payment settings", chainConfig.ChainId) } - //nolint: mnd - nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) - if err != nil { - return nil, err - } expirationTime := time.Now().Add(defaultExpirationDate) - var address common.Address var privateKey *ecdsa.PrivateKey + if chainConfig.SigningKeyOpt == nil { pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyId, Type: kms.KeyTypeEthereum}) if err != nil { @@ -141,48 +135,38 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay } if chainConfig.Iden3PaymentRailsRequestV1 != nil { - data, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, privateKey) + nativeToken, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, address, privateKey) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err } - paymentsList = append(paymentsList, protocol.PaymentRequestInfo{ - Type: string(protocol.Iden3PaymentRailsRequestV1Type), - Description: option.Description, - Credentials: req.Creds, - Data: *data, - }) + dataArr = append(dataArr, *nativeToken) } + if chainConfig.Iden3PaymentRailsERC20RequestV1 != nil { - reqUSDT, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, payments.USDT, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, setting.ERC20.USDT.ContractAddress, privateKey) + reqUSDT, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, address, payments.USDT, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, setting.ERC20.USDT.ContractAddress, privateKey) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err } - reqUSDC, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, nonce, address, payments.USDC, chainConfig.Iden3PaymentRailsERC20RequestV1.USDC.Amount, setting.ERC20.USDC.ContractAddress, privateKey) + dataArr = append(dataArr, reqUSDT) + reqUSDC, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, address, payments.USDC, chainConfig.Iden3PaymentRailsERC20RequestV1.USDC.Amount, setting.ERC20.USDC.ContractAddress, privateKey) if err != nil { log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) return nil, err } - - paymentsList = append(paymentsList, - protocol.PaymentRequestInfo{ - Type: string(protocol.Iden3PaymentRailsERC20RequestV1Type), - Description: option.Description, - Credentials: req.Creds, - Data: *reqUSDT, - }, - protocol.PaymentRequestInfo{ - Type: string(protocol.Iden3PaymentRailsERC20RequestV1Type), - Description: option.Description, - Credentials: req.Creds, - Data: *reqUSDC, - }, - ) + dataArr = append(dataArr, reqUSDC) } } + payments := []protocol.PaymentRequestInfo{ + { + Description: option.Description, + Credentials: req.Creds, + Data: dataArr, + }, + } msgID := uuid.New() message := &protocol.PaymentRequestMessage{ From: req.IssuerDID.String(), @@ -193,7 +177,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay ThreadID: msgID.String(), Body: protocol.PaymentRequestMessageBody{ Agent: fmt.Sprintf(ports.AgentUrl, baseURL), - Payments: paymentsList, + Payments: payments, }, } @@ -363,38 +347,47 @@ func (p *payment) newIden3PaymentRailsRequestV1( chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, - nonce *big.Int, address common.Address, signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management -) (*protocol.PaymentRequestInfoData, error) { +) (*protocol.Iden3PaymentRailsRequestV1, error) { + nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) + if err != nil { + log.Error(ctx, "failed to generate nonce", "err", err) + return nil, err + } metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, "") + signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, "USDT", expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, "") if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err } - paymentInfo := protocol.NewPaymentRequestInfoDataRails(protocol.Iden3PaymentRailsRequestV1{ + amountString := fmt.Sprintf("%f", chainConfig.Iden3PaymentRailsRequestV1.Amount) + if chainConfig.Iden3PaymentRailsRequestV1.Amount == float64(int(chainConfig.Iden3PaymentRailsRequestV1.Amount)) { + // No decimals, convert to integer string + amountString = strconv.Itoa(int(chainConfig.Iden3PaymentRailsRequestV1.Amount)) + } + paymentInfo := protocol.Iden3PaymentRailsRequestV1{ Nonce: nonce.String(), Type: protocol.Iden3PaymentRailsRequestV1Type, - Context: protocol.NewPaymentContextStringCol([]string{ + Context: protocol.NewPaymentContextString( "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", - }), - Amount: fmt.Sprintf("%f", chainConfig.Iden3PaymentRailsRequestV1.Amount), - ExpirationDate: fmt.Sprint(expirationTime.Unix()), + ), + Amount: amountString, + ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), Metadata: metadata, Currency: chainConfig.Iden3PaymentRailsRequestV1.Currency, Recipient: address.String(), - Proof: protocol.NewPaymentProofEip712Signature([]protocol.EthereumEip712Signature2021{ - { + Proof: protocol.PaymentProof{ + protocol.EthereumEip712Signature2021{ Type: "EthereumEip712Signature2021", ProofPurpose: "assertionMethod", - ProofValue: hex.EncodeToString(signature), + ProofValue: fmt.Sprintf("0x%s", hex.EncodeToString(signature)), VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", chainConfig.ChainId, address), Created: time.Now().Format(time.RFC3339), Eip712: protocol.Eip712Data{ - Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", + Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsRequestV1.json", PrimaryType: string(protocol.Iden3PaymentRailsRequestV1Type), Domain: protocol.Eip712Domain{ Name: "MCPayment", @@ -404,8 +397,8 @@ func (p *payment) newIden3PaymentRailsRequestV1( }, }, }, - }), - }) + }, + } return &paymentInfo, nil } @@ -415,37 +408,53 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( chainConfig domain.PaymentOptionConfigChain, setting payments.ChainSettings, expirationTime time.Time, - nonce *big.Int, address common.Address, currency payments.Coin, amount float64, tokenAddress string, signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management -) (*protocol.PaymentRequestInfoData, error) { +) (*protocol.Iden3PaymentRailsERC20RequestV1, error) { + nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) + if err != nil { + log.Error(ctx, "failed to generate nonce", "err", err) + return nil, err + } metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, tokenAddress) + signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, "USDT", expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, tokenAddress) if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err } - - paymentInfo := protocol.NewPaymentRequestInfoDataRailsERC20(protocol.Iden3PaymentRailsERC20RequestV1{ + amountString := fmt.Sprintf("%f", amount) + if amount == float64(int(amount)) { + // No decimals, convert to integer string + amountString = strconv.Itoa(int(amount)) + } + var features []protocol.PaymentFeatures = []protocol.PaymentFeatures{} + if currency == payments.USDC { + features = []protocol.PaymentFeatures{ + "EIP-2612", + } + } + paymentInfo := protocol.Iden3PaymentRailsERC20RequestV1{ Nonce: nonce.String(), Type: protocol.Iden3PaymentRailsERC20RequestV1Type, - Context: protocol.NewPaymentContextStringCol([]string{ + Context: protocol.NewPaymentContextString( "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", "https://w3id.org/security/suites/eip712sig-2021/v1", - }), - Amount: fmt.Sprintf("%f", amount), - ExpirationDate: fmt.Sprint(expirationTime.Unix()), + ), + Amount: amountString, + ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), Metadata: metadata, Currency: string(currency), Recipient: address.String(), - Proof: protocol.NewPaymentProofEip712Signature([]protocol.EthereumEip712Signature2021{ - { + Features: features, + TokenAddress: tokenAddress, + Proof: protocol.PaymentProof{ + protocol.EthereumEip712Signature2021{ Type: "EthereumEip712Signature2021", ProofPurpose: "assertionMethod", - ProofValue: hex.EncodeToString(signature), + ProofValue: fmt.Sprintf("0x%s", hex.EncodeToString(signature)), VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", chainConfig.ChainId, address), Created: time.Now().Format(time.RFC3339), Eip712: protocol.Eip712Data{ @@ -459,8 +468,8 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( }, }, }, - }), - }) + }, + } return &paymentInfo, nil } @@ -470,6 +479,7 @@ func (p *payment) paymentRequestSignature( chainID int, verifContract string, amount float64, + currency string, expTime time.Time, nonce *big.Int, metadata string, @@ -487,18 +497,18 @@ func (p *payment) paymentRequestSignature( ID: signingKeyId, } - typedData, err := typedDataForHashing(paymentType, chainID, verifContract, addr, amount, expTime, nonce, metadata, tokenAddr) + typedData, err := typedDataForHashing(paymentType, chainID, verifContract, addr, amount, currency, expTime, nonce, metadata, tokenAddr) if err != nil { return nil, err } - typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + typedDataBytes, _, err := apitypes.TypedDataAndHash(*typedData) if err != nil { return nil, err } if signingKeyOpt == nil { - signature, err := p.kms.Sign(ctx, keyID, typedDataHash[:]) + signature, err := p.kms.Sign(ctx, keyID, typedDataBytes) if err != nil { log.Error(ctx, "failed to sign typed data hash", "err", err, "keyId", signingKeyId) return nil, err @@ -506,7 +516,8 @@ func (p *payment) paymentRequestSignature( return signature, nil } else { // Temporary solution until we have key management. We use SigningKeyOpt as a private key if present - signature, err := crypto.Sign(typedDataHash[:], signingKeyOpt) + signature, err := crypto.Sign(typedDataBytes, signingKeyOpt) + signature[64] += 27 if err != nil { return nil, err } @@ -514,7 +525,7 @@ func (p *payment) paymentRequestSignature( } } -func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, verifyContract string, address common.Address, amount float64, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { +func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, verifyContract string, address common.Address, amount float64, _ string, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { data := apitypes.TypedData{ Types: apitypes.Types{ "EIP712Domain": []apitypes.Type{ @@ -555,7 +566,7 @@ func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, v }, Message: apitypes.TypedDataMessage{ "recipient": address.String(), - "amount": eth.ToWei(amount), + "amount": int(amount), "expirationDate": fmt.Sprint(expTime.Unix()), "nonce": nonce.String(), "metadata": metadata, From 19c8edeef2219b6c27e9271f213a135630b47141 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Wed, 4 Dec 2024 14:57:55 +0200 Subject: [PATCH 33/66] big int fix --- internal/core/services/payment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 49007a497..f92986884 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -566,7 +566,7 @@ func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, v }, Message: apitypes.TypedDataMessage{ "recipient": address.String(), - "amount": int(amount), + "amount": big.NewInt(int64(amount)), "expirationDate": fmt.Sprint(expTime.Unix()), "nonce": nonce.String(), "metadata": metadata, From f33764d02a47868bc08e92a59c7b14643efaeec6 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Wed, 4 Dec 2024 18:20:23 +0200 Subject: [PATCH 34/66] fix ERC20 type --- internal/core/services/payment.go | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index f92986884..64e208b1b 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -573,10 +573,32 @@ func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, v }, } if paymentType == protocol.Iden3PaymentRailsERC20RequestV1Type { - data.Types[string(paymentType)] = append(data.Types[string(paymentType)], apitypes.Type{ - Name: "tokenAddress", - Type: "address", - }) + data.Types[string(paymentType)] = []apitypes.Type{ + { + Name: "tokenAddress", + Type: "address", + }, + { + Name: "recipient", + Type: "address", + }, + { + Name: "amount", + Type: "uint256", + }, + { + Name: "expirationDate", + Type: "uint256", + }, + { + Name: "nonce", + Type: "uint256", + }, + { + Name: "metadata", + Type: "bytes", + }, + } data.Message["tokenAddress"] = tokenAddress } return &data, nil From 25696448529026976824e323c357ed92418fd3e7 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 4 Dec 2024 18:11:57 +0100 Subject: [PATCH 35/66] feat: Change payment config format file --- internal/api/main_test.go | 62 ++++++++-- internal/payments/settings.go | 116 ++++++++++++++---- internal/payments/settings_test.go | 116 ++++++++++++++---- .../testdata/payment_settings.test.yaml | 60 +++++++-- payment_settings.sample.yaml | 49 ++++++-- 5 files changed, 323 insertions(+), 80 deletions(-) diff --git a/internal/api/main_test.go b/internal/api/main_test.go index 76a31d3fe..6c7dd3fcb 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -285,20 +285,58 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { revocationStatusResolver := revocationstatus.NewRevocationStatusResolver(*networkResolver) paymentSettings, err := payments.SettingsFromReader(common.NewMyYAMLReader([]byte(` -137: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 - ERC20: - USDT: - ContractAddress: 0xc2132D05D31c914a87C6611C10748AEb04B58e8F +80002: + PaymentRails: 0xF8E49b922D5Fb00d3EdD12bd14064f275726D339 + PaymentOption: + - ID: 1 + Name: AmoyNative + Type: Iden3PaymentRailsRequestV1 + - ID: 2 + Name: Amoy USDT + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0x2FE40749812FAC39a0F380649eF59E01bccf3a1A Features: [] - USDC: - ContractAddress: 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 + - ID: 3 + Name: Amoy USDC + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0x2FE40749812FAC39a0F380649eF59E01bccf3a1A Features: - EIP-2612 -80002: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 -1101: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774`, +59141: + PaymentRails: 0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3 + PaymentOption: + - ID: 4 + Name: LineaSepoliaNative + Type: Iden3PaymentRailsRequestV1 + - ID: 5 + Name: Linea Sepolia USDT + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C + Features: [] + - ID: 6 + Name: Linea Sepolia USDC + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C + Features: + - EIP-2612 +2442: + PaymentRails: 0x09c269e74d8B47c98537Acd6CbEe8056806F4c70 + PaymentOption: + - ID: 7 + Name: ZkEvmNative + Type: Iden3PaymentRailsRequestV1 + - ID: 8 + Name: ZkEvm USDT + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db + Features: [] + - ID: 9 + Name: ZkEvm USDC + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db + Features: + - EIP-2612 +`, ))) require.NoError(t, err) @@ -308,7 +346,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { identityService := services.NewIdentity(keyStore, repos.identity, repos.idenMerkleTree, repos.identityState, mtService, qrService, repos.claims, repos.revocation, repos.connection, st, nil, repos.sessions, pubSub, *networkResolver, rhsFactory, revocationStatusResolver) connectionService := services.NewConnection(repos.connection, repos.claims, st) schemaService := services.NewSchema(repos.schemas, schemaLoader) - paymentService := services.NewPaymentService(repos.payments, *networkResolver, *paymentSettings, keyStore) + paymentService := services.NewPaymentService(repos.payments, *networkResolver, &paymentSettings, keyStore) mediaTypeManager := services.NewMediaTypeManager( map[iden3comm.ProtocolMessage][]string{ diff --git a/internal/payments/settings.go b/internal/payments/settings.go index 2c6304c6b..f57c4ed5a 100644 --- a/internal/payments/settings.go +++ b/internal/payments/settings.go @@ -10,37 +10,114 @@ import ( "path/filepath" "strings" + "github.com/ethereum/go-ethereum/common" + "github.com/iden3/iden3comm/v2/protocol" "gopkg.in/yaml.v3" "github.com/polygonid/sh-id-platform/internal/config" "github.com/polygonid/sh-id-platform/internal/log" ) -// Settings holds the payments settings for the different chains -type Settings map[int]ChainSettings +// PaymentOptionConfigID is a custom type to represent the payment option id +type PaymentOptionConfigID int -// ChainSettings holds the settings for a chain -type ChainSettings struct { - MCPayment string `yaml:"MCPayment" json:"MCPayment"` - ERC20 *ERC20 `yaml:"ERC20,omitempty" json:"ERC20,omitempty"` +// Config is a map of payment option id and chain config +type Config map[PaymentOptionConfigID]ChainConfig + +// ChainConfig is the configuration for a chain +type ChainConfig struct { + ChainID int + PaymentRails common.Address + PaymentOption PaymentOptionConfig +} + +// PaymentOptionConfig is the configuration for a payment option +type PaymentOptionConfig struct { + Name string + Type protocol.PaymentRequestType + ContractAddress common.Address + Features []protocol.PaymentFeatures +} + +// configDTO is the data transfer object for the configuration. It maps to payment configuration file +type configDTO map[int]chainConfigDTO + +type chainConfigDTO struct { + PaymentRails string `yaml:"PaymentRails" json:"paymentRails"` + PaymentOptions []paymentOptionConfigDTO `yaml:"PaymentOptions" json:"paymentOptions"` } -// ERC20 holds the settings for the ERC20 tokens -type ERC20 struct { - USDT Token `yaml:"USDT" json:"USDT"` - USDC Token `yaml:"USDC" json:"USDC"` +type paymentOptionConfigDTO struct { + ID int `yaml:"ID" json:"id"` + Name string `yaml:"Name" json:"name"` + Type protocol.PaymentRequestType `yaml:"Type" json:"type"` + ContractAddress string `yaml:"ContractAddress,omitempty" json:"contractAddress,omitempty"` + Features []string `yaml:"Features,omitempty" json:"features,omitempty"` } -// Token holds the settings for a token -type Token struct { - ContractAddress string `yaml:"ContractAddress" json:"contractAddress"` - Features []string `yaml:"Features" json:"features"` +// CustomDecoder wraps yaml.Decoder to add custom functionality +type configDecoder struct { + decoder *yaml.Decoder +} + +// NewCustomDecoder creates a new CustomDecoder +func newConfigDecoder(r io.Reader) *configDecoder { + return &configDecoder{ + decoder: yaml.NewDecoder(r), + } +} + +// Decode parse the payment settings yaml file, do some checks and returns +// a Config object with curated data +// Config object is created from the yaml using the configDTO struct. +// Information is the same but formatted in a more usable way +func (d *configDecoder) Decode() (Config, error) { + var dto configDTO + var cfg Config + + err := d.decoder.Decode(&dto) + if err != nil { + return nil, fmt.Errorf("cannot decode payment settings file: %w", err) + } + cfg = make(Config) + // Converting the dto to a Config object + for id, chainConfig := range dto { + if !common.IsHexAddress(chainConfig.PaymentRails) { + return nil, fmt.Errorf("invalid payment rails address: %s", chainConfig.PaymentRails) + } + for _, option := range chainConfig.PaymentOptions { + if _, found := cfg[PaymentOptionConfigID(id)]; found { + return nil, fmt.Errorf("duplicate payment option id: %d", id) + } + if !common.IsHexAddress(option.ContractAddress) && strings.TrimSpace(option.ContractAddress) != "" { + return nil, fmt.Errorf("invalid PaymentRails address: %s", chainConfig.PaymentRails) + } + var features []protocol.PaymentFeatures + if len(option.Features) > 0 { + features = make([]protocol.PaymentFeatures, len(option.Features)) + for i, feature := range option.Features { + features[i] = protocol.PaymentFeatures(feature) + } + } + cfg[PaymentOptionConfigID(option.ID)] = ChainConfig{ + ChainID: id, + PaymentRails: common.HexToAddress(chainConfig.PaymentRails), + PaymentOption: PaymentOptionConfig{ + Name: option.Name, + Type: option.Type, + ContractAddress: common.HexToAddress(option.ContractAddress), + Features: features, + }, + } + } + } + return cfg, nil } // SettingsFromConfig returns the settings from the configuration // It reads the settings from the file if the path is provided or from the base64 encoded file injected // into the configuration via an environment variable -func SettingsFromConfig(ctx context.Context, cfg *config.Payments) (*Settings, error) { +func SettingsFromConfig(ctx context.Context, cfg *config.Payments) (Config, error) { var reader io.Reader var err error if cfg.SettingsPath != "" { @@ -67,13 +144,8 @@ func settingsFileHasContent(cfg *config.Payments) bool { } // SettingsFromReader reads the settings from a reader -func SettingsFromReader(reader io.Reader) (*Settings, error) { - var settings Settings - decoder := yaml.NewDecoder(reader) - if err := decoder.Decode(&settings); err != nil { - return nil, err - } - return &settings, nil +func SettingsFromReader(reader io.Reader) (Config, error) { + return newConfigDecoder(reader).Decode() } // ReadFileFromPath is a function that returns a reader for the resolver settings file diff --git a/internal/payments/settings_test.go b/internal/payments/settings_test.go index 83ee5afd8..d6371c1a0 100644 --- a/internal/payments/settings_test.go +++ b/internal/payments/settings_test.go @@ -7,10 +7,11 @@ import ( "os" "testing" - "github.com/stretchr/testify/require" - + ethCommon "github.com/ethereum/go-ethereum/common" + "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/common" "github.com/polygonid/sh-id-platform/internal/config" + "github.com/stretchr/testify/require" ) func TestSettingsFromConfig(t *testing.T) { @@ -20,33 +21,103 @@ func TestSettingsFromConfig(t *testing.T) { require.NoError(t, err) fileContentUrlBase64 := base64.StdEncoding.EncodeToString(fileContent) - expectedSettings := Settings{ - 137: { - MCPayment: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", - ERC20: &ERC20{ - USDT: Token{ - ContractAddress: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", - Features: []string{}, - }, - USDC: Token{ - ContractAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", - Features: []string{"EIP-2612"}, - }, + expectedSettings := Config{ + 1: ChainConfig{ + ChainID: 80002, + PaymentRails: ethCommon.HexToAddress("0xF8E49b922D5Fb00d3EdD12bd14064f275726D339"), + PaymentOption: PaymentOptionConfig{ + Name: "AmoyNative", + Type: protocol.Iden3PaymentRailsRequestV1Type, + ContractAddress: ethCommon.HexToAddress(""), + Features: nil, + }, + }, + 2: ChainConfig{ + ChainID: 80002, + PaymentRails: ethCommon.HexToAddress("0xF8E49b922D5Fb00d3EdD12bd14064f275726D339"), + PaymentOption: PaymentOptionConfig{ + Name: "Amoy USDT", + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, + ContractAddress: ethCommon.HexToAddress("0x2FE40749812FAC39a0F380649eF59E01bccf3a1A"), + Features: nil, + }, + }, + 3: ChainConfig{ + ChainID: 80002, + PaymentRails: ethCommon.HexToAddress("0xF8E49b922D5Fb00d3EdD12bd14064f275726D339"), + PaymentOption: PaymentOptionConfig{ + Name: "Amoy USDC", + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, + ContractAddress: ethCommon.HexToAddress("0x2FE40749812FAC39a0F380649eF59E01bccf3a1A"), + Features: []protocol.PaymentFeatures{"EIP-2612"}, + }, + }, + 4: ChainConfig{ + ChainID: 59141, + PaymentRails: ethCommon.HexToAddress("0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3"), + PaymentOption: PaymentOptionConfig{ + Name: "LineaSepoliaNative", + Type: protocol.Iden3PaymentRailsRequestV1Type, + ContractAddress: ethCommon.HexToAddress(""), + Features: nil, }, }, - 80002: { - MCPayment: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + 5: ChainConfig{ + ChainID: 59141, + PaymentRails: ethCommon.HexToAddress("0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3"), + PaymentOption: PaymentOptionConfig{ + Name: "Linea Sepolia USDT", + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, + ContractAddress: ethCommon.HexToAddress("0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C"), + Features: nil, + }, }, - 1101: { - MCPayment: "0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774", + 6: ChainConfig{ + ChainID: 59141, + PaymentRails: ethCommon.HexToAddress("0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3"), + PaymentOption: PaymentOptionConfig{ + Name: "Linea Sepolia USDC", + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, + ContractAddress: ethCommon.HexToAddress("0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C"), + Features: []protocol.PaymentFeatures{"EIP-2612"}, + }, + }, + 7: ChainConfig{ + ChainID: 2442, + PaymentRails: ethCommon.HexToAddress("0x09c269e74d8B47c98537Acd6CbEe8056806F4c70"), + PaymentOption: PaymentOptionConfig{ + Name: "ZkEvmNative", + Type: protocol.Iden3PaymentRailsRequestV1Type, + ContractAddress: ethCommon.HexToAddress(""), + Features: nil, + }, + }, + 8: ChainConfig{ + ChainID: 2442, + PaymentRails: ethCommon.HexToAddress("0x09c269e74d8B47c98537Acd6CbEe8056806F4c70"), + PaymentOption: PaymentOptionConfig{ + Name: "ZkEvm USDT", + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, + ContractAddress: ethCommon.HexToAddress("0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db"), + Features: nil, + }, + }, + 9: ChainConfig{ + ChainID: 2442, + PaymentRails: ethCommon.HexToAddress("0x09c269e74d8B47c98537Acd6CbEe8056806F4c70"), + PaymentOption: PaymentOptionConfig{ + Name: "ZkEvm USDC", + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, + ContractAddress: ethCommon.HexToAddress("0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db"), + Features: []protocol.PaymentFeatures{"EIP-2612"}, + }, }, } type expected struct { err error - settings Settings + settings Config } - for _, tc := range []struct { name string cfg config.Payments @@ -99,7 +170,7 @@ func TestSettingsFromConfig(t *testing.T) { SettingsFile: common.ToPointer(string(fileContent)), }, expected: expected{ - err: errors.New("illegal base64 data at input byte 3"), + err: errors.New("illegal base64 data at input byte 5"), }, }, { @@ -115,7 +186,8 @@ func TestSettingsFromConfig(t *testing.T) { t.Run(tc.name, func(t *testing.T) { settings, err := SettingsFromConfig(ctx, &tc.cfg) if tc.expected.err == nil { - require.EqualValues(t, &tc.expected.settings, settings) + require.NoError(t, err) + require.EqualValues(t, tc.expected.settings, settings) } else { require.Equal(t, tc.expected.err.Error(), err.Error()) } diff --git a/internal/payments/testdata/payment_settings.test.yaml b/internal/payments/testdata/payment_settings.test.yaml index 5f4fd985f..537d71f23 100644 --- a/internal/payments/testdata/payment_settings.test.yaml +++ b/internal/payments/testdata/payment_settings.test.yaml @@ -1,14 +1,52 @@ -137: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 - ERC20: - USDT: - ContractAddress: 0xc2132D05D31c914a87C6611C10748AEb04B58e8F +80002: + PaymentRails: 0xF8E49b922D5Fb00d3EdD12bd14064f275726D339 + PaymentOptions: + - ID: 1 + Name: AmoyNative + Type: Iden3PaymentRailsRequestV1 + - ID: 2 + Name: Amoy USDT + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0x2FE40749812FAC39a0F380649eF59E01bccf3a1A Features: [] - USDC: - ContractAddress: 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 + - ID: 3 + Name: Amoy USDC + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0x2FE40749812FAC39a0F380649eF59E01bccf3a1A Features: - EIP-2612 -80002: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 -1101: - MCPayment: 0x380dd90852d3Fe75B4f08D0c47416D6c4E0dC774 \ No newline at end of file +59141: + PaymentRails: 0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3 + PaymentOptions: + - ID: 4 + Name: LineaSepoliaNative + Type: Iden3PaymentRailsRequestV1 + - ID: 5 + Name: Linea Sepolia USDT + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C + Features: [] + - ID: 6 + Name: Linea Sepolia USDC + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C + Features: + - EIP-2612 +2442: + PaymentRails: 0x09c269e74d8B47c98537Acd6CbEe8056806F4c70 + PaymentOptions: + - ID: 7 + Name: ZkEvmNative + Type: Iden3PaymentRailsRequestV1 + - ID: 8 + Name: ZkEvm USDT + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db + Features: [] + - ID: 9 + Name: ZkEvm USDC + Type: Iden3PaymentRailsERC20RequestV1 + ContractAddress: 0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db + Features: + - EIP-2612 + diff --git a/payment_settings.sample.yaml b/payment_settings.sample.yaml index 38b3787a5..7c73c6b24 100644 --- a/payment_settings.sample.yaml +++ b/payment_settings.sample.yaml @@ -1,30 +1,53 @@ 80002: - MCPayment: 0xF8E49b922D5Fb00d3EdD12bd14064f275726D339 - ERC20: - USDT: + PaymentRails: 0xF8E49b922D5Fb00d3EdD12bd14064f275726D339 + PaymentOptions: + - ID: 1 + Name: AmoyNative + Type: Iden3PaymentRailsRequestV1 + - ID: 2 + Name: Amoy USDT + Type: Iden3PaymentRailsERC20RequestV1 ContractAddress: 0x2FE40749812FAC39a0F380649eF59E01bccf3a1A Features: [] - USDC: + - ID: 3 + Name: Amoy USDC + Type: Iden3PaymentRailsERC20RequestV1 ContractAddress: 0x2FE40749812FAC39a0F380649eF59E01bccf3a1A Features: - EIP-2612 59141: - MCPayment: 0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3 - ERC20: - USDT: + PaymentRails: 0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3 + PaymentOptions: + - ID: 4 + Name: LineaSepoliaNative + Type: Iden3PaymentRailsRequestV1 + - ID: 5 + Name: Linea Sepolia USDT + Type: Iden3PaymentRailsERC20RequestV1 ContractAddress: 0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C Features: [] - USDC: + - ID: 6 + Name: Linea Sepolia USDC + Type: Iden3PaymentRailsERC20RequestV1 ContractAddress: 0xb0101c1Ffdd1213B886FebeF6F07442e48990c9C Features: - EIP-2612 2442: - MCPayment: 0x09c269e74d8B47c98537Acd6CbEe8056806F4c70 - ERC20: - USDT: + PaymentRails: 0x09c269e74d8B47c98537Acd6CbEe8056806F4c70 + PaymentOptions: + - ID: 7 + Name: ZkEvmNative + Type: Iden3PaymentRailsRequestV1 + - ID: 8 + Name: ZkEvm USDT + Type: Iden3PaymentRailsERC20RequestV1 + Currency: USDT ContractAddress: 0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db Features: [] - USDC: + - ID: 9 + Name: ZkEvm USDC + Type: Iden3PaymentRailsERC20RequestV1 ContractAddress: 0x986caE6ADcF5da2a1514afc7317FBdeE0B4048Db Features: - - EIP-2612 \ No newline at end of file + - EIP-2612 + From 7dcd1c655d2d6db804d40c410281202b71872220 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 4 Dec 2024 18:15:05 +0100 Subject: [PATCH 36/66] feat: Remove hack to pass private key to sign payment requests --- internal/api/payment_test.go | 15 ++++++--------- internal/core/domain/payment.go | 14 ++++---------- internal/core/services/payment.go | 27 +++++---------------------- 3 files changed, 15 insertions(+), 41 deletions(-) diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 0e3007107..c58f85e76 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -15,7 +15,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/polygonid/sh-id-platform/internal/common" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/kms" @@ -553,10 +552,9 @@ func TestServer_CreatePaymentRequest(t *testing.T) { configUnsecure := domain.PaymentOptionConfig{ Chains: []domain.PaymentOptionConfigChain{ { - ChainId: 1101, - Recipient: "0x..", - SigningKeyId: signingKeyID.ID, - SigningKeyOpt: common.ToPointer("d1f13a3d23b8a3c5b76fbd62c29e86a3b1cd0e3ef564d11a5e55aab544ce33a4"), + ChainId: 1101, + Recipient: "0x..", + SigningKeyId: signingKeyID.ID, Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ Amount: 0.5, Currency: "ETH", @@ -564,10 +562,9 @@ func TestServer_CreatePaymentRequest(t *testing.T) { Iden3PaymentRailsERC20RequestV1: nil, }, { - ChainId: 137, - Recipient: "0x..", - SigningKeyId: signingKeyID.ID, - SigningKeyOpt: common.ToPointer("d1f13a3d23b8a3c5b76fbd62c29e86a3b1cd0e3ef564d11a5e55aab544ce33a4"), + ChainId: 137, + Recipient: "0x..", + SigningKeyId: signingKeyID.ID, Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ Amount: 0.01, Currency: "POL", diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 3e4f1607d..990ba50ac 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -56,22 +56,16 @@ func NewPaymentOption(issuerDID w3c.DID, name string, description string, config } // PaymentOptionConfig represents the configuration of a payment option +// TODO: Refactor this with new format!!!!! type PaymentOptionConfig struct { Chains []PaymentOptionConfigChain `json:"Chains"` } // PaymentOptionConfigChain represents the configuration of a payment option chain type PaymentOptionConfigChain struct { - ChainId int `json:"ChainId"` - Recipient string `json:"Recipient"` - SigningKeyId string `json:"SigningKeyId"` - - // TODO: remove field SigningKeyOpt once we have key management. - // Temporary solution until we have key management - // WARNING: Only for testing purposes. This is not secure as SigningKey will be stored in the database - // USE AT YOUR OWN RISK - SigningKeyOpt *string `json:"SigningKeyOpt"` - + ChainId int `json:"ChainId"` + Recipient string `json:"Recipient"` + SigningKeyId string `json:"SigningKeyId"` Iden3PaymentRailsRequestV1 *PaymentOptionConfigChainIden3PaymentRailsRequestV1 `json:"Iden3PaymentRailsRequestV1"` Iden3PaymentRailsERC20RequestV1 *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 `json:"Iden3PaymentRailsERC20RequestV1"` } diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index f92986884..6d50013f3 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -110,29 +110,12 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay var address common.Address var privateKey *ecdsa.PrivateKey - if chainConfig.SigningKeyOpt == nil { - pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyId, Type: kms.KeyTypeEthereum}) - if err != nil { - log.Error(ctx, "failed to get kms signing key", "err", err, "keyId", chainConfig.SigningKeyId) - return nil, fmt.Errorf("kms signing key not found: %w", err) - } - address = crypto.PubkeyToAddress(*pubKey) - } else { // Temporary solution until we have key management. We use SigningKeyOpt as a private key if present - privateKeyBytes, err := hex.DecodeString(*chainConfig.SigningKeyOpt) - if err != nil { - return nil, err - } - privateKey, err = crypto.ToECDSA(privateKeyBytes) - if err != nil { - return nil, err - } - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return nil, fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") - } - address = crypto.PubkeyToAddress(*publicKeyECDSA) + pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyId, Type: kms.KeyTypeEthereum}) + if err != nil { + log.Error(ctx, "failed to get kms signing key", "err", err, "keyId", chainConfig.SigningKeyId) + return nil, fmt.Errorf("kms signing key not found: %w", err) } + address = crypto.PubkeyToAddress(*pubKey) if chainConfig.Iden3PaymentRailsRequestV1 != nil { nativeToken, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, address, privateKey) From a2c6520680c49df3f8b0d1b979b1488e4b22c855 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Thu, 5 Dec 2024 11:55:19 +0100 Subject: [PATCH 37/66] feat: config and payments options management --- api/api.yaml | 67 ++-- cmd/platform/main.go | 2 +- internal/api/api.gen.go | 40 ++- internal/api/main_test.go | 2 +- internal/api/payment.go | 23 +- internal/api/payment_test.go | 405 ++++++++++--------------- internal/api/responses.go | 19 +- internal/core/domain/payment.go | 18 +- internal/core/ports/payment_service.go | 2 +- internal/core/services/payment.go | 274 +++++++++-------- internal/payments/settings.go | 8 +- internal/payments/settings_test.go | 5 +- internal/repositories/payment_test.go | 6 +- 13 files changed, 431 insertions(+), 440 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 1fe14a6b1..a735d314e 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1338,7 +1338,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PaymentSettings' + $ref: '#/components/schemas/PaymentOptionConfig' /v2/identities/{identifier}/payment/options: get: @@ -2184,13 +2184,6 @@ components: AgentResponse: $ref: '#/components/schemas/BasicMessage' - PaymentSettings: - type: object - x-go-type: payments.Settings - x-go-type-import: - name: payments - path: "github.com/polygonid/sh-id-platform/internal/payments" - PaymentOptionsPaginated: type: object required: [ items, meta ] @@ -2218,7 +2211,7 @@ components: type: object required: - id - - issuerID + - issuerDID - name - description - config @@ -2231,24 +2224,46 @@ components: x-go-type-import: name: uuid path: github.com/google/uuid - issuerID: + issuerDID: type: string name: type: string description: type: string - # TODO: Convert config into a DTO object when format is more clear. config: - type: object - x-go-type: domain.PaymentOptionConfig - x-go-type-import: - name: domain - path: "github.com/polygonid/sh-id-platform/internal/core/domain" + $ref: '#/components/schemas/PaymentOptionConfig' modifiedAt: $ref: '#/components/schemas/TimeUTC' createdAt: $ref: '#/components/schemas/TimeUTC' + PaymentOptionConfig: + type: object + required: + - config + properties: + config: + type: array + items: + $ref: '#/components/schemas/PaymentOptionConfigItem' + + PaymentOptionConfigItem: + type: object + required: + - paymentOptionId + - amount + - Recipient + - SigningKeyId + properties: + paymentOptionId: + type: integer + amount: + type: string + Recipient: + type: string + SigningKeyId: + type: string + PaymentOptionRequest: type: object required: @@ -2264,13 +2279,8 @@ components: type: string description: type: string - # TODO: Convert config into a DTO object when format is more clear. config: - type: object - x-go-type: domain.PaymentOptionConfig - x-go-type-import: - name: domain - path: "github.com/polygonid/sh-id-platform/internal/core/domain" + $ref: '#/components/schemas/PaymentOptionConfig' CreatePaymentRequest: type: object @@ -2301,6 +2311,19 @@ components: type: string example: "" + # TODO: Add more fields!!! + CreatePaymentRequestResponse: + type: object + required: + - id + properties: + id: + type: string + x-go-type: uuid.UUID + x-go-type-import: + name: uuid + path: github.com/google/uuid + TimeUTC: type: string x-go-type: timeapi.Time diff --git a/cmd/platform/main.go b/cmd/platform/main.go index 05fb23231..ad6cb3e2b 100644 --- a/cmd/platform/main.go +++ b/cmd/platform/main.go @@ -159,7 +159,7 @@ func main() { proofService := services.NewProver(circuitsLoaderService) schemaService := services.NewSchema(schemaRepository, schemaLoader) linkService := services.NewLinkService(storage, claimsService, qrService, claimsRepository, linkRepository, schemaRepository, schemaLoader, sessionRepository, ps, identityService, *networkResolver, cfg.UniversalLinks) - paymentService := services.NewPaymentService(paymentsRepo, *networkResolver, *paymentSettings, keyStore) + paymentService := services.NewPaymentService(paymentsRepo, *networkResolver, paymentSettings, keyStore) transactionService, err := gateways.NewTransaction(*networkResolver) if err != nil { diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 62b80df0d..a305ca993 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -17,8 +17,6 @@ import ( protocol "github.com/iden3/iden3comm/v2/protocol" "github.com/oapi-codegen/runtime" strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" - domain "github.com/polygonid/sh-id-platform/internal/core/domain" - payments "github.com/polygonid/sh-id-platform/internal/payments" timeapi "github.com/polygonid/sh-id-platform/internal/timeapi" ) @@ -479,20 +477,33 @@ type PaymentMessage = protocol.PaymentMessage // PaymentOption defines model for PaymentOption. type PaymentOption struct { - Config domain.PaymentOptionConfig `json:"config"` - CreatedAt TimeUTC `json:"createdAt"` - Description string `json:"description"` - Id uuid.UUID `json:"id"` - IssuerID string `json:"issuerID"` - ModifiedAt TimeUTC `json:"modifiedAt"` - Name string `json:"name"` + Config PaymentOptionConfig `json:"config"` + CreatedAt TimeUTC `json:"createdAt"` + Description string `json:"description"` + Id uuid.UUID `json:"id"` + IssuerDID string `json:"issuerDID"` + ModifiedAt TimeUTC `json:"modifiedAt"` + Name string `json:"name"` +} + +// PaymentOptionConfig defines model for PaymentOptionConfig. +type PaymentOptionConfig struct { + Config []PaymentOptionConfigItem `json:"config"` +} + +// PaymentOptionConfigItem defines model for PaymentOptionConfigItem. +type PaymentOptionConfigItem struct { + Recipient string `json:"Recipient"` + SigningKeyId string `json:"SigningKeyId"` + Amount string `json:"amount"` + PaymentOptionId int `json:"paymentOptionId"` } // PaymentOptionRequest defines model for PaymentOptionRequest. type PaymentOptionRequest struct { - Config domain.PaymentOptionConfig `json:"config"` - Description string `json:"description"` - Name string `json:"name"` + Config PaymentOptionConfig `json:"config"` + Description string `json:"description"` + Name string `json:"name"` } // PaymentOptions defines model for PaymentOptions. @@ -504,9 +515,6 @@ type PaymentOptionsPaginated struct { Meta PaginatedMetadata `json:"meta"` } -// PaymentSettings defines model for PaymentSettings. -type PaymentSettings = payments.Settings - // PaymentStatus defines model for PaymentStatus. type PaymentStatus struct { Status PaymentStatusStatus `json:"status"` @@ -5042,7 +5050,7 @@ type GetPaymentSettingsResponseObject interface { VisitGetPaymentSettingsResponse(w http.ResponseWriter) error } -type GetPaymentSettings200JSONResponse PaymentSettings +type GetPaymentSettings200JSONResponse PaymentOptionConfig func (response GetPaymentSettings200JSONResponse) VisitGetPaymentSettingsResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") diff --git a/internal/api/main_test.go b/internal/api/main_test.go index 6c7dd3fcb..aa683f1c9 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -346,7 +346,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { identityService := services.NewIdentity(keyStore, repos.identity, repos.idenMerkleTree, repos.identityState, mtService, qrService, repos.claims, repos.revocation, repos.connection, st, nil, repos.sessions, pubSub, *networkResolver, rhsFactory, revocationStatusResolver) connectionService := services.NewConnection(repos.connection, repos.claims, st) schemaService := services.NewSchema(repos.schemas, schemaLoader) - paymentService := services.NewPaymentService(repos.payments, *networkResolver, &paymentSettings, keyStore) + paymentService := services.NewPaymentService(repos.payments, *networkResolver, paymentSettings, keyStore) mediaTypeManager := services.NewMediaTypeManager( map[iden3comm.ProtocolMessage][]string{ diff --git a/internal/api/payment.go b/internal/api/payment.go index be7531b86..fe6f9ba3a 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -5,9 +5,11 @@ import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/log" "github.com/polygonid/sh-id-platform/internal/repositories" @@ -48,7 +50,7 @@ func (s *Server) CreatePaymentOption(ctx context.Context, request CreatePaymentO log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) return CreatePaymentOption400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil } - id, err := s.paymentService.CreatePaymentOption(ctx, issuerDID, request.Body.Name, request.Body.Description, &request.Body.Config) + id, err := s.paymentService.CreatePaymentOption(ctx, issuerDID, request.Body.Name, request.Body.Description, newPaymentOptionConfig(&request.Body.Config)) if err != nil { log.Error(ctx, "creating payment option", "err", err, "issuer", issuerDID, "request", request.Body) if errors.Is(err, repositories.ErrIdentityNotFound) { @@ -149,7 +151,9 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment // GetPaymentSettings is the controller to get payment settings func (s *Server) GetPaymentSettings(_ context.Context, _ GetPaymentSettingsRequestObject) (GetPaymentSettingsResponseObject, error) { - return GetPaymentSettings200JSONResponse(s.paymentService.GetSettings()), nil + // TODO: Implement + // return GetPaymentSettings200JSONResponse(s.paymentService.GetSettings()), nil + return GetPaymentSettings200JSONResponse{}, nil } // VerifyPayment is the controller to verify payment @@ -164,3 +168,18 @@ func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequest } return VerifyPayment200JSONResponse{Status: PaymentStatusStatusSuccess}, nil } + +func newPaymentOptionConfig(config *PaymentOptionConfig) *domain.PaymentOptionConfig { + cfg := &domain.PaymentOptionConfig{ + Config: make([]domain.PaymentOptionConfigItem, len(config.Config)), + } + for i, item := range config.Config { + cfg.Config[i] = domain.PaymentOptionConfigItem{ + PaymentOptionID: item.PaymentOptionId, + Amount: item.Amount, + Recipient: common.HexToAddress(item.Recipient), + SigningKeyID: item.SigningKeyId, + } + } + return cfg +} diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index c58f85e76..a649d5ed3 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -9,45 +9,32 @@ import ( "net/http/httptest" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/iden3comm/v2/protocol" + "github.com/polygonid/sh-id-platform/internal/kms" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" - "github.com/polygonid/sh-id-platform/internal/kms" ) const paymentOptionConfigurationTesting = ` { - "Chains": [ + "Config": [ { - "ChainId": 137, - "Recipient": "0x..", - "SigningKeyId": "testSigninKey1", - "Iden3PaymentRailsRequestV1": { - "Amount": "0.01", - "Currency": "POL" - }, - "Iden3PaymentRailsERC20RequestV1": { - "USDT": { - "Amount": "3" - }, - "USDC": { - "Amount": "3" - } - } + "paymentOptionId": "1", + "amount": "500000000000000000", + "Recipient": "0x1..", + "SigningKeyId": "pubId" }, { - "ChainId": 1101, - "Recipient": "0x..", - "SigningKeyId": "testSigninKey2", - "Iden3PaymentRailsRequestV1": { - "Amount": "0.5", - "Currency": "ETH" - } + "paymentOptionId": "2", + "amount": "1500000000000000000", + "Recipient": "0x2..", + "SigningKeyId": "pubId" } ] } @@ -78,7 +65,7 @@ func TestServer_CreatePaymentOption(t *testing.T) { BJJ = "BJJ" ) - var config domain.PaymentOptionConfig + var config PaymentOptionConfig ctx := context.Background() server := newTestServer(t, nil) @@ -181,7 +168,9 @@ func TestServer_GetPaymentOption(t *testing.T) { BJJ = "BJJ" ) - var config domain.PaymentOptionConfig + var config PaymentOptionConfig + var domainConfig domain.PaymentOptionConfig + ctx := context.Background() server := newTestServer(t, nil) @@ -196,14 +185,15 @@ func TestServer_GetPaymentOption(t *testing.T) { require.NoError(t, err) require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) + require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &domainConfig)) - optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", &config) + optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", &domainConfig) require.NoError(t, err) type expected struct { httpCode int msg string - option domain.PaymentOption + option PaymentOption } for _, tc := range []struct { @@ -229,12 +219,12 @@ func TestServer_GetPaymentOption(t *testing.T) { optionID: optionID, expected: expected{ httpCode: http.StatusOK, - option: domain.PaymentOption{ - ID: optionID, - IssuerDID: *issuerDID, + option: PaymentOption{ + Id: optionID, + IssuerDID: issuerDID.String(), Name: "1 POL Payment", Description: "Payment Option explanation", - Config: &config, + Config: config, }, }, }, @@ -276,7 +266,7 @@ func TestServer_GetPaymentOption(t *testing.T) { assert.Equal(t, tc.optionID, response.Id) assert.Equal(t, tc.expected.option.Name, response.Name) assert.Equal(t, tc.expected.option.Description, response.Description) - assert.Equal(t, tc.expected.option.Config, &response.Config) + assert.Equal(t, tc.expected.option.Config, response.Config) case http.StatusNotFound: var response GetPaymentOption404JSONResponse @@ -511,76 +501,13 @@ func TestServer_CreatePaymentRequest(t *testing.T) { signingKeyID, err := keyStore.CreateKey(kms.KeyTypeEthereum, issuerDID) require.NoError(t, err) - // Creating a payment config using previously created key config := domain.PaymentOptionConfig{ - Chains: []domain.PaymentOptionConfigChain{ + Config: []domain.PaymentOptionConfigItem{ { - ChainId: 1101, - Recipient: "0x..", - SigningKeyId: signingKeyID.ID, - Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ - Amount: 0.5, - Currency: "ETH", - }, - Iden3PaymentRailsERC20RequestV1: nil, - }, - { - ChainId: 137, - Recipient: "0x..", - SigningKeyId: signingKeyID.ID, - Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ - Amount: 0.01, - Currency: "POL", - }, - Iden3PaymentRailsERC20RequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1{ - USDT: struct { - Amount float64 `json:"Amount"` - }{ - Amount: 5.2, - }, - USDC: struct { - Amount float64 `json:"Amount"` - }{ - Amount: 4.3, - }, - }, - }, - }, - } - - // Payment option with private key inside payment for temporary solution - configUnsecure := domain.PaymentOptionConfig{ - Chains: []domain.PaymentOptionConfigChain{ - { - ChainId: 1101, - Recipient: "0x..", - SigningKeyId: signingKeyID.ID, - Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ - Amount: 0.5, - Currency: "ETH", - }, - Iden3PaymentRailsERC20RequestV1: nil, - }, - { - ChainId: 137, - Recipient: "0x..", - SigningKeyId: signingKeyID.ID, - Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ - Amount: 0.01, - Currency: "POL", - }, - Iden3PaymentRailsERC20RequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1{ - USDT: struct { - Amount float64 `json:"Amount"` - }{ - Amount: 5.2, - }, - USDC: struct { - Amount float64 `json:"Amount"` - }{ - Amount: 4.3, - }, - }, + PaymentOptionID: 1, + Amount: "333", + Recipient: common.Address{}, + SigningKeyID: signingKeyID.ID, }, }, } @@ -588,9 +515,6 @@ func TestServer_CreatePaymentRequest(t *testing.T) { paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) require.NoError(t, err) - paymentOptionIDUnsecure, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &configUnsecure) - require.NoError(t, err) - type expected struct { httpCode int msg string @@ -667,28 +591,6 @@ func TestServer_CreatePaymentRequest(t *testing.T) { count: 10, }, }, - { - name: "Happy Path with with unsecure payment option including private key", - auth: authOk, - issuerDID: *issuerDID, - body: CreatePaymentRequestJSONRequestBody{ - UserDID: receiverDID.String(), - Option: paymentOptionIDUnsecure, - Credentials: []struct { - Context string `json:"context"` - Type string `json:"type"` - }{ - { - Context: "context", - Type: "type", - }, - }, - }, - expected: expected{ - httpCode: http.StatusCreated, - count: 10, - }, - }, } { t.Run(tc.name, func(t *testing.T) { rr := httptest.NewRecorder() @@ -730,141 +632,144 @@ func TestServer_CreatePaymentRequest(t *testing.T) { // TODO: Review this test!! func TestServer_VerifyPayment(t *testing.T) { - const ( - method = "polygonid" - blockchain = "polygon" - network = "amoy" - BJJ = "BJJ" - ) - ctx := context.Background() - server := newTestServer(t, nil) - handler := getHandler(ctx, server) - - iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) - require.NoError(t, err) - issuerDID, err := w3c.ParseDID(iden.Identifier) - require.NoError(t, err) + /* + const ( + method = "polygonid" + blockchain = "polygon" + network = "amoy" + BJJ = "BJJ" + ) + ctx := context.Background() + server := newTestServer(t, nil) + handler := getHandler(ctx, server) + + iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) + require.NoError(t, err) + issuerDID, err := w3c.ParseDID(iden.Identifier) + require.NoError(t, err) - receiverDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") - require.NoError(t, err) + receiverDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") + require.NoError(t, err) - _ = receiverDID + _ = receiverDID - // Creating an ethereum key - signingKeyID, err := keyStore.CreateKey(kms.KeyTypeEthereum, issuerDID) - require.NoError(t, err) + // Creating an ethereum key + signingKeyID, err := keyStore.CreateKey(kms.KeyTypeEthereum, issuerDID) + require.NoError(t, err) - // Creating a payment config using previously created key - config := domain.PaymentOptionConfig{ - Chains: []domain.PaymentOptionConfigChain{ - { - ChainId: 1101, - Recipient: "0x1101...", - SigningKeyId: signingKeyID.ID, - Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ - Amount: 0.5, - Currency: "ETH", - }, - Iden3PaymentRailsERC20RequestV1: nil, - }, - { - ChainId: 137, - Recipient: "0x137...", - SigningKeyId: signingKeyID.ID, - Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ - Amount: 0.01, - Currency: "POL", + // Creating a payment config using previously created key + config := domain.PaymentOptionConfig{ + Chains: []domain.PaymentOptionConfigChain{ + { + ChainId: 1101, + Recipient: "0x1101...", + SigningKeyID: signingKeyID.ID, + Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ + Amount: 0.5, + Currency: "ETH", + }, + Iden3PaymentRailsERC20RequestV1: nil, }, - Iden3PaymentRailsERC20RequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1{ - USDT: struct { - Amount float64 `json:"Amount"` - }{ - Amount: 5.2, + { + ChainId: 137, + Recipient: "0x137...", + SigningKeyID: signingKeyID.ID, + Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ + Amount: 0.01, + Currency: "POL", }, - USDC: struct { - Amount float64 `json:"Amount"` - }{ - Amount: 4.3, + Iden3PaymentRailsERC20RequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1{ + USDT: struct { + Amount float64 `json:"Amount"` + }{ + Amount: 5.2, + }, + USDC: struct { + Amount float64 `json:"Amount"` + }{ + Amount: 4.3, + }, }, }, }, - }, - } - - paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) - require.NoError(t, err) + } - type expected struct { - httpCode int - msg string - } + paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) + require.NoError(t, err) - for _, tc := range []struct { - name string - issuerDID w3c.DID - auth func() (string, string) - PaymentOptionID uuid.UUID - body protocol.PaymentMessage - expected expected - }{ - { - name: "Happy Path", - auth: authOk, - issuerDID: *issuerDID, - PaymentOptionID: paymentOptionID, - body: protocol.PaymentMessage{ - ID: uuid.New().String(), - Typ: "application/iden3comm-plain-json", - Type: "https://iden3-communication.io/credentials/0.1/payment", - ThreadID: uuid.New().String(), - From: "did:iden3:polygon:mumbai:x3HstHLj2rTp6HHXk2WczYP7w3rpCsRbwCMeaQ2H2", - To: "did:polygonid:polygon:mumbai:2qJUZDSCFtpR8QvHyBC4eFm6ab9sJo5rqPbcaeyGC4", - Body: protocol.PaymentMessageBody{ - Payments: []protocol.Payment{protocol.NewPaymentRails(protocol.Iden3PaymentRailsV1{ - Nonce: "123", - Type: "Iden3PaymentRailsV1", - Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), - PaymentData: struct { - TxID string `json:"txId"` - ChainID string `json:"chainId"` - }{ - TxID: "0x123", - ChainID: "137", - }, - })}, + type expected struct { + httpCode int + msg string + } + + for _, tc := range []struct { + name string + issuerDID w3c.DID + auth func() (string, string) + PaymentOptionID uuid.UUID + body protocol.PaymentMessage + expected expected + }{ + { + name: "Happy Path", + auth: authOk, + issuerDID: *issuerDID, + PaymentOptionID: paymentOptionID, + body: protocol.PaymentMessage{ + ID: uuid.New().String(), + Typ: "application/iden3comm-plain-json", + Type: "https://iden3-communication.io/credentials/0.1/payment", + ThreadID: uuid.New().String(), + From: "did:iden3:polygon:mumbai:x3HstHLj2rTp6HHXk2WczYP7w3rpCsRbwCMeaQ2H2", + To: "did:polygonid:polygon:mumbai:2qJUZDSCFtpR8QvHyBC4eFm6ab9sJo5rqPbcaeyGC4", + Body: protocol.PaymentMessageBody{ + Payments: []protocol.Payment{protocol.NewPaymentRails(protocol.Iden3PaymentRailsV1{ + Nonce: "123", + Type: "Iden3PaymentRailsV1", + Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), + PaymentData: struct { + TxID string `json:"txId"` + ChainID string `json:"chainId"` + }{ + TxID: "0x123", + ChainID: "137", + }, + })}, + }, + }, + expected: expected{ + httpCode: http.StatusOK, }, }, - expected: expected{ - httpCode: http.StatusOK, - }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - rr := httptest.NewRecorder() - payload, err := json.Marshal(tc.body) - require.NoError(t, err) - url := fmt.Sprintf("/v2/payment/verify/%s", tc.PaymentOptionID) - req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payload)) - assert.NoError(t, err) - req.SetBasicAuth(tc.auth()) - - handler.ServeHTTP(rr, req) - require.Equal(t, tc.expected.httpCode, rr.Code) - - switch tc.expected.httpCode { - case http.StatusCreated: - var response VerifyPayment200JSONResponse - require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) - - case http.StatusBadRequest: - var response VerifyPayment400JSONResponse - require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) - assert.Equal(t, tc.expected.msg, response.Message) - case http.StatusInternalServerError: - var response VerifyPayment500JSONResponse - require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) - assert.Equal(t, tc.expected.msg, response.Message) - } - }) - } + } { + t.Run(tc.name, func(t *testing.T) { + rr := httptest.NewRecorder() + payload, err := json.Marshal(tc.body) + require.NoError(t, err) + url := fmt.Sprintf("/v2/payment/verify/%s", tc.PaymentOptionID) + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payload)) + assert.NoError(t, err) + req.SetBasicAuth(tc.auth()) + + handler.ServeHTTP(rr, req) + require.Equal(t, tc.expected.httpCode, rr.Code) + + switch tc.expected.httpCode { + case http.StatusCreated: + var response VerifyPayment200JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + + case http.StatusBadRequest: + var response VerifyPayment400JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + case http.StatusInternalServerError: + var response VerifyPayment500JSONResponse + require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) + assert.Equal(t, tc.expected.msg, response.Message) + } + }) + } + + */ } diff --git a/internal/api/responses.go b/internal/api/responses.go index e647e39c0..b10807314 100644 --- a/internal/api/responses.go +++ b/internal/api/responses.go @@ -305,11 +305,26 @@ func toPaymentOption(opt *domain.PaymentOption) (PaymentOption, error) { } return PaymentOption{ Id: opt.ID, - IssuerID: opt.IssuerDID.String(), + IssuerDID: opt.IssuerDID.String(), Name: opt.Name, Description: opt.Description, - Config: *opt.Config, + Config: toPaymentOptionConfig(opt.Config), CreatedAt: TimeUTC(opt.CreatedAt), ModifiedAt: TimeUTC(opt.UpdatedAt), }, nil } + +func toPaymentOptionConfig(config domain.PaymentOptionConfig) PaymentOptionConfig { + cfg := PaymentOptionConfig{ + Config: make([]PaymentOptionConfigItem, len(config.Config)), + } + for i, item := range config.Config { + cfg.Config[i] = PaymentOptionConfigItem{ + PaymentOptionId: item.PaymentOptionID, + Amount: item.Amount, + Recipient: item.Recipient.String(), + SigningKeyId: item.SigningKeyID, + } + } + return cfg +} diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 990ba50ac..6dd9fa434 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -7,6 +7,7 @@ import ( "strconv" "time" + "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/iden3comm/v2/protocol" @@ -37,7 +38,7 @@ type PaymentOption struct { IssuerDID w3c.DID Name string Description string - Config *PaymentOptionConfig + Config PaymentOptionConfig CreatedAt time.Time UpdatedAt time.Time } @@ -49,23 +50,30 @@ func NewPaymentOption(issuerDID w3c.DID, name string, description string, config IssuerDID: issuerDID, Name: name, Description: description, - Config: config, + Config: *config, CreatedAt: time.Now(), UpdatedAt: time.Now(), } } // PaymentOptionConfig represents the configuration of a payment option -// TODO: Refactor this with new format!!!!! type PaymentOptionConfig struct { - Chains []PaymentOptionConfigChain `json:"Chains"` + Config []PaymentOptionConfigItem `json:"Config"` +} + +// PaymentOptionConfigItem is an item in Payment option config +type PaymentOptionConfigItem struct { + PaymentOptionID int `json:"paymentOptionId"` + Amount string `json:"amount"` + Recipient common.Address `json:"Recipient"` + SigningKeyID string `json:"SigningKeyID"` } // PaymentOptionConfigChain represents the configuration of a payment option chain type PaymentOptionConfigChain struct { ChainId int `json:"ChainId"` Recipient string `json:"Recipient"` - SigningKeyId string `json:"SigningKeyId"` + SigningKeyId string `json:"SigningKeyID"` Iden3PaymentRailsRequestV1 *PaymentOptionConfigChainIden3PaymentRailsRequestV1 `json:"Iden3PaymentRailsRequestV1"` Iden3PaymentRailsERC20RequestV1 *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 `json:"Iden3PaymentRailsERC20RequestV1"` } diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index ded160d5c..e709b3636 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -70,7 +70,7 @@ type CreatePaymentRequestReq struct { type PaymentService interface { CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq, baseURL string) (*protocol.PaymentRequestMessage, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) - GetSettings() payments.Settings + GetSettings() payments.Config VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, message *protocol.PaymentMessage) (bool, error) CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config *domain.PaymentOptionConfig) (uuid.UUID, error) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 6d50013f3..5f095c296 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -2,20 +2,12 @@ package services import ( "context" - "crypto/ecdsa" - "crypto/rand" - "encoding/hex" "fmt" "math/big" - "strconv" - "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" @@ -33,16 +25,16 @@ import ( type payment struct { networkResolver network.Resolver - settings payments.Settings + settings payments.Config paymentsStore ports.PaymentRepository kms kms.KMSType } // NewPaymentService creates a new payment service -func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, settings payments.Settings, kms kms.KMSType) ports.PaymentService { +func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, settings *payments.Config, kms kms.KMSType) ports.PaymentService { return &payment{ networkResolver: resolver, - settings: settings, + settings: *settings, paymentsStore: payOptsRepo, kms: kms, } @@ -91,65 +83,67 @@ func (p *payment) DeletePaymentOption(ctx context.Context, issuerDID *w3c.DID, i // CreatePaymentRequest creates a payment request func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePaymentRequestReq, baseURL string) (*protocol.PaymentRequestMessage, error) { - const defaultExpirationDate = 1 * time.Hour + // const defaultExpirationDate = 1 * time.Hour - option, err := p.paymentsStore.GetPaymentOptionByID(ctx, &req.IssuerDID, req.OptionID) + _, err := p.paymentsStore.GetPaymentOptionByID(ctx, &req.IssuerDID, req.OptionID) if err != nil { log.Error(ctx, "failed to get payment option", "err", err, "issuerDID", req.IssuerDID, "optionID", req.OptionID) return nil, err } - var dataArr protocol.PaymentRequestInfoData - for _, chainConfig := range option.Config.Chains { - setting, found := p.settings[chainConfig.ChainId] - if !found { - log.Error(ctx, "chain not found in settings", "chainId", chainConfig.ChainId) - return nil, fmt.Errorf("chain not <%d> not found in payment settings", chainConfig.ChainId) - } - - expirationTime := time.Now().Add(defaultExpirationDate) - var address common.Address - var privateKey *ecdsa.PrivateKey + /* + var dataArr protocol.PaymentRequestInfoData + for _, chainConfig := range option.Config.Chains { + setting, found := p.settings[chainConfig.ChainId] + if !found { + log.Error(ctx, "chain not found in settings", "chainId", chainConfig.ChainId) + return nil, fmt.Errorf("chain not <%d> not found in payment settings", chainConfig.ChainId) + } - pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyId, Type: kms.KeyTypeEthereum}) - if err != nil { - log.Error(ctx, "failed to get kms signing key", "err", err, "keyId", chainConfig.SigningKeyId) - return nil, fmt.Errorf("kms signing key not found: %w", err) - } - address = crypto.PubkeyToAddress(*pubKey) + expirationTime := time.Now().Add(defaultExpirationDate) + var address common.Address + var privateKey *ecdsa.PrivateKey - if chainConfig.Iden3PaymentRailsRequestV1 != nil { - nativeToken, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, address, privateKey) + pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyID, Type: kms.KeyTypeEthereum}) if err != nil { - log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) - return nil, err + log.Error(ctx, "failed to get kms signing key", "err", err, "keyId", chainConfig.SigningKeyID) + return nil, fmt.Errorf("kms signing key not found: %w", err) } + address = crypto.PubkeyToAddress(*pubKey) - dataArr = append(dataArr, *nativeToken) - } + if chainConfig.Iden3PaymentRailsRequestV1 != nil { + nativeToken, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, address, privateKey) + if err != nil { + log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) + return nil, err + } - if chainConfig.Iden3PaymentRailsERC20RequestV1 != nil { - reqUSDT, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, address, payments.USDT, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, setting.ERC20.USDT.ContractAddress, privateKey) - if err != nil { - log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) - return nil, err + dataArr = append(dataArr, *nativeToken) } - dataArr = append(dataArr, reqUSDT) - reqUSDC, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, address, payments.USDC, chainConfig.Iden3PaymentRailsERC20RequestV1.USDC.Amount, setting.ERC20.USDC.ContractAddress, privateKey) - if err != nil { - log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) - return nil, err + + if chainConfig.Iden3PaymentRailsERC20RequestV1 != nil { + reqUSDT, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, address, payments.USDT, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, setting.ERC20.USDT.ContractAddress, privateKey) + if err != nil { + log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) + return nil, err + } + dataArr = append(dataArr, reqUSDT) + reqUSDC, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, address, payments.USDC, chainConfig.Iden3PaymentRailsERC20RequestV1.USDC.Amount, setting.ERC20.USDC.ContractAddress, privateKey) + if err != nil { + log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) + return nil, err + } + dataArr = append(dataArr, reqUSDC) } - dataArr = append(dataArr, reqUSDC) } - } - payments := []protocol.PaymentRequestInfo{ - { - Description: option.Description, - Credentials: req.Creds, - Data: dataArr, - }, - } + payments := []protocol.PaymentRequestInfo{ + { + Description: option.Description, + Credentials: req.Creds, + Data: dataArr, + }, + } + */ msgID := uuid.New() message := &protocol.PaymentRequestMessage{ From: req.IssuerDID.String(), @@ -159,8 +153,8 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay ID: msgID.String(), ThreadID: msgID.String(), Body: protocol.PaymentRequestMessageBody{ - Agent: fmt.Sprintf(ports.AgentUrl, baseURL), - Payments: payments, + Agent: fmt.Sprintf(ports.AgentUrl, baseURL), + // Payments: payments, }, } @@ -180,7 +174,7 @@ func (p *payment) CreatePaymentRequestForProposalRequest(_ context.Context, prop } // GetSettings returns the current payment settings -func (p *payment) GetSettings() payments.Settings { +func (p *payment) GetSettings() payments.Config { return p.settings } @@ -231,70 +225,78 @@ func (p *payment) VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, return isPaid, nil } -func contractAddressFromPayment(data *protocol.Payment, config payments.Settings) (*common.Address, error) { - var sChainID string - switch data.Type() { - case protocol.Iden3PaymentCryptoV1Type: - return nil, nil - case protocol.Iden3PaymentRailsV1Type: - d := data.Data() - t, ok := d.(*protocol.Iden3PaymentRailsV1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") +func contractAddressFromPayment(data *protocol.Payment, config payments.Config) (*common.Address, error) { + /* + var sChainID string + switch data.Type() { + case protocol.Iden3PaymentCryptoV1Type: + return nil, nil + case protocol.Iden3PaymentRailsV1Type: + d := data.Data() + t, ok := d.(*protocol.Iden3PaymentRailsV1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") + } + sChainID = t.PaymentData.ChainID + case protocol.Iden3PaymentRailsERC20V1Type: + t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") + } + sChainID = t.PaymentData.ChainID + default: + return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) } - sChainID = t.PaymentData.ChainID - case protocol.Iden3PaymentRailsERC20V1Type: - t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") + chainID, err := strconv.Atoi(sChainID) + if err != nil { + return nil, fmt.Errorf("failed to parse chain id: %w", err) } - sChainID = t.PaymentData.ChainID - default: - return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) - } - chainID, err := strconv.Atoi(sChainID) - if err != nil { - return nil, fmt.Errorf("failed to parse chain id: %w", err) - } - c, found := config[chainID] - if !found { - return nil, fmt.Errorf("chain id not found in settings: %d", chainID) - } - addr := common.HexToAddress(c.MCPayment) - return &addr, nil + c, found := config[chainID] + if !found { + return nil, fmt.Errorf("chain id not found in settings: %d", chainID) + } + addr := common.HexToAddress(c.MCPayment) + return &addr, nil + + */ + return &common.Address{}, nil } func recipientAddressFromPayment(data *protocol.Payment, option *domain.PaymentOption) (*common.Address, error) { - var address common.Address - switch data.Type() { - case protocol.Iden3PaymentCryptoV1Type: - address = common.Address{} - case protocol.Iden3PaymentRailsV1Type: - t, ok := data.Data().(*protocol.Iden3PaymentRailsV1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") - } - for _, chain := range option.Config.Chains { - if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { - address = common.HexToAddress(chain.Recipient) - break + /* + var address common.Address + switch data.Type() { + case protocol.Iden3PaymentCryptoV1Type: + address = common.Address{} + case protocol.Iden3PaymentRailsV1Type: + t, ok := data.Data().(*protocol.Iden3PaymentRailsV1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") } - } - case protocol.Iden3PaymentRailsERC20V1Type: - t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") - } - for _, chain := range option.Config.Chains { - if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { - address = common.HexToAddress(chain.Recipient) - break + for _, chain := range option.Config.Chains { + if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { + address = common.HexToAddress(chain.Recipient) + break + } + } + case protocol.Iden3PaymentRailsERC20V1Type: + t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) + if !ok { + return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") } + for _, chain := range option.Config.Chains { + if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { + address = common.HexToAddress(chain.Recipient) + break + } + } + default: + return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) } - default: - return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) - } - return &address, nil + return &address, nil + + */ + return &common.Address{}, nil } func nonceFromPayment(data *protocol.Payment) (*big.Int, error) { @@ -325,21 +327,22 @@ func nonceFromPayment(data *protocol.Payment) (*big.Int, error) { return bigIntNonce, nil } +/* func (p *payment) newIden3PaymentRailsRequestV1( ctx context.Context, - chainConfig domain.PaymentOptionConfigChain, - setting payments.ChainSettings, + chainConfig *domain.PaymentOptionConfigChain, + setting *payments.ChainConfig, expirationTime time.Time, address common.Address, signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management ) (*protocol.Iden3PaymentRailsRequestV1, error) { - nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) + nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) //nolint: mnd if err != nil { log.Error(ctx, "failed to generate nonce", "err", err) return nil, err } metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsRequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsRequestV1.Amount, "USDT", expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, "") + signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsRequestV1Type, chainConfig.ChainId, "lala setting.MCPayment", chainConfig.Iden3PaymentRailsRequestV1.Amount, "USDT", expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, "") if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err @@ -373,10 +376,10 @@ func (p *payment) newIden3PaymentRailsRequestV1( Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsRequestV1.json", PrimaryType: string(protocol.Iden3PaymentRailsRequestV1Type), Domain: protocol.Eip712Domain{ - Name: "MCPayment", - Version: "1.0.0", - ChainID: strconv.Itoa(chainConfig.ChainId), - VerifyingContract: setting.MCPayment, + Name: "MCPayment", + Version: "1.0.0", + ChainID: strconv.Itoa(chainConfig.ChainId), + // VerifyingContract: setting.MCPayment, }, }, }, @@ -384,12 +387,13 @@ func (p *payment) newIden3PaymentRailsRequestV1( } return &paymentInfo, nil } - +*/ +/* // newIden3PaymentRailsERC20RequestV1 creates a new Iden3PaymentRailsERC20RequestV1 func (p *payment) newIden3PaymentRailsERC20RequestV1( ctx context.Context, - chainConfig domain.PaymentOptionConfigChain, - setting payments.ChainSettings, + chainConfig *domain.PaymentOptionConfigChain, + setting *payments.ChainConfig, expirationTime time.Time, address common.Address, currency payments.Coin, @@ -397,13 +401,13 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( tokenAddress string, signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management ) (*protocol.Iden3PaymentRailsERC20RequestV1, error) { - nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) + nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) //nolint: mnd if err != nil { log.Error(ctx, "failed to generate nonce", "err", err) return nil, err } metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, setting.MCPayment, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, "USDT", expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, tokenAddress) + signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, "lala setting.MCPayment", chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, "USDT", expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, tokenAddress) if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err @@ -444,10 +448,10 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", PrimaryType: string(protocol.Iden3PaymentRailsERC20RequestV1Type), Domain: protocol.Eip712Domain{ - Name: "MCPayment", - Version: "1.0.0", - ChainID: strconv.Itoa(chainConfig.ChainId), - VerifyingContract: setting.MCPayment, + Name: "MCPayment", + Version: "1.0.0", + ChainID: strconv.Itoa(chainConfig.ChainId), + // VerifyingContract: setting.MCPayment, }, }, }, @@ -456,6 +460,9 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( return &paymentInfo, nil } +*/ + +/* func (p *payment) paymentRequestSignature( ctx context.Context, paymentType protocol.PaymentRequestType, @@ -508,6 +515,9 @@ func (p *payment) paymentRequestSignature( } } +*/ + +/* func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, verifyContract string, address common.Address, amount float64, _ string, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { data := apitypes.TypedData{ Types: apitypes.Types{ @@ -564,3 +574,5 @@ func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, v } return &data, nil } + +*/ diff --git a/internal/payments/settings.go b/internal/payments/settings.go index f57c4ed5a..3b43e747d 100644 --- a/internal/payments/settings.go +++ b/internal/payments/settings.go @@ -71,7 +71,7 @@ func newConfigDecoder(r io.Reader) *configDecoder { // a Config object with curated data // Config object is created from the yaml using the configDTO struct. // Information is the same but formatted in a more usable way -func (d *configDecoder) Decode() (Config, error) { +func (d *configDecoder) Decode() (*Config, error) { var dto configDTO var cfg Config @@ -111,13 +111,13 @@ func (d *configDecoder) Decode() (Config, error) { } } } - return cfg, nil + return &cfg, nil } // SettingsFromConfig returns the settings from the configuration // It reads the settings from the file if the path is provided or from the base64 encoded file injected // into the configuration via an environment variable -func SettingsFromConfig(ctx context.Context, cfg *config.Payments) (Config, error) { +func SettingsFromConfig(ctx context.Context, cfg *config.Payments) (*Config, error) { var reader io.Reader var err error if cfg.SettingsPath != "" { @@ -144,7 +144,7 @@ func settingsFileHasContent(cfg *config.Payments) bool { } // SettingsFromReader reads the settings from a reader -func SettingsFromReader(reader io.Reader) (Config, error) { +func SettingsFromReader(reader io.Reader) (*Config, error) { return newConfigDecoder(reader).Decode() } diff --git a/internal/payments/settings_test.go b/internal/payments/settings_test.go index d6371c1a0..fc3093518 100644 --- a/internal/payments/settings_test.go +++ b/internal/payments/settings_test.go @@ -9,9 +9,10 @@ import ( ethCommon "github.com/ethereum/go-ethereum/common" "github.com/iden3/iden3comm/v2/protocol" + "github.com/stretchr/testify/require" + "github.com/polygonid/sh-id-platform/internal/common" "github.com/polygonid/sh-id-platform/internal/config" - "github.com/stretchr/testify/require" ) func TestSettingsFromConfig(t *testing.T) { @@ -187,7 +188,7 @@ func TestSettingsFromConfig(t *testing.T) { settings, err := SettingsFromConfig(ctx, &tc.cfg) if tc.expected.err == nil { require.NoError(t, err) - require.EqualValues(t, tc.expected.settings, settings) + require.EqualValues(t, tc.expected.settings, *settings) } else { require.Equal(t, tc.expected.err.Error(), err.Error()) } diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go index 65b3a2039..d05c0a511 100644 --- a/internal/repositories/payment_test.go +++ b/internal/repositories/payment_test.go @@ -99,7 +99,7 @@ func TestPayment_GetAllPaymentOptions(t *testing.T) { IssuerDID: *issuerID, Name: fmt.Sprintf("name %d", i), Description: fmt.Sprintf("description %d", i), - Config: &paymentOptionConfig, + Config: paymentOptionConfig, CreatedAt: now, UpdatedAt: now, }) @@ -116,7 +116,7 @@ func TestPayment_GetAllPaymentOptions(t *testing.T) { assert.Equal(t, ids[i], opt.ID) assert.Equal(t, fmt.Sprintf("name %d", len(opts)-i-1), opt.Name) assert.Equal(t, fmt.Sprintf("description %d", len(opts)-i-1), opt.Description) - assert.Equal(t, paymentOptionConfig, *opt.Config) + assert.Equal(t, paymentOptionConfig, opt.Config) } }) t.Run("Get all payment options linked to non existing issuer", func(t *testing.T) { @@ -149,7 +149,7 @@ func TestPayment_GetPaymentOptionByID(t *testing.T) { assert.Equal(t, id, opt.ID) assert.Equal(t, "name", opt.Name) assert.Equal(t, "description", opt.Description) - assert.Equal(t, paymentOptionConfig, *opt.Config) + assert.Equal(t, paymentOptionConfig, opt.Config) }) t.Run("Get payment option linked to non existing issuer", func(t *testing.T) { opt, err := repo.GetPaymentOptionByID(ctx, issuerDIDOther, id) From f037545c78439d72044c905946d832f3a213ea59 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Thu, 5 Dec 2024 13:29:35 +0200 Subject: [PATCH 38/66] remove currency --- go.mod | 2 +- go.sum | 2 ++ internal/core/services/payment.go | 2 -- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7724a5de1..38c6cb443 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20240914111027-9588ce2d7e1b github.com/iden3/go-schema-processor v1.3.1 github.com/iden3/go-schema-processor/v2 v2.5.0 - github.com/iden3/iden3comm/v2 v2.8.3-0.20241203145634-5c082c18773e + github.com/iden3/iden3comm/v2 v2.8.3-0.20241204161130-150691fe0395 github.com/iden3/merkletree-proof v0.3.0 github.com/ipfs/go-ipfs-api v0.7.0 github.com/jackc/pgconn v1.14.3 diff --git a/go.sum b/go.sum index 993c606ad..bde78d3d2 100644 --- a/go.sum +++ b/go.sum @@ -451,6 +451,8 @@ github.com/iden3/iden3comm/v2 v2.8.1 h1:SjDXYS2O9jfpXNUzSdOBRTs+EjFKPkKOz4/IFkaO github.com/iden3/iden3comm/v2 v2.8.1/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= github.com/iden3/iden3comm/v2 v2.8.3-0.20241203145634-5c082c18773e h1:kKVW1Y3hHyBKp3ZCOzHQxOGsXfJP5x3CgT6LeL24HPY= github.com/iden3/iden3comm/v2 v2.8.3-0.20241203145634-5c082c18773e/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= +github.com/iden3/iden3comm/v2 v2.8.3-0.20241204161130-150691fe0395 h1:8nq2Oz5V0f64v0J4VKDwrnC1ycfe19qAQXwQJr9RxgE= +github.com/iden3/iden3comm/v2 v2.8.3-0.20241204161130-150691fe0395/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= github.com/iden3/merkletree-proof v0.3.0 h1:NVlvtUBEgn4Etxxn+RsOmXP/qlI+85BdN8oUDTf3mxI= github.com/iden3/merkletree-proof v0.3.0/go.mod h1:+E2sBxMqhcn/fcu0LDGjmk3us+Vr+fxQUiZMxdpbgUE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 64e208b1b..048059432 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -377,7 +377,6 @@ func (p *payment) newIden3PaymentRailsRequestV1( Amount: amountString, ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), Metadata: metadata, - Currency: chainConfig.Iden3PaymentRailsRequestV1.Currency, Recipient: address.String(), Proof: protocol.PaymentProof{ protocol.EthereumEip712Signature2021{ @@ -446,7 +445,6 @@ func (p *payment) newIden3PaymentRailsERC20RequestV1( Amount: amountString, ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), Metadata: metadata, - Currency: string(currency), Recipient: address.String(), Features: features, TokenAddress: tokenAddress, From 714bf11b4e8e9566fdb1bd0ae0a22e8646033d6f Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 9 Dec 2024 15:45:42 +0100 Subject: [PATCH 39/66] feat: Implement create payment request endpoint --- api/api.yaml | 66 ++- internal/api/api.gen.go | 31 +- internal/api/main_test.go | 6 +- internal/api/payment.go | 51 ++- internal/api/payment_test.go | 96 +++-- internal/api/responses.go | 39 +- internal/core/domain/payment.go | 111 +---- internal/core/ports/payment_service.go | 11 +- internal/core/services/payment.go | 386 +++++++----------- .../202411131724000_payment_options.sql | 3 +- internal/kms/vault_eth_key_provider.go | 1 + internal/payments/currency.go | 14 - internal/payments/settings.go | 10 +- internal/repositories/payment.go | 35 +- .../repositories/payment_request_fixture.go | 43 +- internal/repositories/payment_test.go | 33 +- 16 files changed, 461 insertions(+), 475 deletions(-) delete mode 100644 internal/payments/currency.go diff --git a/api/api.yaml b/api/api.yaml index a735d314e..0879febee 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1315,7 +1315,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/BasicMessage' + $ref: '#/components/schemas/CreatePaymentRequestResponse' '400': $ref: '#/components/responses/400' '401': @@ -2287,6 +2287,7 @@ components: required: - option - credentials + - description - userDID properties: option: @@ -2307,22 +2308,75 @@ components: context: type: string example: "" + description: + type: string + example: "Payment for AML" userDID: type: string example: "" - # TODO: Add more fields!!! CreatePaymentRequestResponse: type: object required: - id + - credentials + - description + - issuerDID + - recipientDID + - paymentOptionID + - payments + - CreatedAt properties: id: type: string - x-go-type: uuid.UUID - x-go-type-import: - name: uuid - path: github.com/google/uuid + format: uuid + credentials: + type: array + items: + type: object + required: [ type, context ] + properties: + type: + type: string + example: "AML" + context: + type: string + example: "" + description: + type: string + issuerDID: + type: string + recipientDID: + type: string + paymentOptionID: + type: string + format: uuid + payments: + type: array + items: + $ref: '#/components/schemas/PaymentRequestItem' + CreatedAt: + type: string + format: date-time + + PaymentRequestItem: + type: object + required: + - id + - nonce + - paymentRequestID + - payment + properties: + id: + type: string + format: uuid + nonce: + type: string + paymentRequestID: + type: string + format: uuid + payment: + type: null TimeUTC: type: string diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index a305ca993..032b0292e 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -17,6 +17,7 @@ import ( protocol "github.com/iden3/iden3comm/v2/protocol" "github.com/oapi-codegen/runtime" strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" + openapi_types "github.com/oapi-codegen/runtime/types" timeapi "github.com/polygonid/sh-id-platform/internal/timeapi" ) @@ -287,8 +288,24 @@ type CreatePaymentRequest struct { Context string `json:"context"` Type string `json:"type"` } `json:"credentials"` - Option uuid.UUID `json:"option"` - UserDID string `json:"userDID"` + Description string `json:"description"` + Option uuid.UUID `json:"option"` + UserDID string `json:"userDID"` +} + +// CreatePaymentRequestResponse defines model for CreatePaymentRequestResponse. +type CreatePaymentRequestResponse struct { + CreatedAt time.Time `json:"CreatedAt"` + Credentials []struct { + Context string `json:"context"` + Type string `json:"type"` + } `json:"credentials"` + Description string `json:"description"` + Id openapi_types.UUID `json:"id"` + IssuerDID string `json:"issuerDID"` + PaymentOptionID openapi_types.UUID `json:"paymentOptionID"` + Payments []PaymentRequestItem `json:"payments"` + RecipientDID string `json:"recipientDID"` } // Credential defines model for Credential. @@ -515,6 +532,14 @@ type PaymentOptionsPaginated struct { Meta PaginatedMetadata `json:"meta"` } +// PaymentRequestItem defines model for PaymentRequestItem. +type PaymentRequestItem struct { + Id openapi_types.UUID `json:"id"` + Nonce string `json:"nonce"` + Payment interface{} `json:"payment"` + PaymentRequestID openapi_types.UUID `json:"paymentRequestID"` +} + // PaymentStatus defines model for PaymentStatus. type PaymentStatus struct { Status PaymentStatusStatus `json:"status"` @@ -4561,7 +4586,7 @@ type CreatePaymentRequestResponseObject interface { VisitCreatePaymentRequestResponse(w http.ResponseWriter) error } -type CreatePaymentRequest201JSONResponse BasicMessage +type CreatePaymentRequest201JSONResponse CreatePaymentRequestResponse func (response CreatePaymentRequest201JSONResponse) VisitCreatePaymentRequestResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") diff --git a/internal/api/main_test.go b/internal/api/main_test.go index aa683f1c9..28c4b8d85 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -287,7 +287,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { paymentSettings, err := payments.SettingsFromReader(common.NewMyYAMLReader([]byte(` 80002: PaymentRails: 0xF8E49b922D5Fb00d3EdD12bd14064f275726D339 - PaymentOption: + PaymentOptions: - ID: 1 Name: AmoyNative Type: Iden3PaymentRailsRequestV1 @@ -304,7 +304,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { - EIP-2612 59141: PaymentRails: 0x40E3EF221AA93F6Fe997c9b0393322823Bb207d3 - PaymentOption: + PaymentOptions: - ID: 4 Name: LineaSepoliaNative Type: Iden3PaymentRailsRequestV1 @@ -321,7 +321,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { - EIP-2612 2442: PaymentRails: 0x09c269e74d8B47c98537Acd6CbEe8056806F4c70 - PaymentOption: + PaymentOptions: - ID: 7 Name: ZkEvmNative Type: Iden3PaymentRailsRequestV1 diff --git a/internal/api/payment.go b/internal/api/payment.go index fe6f9ba3a..8028d35aa 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math/big" "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" @@ -12,6 +13,7 @@ import ( "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/log" + "github.com/polygonid/sh-id-platform/internal/payments" "github.com/polygonid/sh-id-platform/internal/repositories" ) @@ -50,7 +52,12 @@ func (s *Server) CreatePaymentOption(ctx context.Context, request CreatePaymentO log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) return CreatePaymentOption400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil } - id, err := s.paymentService.CreatePaymentOption(ctx, issuerDID, request.Body.Name, request.Body.Description, newPaymentOptionConfig(&request.Body.Config)) + payOptConf, err := newPaymentOptionConfig(&request.Body.Config) + if err != nil { + log.Error(ctx, "creating payment option config", "err", err) + return CreatePaymentOption400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("invalid config: %s", err)}}, nil + } + id, err := s.paymentService.CreatePaymentOption(ctx, issuerDID, request.Body.Name, request.Body.Description, payOptConf) if err != nil { log.Error(ctx, "creating payment option", "err", err, "issuer", issuerDID, "request", request.Body) if errors.Is(err, repositories.ErrIdentityNotFound) { @@ -122,31 +129,22 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment } req := &ports.CreatePaymentRequestReq{ - IssuerDID: *issuerDID, - UserDID: *userDID, - OptionID: request.Body.Option, + IssuerDID: *issuerDID, + UserDID: *userDID, + OptionID: request.Body.Option, + Description: request.Body.Description, } - req.Creds = make([]protocol.PaymentRequestInfoCredentials, len(request.Body.Credentials)) + req.Credentials = make([]protocol.PaymentRequestInfoCredentials, len(request.Body.Credentials)) for i, cred := range request.Body.Credentials { - req.Creds[i] = protocol.PaymentRequestInfoCredentials{Type: cred.Type, Context: cred.Context} + req.Credentials[i] = protocol.PaymentRequestInfoCredentials{Type: cred.Type, Context: cred.Context} } - paymentRequest, err := s.paymentService.CreatePaymentRequest(ctx, req, s.cfg.ServerUrl) + payReq, err := s.paymentService.CreatePaymentRequest(ctx, req) if err != nil { log.Error(ctx, "creating payment request", "err", err) return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("can't create payment-request: %s", err)}}, nil } - - basicMessage := BasicMessage{ - From: paymentRequest.From, - To: paymentRequest.To, - ThreadID: paymentRequest.ThreadID, - Id: paymentRequest.ID, - Typ: string(paymentRequest.Typ), - Type: string(paymentRequest.Type), - Body: paymentRequest.Body, - } - return CreatePaymentRequest201JSONResponse(basicMessage), nil + return CreatePaymentRequest201JSONResponse(toCreatePaymentRequestResponse(payReq)), nil } // GetPaymentSettings is the controller to get payment settings @@ -169,17 +167,26 @@ func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequest return VerifyPayment200JSONResponse{Status: PaymentStatusStatusSuccess}, nil } -func newPaymentOptionConfig(config *PaymentOptionConfig) *domain.PaymentOptionConfig { +func newPaymentOptionConfig(config *PaymentOptionConfig) (*domain.PaymentOptionConfig, error) { + const base10 = 10 cfg := &domain.PaymentOptionConfig{ Config: make([]domain.PaymentOptionConfigItem, len(config.Config)), } for i, item := range config.Config { + if !common.IsHexAddress(item.Recipient) { + return nil, fmt.Errorf("invalid recipient address: %s", item.Recipient) + } + amount, ok := new(big.Int).SetString(item.Amount, base10) + if !ok { + return nil, fmt.Errorf("could not parse amount: %s", item.Amount) + } + cfg.Config[i] = domain.PaymentOptionConfigItem{ - PaymentOptionID: item.PaymentOptionId, - Amount: item.Amount, + PaymentOptionID: payments.OptionConfigIDType(item.PaymentOptionId), + Amount: *amount, Recipient: common.HexToAddress(item.Recipient), SigningKeyID: item.SigningKeyId, } } - return cfg + return cfg, nil } diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index a649d5ed3..1650df216 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -5,35 +5,38 @@ import ( "context" "encoding/json" "fmt" + "math/big" "net/http" "net/http/httptest" "testing" + "time" "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/iden3comm/v2/protocol" - "github.com/polygonid/sh-id-platform/internal/kms" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" + "github.com/polygonid/sh-id-platform/internal/kms" + "github.com/polygonid/sh-id-platform/internal/payments" ) const paymentOptionConfigurationTesting = ` { "Config": [ { - "paymentOptionId": "1", + "paymentOptionId": 1, "amount": "500000000000000000", - "Recipient": "0x1..", + "Recipient": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "SigningKeyId": "pubId" }, { - "paymentOptionId": "2", + "paymentOptionId": 2, "amount": "1500000000000000000", - "Recipient": "0x2..", + "Recipient": "0x53d284357ec70cE289D6D64134DfAc8E511c8a3D", "SigningKeyId": "pubId" } ] @@ -168,9 +171,6 @@ func TestServer_GetPaymentOption(t *testing.T) { BJJ = "BJJ" ) - var config PaymentOptionConfig - var domainConfig domain.PaymentOptionConfig - ctx := context.Background() server := newTestServer(t, nil) @@ -184,10 +184,25 @@ func TestServer_GetPaymentOption(t *testing.T) { otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") require.NoError(t, err) + var config PaymentOptionConfig require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) - require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &domainConfig)) - - optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", &domainConfig) + domainConfig := domain.PaymentOptionConfig{} + for _, item := range config.Config { + amount, ok := new(big.Int).SetString(item.Amount, 10) + require.True(t, ok) + domainConfig.Config = append(domainConfig.Config, domain.PaymentOptionConfigItem{ + PaymentOptionID: payments.OptionConfigIDType(item.PaymentOptionId), + Amount: *amount, + Recipient: common.HexToAddress(item.Recipient), + SigningKeyID: item.SigningKeyId, + }) + } + optionID, err := server.Services.payments.CreatePaymentOption( + ctx, + issuerDID, + "1 POL Payment", + "Payment Option explanation", + &domainConfig) require.NoError(t, err) type expected struct { @@ -266,6 +281,7 @@ func TestServer_GetPaymentOption(t *testing.T) { assert.Equal(t, tc.optionID, response.Id) assert.Equal(t, tc.expected.option.Name, response.Name) assert.Equal(t, tc.expected.option.Description, response.Description) + assert.Equal(t, tc.expected.option.IssuerDID, response.IssuerDID) assert.Equal(t, tc.expected.option.Config, response.Config) case http.StatusNotFound: @@ -305,13 +321,11 @@ func TestServer_GetPaymentOptions(t *testing.T) { _, err = server.Services.payments.CreatePaymentOption(ctx, issuerDID, fmt.Sprintf("Payment Option %d", i+1), "Payment Option explanation", &config) require.NoError(t, err) } - type expected struct { httpCode int msg string count int } - for _, tc := range []struct { name string issuerDID w3c.DID @@ -395,12 +409,10 @@ func TestServer_DeletePaymentOption(t *testing.T) { optionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "1 POL Payment", "Payment Option explanation", &config) require.NoError(t, err) - type expected struct { httpCode int msg string } - for _, tc := range []struct { name string issuerDID w3c.DID @@ -501,11 +513,12 @@ func TestServer_CreatePaymentRequest(t *testing.T) { signingKeyID, err := keyStore.CreateKey(kms.KeyTypeEthereum, issuerDID) require.NoError(t, err) + amount := new(big.Int).SetUint64(500000000000000000) config := domain.PaymentOptionConfig{ Config: []domain.PaymentOptionConfigItem{ { PaymentOptionID: 1, - Amount: "333", + Amount: *amount, Recipient: common.Address{}, SigningKeyID: signingKeyID.ID, }, @@ -514,13 +527,11 @@ func TestServer_CreatePaymentRequest(t *testing.T) { paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) require.NoError(t, err) - type expected struct { httpCode int msg string - count int + resp CreatePaymentRequestResponse } - for _, tc := range []struct { name string issuerDID w3c.DID @@ -574,8 +585,9 @@ func TestServer_CreatePaymentRequest(t *testing.T) { auth: authOk, issuerDID: *issuerDID, body: CreatePaymentRequestJSONRequestBody{ - UserDID: receiverDID.String(), - Option: paymentOptionID, + UserDID: receiverDID.String(), + Option: paymentOptionID, + Description: "Payment Request", Credentials: []struct { Context string `json:"context"` Type string `json:"type"` @@ -588,7 +600,25 @@ func TestServer_CreatePaymentRequest(t *testing.T) { }, expected: expected{ httpCode: http.StatusCreated, - count: 10, + resp: CreatePaymentRequestResponse{ + IssuerDID: issuerDID.String(), + RecipientDID: receiverDID.String(), + Credentials: []struct { + Context string `json:"context"` + Type string `json:"type"` + }{ + { + Context: "context", + Type: "type", + }, + }, + Description: "Payment Request", + Payments: []PaymentRequestItem{ + { + Payment: protocol.Iden3PaymentRailsERC20RequestV1{}, + }, + }, + }, }, }, } { @@ -608,15 +638,19 @@ func TestServer_CreatePaymentRequest(t *testing.T) { case http.StatusCreated: var response CreatePaymentRequest201JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) - raw, err := json.Marshal(response) - require.NoError(t, err) - - var requestMessage protocol.PaymentRequestMessage - require.NoError(t, json.Unmarshal(raw, &requestMessage)) - assert.Equal(t, issuerDID.String(), requestMessage.From) - assert.Equal(t, receiverDID.String(), requestMessage.To) - assert.Len(t, requestMessage.Body.Payments, 4) - + assert.NotEqual(t, uuid.Nil, response.Id) + assert.Equal(t, tc.expected.resp.IssuerDID, response.IssuerDID) + assert.Equal(t, tc.expected.resp.RecipientDID, response.RecipientDID) + assert.InDelta(t, time.Now().UnixMilli(), response.CreatedAt.UnixMilli(), 10) + assert.Equal(t, tc.expected.resp.Description, response.Description) + assert.Equal(t, tc.expected.resp.Credentials, response.Credentials) + assert.Equal(t, len(tc.expected.resp.Payments), len(response.Payments)) + for i := range tc.expected.resp.Payments { + assert.NotEqual(t, big.Int{}, response.Payments[i].Nonce) + assert.NotEqual(t, uuid.Nil, response.Payments[i].PaymentRequestID) + assert.NotEqual(t, uuid.Nil, response.Payments[i].Id) + // TODO: Fix it assert.Equal(t, tc.expected.resp.Payments[i].Payment, response.Payments[i].Payment) + } case http.StatusBadRequest: var response CreatePaymentRequest400JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) diff --git a/internal/api/responses.go b/internal/api/responses.go index b10807314..61779359f 100644 --- a/internal/api/responses.go +++ b/internal/api/responses.go @@ -320,11 +320,46 @@ func toPaymentOptionConfig(config domain.PaymentOptionConfig) PaymentOptionConfi } for i, item := range config.Config { cfg.Config[i] = PaymentOptionConfigItem{ - PaymentOptionId: item.PaymentOptionID, - Amount: item.Amount, + PaymentOptionId: int(item.PaymentOptionID), + Amount: item.Amount.String(), Recipient: item.Recipient.String(), SigningKeyId: item.SigningKeyID, } } return cfg } + +func toCreatePaymentRequestResponse(payReq *domain.PaymentRequest) CreatePaymentRequestResponse { + creds := make([]struct { + Context string `json:"context"` + Type string `json:"type"` + }, len(payReq.Credentials)) + for i, cred := range payReq.Credentials { + creds[i] = struct { + Context string `json:"context"` + Type string `json:"type"` + }{ + Context: cred.Context, + Type: cred.Type, + } + } + payments := make([]PaymentRequestItem, len(payReq.Payments)) + for i, payment := range payReq.Payments { + payments[i] = PaymentRequestItem{ + Id: payment.ID, + Nonce: payment.Nonce.String(), + PaymentRequestID: payment.PaymentRequestID, + Payment: payment.Payment, + } + } + return CreatePaymentRequestResponse{ + CreatedAt: payReq.CreatedAt, + Credentials: creds, + Description: payReq.Description, + Id: payReq.ID, + IssuerDID: payReq.IssuerDID.String(), + RecipientDID: payReq.RecipientDID.String(), + PaymentOptionID: payReq.PaymentOptionID, + Payments: payments, + } +} diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 6dd9fa434..7725eed42 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -1,24 +1,24 @@ package domain import ( - "encoding/json" - "errors" "math/big" - "strconv" "time" "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/iden3comm/v2/protocol" + + "github.com/polygonid/sh-id-platform/internal/payments" ) // PaymentRequest represents a payment request type PaymentRequest struct { ID uuid.UUID + Credentials []protocol.PaymentRequestInfoCredentials + Description string IssuerDID w3c.DID RecipientDID w3c.DID - ThreadID string PaymentOptionID uuid.UUID Payments []PaymentRequestItem CreatedAt time.Time @@ -29,7 +29,7 @@ type PaymentRequestItem struct { ID uuid.UUID Nonce big.Int PaymentRequestID uuid.UUID - Payment protocol.Payment + Payment protocol.PaymentRequestInfoDataItem } // PaymentOption represents a payment option @@ -63,101 +63,8 @@ type PaymentOptionConfig struct { // PaymentOptionConfigItem is an item in Payment option config type PaymentOptionConfigItem struct { - PaymentOptionID int `json:"paymentOptionId"` - Amount string `json:"amount"` - Recipient common.Address `json:"Recipient"` - SigningKeyID string `json:"SigningKeyID"` -} - -// PaymentOptionConfigChain represents the configuration of a payment option chain -type PaymentOptionConfigChain struct { - ChainId int `json:"ChainId"` - Recipient string `json:"Recipient"` - SigningKeyId string `json:"SigningKeyID"` - Iden3PaymentRailsRequestV1 *PaymentOptionConfigChainIden3PaymentRailsRequestV1 `json:"Iden3PaymentRailsRequestV1"` - Iden3PaymentRailsERC20RequestV1 *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 `json:"Iden3PaymentRailsERC20RequestV1"` -} - -// PaymentOptionConfigChainIden3PaymentRailsRequestV1 represents the configuration of a payment option rails -type PaymentOptionConfigChainIden3PaymentRailsRequestV1 struct { - Amount float64 `json:"Amount"` - Currency string `json:"Currency"` -} - -// UnmarshalJSON unmarshals a PaymentOptionConfigChainIden3PaymentRailsRequestV1 -// so we can accept amounts with string or float64 types -func (p *PaymentOptionConfigChainIden3PaymentRailsRequestV1) UnmarshalJSON(data []byte) error { - var temp struct { - Amount any `json:"Amount"` - Currency string `json:"Currency"` - } - err := json.Unmarshal(data, &temp) - if err != nil { - return err - } - - switch temp.Amount.(type) { - case float64: - p.Amount = temp.Amount.(float64) // nolint: forcetypeassert - case string: - amountFloat, err := strconv.ParseFloat(temp.Amount.(string), 64) - if err != nil { - return err - } - p.Amount = amountFloat - default: - return errors.New("unexpected format of amount") - } - - p.Currency = temp.Currency - - return nil -} - -// PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 represents the configuration of a rails ERC20 payment option -type PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 struct { - USDT struct { - Amount float64 `json:"Amount"` - } `json:"USDT"` - USDC struct { - Amount float64 `json:"Amount"` - } `json:"USDC"` -} - -// UnmarshalJSON unmarshals a PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1 -// so we can accept amounts with string or float64 types -func (p *PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1) UnmarshalJSON(data []byte) error { - var temp struct { - USDT struct { - Amount any `json:"Amount"` - } - USDC struct { - Amount any `json:"Amount"` - } - } - err := json.Unmarshal(data, &temp) - if err != nil { - return err - } - switch temp.USDT.Amount.(type) { - case float64: - p.USDT.Amount = temp.USDT.Amount.(float64) // nolint: forcetypeassert - case string: - amountFloat, err := strconv.ParseFloat(temp.USDT.Amount.(string), 64) - if err != nil { - return err - } - p.USDT.Amount = amountFloat - } - switch temp.USDC.Amount.(type) { - case float64: - p.USDC.Amount = temp.USDC.Amount.(float64) // nolint: forcetypeassert - case string: - amountFloat, err := strconv.ParseFloat(temp.USDC.Amount.(string), 64) - if err != nil { - return err - } - p.USDC.Amount = amountFloat - } - return nil + PaymentOptionID payments.OptionConfigIDType `json:"paymentOptionId"` + Amount big.Int `json:"amount"` + Recipient common.Address `json:"Recipient"` + SigningKeyID string `json:"SigningKeyID"` } diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index e709b3636..32566896b 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -60,15 +60,16 @@ func NewAgentProposalRequest(basicMessage *comm.BasicMessage) (*protocol.Credent // CreatePaymentRequestReq is the request for PaymentService.CreatePaymentRequest type CreatePaymentRequestReq struct { - IssuerDID w3c.DID - UserDID w3c.DID - OptionID uuid.UUID - Creds []protocol.PaymentRequestInfoCredentials + IssuerDID w3c.DID + UserDID w3c.DID + OptionID uuid.UUID + Description string + Credentials []protocol.PaymentRequestInfoCredentials } // PaymentService is the interface implemented by the payment service type PaymentService interface { - CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq, baseURL string) (*protocol.PaymentRequestMessage, error) + CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq) (*domain.PaymentRequest, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) GetSettings() payments.Config VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, message *protocol.PaymentMessage) (bool, error) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index fcbf043de..4db2b5dd4 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -2,16 +2,21 @@ package services import ( "context" + "crypto/rand" + "encoding/hex" "fmt" "math/big" + "strconv" + "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" - "github.com/iden3/iden3comm/v2/packers" "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/core/domain" @@ -82,83 +87,57 @@ func (p *payment) DeletePaymentOption(ctx context.Context, issuerDID *w3c.DID, i } // CreatePaymentRequest creates a payment request -func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePaymentRequestReq, baseURL string) (*protocol.PaymentRequestMessage, error) { - // const defaultExpirationDate = 1 * time.Hour - - _, err := p.paymentsStore.GetPaymentOptionByID(ctx, &req.IssuerDID, req.OptionID) +func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePaymentRequestReq) (*domain.PaymentRequest, error) { + option, err := p.paymentsStore.GetPaymentOptionByID(ctx, &req.IssuerDID, req.OptionID) if err != nil { log.Error(ctx, "failed to get payment option", "err", err, "issuerDID", req.IssuerDID, "optionID", req.OptionID) return nil, err } - /* - var dataArr protocol.PaymentRequestInfoData - for _, chainConfig := range option.Config.Chains { - setting, found := p.settings[chainConfig.ChainId] - if !found { - log.Error(ctx, "chain not found in settings", "chainId", chainConfig.ChainId) - return nil, fmt.Errorf("chain not <%d> not found in payment settings", chainConfig.ChainId) - } - - expirationTime := time.Now().Add(defaultExpirationDate) - var address common.Address - var privateKey *ecdsa.PrivateKey - - pubKey, err := kms.EthPubKey(ctx, p.kms, kms.KeyID{ID: chainConfig.SigningKeyID, Type: kms.KeyTypeEthereum}) - if err != nil { - log.Error(ctx, "failed to get kms signing key", "err", err, "keyId", chainConfig.SigningKeyID) - return nil, fmt.Errorf("kms signing key not found: %w", err) - } - address = crypto.PubkeyToAddress(*pubKey) - if chainConfig.Iden3PaymentRailsRequestV1 != nil { - nativeToken, err := p.newIden3PaymentRailsRequestV1(ctx, chainConfig, setting, expirationTime, address, privateKey) - if err != nil { - log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) - return nil, err - } + paymentRequest := &domain.PaymentRequest{ + ID: uuid.New(), + IssuerDID: req.IssuerDID, + RecipientDID: req.UserDID, + Credentials: req.Credentials, + Description: req.Description, + PaymentOptionID: req.OptionID, + CreatedAt: time.Now(), + } + for _, chainConfig := range option.Config.Config { + setting, found := p.settings[chainConfig.PaymentOptionID] + if !found { + log.Error(ctx, "chain not found in configuration", "paymentOptionID", chainConfig.PaymentOptionID) + return nil, fmt.Errorf("payment Option <%d> not found in payment configuration", chainConfig.PaymentOptionID) + } - dataArr = append(dataArr, *nativeToken) - } + nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) //nolint: mnd + if err != nil { + log.Error(ctx, "failed to generate nonce", "err", err) + return nil, err + } - if chainConfig.Iden3PaymentRailsERC20RequestV1 != nil { - reqUSDT, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, address, payments.USDT, chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, setting.ERC20.USDT.ContractAddress, privateKey) - if err != nil { - log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) - return nil, err - } - dataArr = append(dataArr, reqUSDT) - reqUSDC, err := p.newIden3PaymentRailsERC20RequestV1(ctx, chainConfig, setting, expirationTime, address, payments.USDC, chainConfig.Iden3PaymentRailsERC20RequestV1.USDC.Amount, setting.ERC20.USDC.ContractAddress, privateKey) - if err != nil { - log.Error(ctx, "failed to create Iden3PaymentRailsRequestV1", "err", err) - return nil, err - } - dataArr = append(dataArr, reqUSDC) - } + data, err := p.paymentInfo(ctx, setting, &chainConfig, nonce) + if err != nil { + log.Error(ctx, "failed to create payment info", "err", err) + return nil, err } + item := domain.PaymentRequestItem{ + ID: uuid.New(), + Nonce: *nonce, + PaymentRequestID: paymentRequest.ID, - payments := []protocol.PaymentRequestInfo{ - { - Description: option.Description, - Credentials: req.Creds, - Data: dataArr, - }, + Payment: data, } - */ - msgID := uuid.New() - message := &protocol.PaymentRequestMessage{ - From: req.IssuerDID.String(), - To: req.UserDID.String(), - Typ: packers.MediaTypePlainMessage, - Type: protocol.PaymentRequestMessageType, - ID: msgID.String(), - ThreadID: msgID.String(), - Body: protocol.PaymentRequestMessageBody{ - Agent: fmt.Sprintf(ports.AgentUrl, baseURL), - // Payments: payments, - }, + paymentRequest.Payments = append(paymentRequest.Payments, item) + } + + _, err = p.paymentsStore.SavePaymentRequest(ctx, paymentRequest) + if err != nil { + log.Error(ctx, "failed to save payment request", "err", err, "paymentRequest", paymentRequest) + return nil, fmt.Errorf("failed to save payment request: %w", err) } - return message, nil + return paymentRequest, nil } // CreatePaymentRequestForProposalRequest creates a payment request for a proposal request @@ -178,6 +157,8 @@ func (p *payment) GetSettings() payments.Config { return p.settings } +// VerifyPayment verifies a payment +// TODO: Total refactor! Reimplement from scratch!!!! func (p *payment) VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, message *protocol.PaymentMessage) (bool, error) { if len(message.Body.Payments) != 1 { return false, fmt.Errorf("expected one payment, got %d", len(message.Body.Payments)) @@ -327,197 +308,102 @@ func nonceFromPayment(data *protocol.Payment) (*big.Int, error) { return bigIntNonce, nil } -/* -func (p *payment) newIden3PaymentRailsRequestV1( - ctx context.Context, - chainConfig *domain.PaymentOptionConfigChain, - setting *payments.ChainConfig, - expirationTime time.Time, - address common.Address, - signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management -) (*protocol.Iden3PaymentRailsRequestV1, error) { - nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) //nolint: mnd - if err != nil { - log.Error(ctx, "failed to generate nonce", "err", err) - return nil, err - } +func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, chainConfig *domain.PaymentOptionConfigItem, nonce *big.Int) (protocol.PaymentRequestInfoDataItem, error) { + const defaultExpirationDate = 1 * time.Hour + expirationTime := time.Now().Add(defaultExpirationDate) + metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsRequestV1Type, chainConfig.ChainId, "lala setting.MCPayment", chainConfig.Iden3PaymentRailsRequestV1.Amount, "USDT", expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, "") + signature, err := p.paymentRequestSignature(ctx, setting, chainConfig, expirationTime, nonce, metadata) if err != nil { log.Error(ctx, "failed to create payment request signature", "err", err) return nil, err } - amountString := fmt.Sprintf("%f", chainConfig.Iden3PaymentRailsRequestV1.Amount) - if chainConfig.Iden3PaymentRailsRequestV1.Amount == float64(int(chainConfig.Iden3PaymentRailsRequestV1.Amount)) { - // No decimals, convert to integer string - amountString = strconv.Itoa(int(chainConfig.Iden3PaymentRailsRequestV1.Amount)) - } - paymentInfo := protocol.Iden3PaymentRailsRequestV1{ - Nonce: nonce.String(), - Type: protocol.Iden3PaymentRailsRequestV1Type, - Context: protocol.NewPaymentContextString( - "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", - "https://w3id.org/security/suites/eip712sig-2021/v1", - ), - Amount: amountString, - ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), - Metadata: metadata, - Recipient: address.String(), - Proof: protocol.PaymentProof{ - protocol.EthereumEip712Signature2021{ - Type: "EthereumEip712Signature2021", - ProofPurpose: "assertionMethod", - ProofValue: fmt.Sprintf("0x%s", hex.EncodeToString(signature)), - VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", chainConfig.ChainId, address), - Created: time.Now().Format(time.RFC3339), - Eip712: protocol.Eip712Data{ - Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsRequestV1.json", - PrimaryType: string(protocol.Iden3PaymentRailsRequestV1Type), - Domain: protocol.Eip712Domain{ - Name: "MCPayment", - Version: "1.0.0", - ChainID: strconv.Itoa(chainConfig.ChainId), - // VerifyingContract: setting.MCPayment, - }, - }, - }, - }, + switch setting.PaymentOption.Type { + case protocol.Iden3PaymentRailsRequestV1Type: + return &protocol.Iden3PaymentRailsRequestV1{ + Nonce: nonce.String(), + Type: protocol.Iden3PaymentRailsRequestV1Type, + Context: protocol.NewPaymentContextString( + "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsRequestV1", + "https://w3id.org/security/suites/eip712sig-2021/v1", + ), + Amount: chainConfig.Amount.String(), + ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), + Metadata: metadata, + Recipient: chainConfig.Recipient.String(), + Proof: paymentProof(&setting, signature), + }, nil + + case protocol.Iden3PaymentRailsERC20RequestV1Type: + return &protocol.Iden3PaymentRailsERC20RequestV1{ + Nonce: nonce.String(), + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, + Context: protocol.NewPaymentContextString( + "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", + "https://w3id.org/security/suites/eip712sig-2021/v1", + ), + Amount: chainConfig.Amount.String(), + ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), + Metadata: metadata, + Recipient: chainConfig.Recipient.String(), + Features: setting.PaymentOption.Features, + TokenAddress: setting.PaymentOption.ContractAddress.String(), + Proof: paymentProof(&setting, signature), + }, nil + + case protocol.Iden3PaymentRequestCryptoV1Type: + return &protocol.Iden3PaymentRequestCryptoV1{}, nil + default: + return nil, fmt.Errorf("unsupported payment option type: %s", setting.PaymentOption.Type) } - return &paymentInfo, nil } -*/ -/* -// newIden3PaymentRailsERC20RequestV1 creates a new Iden3PaymentRailsERC20RequestV1 -func (p *payment) newIden3PaymentRailsERC20RequestV1( - ctx context.Context, - chainConfig *domain.PaymentOptionConfigChain, - setting *payments.ChainConfig, - expirationTime time.Time, - address common.Address, - currency payments.Coin, - amount float64, - tokenAddress string, - signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management -) (*protocol.Iden3PaymentRailsERC20RequestV1, error) { - nonce, err := rand.Int(rand.Reader, big.NewInt(0).Exp(big.NewInt(2), big.NewInt(130), nil)) //nolint: mnd - if err != nil { - log.Error(ctx, "failed to generate nonce", "err", err) - return nil, err - } - metadata := "0x" - signature, err := p.paymentRequestSignature(ctx, protocol.Iden3PaymentRailsERC20RequestV1Type, chainConfig.ChainId, "lala setting.MCPayment", chainConfig.Iden3PaymentRailsERC20RequestV1.USDT.Amount, "USDT", expirationTime, nonce, metadata, address, chainConfig.SigningKeyId, signingKeyOpt, tokenAddress) - if err != nil { - log.Error(ctx, "failed to create payment request signature", "err", err) - return nil, err - } - amountString := fmt.Sprintf("%f", amount) - if amount == float64(int(amount)) { - // No decimals, convert to integer string - amountString = strconv.Itoa(int(amount)) - } - var features []protocol.PaymentFeatures = []protocol.PaymentFeatures{} - if currency == payments.USDC { - features = []protocol.PaymentFeatures{ - "EIP-2612", - } - } - paymentInfo := protocol.Iden3PaymentRailsERC20RequestV1{ - Nonce: nonce.String(), - Type: protocol.Iden3PaymentRailsERC20RequestV1Type, - Context: protocol.NewPaymentContextString( - "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsERC20RequestV1", - "https://w3id.org/security/suites/eip712sig-2021/v1", - ), - Amount: amountString, - ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), - Metadata: metadata, - Recipient: address.String(), - Features: features, - TokenAddress: tokenAddress, - Proof: protocol.PaymentProof{ - protocol.EthereumEip712Signature2021{ - Type: "EthereumEip712Signature2021", - ProofPurpose: "assertionMethod", - ProofValue: fmt.Sprintf("0x%s", hex.EncodeToString(signature)), - VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", chainConfig.ChainId, address), - Created: time.Now().Format(time.RFC3339), - Eip712: protocol.Eip712Data{ - Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json", - PrimaryType: string(protocol.Iden3PaymentRailsERC20RequestV1Type), - Domain: protocol.Eip712Domain{ - Name: "MCPayment", - Version: "1.0.0", - ChainID: strconv.Itoa(chainConfig.ChainId), - // VerifyingContract: setting.MCPayment, - }, + +func paymentProof(setting *payments.ChainConfig, signature []byte) protocol.PaymentProof { + var eip712DataTypes string + if setting.PaymentOption.Type == protocol.Iden3PaymentRailsRequestV1Type { + eip712DataTypes = "https://schema.iden3.io/core/json/Iden3PaymentRailsRequestV1.json" + } + if setting.PaymentOption.Type == protocol.Iden3PaymentRailsERC20RequestV1Type { + eip712DataTypes = "https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json" + } + return protocol.PaymentProof{ + protocol.EthereumEip712Signature2021{ + Type: "EthereumEip712Signature2021", + ProofPurpose: "assertionMethod", + ProofValue: fmt.Sprintf("0x%s", hex.EncodeToString(signature)), + VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", setting.ChainID, setting.PaymentRails), + Created: time.Now().Format(time.RFC3339), + Eip712: protocol.Eip712Data{ + Types: eip712DataTypes, + PrimaryType: string(setting.PaymentOption.Type), + Domain: protocol.Eip712Domain{ + Name: "MCPayment", + Version: "1.0.0", + ChainID: strconv.Itoa(setting.ChainID), + VerifyingContract: setting.PaymentRails.String(), }, }, }, } - return &paymentInfo, nil } -*/ - -/* func (p *payment) paymentRequestSignature( ctx context.Context, - paymentType protocol.PaymentRequestType, - chainID int, - verifContract string, - amount float64, - currency string, + setting payments.ChainConfig, + chainConfig *domain.PaymentOptionConfigItem, expTime time.Time, nonce *big.Int, metadata string, - addr common.Address, - signingKeyId string, - signingKeyOpt *ecdsa.PrivateKey, // Temporary solution until we have key management - tokenAddr string, ) ([]byte, error) { - if paymentType != protocol.Iden3PaymentRailsRequestV1Type && paymentType != protocol.Iden3PaymentRailsERC20RequestV1Type { - return nil, fmt.Errorf("unsupported payment type: %s", paymentType) - } + paymentType := string(setting.PaymentOption.Type) keyID := kms.KeyID{ Type: kms.KeyTypeEthereum, - ID: signingKeyId, - } - - typedData, err := typedDataForHashing(paymentType, chainID, verifContract, addr, amount, currency, expTime, nonce, metadata, tokenAddr) - if err != nil { - return nil, err - } - - typedDataBytes, _, err := apitypes.TypedDataAndHash(*typedData) - if err != nil { - return nil, err + ID: chainConfig.SigningKeyID, } - if signingKeyOpt == nil { - signature, err := p.kms.Sign(ctx, keyID, typedDataBytes) - if err != nil { - log.Error(ctx, "failed to sign typed data hash", "err", err, "keyId", signingKeyId) - return nil, err - } - - return signature, nil - } else { // Temporary solution until we have key management. We use SigningKeyOpt as a private key if present - signature, err := crypto.Sign(typedDataBytes, signingKeyOpt) - signature[64] += 27 - if err != nil { - return nil, err - } - return signature, nil - } -} - -*/ - -/* -func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, verifyContract string, address common.Address, amount float64, _ string, expTime time.Time, nonce *big.Int, metadata string, tokenAddress string) (*apitypes.TypedData, error) { - data := apitypes.TypedData{ + typedData := apitypes.TypedData{ Types: apitypes.Types{ "EIP712Domain": []apitypes.Type{ {Name: "name", Type: "string"}, @@ -525,7 +411,7 @@ func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, v {Name: "chainId", Type: "uint256"}, {Name: "verifyingContract", Type: "address"}, }, - string(paymentType): []apitypes.Type{ + paymentType: []apitypes.Type{ { Name: "recipient", Type: "address", @@ -548,23 +434,23 @@ func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, v }, }, }, - PrimaryType: string(paymentType), + PrimaryType: paymentType, Domain: apitypes.TypedDataDomain{ Name: "MCPayment", Version: "1.0.0", - ChainId: math.NewHexOrDecimal256(int64(chainID)), - VerifyingContract: verifyContract, + ChainId: math.NewHexOrDecimal256(int64(setting.ChainID)), + VerifyingContract: setting.PaymentRails.String(), }, Message: apitypes.TypedDataMessage{ - "recipient": address.String(), - "amount": big.NewInt(int64(amount)), - "expirationDate": fmt.Sprint(expTime.Unix()), - "nonce": nonce.String(), + "recipient": chainConfig.Recipient.String(), + "amount": chainConfig.Amount.String(), + "expirationDate": big.NewInt(expTime.Unix()), + "nonce": nonce, "metadata": metadata, }, } - if paymentType == protocol.Iden3PaymentRailsERC20RequestV1Type { - data.Types[string(paymentType)] = []apitypes.Type{ + if paymentType == string(protocol.Iden3PaymentRailsERC20RequestV1Type) { + typedData.Types[paymentType] = []apitypes.Type{ { Name: "tokenAddress", Type: "address", @@ -590,9 +476,17 @@ func typedDataForHashing(paymentType protocol.PaymentRequestType, chainID int, v Type: "bytes", }, } - data.Message["tokenAddress"] = tokenAddress + typedData.Message["tokenAddress"] = setting.PaymentOption.ContractAddress.String() + } + typedDataBytes, _, err := apitypes.TypedDataAndHash(typedData) + if err != nil { + return nil, err } - return &data, nil -} -*/ + signature, err := p.kms.Sign(ctx, keyID, typedDataBytes) + if err != nil { + log.Error(ctx, "failed to sign typed data hash", "err", err, "keyId", keyID) + return nil, err + } + return signature, nil +} diff --git a/internal/db/schema/migrations/202411131724000_payment_options.sql b/internal/db/schema/migrations/202411131724000_payment_options.sql index 824fb51e6..bfbe03fca 100644 --- a/internal/db/schema/migrations/202411131724000_payment_options.sql +++ b/internal/db/schema/migrations/202411131724000_payment_options.sql @@ -18,9 +18,10 @@ CREATE INDEX payment_options_issuer_did ON payment_options (issuer_did); CREATE TABLE payment_requests ( id UUID PRIMARY KEY NOT NULL, + credentials jsonb NOT NULL, /* []protocol.PaymentRequestCredentials */ + description text NOT NULL, issuer_did text NOT NULL REFERENCES identities (identifier), recipient_did text NOT NULL, - thread_id text NOT NULL, payment_option_id UUID REFERENCES payment_options (id), created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP ); diff --git a/internal/kms/vault_eth_key_provider.go b/internal/kms/vault_eth_key_provider.go index 2e8b876a4..9223f47b7 100644 --- a/internal/kms/vault_eth_key_provider.go +++ b/internal/kms/vault_eth_key_provider.go @@ -208,6 +208,7 @@ func DecodeETHPubKey(key []byte) (*ecdsa.PublicKey, error) { // EthPubKey returns the ethereum public key from the key manager service. // the public key is either uncompressed or compressed, so we need to handle both cases. +// TODO: Move out of this package func EthPubKey(ctx context.Context, keyMS KMSType, keyID KeyID) (*ecdsa.PublicKey, error) { const ( uncompressedKeyLength = 65 diff --git a/internal/payments/currency.go b/internal/payments/currency.go deleted file mode 100644 index 077099c11..000000000 --- a/internal/payments/currency.go +++ /dev/null @@ -1,14 +0,0 @@ -package payments - -const ( - USDT Coin = "USDT" // USDT Coin - USDC Coin = "USDC" // USDC Coin -) - -// Coin represents a coin -type Coin string - -// IsStableCoin returns true if the coin is a stable coin -func (c Coin) IsStableCoin() bool { - return c == USDT || c == USDC -} diff --git a/internal/payments/settings.go b/internal/payments/settings.go index 3b43e747d..b1bb7ba5c 100644 --- a/internal/payments/settings.go +++ b/internal/payments/settings.go @@ -18,11 +18,11 @@ import ( "github.com/polygonid/sh-id-platform/internal/log" ) -// PaymentOptionConfigID is a custom type to represent the payment option id -type PaymentOptionConfigID int +// OptionConfigIDType is a custom type to represent the payment option id +type OptionConfigIDType int // Config is a map of payment option id and chain config -type Config map[PaymentOptionConfigID]ChainConfig +type Config map[OptionConfigIDType]ChainConfig // ChainConfig is the configuration for a chain type ChainConfig struct { @@ -86,7 +86,7 @@ func (d *configDecoder) Decode() (*Config, error) { return nil, fmt.Errorf("invalid payment rails address: %s", chainConfig.PaymentRails) } for _, option := range chainConfig.PaymentOptions { - if _, found := cfg[PaymentOptionConfigID(id)]; found { + if _, found := cfg[OptionConfigIDType(id)]; found { return nil, fmt.Errorf("duplicate payment option id: %d", id) } if !common.IsHexAddress(option.ContractAddress) && strings.TrimSpace(option.ContractAddress) != "" { @@ -99,7 +99,7 @@ func (d *configDecoder) Decode() (*Config, error) { features[i] = protocol.PaymentFeatures(feature) } } - cfg[PaymentOptionConfigID(option.ID)] = ChainConfig{ + cfg[OptionConfigIDType(option.ID)] = ChainConfig{ ChainID: id, PaymentRails: common.HexToAddress(chainConfig.PaymentRails), PaymentOption: PaymentOptionConfig{ diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go index a804bd177..3c5f33faf 100644 --- a/internal/repositories/payment.go +++ b/internal/repositories/payment.go @@ -2,6 +2,7 @@ package repositories import ( "context" + "encoding/json" "errors" "fmt" "math/big" @@ -9,6 +10,7 @@ import ( "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" @@ -33,8 +35,8 @@ func (p *payment) SavePaymentRequest(ctx context.Context, req *domain.PaymentReq const ( insertPaymentRequest = ` INSERT -INTO payment_requests (id, issuer_did, recipient_did, thread_id, payment_option_id, created_at) -VALUES ($1, $2, $3, $4, $5, $6);` +INTO payment_requests (id, credentials, description, issuer_did, recipient_did, payment_option_id, created_at) +VALUES ($1, $2, $3, $4, $5, $6, $7);` insertPaymentRequestItem = ` INSERT INTO payment_request_items (id, nonce, payment_request_id, payment_request_info) @@ -47,7 +49,7 @@ VALUES ($1, $2, $3, $4);` } defer func() { _ = tx.Rollback(ctx) }() - _, err = tx.Exec(ctx, insertPaymentRequest, req.ID, req.IssuerDID.String(), req.RecipientDID.String(), req.ThreadID, req.PaymentOptionID, req.CreatedAt) + _, err = tx.Exec(ctx, insertPaymentRequest, req.ID, req.Credentials, req.Description, req.IssuerDID.String(), req.RecipientDID.String(), req.PaymentOptionID, req.CreatedAt) if err != nil { return uuid.Nil, fmt.Errorf("could not insert payment request: %w", err) } @@ -66,7 +68,7 @@ VALUES ($1, $2, $3, $4);` // GetPaymentRequestByID returns a payment request by ID func (p *payment) GetPaymentRequestByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentRequest, error) { const query = ` -SELECT pr.id, pr.issuer_did, pr.recipient_did, pr.thread_id, pr.payment_option_id, pr.created_at, pri.id, pri.nonce, pri.payment_request_id, pri.payment_request_info +SELECT pr.id, pr.issuer_did, pr.recipient_did, pr.payment_option_id, pr.created_at, pri.id, pri.nonce, pri.payment_request_id, pri.payment_request_info FROM payment_requests pr LEFT JOIN payment_request_items pri ON pr.id = pri.payment_request_id WHERE pr.issuer_did = $1 AND pr.id = $2;` @@ -81,20 +83,26 @@ WHERE pr.issuer_did = $1 AND pr.id = $2;` var strIssuerDID, strRecipientDID string var sNonce string var did *w3c.DID + var paymentRequestInfoBytes []byte if err := rows.Scan( &pr.ID, &strIssuerDID, &strRecipientDID, - &pr.ThreadID, &pr.PaymentOptionID, &pr.CreatedAt, &item.ID, &sNonce, &item.PaymentRequestID, - &item.Payment, + &paymentRequestInfoBytes, ); err != nil { return nil, fmt.Errorf("could not scan payment request: %w", err) } + + item.Payment, err = p.paymentRequestItem(paymentRequestInfoBytes) + if err != nil { + return nil, fmt.Errorf("could not unmarshal payment request info: %w", err) + } + const base10 = 10 nonce, ok := new(big.Int).SetString(sNonce, base10) if !ok { @@ -229,3 +237,18 @@ func (p *payment) DeletePaymentOption(ctx context.Context, issuerDID w3c.DID, id } return nil } + +// paymentRequestItem extracts the payment request item from the payload +// It uses an intermediate structure of type protocol.PaymentRequestInfoData +// to unmarshal the payment request info. +// This is necessary because PaymentRequestInfoDataItem is an interface and the unmarshal fails +func (p *payment) paymentRequestItem(payload []byte) (protocol.PaymentRequestInfoDataItem, error) { + var data protocol.PaymentRequestInfoData + if err := json.Unmarshal(payload, &data); err != nil { + return nil, fmt.Errorf("could not unmarshal payment request info: %w", err) + } + if len(data) == 0 { + return nil, errors.New("payment request info is empty") + } + return data[0], nil +} diff --git a/internal/repositories/payment_request_fixture.go b/internal/repositories/payment_request_fixture.go index 59d42d858..566f08441 100644 --- a/internal/repositories/payment_request_fixture.go +++ b/internal/repositories/payment_request_fixture.go @@ -8,6 +8,7 @@ import ( "time" "github.com/google/uuid" + "github.com/iden3/driver-did-iden3/pkg/document" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/iden3comm/v2/protocol" "github.com/stretchr/testify/require" @@ -27,25 +28,43 @@ func (f *Fixture) CreatePaymentRequest(t *testing.T, issuerDID, recipientDID w3c ID: uuid.New(), Nonce: *nonce, PaymentRequestID: paymentRequestID, - Payment: protocol.NewPaymentRails(protocol.Iden3PaymentRailsV1{ - Nonce: nonce.String(), - Type: "Iden3PaymentRailsV1", - Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), - PaymentData: struct { - TxID string `json:"txId"` - ChainID string `json:"chainId"` - }{ - TxID: "0x123", - ChainID: "137", + Payment: protocol.Iden3PaymentRailsRequestV1{ + Nonce: "25", + Type: protocol.Iden3PaymentRailsRequestV1Type, + Context: protocol.NewPaymentContextString( + "https://schema.iden3.io/core/jsonld/payment.jsonld#Iden3PaymentRailsRequestV1", + "https://w3id.org/security/suites/eip712sig-2021/v1", + ), + Recipient: "0xaddress", + Amount: "100", + ExpirationDate: "ISO string", + Proof: protocol.PaymentProof{ + protocol.EthereumEip712Signature2021{ + Type: document.EthereumEip712SignatureProof2021Type, + ProofPurpose: "assertionMethod", + ProofValue: "0xa05292e9874240c5c2bbdf5a8fefff870c9fc801bde823189fc013d8ce39c7e5431bf0585f01c7e191ea7bbb7110a22e018d7f3ea0ed81a5f6a3b7b828f70f2d1c", + VerificationMethod: "did:pkh:eip155:0:0x3e1cFE1b83E7C1CdB0c9558236c1f6C7B203C34e#blockchainAccountId", + Created: "2024-09-26T12:28:19.702580067Z", + Eip712: protocol.Eip712Data{ + Types: "https://schema.iden3.io/core/json/Iden3PaymentRailsRequestV1.json", + PrimaryType: "Iden3PaymentRailsRequestV1", + Domain: protocol.Eip712Domain{ + Name: "MCPayment", + Version: "1.0.0", + ChainID: "0x0", + VerifyingContract: "0x0000000000000000000000000000000000000000", + }, + }, + }, }, - }), + Metadata: "0x", + }, }) } paymentRequest := &domain.PaymentRequest{ ID: paymentRequestID, IssuerDID: issuerDID, RecipientDID: recipientDID, - ThreadID: "thread-id", PaymentOptionID: paymentOptionID, Payments: payments, CreatedAt: time.Now(), diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go index d05c0a511..537ce39c1 100644 --- a/internal/repositories/payment_test.go +++ b/internal/repositories/payment_test.go @@ -198,7 +198,8 @@ func TestPayment_SavePaymentRequest(t *testing.T) { ID: uuid.New(), IssuerDID: *issuerID, RecipientDID: *issuerID, - ThreadID: "thread-id", + Description: "this is a payment for cinema", + Credentials: []protocol.PaymentRequestInfoCredentials{{Context: "context", Type: "type"}}, PaymentOptionID: uuid.New(), Payments: []domain.PaymentRequestItem{}, CreatedAt: time.Now(), @@ -211,7 +212,8 @@ func TestPayment_SavePaymentRequest(t *testing.T) { ID: uuid.New(), IssuerDID: *issuerID, RecipientDID: *issuerID, - ThreadID: "thread-id", + Description: "this is a payment for cinema", + Credentials: []protocol.PaymentRequestInfoCredentials{{Context: "context", Type: "type"}}, PaymentOptionID: payymentOptionID, Payments: nil, CreatedAt: time.Now(), @@ -228,25 +230,23 @@ func TestPayment_SavePaymentRequest(t *testing.T) { ID: uuid.New(), Nonce: *nonce, PaymentRequestID: paymentRequestID, - Payment: protocol.NewPaymentRails(protocol.Iden3PaymentRailsV1{ - Nonce: nonce.String(), - Type: "Iden3PaymentRailsV1", - Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), - PaymentData: struct { - TxID string `json:"txId"` - ChainID string `json:"chainId"` - }{ - TxID: "0x123", - ChainID: "137", - }, - }), - }) + Payment: protocol.Iden3PaymentRailsERC20RequestV1{ + Nonce: "123", + Type: protocol.Iden3PaymentRailsERC20RequestV1Type, + Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), + Recipient: "did1", + Amount: "11", + // etc... + }, + }, + ) } id, err := repo.SavePaymentRequest(ctx, &domain.PaymentRequest{ ID: paymentRequestID, IssuerDID: *issuerID, RecipientDID: *issuerID, - ThreadID: "thread-id", + Description: "this is a payment for cinema", + Credentials: []protocol.PaymentRequestInfoCredentials{{Context: "context", Type: "type"}}, PaymentOptionID: payymentOptionID, Payments: payments, CreatedAt: time.Now(), @@ -275,7 +275,6 @@ func TestPayment_GetPaymentRequestByID(t *testing.T) { assert.Equal(t, expected.ID, paymentRequest.ID) assert.Equal(t, expected.IssuerDID, paymentRequest.IssuerDID) assert.Equal(t, expected.RecipientDID, paymentRequest.RecipientDID) - assert.Equal(t, expected.ThreadID, paymentRequest.ThreadID) assert.Equal(t, expected.PaymentOptionID, paymentRequest.PaymentOptionID) assert.InDelta(t, expected.CreatedAt.UnixMilli(), paymentRequest.CreatedAt.UnixMilli(), 1) assert.Len(t, expected.Payments, len(paymentRequest.Payments)) From ff20d8c3d296aa5ff7651f9b7053fedb8deaf34f Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 9 Dec 2024 15:51:22 +0100 Subject: [PATCH 40/66] fix: Failing test. --- internal/api/payment_test.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 1650df216..bbda5df82 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -301,7 +301,6 @@ func TestServer_GetPaymentOptions(t *testing.T) { BJJ = "BJJ" ) - var config domain.PaymentOptionConfig ctx := context.Background() server := newTestServer(t, nil) @@ -315,7 +314,22 @@ func TestServer_GetPaymentOptions(t *testing.T) { otherDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") require.NoError(t, err) - require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) + config := domain.PaymentOptionConfig{ + Config: []domain.PaymentOptionConfigItem{ + { + PaymentOptionID: 1, + Amount: *big.NewInt(500000000000000000), + Recipient: common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc454e4438f44e"), + SigningKeyID: "pubId", + }, + { + PaymentOptionID: 2, + Amount: *big.NewInt(1500000000000000000), + Recipient: common.HexToAddress("0x53d284357ec70cE289D6D64134DfAc8E511c8a3D"), + SigningKeyID: "pubId", + }, + }, + } for i := 0; i < 10; i++ { _, err = server.Services.payments.CreatePaymentOption(ctx, issuerDID, fmt.Sprintf("Payment Option %d", i+1), "Payment Option explanation", &config) From a58e7569730929cdd010cdfd7b0890df317e484d Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 9 Dec 2024 17:05:14 +0100 Subject: [PATCH 41/66] feat: revert changes in agent endpoint. --- internal/api/agent.go | 52 ++++++++----------------------------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/internal/api/agent.go b/internal/api/agent.go index 522175f11..b5c01a116 100644 --- a/internal/api/agent.go +++ b/internal/api/agent.go @@ -3,9 +3,6 @@ package api import ( "context" - "github.com/iden3/iden3comm/v2/protocol" - - "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/log" ) @@ -23,48 +20,17 @@ func (s *Server) Agent(ctx context.Context, request AgentRequestObject) (AgentRe return Agent400JSONResponse{N400JSONResponse{"cannot proceed with the given request"}}, nil } - var agent *domain.Agent - - switch basicMessage.Type { - case protocol.CredentialFetchRequestMessageType, protocol.RevocationStatusRequestMessageType: - req, err := ports.NewAgentRequest(basicMessage) - if err != nil { - log.Error(ctx, "agent parsing request", "err", err) - return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil - } - - agent, err = s.claimService.Agent(ctx, req, mediatype) - if err != nil { - log.Error(ctx, "agent error", "err", err) - return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil - } - case protocol.CredentialProposalRequestMessageType: - proposalRequest, err := ports.NewAgentProposalRequest(basicMessage) - if err != nil { - log.Error(ctx, "agent parsing proposal request", "err", err) - return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil - } - paymentRequest, err := s.paymentService.CreatePaymentRequestForProposalRequest(ctx, proposalRequest) - - agent = &domain.Agent{ - ID: paymentRequest.ID, - Type: paymentRequest.Type, - ThreadID: paymentRequest.ThreadID, - Body: paymentRequest.Body, - From: paymentRequest.From, - To: paymentRequest.To, - Typ: paymentRequest.Typ, - } - - if err != nil { - log.Error(ctx, "agent error", "err", err) - return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil - } - default: - log.Debug(ctx, "agent not supported type", basicMessage.Type) - return Agent400JSONResponse{N400JSONResponse{"cannot proceed with the given message type"}}, nil + req, err := ports.NewAgentRequest(basicMessage) + if err != nil { + log.Error(ctx, "agent parsing request", "err", err) + return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil } + agent, err := s.claimService.Agent(ctx, req, mediatype) + if err != nil { + log.Error(ctx, "agent error", "err", err) + return Agent400JSONResponse{N400JSONResponse{err.Error()}}, nil + } return Agent200JSONResponse{ Body: agent.Body, From: agent.From, From 37c801ec8219539abc3bbedc45f087ebcfab2d06 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 9 Dec 2024 18:08:32 +0100 Subject: [PATCH 42/66] feat: revert changes in function to extrack ethkey from vault --- .golangci.yml | 2 + go.mod | 132 ++++++------ go.sum | 276 +++++++++++++------------ internal/core/services/identity.go | 40 +++- internal/kms/vault_eth_key_provider.go | 40 ---- 5 files changed, 240 insertions(+), 250 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 909250a81..705b21b20 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -66,6 +66,7 @@ linters-settings: min-complexity: 35 revive: enable-all-rules: false + rules: - name: argument-limit severity: warning @@ -74,6 +75,7 @@ linters-settings: - name: exported severity: warning disabled: false + arguments: [ "disableChecksOnConstants", "disableChecksOnVariables" ] issues: diff --git a/go.mod b/go.mod index 38c6cb443..17c9cb671 100644 --- a/go.mod +++ b/go.mod @@ -9,21 +9,22 @@ require ( github.com/aws/aws-sdk-go-v2/credentials v1.17.30 github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 github.com/caarlos0/env/v11 v11.2.2 - github.com/ethereum/go-ethereum v1.14.11 + github.com/ethereum/go-ethereum v1.14.12 github.com/getkin/kin-openapi v0.128.0 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-redis/cache/v8 v8.4.4 github.com/go-redis/redis/v8 v8.11.5 - github.com/golangci/golangci-lint v1.60.3 + github.com/golangci/golangci-lint v1.62.2 github.com/google/uuid v1.6.0 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/vault/api v1.14.0 github.com/hashicorp/vault/api/auth/userpass v0.7.0 github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi v0.0.0-20230911113809-c58b7e7a69b0 github.com/iden3/contracts-abi/state/go/abi v1.0.2-0.20230911112726-4533c5701af1 + github.com/iden3/driver-did-iden3 v0.0.7 github.com/iden3/go-circuits/v2 v2.4.0 - github.com/iden3/go-iden3-auth/v2 v2.5.1 + github.com/iden3/go-iden3-auth/v2 v2.6.0 github.com/iden3/go-iden3-core/v2 v2.3.1 github.com/iden3/go-iden3-crypto v0.0.17 github.com/iden3/go-jwz/v2 v2.2.0 @@ -35,51 +36,49 @@ require ( github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20240914111027-9588ce2d7e1b github.com/iden3/go-schema-processor v1.3.1 github.com/iden3/go-schema-processor/v2 v2.5.0 - github.com/iden3/iden3comm/v2 v2.8.3-0.20241204161130-150691fe0395 + github.com/iden3/iden3comm/v2 v2.9.0 github.com/iden3/merkletree-proof v0.3.0 github.com/ipfs/go-ipfs-api v0.7.0 github.com/jackc/pgconn v1.14.3 - github.com/jackc/pgtype v1.14.3 - github.com/jackc/pgx/v4 v4.18.2 - github.com/jmoiron/sqlx v1.3.5 + github.com/jackc/pgtype v1.14.4 + github.com/jackc/pgx/v4 v4.18.3 + github.com/jmoiron/sqlx v1.4.0 github.com/joho/godotenv v1.5.1 github.com/labstack/gommon v0.4.2 github.com/lib/pq v1.10.9 github.com/mitchellh/mapstructure v1.5.0 github.com/mr-tron/base58 v1.2.0 - github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 + github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 github.com/oapi-codegen/runtime v1.1.1 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f github.com/pkg/errors v0.9.1 - github.com/pressly/goose/v3 v3.22.0 - github.com/stretchr/testify v1.9.0 - github.com/valkey-io/valkey-go v1.0.45 - golang.org/x/crypto v0.26.0 + github.com/pressly/goose/v3 v3.23.0 + github.com/stretchr/testify v1.10.0 + github.com/valkey-io/valkey-go v1.0.51 + golang.org/x/crypto v0.30.0 gopkg.in/yaml.v3 v3.0.1 ) -require github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect github.com/4meepo/tagalign v1.3.4 // indirect - github.com/Abirdcfly/dupword v0.1.1 // indirect - github.com/Antonboom/errname v0.1.13 // indirect - github.com/Antonboom/nilnil v0.1.9 // indirect - github.com/Antonboom/testifylint v1.4.3 // indirect + github.com/Abirdcfly/dupword v0.1.3 // indirect + github.com/Antonboom/errname v1.0.0 // indirect + github.com/Antonboom/nilnil v1.0.0 // indirect + github.com/Antonboom/testifylint v1.5.2 // indirect github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect - github.com/Crocmagnon/fatcontext v0.4.0 // indirect + github.com/Crocmagnon/fatcontext v0.5.3 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect github.com/VictoriaMetrics/fastcache v1.12.2 // indirect - github.com/alecthomas/go-check-sumtype v0.1.4 // indirect - github.com/alexkohler/nakedret/v2 v2.0.4 // indirect + github.com/alecthomas/go-check-sumtype v0.2.0 // indirect + github.com/alexkohler/nakedret/v2 v2.0.5 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/alingse/asasalint v0.0.11 // indirect @@ -99,13 +98,12 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect - github.com/bkielbasa/cyclop v1.2.1 // indirect + github.com/bkielbasa/cyclop v1.2.3 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect github.com/bombsimon/wsl/v4 v4.4.1 // indirect - github.com/breml/bidichk v0.2.7 // indirect - github.com/breml/errchkjson v0.3.6 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect + github.com/breml/bidichk v0.3.2 // indirect + github.com/breml/errchkjson v0.4.0 // indirect github.com/butuzov/ireturn v0.3.0 // indirect github.com/butuzov/mirror v1.2.0 // indirect github.com/catenacyber/perfsprint v0.7.1 // indirect @@ -114,19 +112,14 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/chavacava/garif v0.1.0 // indirect - github.com/ckaznocha/intrange v0.1.2 // indirect - github.com/cockroachdb/errors v1.11.3 // indirect - github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect - github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v1.1.2 // indirect - github.com/cockroachdb/redact v1.1.5 // indirect + github.com/ckaznocha/intrange v0.2.1 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect - github.com/daixiang0/gci v0.13.4 // indirect + github.com/daixiang0/gci v0.13.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dchest/blake512 v1.0.0 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect @@ -137,14 +130,13 @@ require ( github.com/ethereum/c-kzg-4844 v1.0.1 // indirect github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/ettle/strcase v0.2.0 // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/firefart/nonamedreturns v1.0.5 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/getsentry/sentry-go v0.27.0 // indirect - github.com/ghostiam/protogetter v0.3.6 // indirect - github.com/go-critic/go-critic v0.11.4 // indirect + github.com/ghostiam/protogetter v0.3.8 // indirect + github.com/go-critic/go-critic v0.11.5 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -156,14 +148,14 @@ require ( github.com/go-toolsmith/astp v1.1.0 // indirect github.com/go-toolsmith/strparse v1.1.0 // indirect github.com/go-toolsmith/typep v1.1.0 // indirect - github.com/go-viper/mapstructure/v2 v2.0.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gofrs/flock v0.12.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect + github.com/golangci/go-printf-func-name v0.1.0 // indirect github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 // indirect github.com/golangci/misspell v0.6.0 // indirect github.com/golangci/modinfo v0.3.4 // indirect @@ -190,7 +182,6 @@ require ( github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.3.1 // indirect github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48 // indirect - github.com/iden3/driver-did-iden3 v0.0.5 // indirect github.com/iden3/go-iden3-core v1.0.2 // indirect github.com/iden3/go-rapidsnark/verifier v0.0.5 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -201,25 +192,22 @@ require ( github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect - github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle v1.3.0 // indirect github.com/jgautheron/goconst v1.7.1 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect - github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect github.com/jjti/go-spancheck v0.6.2 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/julz/importas v0.1.0 // indirect github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect - github.com/kisielk/errcheck v1.7.0 // indirect + github.com/kisielk/errcheck v1.8.0 // indirect github.com/kkHAIKE/contextcheck v1.1.5 // indirect github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/kr/text v0.2.0 // indirect github.com/kulti/thelper v0.6.3 // indirect github.com/kunwardeep/paralleltest v1.0.10 // indirect github.com/kyoh86/exportloopref v0.1.11 // indirect - github.com/lasiar/canonicalheader v1.1.1 // indirect + github.com/lasiar/canonicalheader v1.1.2 // indirect github.com/ldez/gomoddirectives v0.2.4 // indirect github.com/ldez/tagliatelle v0.5.0 // indirect github.com/leonklingele/grouper v1.1.2 // indirect @@ -232,7 +220,6 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-libp2p v0.33.2 // indirect - github.com/lufeee/execinquery v1.2.1 // indirect github.com/macabu/inamedparam v0.1.3 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -241,9 +228,9 @@ require ( github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mfridman/interpolate v0.0.2 // indirect - github.com/mgechev/revive v1.3.9 // indirect + github.com/mgechev/revive v1.5.1 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect @@ -260,25 +247,26 @@ require ( github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.16.2 // indirect + github.com/nunnatsa/ginkgolinter v0.18.3 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/polyfloyd/go-errorlint v1.6.0 // indirect + github.com/polyfloyd/go-errorlint v1.7.0 // indirect github.com/pquerna/cachecontrol v0.2.0 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect github.com/prometheus/common v0.47.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/quasilyte/go-ruleguard v0.4.2 // indirect + github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect - github.com/rivo/uniseg v0.4.4 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/ryancurrah/gomodguard v1.3.3 // indirect + github.com/raeperd/recvcheck v0.1.2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/ryancurrah/gomodguard v1.3.5 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -287,15 +275,15 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.27.0 // indirect - github.com/securego/gosec/v2 v2.20.1-0.20240822074752-ab3f6c1c83a0 // indirect + github.com/securego/gosec/v2 v2.21.4 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect - github.com/sivchari/tenv v1.10.0 // indirect - github.com/sonatard/noctx v0.0.2 // indirect + github.com/sivchari/tenv v1.12.1 // indirect + github.com/sonatard/noctx v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect @@ -309,12 +297,11 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.13 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tdakkota/asciicheck v0.2.0 // indirect - github.com/tetafro/godot v1.4.16 // indirect + github.com/tetafro/godot v1.4.18 // indirect github.com/tetratelabs/wazero v1.8.0 // indirect github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect - github.com/timonwong/loggercheck v0.9.4 // indirect + github.com/timonwong/loggercheck v0.10.1 // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect github.com/tklauser/numcpus v0.7.0 // indirect github.com/tomarrell/wrapcheck/v2 v2.9.0 // indirect @@ -323,6 +310,7 @@ require ( github.com/ultraware/funlen v0.1.0 // indirect github.com/ultraware/whitespace v0.1.1 // indirect github.com/uudashr/gocognit v1.1.3 // indirect + github.com/uudashr/iface v1.2.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect github.com/vmihailenco/go-tinylfu v0.2.2 // indirect @@ -335,20 +323,20 @@ require ( github.com/yuin/gopher-lua v1.1.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/bosi/decorder v0.4.2 // indirect - go-simpler.org/musttag v0.12.2 // indirect + go-simpler.org/musttag v0.13.0 // indirect go-simpler.org/sloglint v0.7.2 // indirect - go.uber.org/automaxprocs v1.5.3 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.6.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/tools v0.27.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index bde78d3d2..1e810c9b8 100644 --- a/go.sum +++ b/go.sum @@ -6,19 +6,19 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= -github.com/Abirdcfly/dupword v0.1.1 h1:Bsxe0fIw6OwBtXMIncaTxCLHYO5BB+3mcsR5E8VXloY= -github.com/Abirdcfly/dupword v0.1.1/go.mod h1:B49AcJdTYYkpd4HjgAcutNGG9HZ2JWwKunH9Y2BA6sM= -github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM= -github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns= -github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ= -github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ= -github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck= -github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA= +github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE= +github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw= +github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA= +github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI= +github.com/Antonboom/nilnil v1.0.0 h1:n+v+B12dsE5tbAqRODXmEKfZv9j2KcTBrp+LkoM4HZk= +github.com/Antonboom/nilnil v1.0.0/go.mod h1:fDJ1FSFoLN6yoG65ANb1WihItf6qt9PJVTn/s2IrcII= +github.com/Antonboom/testifylint v1.5.2 h1:4s3Xhuv5AvdIgbd8wOOEeo0uZG7PbDKQyKY5lGoQazk= +github.com/Antonboom/testifylint v1.5.2/go.mod h1:vxy8VJ0bc6NavlYqjZfmp6EfqXMtBgQ4+mhCojwC1P8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/Crocmagnon/fatcontext v0.4.0 h1:4ykozu23YHA0JB6+thiuEv7iT6xq995qS1vcuWZq0tg= -github.com/Crocmagnon/fatcontext v0.4.0/go.mod h1:ZtWrXkgyfsYPzS6K3O88va6t2GEglG93vnII/F94WC0= +github.com/Crocmagnon/fatcontext v0.5.3 h1:zCh/wjc9oyeF+Gmp+V60wetm8ph2tlsxocgg/J0hOps= +github.com/Crocmagnon/fatcontext v0.5.3/go.mod h1:XoCQYY1J+XTfyv74qLXvNw4xFunr3L1wkopIIKG7wGM= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= @@ -26,8 +26,8 @@ github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5H github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU= github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= @@ -37,12 +37,12 @@ github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjC github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= -github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c= -github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= +github.com/alecthomas/go-check-sumtype v0.2.0 h1:Bo+e4DFf3rs7ME9w/0SU/g6nmzJaphduP8Cjiz0gbwY= +github.com/alecthomas/go-check-sumtype v0.2.0/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg= -github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= +github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU= +github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= @@ -93,8 +93,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY= -github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= +github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= +github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= @@ -102,10 +102,10 @@ github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkAp github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw= github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= -github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= -github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= -github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= -github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U= +github.com/breml/bidichk v0.3.2 h1:xV4flJ9V5xWTqxL+/PMFF6dtJPvZLPsyixAoPe8BGJs= +github.com/breml/bidichk v0.3.2/go.mod h1:VzFLBxuYtT23z5+iVkamXO386OB+/sVwZOpIj6zXGos= +github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAdk= +github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8= github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= @@ -139,12 +139,10 @@ github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= -github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9MTI= -github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE= +github.com/ckaznocha/intrange v0.2.1 h1:M07spnNEQoALOJhwrImSrJLaxwuiQK+hA2DeajBlwYk= +github.com/ckaznocha/intrange v0.2.1/go.mod h1:7NEhVyf8fzZO5Ds7CRaqPEm52Ut83hsTiL5zbER/HYk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= -github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= @@ -175,8 +173,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= -github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw= -github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= +github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -199,14 +197,14 @@ github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJL github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8= github.com/ethereum/c-kzg-4844 v1.0.1 h1:pGixCbGizcVKSwoV70ge48+PrbB+iSKs2rjgfE4yJmQ= github.com/ethereum/c-kzg-4844 v1.0.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.14.11 h1:8nFDCUUE67rPc6AKxFj7JKaOa2W/W1Rse3oS6LvvxEY= -github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= +github.com/ethereum/go-ethereum v1.14.12 h1:8hl57x77HSUo+cXExrURjU/w1VhL+ShCTJrTwcCQSe4= +github.com/ethereum/go-ethereum v1.14.12/go.mod h1:RAC2gVMWJ6FkxSPESfbshrcKpIokgQKsVKmAuqdekDY= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= @@ -219,22 +217,18 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= -github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk= -github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw= +github.com/ghostiam/protogetter v0.3.8 h1:LYcXbYvybUyTIxN2Mj9h6rHrDZBDwZloPoKctWrFyJY= +github.com/ghostiam/protogetter v0.3.8/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU= -github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-critic/go-critic v0.11.5 h1:TkDTOn5v7EEngMxu8KbuFqFR43USaaH8XRJLz1jhVYA= +github.com/go-critic/go-critic v0.11.5/go.mod h1:wu6U7ny9PiaHaZHcvMDmdysMqvDem162Rh3zWTrqk8M= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -284,8 +278,8 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= -github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= -github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -298,8 +292,8 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -316,10 +310,12 @@ github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXi github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU= +github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s= github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME= github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE= -github.com/golangci/golangci-lint v1.60.3 h1:l38A5de24ZeDlcFF+EB7m3W5joPD99/hS5SIHJPyZa0= -github.com/golangci/golangci-lint v1.60.3/go.mod h1:J4vOpcjzRI+lDL2DKNGBZVB3EQSBfCBCMpaydWLtJNo= +github.com/golangci/golangci-lint v1.62.2 h1:b8K5K9PN+rZN1+mKLtsZHz2XXS9aYKzQ9i25x3Qnxxw= +github.com/golangci/golangci-lint v1.62.2/go.mod h1:ILWWyeFUrctpHVGMa1dg2xZPKoMUTc5OIMgW7HZr34g= github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA= @@ -343,8 +339,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -415,12 +411,14 @@ github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48/go.mod h1:kJmVPMk4HfWyl2kcta34aad/K4TAfCwB29xX9PsR7LQ= github.com/iden3/contracts-abi/state/go/abi v1.0.2-0.20230911112726-4533c5701af1 h1:67302Oni3SpT2sbz7HXXEOR3zK3/usEC69W9ULILR1o= github.com/iden3/contracts-abi/state/go/abi v1.0.2-0.20230911112726-4533c5701af1/go.mod h1:TxgIrXCvxms3sbOdsy8kTvffUCIpEEifNy0fSXdkU4w= -github.com/iden3/driver-did-iden3 v0.0.5 h1:N3B9S3CBZHqWIZWspAiq2JFaLyq0eLZf26sN3ZEMAhQ= -github.com/iden3/driver-did-iden3 v0.0.5/go.mod h1:TcEG6fkExW6hgafjrU4ObOQ/HZqIRPQoL3TMU+URbS0= +github.com/iden3/driver-did-iden3 v0.0.7 h1:qDbVpEwbk25TCpb1Fw/9+vFiEWRdKTwSYNgWBd9AECI= +github.com/iden3/driver-did-iden3 v0.0.7/go.mod h1:V0zeeDNxrU/yKRFPgVfntMhBfmDL+GxcvHhW+Fdcz9I= github.com/iden3/go-circuits/v2 v2.4.0 h1:m+7uYtrvJKuc+gVhbXDXl1BJQyK7sWdW7OWttM3R/8I= github.com/iden3/go-circuits/v2 v2.4.0/go.mod h1:k0uYx/ZdZPiDEIy7kI3MAixnREKcc7NdCKDRw8Q+iFA= github.com/iden3/go-iden3-auth/v2 v2.5.1 h1:2+YPVsYpbTl+eKzVDqEeiiVoAD+c6s/TYPo/oWbVjwg= github.com/iden3/go-iden3-auth/v2 v2.5.1/go.mod h1:8DYjax0IdfzN+LUvCuGUJkjMPgq7o0byZIrZGdhIxjM= +github.com/iden3/go-iden3-auth/v2 v2.6.0 h1:R+QD/H1zhtO9+uvZ73Z9CVU7Omx8xf8ckgv4Syv4IsI= +github.com/iden3/go-iden3-auth/v2 v2.6.0/go.mod h1:s6t4ierMRafmJPxHSfwDW3Mh5+ceNbUrtbdP1EVoqfI= github.com/iden3/go-iden3-core v1.0.2 h1:HwNDFeqcUv4ybZj5tH+58JKWKarn/qqBpNCqTLxGP0Y= github.com/iden3/go-iden3-core v1.0.2/go.mod h1:X4PjlJG8OsEQEsSbzzYqqAk2olYGZ2nuGqiUPyEYjOo= github.com/iden3/go-iden3-core/v2 v2.3.1 h1:ytQqiclnVAIWyRKR2LF31hfz4DGRBD6nMjiPILXGSKk= @@ -447,12 +445,8 @@ github.com/iden3/go-schema-processor v1.3.1 h1:LJfFInfYGMOp0bTKKC17R8q4XI+VtqhFL github.com/iden3/go-schema-processor v1.3.1/go.mod h1:NwJ1nuGdRlCFaN1/V6mS0AOAdvpLcGf4KKq0mluLG7U= github.com/iden3/go-schema-processor/v2 v2.5.0 h1:MX84oFb9kYq0ntKiU4DrPPaRgCDUCKlurtHB6nvPPAs= github.com/iden3/go-schema-processor/v2 v2.5.0/go.mod h1:hMqYi4lKOzEGkmCRks/r4Crj8H4G8YaTt8H4jZHzX9Y= -github.com/iden3/iden3comm/v2 v2.8.1 h1:SjDXYS2O9jfpXNUzSdOBRTs+EjFKPkKOz4/IFkaOfXQ= -github.com/iden3/iden3comm/v2 v2.8.1/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= -github.com/iden3/iden3comm/v2 v2.8.3-0.20241203145634-5c082c18773e h1:kKVW1Y3hHyBKp3ZCOzHQxOGsXfJP5x3CgT6LeL24HPY= -github.com/iden3/iden3comm/v2 v2.8.3-0.20241203145634-5c082c18773e/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= -github.com/iden3/iden3comm/v2 v2.8.3-0.20241204161130-150691fe0395 h1:8nq2Oz5V0f64v0J4VKDwrnC1ycfe19qAQXwQJr9RxgE= -github.com/iden3/iden3comm/v2 v2.8.3-0.20241204161130-150691fe0395/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= +github.com/iden3/iden3comm/v2 v2.9.0 h1:LoD9/1A6L/HO1roWjjzTooka1m1y2SGSgRvcq+UMJS0= +github.com/iden3/iden3comm/v2 v2.9.0/go.mod h1:l8t+BkbHU3ri7kiExZYc4CTY6erkzkcBbYXw416nwhA= github.com/iden3/merkletree-proof v0.3.0 h1:NVlvtUBEgn4Etxxn+RsOmXP/qlI+85BdN8oUDTf3mxI= github.com/iden3/merkletree-proof v0.3.0/go.mod h1:+E2sBxMqhcn/fcu0LDGjmk3us+Vr+fxQUiZMxdpbgUE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -498,6 +492,8 @@ github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4 github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -505,12 +501,16 @@ github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9 github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus= github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= +github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= +github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= +github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -522,12 +522,12 @@ github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5 github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= -github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= -github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk= github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -537,9 +537,8 @@ github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos= github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= -github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= +github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg= +github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg= github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA= @@ -569,8 +568,8 @@ github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I= -github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0= +github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= +github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg= github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g= github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= @@ -603,8 +602,6 @@ github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFG github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= github.com/libp2p/go-libp2p v0.33.2 h1:vCdwnFxoGOXMKmaGHlDSnL4bM3fQeW8pgIa9DECnb40= github.com/libp2p/go-libp2p v0.33.2/go.mod h1:zTeppLuCvUIkT118pFVzA8xzP/p2dJYOMApCkFh0Yww= -github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= -github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -630,14 +627,16 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= -github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A= -github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU= +github.com/mgechev/revive v1.5.1 h1:hE+QPeq0/wIzJwOphdVyUJ82njdd8Khp4fUIHGZHW3M= +github.com/mgechev/revive v1.5.1/go.mod h1:lC9AhkJIBs5zwx8wkudyHrU+IJkrEKmpCmGMnIJPk4o= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -679,13 +678,15 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk= -github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ= +github.com/nunnatsa/ginkgolinter v0.18.3 h1:WgS7X3zzmni3vwHSBhvSgqrRgUecN6PQUcfB0j1noDw= +github.com/nunnatsa/ginkgolinter v0.18.3/go.mod h1:BE1xyB/PNtXXG1azrvrqJW5eFH0hSRylNzFy8QHPwzs= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 h1:rICjNsHbPP1LttefanBPnwsSwl09SqhCO7Ee623qR84= github.com/oapi-codegen/oapi-codegen/v2 v2.3.0/go.mod h1:4k+cJeSq5ntkwlcpQSxLxICCxQzCL772o30PxdibRt4= +github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q= +github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -694,17 +695,16 @@ github.com/olomix/go-test-pg v1.0.2 h1:4ey3mFBhPx93PdgyshOJI1WrQzqzkWEnb0wL/7UbF github.com/olomix/go-test-pg v1.0.2/go.mod h1:rHMame/S99rnU5YhVYFa3rr0riKqBYe8xsMDP4YzeHA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.20.0 h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw= -github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -714,29 +714,28 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f h1:HlPa7RcxTCrva5izPfTEfvYecO7LTahgmMRD1Qp13xg= github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY= -github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw= +github.com/polyfloyd/go-errorlint v1.7.0 h1:Zp6lzCK4hpBDj8y8a237YK4EPrMXQWvOe3nGoH4pFrU= +github.com/polyfloyd/go-errorlint v1.7.0/go.mod h1:dGWKu85mGHnegQ2SWpEybFityCg3j7ZbwsVUxAOk9gY= github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k= github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/pressly/goose/v3 v3.22.0 h1:wd/7kNiPTuNAztWun7iaB98DrhulbWPrzMAaw2DEZNw= github.com/pressly/goose/v3 v3.22.0/go.mod h1:yJM3qwSj2pp7aAaCvso096sguezamNb2OBgxCnh/EYg= +github.com/pressly/goose/v3 v3.23.0 h1:57hqKos8izGek4v6D5+OXBa+Y4Rq8MU//+MmnevdpVA= +github.com/pressly/goose/v3 v3.23.0/go.mod h1:rpx+D9GX/+stXmzKa+uh1DkjPnNVMdiOCV9iLdle4N8= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= @@ -745,8 +744,8 @@ github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpj github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs= -github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= +github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= +github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= @@ -755,16 +754,17 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= +github.com/raeperd/recvcheck v0.1.2 h1:SjdquRsRXJc26eSonWIo8b7IMtKD3OAT2Lb5G3ZX1+4= +github.com/raeperd/recvcheck v0.1.2/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -772,8 +772,8 @@ github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OK github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.3.3 h1:eiSQdJVNr9KTNxY2Niij8UReSwR8Xrte3exBrAZfqpg= -github.com/ryancurrah/gomodguard v1.3.3/go.mod h1:rsKQjj4l3LXe8N344Ow7agAy5p9yjsWOtRzUMYmA0QY= +github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU= +github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -791,8 +791,8 @@ github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84d github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI= github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/securego/gosec/v2 v2.20.1-0.20240822074752-ab3f6c1c83a0 h1:VqD4JMoqwuuCz8GZlBDsIDyE6K4YUsWJpbNtuOWHoFk= -github.com/securego/gosec/v2 v2.20.1-0.20240822074752-ab3f6c1c83a0/go.mod h1:iyeMMRw8QEmueUSZ2VqmkQMiDyDcobfPnG00CV/NWdE= +github.com/securego/gosec/v2 v2.21.4 h1:Le8MSj0PDmOnHJgUATjD96PaXRvCpKC+DGJvwyy0Mlk= +github.com/securego/gosec/v2 v2.21.4/go.mod h1:Jtb/MwRQfRxCXyCm1rfM1BEiiiTfUOdyzzAhlr6lUTA= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= @@ -813,10 +813,10 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= -github.com/sivchari/tenv v1.10.0 h1:g/hzMA+dBCKqGXgW8AV/1xIWhAvDrx0zFKNR48NFMg0= -github.com/sivchari/tenv v1.10.0/go.mod h1:tdY24masnVoZFxYrHv/nD6Tc8FbkEtAQEEziXpyMgqY= -github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00= -github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo= +github.com/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY= +github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw= +github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= +github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= @@ -836,8 +836,6 @@ github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMV github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= -github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= -github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc= github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -857,8 +855,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= @@ -871,14 +869,14 @@ github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= -github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/tetafro/godot v1.4.18 h1:ouX3XGiziKDypbpXqShBfnNLTSjR8r3/HVzrtJ+bHlI= +github.com/tetafro/godot v1.4.18/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g= github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= -github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= -github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= +github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg= +github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= @@ -887,8 +885,6 @@ github.com/tomarrell/wrapcheck/v2 v2.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+ github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= -github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= -github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= @@ -899,8 +895,12 @@ github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM= github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U= +github.com/uudashr/iface v1.2.1 h1:vHHyzAUmWZ64Olq6NZT3vg/z1Ws56kyPdBOd5kTXDF8= +github.com/uudashr/iface v1.2.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= github.com/valkey-io/valkey-go v1.0.45 h1:d2ksu+FvKEy9pU9CCMZ94ABTLm2kNHU0jxEJZRqpFA4= github.com/valkey-io/valkey-go v1.0.45/go.mod h1:BXlVAPIL9rFQinSFM+N32JfWzfCaUAqBpZkc4vPY6fM= +github.com/valkey-io/valkey-go v1.0.51 h1:qioDrTBplnkWLK5TrUq0rkuIv7HUL/RPJU/79wB93Lg= +github.com/valkey-io/valkey-go v1.0.51/go.mod h1:BXlVAPIL9rFQinSFM+N32JfWzfCaUAqBpZkc4vPY6fM= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= @@ -923,7 +923,6 @@ github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+ github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -938,16 +937,16 @@ gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= -go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs= -go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM= +go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= +go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -974,14 +973,16 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8= -golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f h1:WTyX8eCCyfdqiPYkRGm0MqElSfYFH3yR1+rl/mct9sA= +golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -995,8 +996,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1005,7 +1006,6 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -1018,8 +1018,8 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1028,8 +1028,10 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1046,8 +1048,6 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1071,8 +1071,10 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1093,8 +1095,10 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1106,17 +1110,14 @@ golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= @@ -1129,8 +1130,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1186,6 +1187,7 @@ modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= +modernc.org/sqlite v1.34.1 h1:u3Yi6M0N8t9yKRDwhXcyp1eS5/ErhPTBggxWFuR6Hfk= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/internal/core/services/identity.go b/internal/core/services/identity.go index bec7f234e..0c70dfc91 100644 --- a/internal/core/services/identity.go +++ b/internal/core/services/identity.go @@ -3,6 +3,7 @@ package services import ( "bytes" "context" + "crypto/ecdsa" "encoding/hex" "encoding/json" "errors" @@ -851,7 +852,7 @@ func (i *identity) createIdentity(ctx context.Context, tx db.Querier, hostURL st } func (i *identity) createEthIdentityFromKeyID(ctx context.Context, mts *domain.IdentityMerkleTrees, key *kms.KeyID, didOptions *ports.DIDCreationOptions, tx db.Querier) (*domain.Identity, *w3c.DID, error) { - pubKey, err := kms.EthPubKey(ctx, i.kms, *key) + pubKey, err := ethPubKey(ctx, i.kms, *key) if err != nil { return nil, nil, err } @@ -1257,3 +1258,40 @@ func sanitizeIssuerDoc(issDoc []byte) []byte { str := strings.Replace(string(issDoc), "\\u0000", "", -1) return []byte(str) } + +// EthPubKey returns the ethereum public key from the key manager service. +// the public key is either uncompressed or compressed, so we need to handle both cases. +func ethPubKey(ctx context.Context, keyMS kms.KMSType, keyID kms.KeyID) (*ecdsa.PublicKey, error) { + const ( + uncompressedKeyLength = 65 + awsKeyLength = 88 + defaultKeyLength = 33 + ) + + if keyID.Type != kms.KeyTypeEthereum { + return nil, errors.New("key type is not ethereum") + } + + keyBytes, err := keyMS.PublicKey(keyID) + if err != nil { + log.Error(ctx, "can't get bytes from public key", "err", err) + return nil, err + } + + // public key is uncompressed. It's 65 bytes long. + if len(keyBytes) == uncompressedKeyLength { + return crypto.UnmarshalPubkey(keyBytes) + } + + // public key is AWS format. It's 88 bytes long. + if len(keyBytes) == awsKeyLength { + return kms.DecodeAWSETHPubKey(ctx, keyBytes) + } + + // public key is compressed. It's 33 bytes long. + if len(keyBytes) == defaultKeyLength { + return kms.DecodeETHPubKey(keyBytes) + } + + return nil, errors.New("unsupported public key format") +} diff --git a/internal/kms/vault_eth_key_provider.go b/internal/kms/vault_eth_key_provider.go index 9223f47b7..aedc61abc 100644 --- a/internal/kms/vault_eth_key_provider.go +++ b/internal/kms/vault_eth_key_provider.go @@ -10,8 +10,6 @@ import ( "github.com/hashicorp/vault/api" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/pkg/errors" - - "github.com/polygonid/sh-id-platform/internal/log" ) type vaultETHKeyProvider struct { @@ -205,41 +203,3 @@ func DecodeETHPubKey(key []byte) (*ecdsa.PublicKey, error) { pubKey, err := crypto.DecompressPubkey(key) return pubKey, errors.WithStack(err) } - -// EthPubKey returns the ethereum public key from the key manager service. -// the public key is either uncompressed or compressed, so we need to handle both cases. -// TODO: Move out of this package -func EthPubKey(ctx context.Context, keyMS KMSType, keyID KeyID) (*ecdsa.PublicKey, error) { - const ( - uncompressedKeyLength = 65 - awsKeyLength = 88 - defaultKeyLength = 33 - ) - - if keyID.Type != KeyTypeEthereum { - return nil, errors.New("key type is not ethereum") - } - - keyBytes, err := keyMS.PublicKey(keyID) - if err != nil { - log.Error(ctx, "can't get bytes from public key", "err", err) - return nil, err - } - - // public key is uncompressed. It's 65 bytes long. - if len(keyBytes) == uncompressedKeyLength { - return crypto.UnmarshalPubkey(keyBytes) - } - - // public key is AWS format. It's 88 bytes long. - if len(keyBytes) == awsKeyLength { - return DecodeAWSETHPubKey(ctx, keyBytes) - } - - // public key is compressed. It's 33 bytes long. - if len(keyBytes) == defaultKeyLength { - return DecodeETHPubKey(keyBytes) - } - - return nil, errors.New("unsupported public key format") -} From 85216e149b2cdec060b93f1d0f2f22d807a91e30 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 9 Dec 2024 18:16:23 +0100 Subject: [PATCH 43/66] chore: Extra check and remove unused code --- internal/core/ports/claim_service.go | 4 ++++ internal/network/resolver.go | 16 ---------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/internal/core/ports/claim_service.go b/internal/core/ports/claim_service.go index 15ae339f9..73264e40f 100644 --- a/internal/core/ports/claim_service.go +++ b/internal/core/ports/claim_service.go @@ -186,6 +186,10 @@ func NewAgentRequest(basicMessage *comm.BasicMessage) (*AgentRequest, error) { return nil, fmt.Errorf("invalid type") } + if basicMessage.ID == "" { + return nil, fmt.Errorf("'id' field cannot be empty") + } + return &AgentRequest{ Body: basicMessage.Body, UserDID: fromDID, diff --git a/internal/network/resolver.go b/internal/network/resolver.go index 2f12e4337..f2c84f018 100644 --- a/internal/network/resolver.go +++ b/internal/network/resolver.go @@ -73,22 +73,6 @@ type RhsSettings struct { SingleIssuer bool } -// PaymentSettings holds the payment settings -type PaymentSettings struct { - MCPaymentContract string `yaml:"mcPaymentContract"` - Recipient string `yaml:"recipient"` - Amount uint `yaml:"amount"` - Erc20Tokens []ERC20TokenSettings `yaml:"Erc20Tokens"` -} - -// ERC20TokenSettings holds the ERC20 payment settings -type ERC20TokenSettings struct { - TokenAddress string `yaml:"TokenAddress"` - TokenSymbol string `yaml:"TokenSymbol"` - TokenName string `yaml:"TokenName"` - TokenAmount uint `yaml:"TokenAmount"` -} - // ResolverSettings holds the resolver settings type ResolverSettings map[string]map[string]struct { ContractAddress string `yaml:"contractAddress"` From 296810c4ac25a4e80ff3efd4580c88bc1c165e7b Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 9 Dec 2024 18:18:59 +0100 Subject: [PATCH 44/66] chore: Remove unused NewAgentProposalRequest function --- internal/core/ports/payment_service.go | 46 -------------------------- 1 file changed, 46 deletions(-) diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index 32566896b..fddaceaab 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -2,8 +2,6 @@ package ports import ( "context" - "encoding/json" - "fmt" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" @@ -14,50 +12,6 @@ import ( "github.com/polygonid/sh-id-platform/internal/payments" ) -// NewAgentProposalRequest validates the inputs and returns a new AgentRequest -func NewAgentProposalRequest(basicMessage *comm.BasicMessage) (*protocol.CredentialsProposalRequestMessage, error) { - if basicMessage.To == "" { - return nil, fmt.Errorf("'to' field cannot be empty") - } - - toDID, err := w3c.ParseDID(basicMessage.To) - if err != nil { - return nil, err - } - - if basicMessage.From == "" { - return nil, fmt.Errorf("'from' field cannot be empty") - } - - fromDID, err := w3c.ParseDID(basicMessage.From) - if err != nil { - return nil, err - } - - if basicMessage.ID == "" { - return nil, fmt.Errorf("'id' field cannot be empty") - } - - if basicMessage.Type != protocol.CredentialProposalRequestMessageType { - return nil, fmt.Errorf("invalid type") - } - - var body *protocol.CredentialsProposalRequestBody - if err := json.Unmarshal(basicMessage.Body, body); err != nil { - return nil, err - } - - return &protocol.CredentialsProposalRequestMessage{ - ID: basicMessage.ID, - ThreadID: basicMessage.ThreadID, - Typ: basicMessage.Typ, - Type: basicMessage.Type, - Body: *body, - To: toDID.String(), - From: fromDID.String(), - }, nil -} - // CreatePaymentRequestReq is the request for PaymentService.CreatePaymentRequest type CreatePaymentRequestReq struct { IssuerDID w3c.DID From 7b7430f5cc6018d57d7272f911b244a609bed7cc Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 9 Dec 2024 21:42:39 +0200 Subject: [PATCH 45/66] fix verificationMethod and signature += 27 --- internal/core/services/payment.go | 46 +++++++++++++++++-- .../kms/local_storage_eth_key_provider.go | 1 + internal/kms/vault_eth_key_provider.go | 1 + 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 4db2b5dd4..bc6c95ecf 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -2,8 +2,10 @@ package services import ( "context" + "crypto/ecdsa" "crypto/rand" "encoding/hex" + "errors" "fmt" "math/big" "strconv" @@ -12,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" @@ -319,6 +322,14 @@ func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, return nil, err } + signerAddress, err := p.getAddress(kms.KeyID{ + Type: kms.KeyTypeEthereum, + ID: chainConfig.SigningKeyID, + }) + if err != nil { + log.Error(ctx, "failed to retrieve signer address", "err", err) + return nil, err + } switch setting.PaymentOption.Type { case protocol.Iden3PaymentRailsRequestV1Type: return &protocol.Iden3PaymentRailsRequestV1{ @@ -332,7 +343,7 @@ func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, ExpirationDate: fmt.Sprint(expirationTime.Format(time.RFC3339)), Metadata: metadata, Recipient: chainConfig.Recipient.String(), - Proof: paymentProof(&setting, signature), + Proof: paymentProof(&setting, signature, signerAddress), }, nil case protocol.Iden3PaymentRailsERC20RequestV1Type: @@ -349,7 +360,7 @@ func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, Recipient: chainConfig.Recipient.String(), Features: setting.PaymentOption.Features, TokenAddress: setting.PaymentOption.ContractAddress.String(), - Proof: paymentProof(&setting, signature), + Proof: paymentProof(&setting, signature, signerAddress), }, nil case protocol.Iden3PaymentRequestCryptoV1Type: @@ -359,7 +370,7 @@ func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, } } -func paymentProof(setting *payments.ChainConfig, signature []byte) protocol.PaymentProof { +func paymentProof(setting *payments.ChainConfig, signature []byte, signerAddress common.Address) protocol.PaymentProof { var eip712DataTypes string if setting.PaymentOption.Type == protocol.Iden3PaymentRailsRequestV1Type { eip712DataTypes = "https://schema.iden3.io/core/json/Iden3PaymentRailsRequestV1.json" @@ -372,7 +383,7 @@ func paymentProof(setting *payments.ChainConfig, signature []byte) protocol.Paym Type: "EthereumEip712Signature2021", ProofPurpose: "assertionMethod", ProofValue: fmt.Sprintf("0x%s", hex.EncodeToString(signature)), - VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", setting.ChainID, setting.PaymentRails), + VerificationMethod: fmt.Sprintf("did:pkh:eip155:%d:%s", setting.ChainID, signerAddress), Created: time.Now().Format(time.RFC3339), Eip712: protocol.Eip712Data{ Types: eip712DataTypes, @@ -490,3 +501,30 @@ func (p *payment) paymentRequestSignature( } return signature, nil } + +func (p *payment) getAddress(k kms.KeyID) (common.Address, error) { + if p.kms == nil { + return common.Address{}, errors.Join(errors.New("the signer is read-only")) + } + bytesPubKey, err := p.kms.PublicKey(k) + if err != nil { + return common.Address{}, err + } + var pubKey *ecdsa.PublicKey + switch len(bytesPubKey) { + case eth.CompressedPublicKeyLength: + pubKey, err = crypto.DecompressPubkey(bytesPubKey) + case eth.AwsKmsPublicKeyLength: + pubKey, err = kms.DecodeAWSETHPubKey(context.Background(), bytesPubKey) + if err != nil { + return common.Address{}, err + } + default: + pubKey, err = crypto.UnmarshalPubkey(bytesPubKey) + } + if err != nil { + return common.Address{}, err + } + fromAddress := crypto.PubkeyToAddress(*pubKey) + return fromAddress, nil +} diff --git a/internal/kms/local_storage_eth_key_provider.go b/internal/kms/local_storage_eth_key_provider.go index 9cbf29d64..8ca1e4df1 100644 --- a/internal/kms/local_storage_eth_key_provider.go +++ b/internal/kms/local_storage_eth_key_provider.go @@ -101,6 +101,7 @@ func (ls *localStorageEthKeyProvider) Sign(ctx context.Context, keyID KeyID, dat } sig, err := crypto.Sign(data, privKey) + sig[64] += 27 return sig, err } diff --git a/internal/kms/vault_eth_key_provider.go b/internal/kms/vault_eth_key_provider.go index aedc61abc..6e6226a15 100644 --- a/internal/kms/vault_eth_key_provider.go +++ b/internal/kms/vault_eth_key_provider.go @@ -101,6 +101,7 @@ func (v *vaultETHKeyProvider) Sign(_ context.Context, keyID KeyID, data []byte) } sig, err := crypto.Sign(data, privKey) + sig[64] += 27 return sig, errors.WithStack(err) } From 7bb30c86af5922307b8b32d67cdc93f5ab13786b Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 9 Dec 2024 21:51:41 +0200 Subject: [PATCH 46/66] fix linter --- internal/core/services/payment.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index bc6c95ecf..52a84f824 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -322,7 +322,7 @@ func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, return nil, err } - signerAddress, err := p.getAddress(kms.KeyID{ + signerAddress, err := p.getAddress(ctx, kms.KeyID{ Type: kms.KeyTypeEthereum, ID: chainConfig.SigningKeyID, }) @@ -502,7 +502,7 @@ func (p *payment) paymentRequestSignature( return signature, nil } -func (p *payment) getAddress(k kms.KeyID) (common.Address, error) { +func (p *payment) getAddress(ctx context.Context, k kms.KeyID) (common.Address, error) { if p.kms == nil { return common.Address{}, errors.Join(errors.New("the signer is read-only")) } @@ -515,7 +515,7 @@ func (p *payment) getAddress(k kms.KeyID) (common.Address, error) { case eth.CompressedPublicKeyLength: pubKey, err = crypto.DecompressPubkey(bytesPubKey) case eth.AwsKmsPublicKeyLength: - pubKey, err = kms.DecodeAWSETHPubKey(context.Background(), bytesPubKey) + pubKey, err = kms.DecodeAWSETHPubKey(ctx, bytesPubKey) if err != nil { return common.Address{}, err } From 37455ab68893d014cfc5202b04fa57fd45c7891a Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 10 Dec 2024 11:38:20 +0100 Subject: [PATCH 47/66] feat: MVP for verify payment endpoint --- api/api.yaml | 29 +- internal/api/api.gen.go | 274 +++++++++--------- internal/api/payment.go | 13 +- internal/core/domain/payment.go | 1 + internal/core/ports/payment_service.go | 3 +- internal/core/services/payment.go | 144 +-------- .../202411131724000_payment_options.sql | 1 + internal/eth/util.go | 19 -- .../kms/local_storage_eth_key_provider.go | 9 +- internal/kms/vault_eth_key_provider.go | 9 +- internal/repositories/payment.go | 13 +- .../repositories/payment_request_fixture.go | 8 +- internal/repositories/payment_test.go | 2 + 13 files changed, 216 insertions(+), 309 deletions(-) delete mode 100644 internal/eth/util.go diff --git a/api/api.yaml b/api/api.yaml index 0879febee..eb4e9d88c 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1443,28 +1443,32 @@ paths: $ref: '#/components/responses/500' - /v2/payment/verify/{paymentOptionID}: + /v2/identities/{identifier}/payment/verify/{nonce}: post: summary: Verify Payment operationId: VerifyPayment - description: Verify a payment. + description: Verify a payment by nonce. tags: - Payment security: - basicAuth: [ ] parameters: - - name: paymentOptionID + - name: identifier schema: type: string - x-go-type: uuid.UUID required: true in: path + - name: nonce + schema: + type: string + in: path + required: true requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/PaymentMessage' + $ref: '#/components/schemas/PaymentVerifyRequest' responses: '200': description: Payment verified @@ -2145,15 +2149,14 @@ components: value: type: string - PaymentMessage: + PaymentVerifyRequest: type: object - x-go-type: protocol.PaymentMessage - x-go-type-import: - name: protocol - path: "github.com/iden3/iden3comm/v2/protocol" - description: | - // protocol.PaymentMessage - // https://iden3-communication.io/credentials/0.1/payment/ + required: + - txHash + properties: + txHash: + type: string + example: "0x8f271174b45ba7892d83..." BasicMessage: type: object diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 032b0292e..703376dcb 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -488,10 +488,6 @@ type PaginatedMetadata struct { Total uint `json:"total"` } -// PaymentMessage // protocol.PaymentMessage -// // https://iden3-communication.io/credentials/0.1/payment/ -type PaymentMessage = protocol.PaymentMessage - // PaymentOption defines model for PaymentOption. type PaymentOption struct { Config PaymentOptionConfig `json:"config"` @@ -548,6 +544,11 @@ type PaymentStatus struct { // PaymentStatusStatus defines model for PaymentStatus.Status. type PaymentStatusStatus string +// PaymentVerifyRequest defines model for PaymentVerifyRequest. +type PaymentVerifyRequest struct { + TxHash string `json:"txHash"` +} + // PublishIdentityStateResponse defines model for PublishIdentityStateResponse. type PublishIdentityStateResponse struct { ClaimsTreeRoot *string `json:"claimsTreeRoot,omitempty"` @@ -887,12 +888,12 @@ type CreatePaymentRequestJSONRequestBody = CreatePaymentRequest // CreatePaymentOptionJSONRequestBody defines body for CreatePaymentOption for application/json ContentType. type CreatePaymentOptionJSONRequestBody = PaymentOptionRequest +// VerifyPaymentJSONRequestBody defines body for VerifyPayment for application/json ContentType. +type VerifyPaymentJSONRequestBody = PaymentVerifyRequest + // ImportSchemaJSONRequestBody defines body for ImportSchema for application/json ContentType. type ImportSchemaJSONRequestBody = ImportSchemaRequest -// VerifyPaymentJSONRequestBody defines body for VerifyPayment for application/json ContentType. -type VerifyPaymentJSONRequestBody = PaymentMessage - // ServerInterface represents all server handlers. type ServerInterface interface { // Healthcheck @@ -1000,6 +1001,9 @@ type ServerInterface interface { // Get Payment Option // (GET /v2/identities/{identifier}/payment/options/{id}) GetPaymentOption(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) + // Verify Payment + // (POST /v2/identities/{identifier}/payment/verify/{nonce}) + VerifyPayment(w http.ResponseWriter, r *http.Request, identifier string, nonce string) // Get Schemas // (GET /v2/identities/{identifier}/schemas) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) @@ -1024,9 +1028,6 @@ type ServerInterface interface { // Return payment settings // (GET /v2/payment/settings) GetPaymentSettings(w http.ResponseWriter, r *http.Request) - // Verify Payment - // (POST /v2/payment/verify/{paymentOptionID}) - VerifyPayment(w http.ResponseWriter, r *http.Request, paymentOptionID uuid.UUID) // Get QrCode from store // (GET /v2/qr-store) GetQrFromStore(w http.ResponseWriter, r *http.Request, params GetQrFromStoreParams) @@ -1252,6 +1253,12 @@ func (_ Unimplemented) GetPaymentOption(w http.ResponseWriter, r *http.Request, w.WriteHeader(http.StatusNotImplemented) } +// Verify Payment +// (POST /v2/identities/{identifier}/payment/verify/{nonce}) +func (_ Unimplemented) VerifyPayment(w http.ResponseWriter, r *http.Request, identifier string, nonce string) { + w.WriteHeader(http.StatusNotImplemented) +} + // Get Schemas // (GET /v2/identities/{identifier}/schemas) func (_ Unimplemented) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) { @@ -1300,12 +1307,6 @@ func (_ Unimplemented) GetPaymentSettings(w http.ResponseWriter, r *http.Request w.WriteHeader(http.StatusNotImplemented) } -// Verify Payment -// (POST /v2/payment/verify/{paymentOptionID}) -func (_ Unimplemented) VerifyPayment(w http.ResponseWriter, r *http.Request, paymentOptionID uuid.UUID) { - w.WriteHeader(http.StatusNotImplemented) -} - // Get QrCode from store // (GET /v2/qr-store) func (_ Unimplemented) GetQrFromStore(w http.ResponseWriter, r *http.Request, params GetQrFromStoreParams) { @@ -2629,6 +2630,46 @@ func (siw *ServerInterfaceWrapper) GetPaymentOption(w http.ResponseWriter, r *ht handler.ServeHTTP(w, r) } +// VerifyPayment operation middleware +func (siw *ServerInterfaceWrapper) VerifyPayment(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier string + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + // ------------- Path parameter "nonce" ------------- + var nonce string + + err = runtime.BindStyledParameterWithOptions("simple", "nonce", chi.URLParam(r, "nonce"), &nonce, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "nonce", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.VerifyPayment(w, r, identifier, nonce) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // GetSchemas operation middleware func (siw *ServerInterfaceWrapper) GetSchemas(w http.ResponseWriter, r *http.Request) { @@ -2921,37 +2962,6 @@ func (siw *ServerInterfaceWrapper) GetPaymentSettings(w http.ResponseWriter, r * handler.ServeHTTP(w, r) } -// VerifyPayment operation middleware -func (siw *ServerInterfaceWrapper) VerifyPayment(w http.ResponseWriter, r *http.Request) { - - var err error - - // ------------- Path parameter "paymentOptionID" ------------- - var paymentOptionID uuid.UUID - - err = runtime.BindStyledParameterWithOptions("simple", "paymentOptionID", chi.URLParam(r, "paymentOptionID"), &paymentOptionID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "paymentOptionID", Err: err}) - return - } - - ctx := r.Context() - - ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) - - r = r.WithContext(ctx) - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.VerifyPayment(w, r, paymentOptionID) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - // GetQrFromStore operation middleware func (siw *ServerInterfaceWrapper) GetQrFromStore(w http.ResponseWriter, r *http.Request) { @@ -3261,6 +3271,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/identities/{identifier}/payment/options/{id}", wrapper.GetPaymentOption) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/v2/identities/{identifier}/payment/verify/{nonce}", wrapper.VerifyPayment) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/identities/{identifier}/schemas", wrapper.GetSchemas) }) @@ -3285,9 +3298,6 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/payment/settings", wrapper.GetPaymentSettings) }) - r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/v2/payment/verify/{paymentOptionID}", wrapper.VerifyPayment) - }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/qr-store", wrapper.GetQrFromStore) }) @@ -4792,6 +4802,52 @@ func (response GetPaymentOption500JSONResponse) VisitGetPaymentOptionResponse(w return json.NewEncoder(w).Encode(response) } +type VerifyPaymentRequestObject struct { + Identifier string `json:"identifier"` + Nonce string `json:"nonce"` + Body *VerifyPaymentJSONRequestBody +} + +type VerifyPaymentResponseObject interface { + VisitVerifyPaymentResponse(w http.ResponseWriter) error +} + +type VerifyPayment200JSONResponse PaymentStatus + +func (response VerifyPayment200JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type VerifyPayment400JSONResponse struct{ N400JSONResponse } + +func (response VerifyPayment400JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type VerifyPayment401JSONResponse struct{ N401JSONResponse } + +func (response VerifyPayment401JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +type VerifyPayment500JSONResponse struct{ N500JSONResponse } + +func (response VerifyPayment500JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type GetSchemasRequestObject struct { Identifier PathIdentifier `json:"identifier"` Params GetSchemasParams @@ -5084,51 +5140,6 @@ func (response GetPaymentSettings200JSONResponse) VisitGetPaymentSettingsRespons return json.NewEncoder(w).Encode(response) } -type VerifyPaymentRequestObject struct { - PaymentOptionID uuid.UUID `json:"paymentOptionID"` - Body *VerifyPaymentJSONRequestBody -} - -type VerifyPaymentResponseObject interface { - VisitVerifyPaymentResponse(w http.ResponseWriter) error -} - -type VerifyPayment200JSONResponse PaymentStatus - -func (response VerifyPayment200JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - -type VerifyPayment400JSONResponse struct{ N400JSONResponse } - -func (response VerifyPayment400JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type VerifyPayment401JSONResponse struct{ N401JSONResponse } - -func (response VerifyPayment401JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(401) - - return json.NewEncoder(w).Encode(response) -} - -type VerifyPayment500JSONResponse struct{ N500JSONResponse } - -func (response VerifyPayment500JSONResponse) VisitVerifyPaymentResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - type GetQrFromStoreRequestObject struct { Params GetQrFromStoreParams } @@ -5377,6 +5388,9 @@ type StrictServerInterface interface { // Get Payment Option // (GET /v2/identities/{identifier}/payment/options/{id}) GetPaymentOption(ctx context.Context, request GetPaymentOptionRequestObject) (GetPaymentOptionResponseObject, error) + // Verify Payment + // (POST /v2/identities/{identifier}/payment/verify/{nonce}) + VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) // Get Schemas // (GET /v2/identities/{identifier}/schemas) GetSchemas(ctx context.Context, request GetSchemasRequestObject) (GetSchemasResponseObject, error) @@ -5401,9 +5415,6 @@ type StrictServerInterface interface { // Return payment settings // (GET /v2/payment/settings) GetPaymentSettings(ctx context.Context, request GetPaymentSettingsRequestObject) (GetPaymentSettingsResponseObject, error) - // Verify Payment - // (POST /v2/payment/verify/{paymentOptionID}) - VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) // Get QrCode from store // (GET /v2/qr-store) GetQrFromStore(ctx context.Context, request GetQrFromStoreRequestObject) (GetQrFromStoreResponseObject, error) @@ -6454,6 +6465,40 @@ func (sh *strictHandler) GetPaymentOption(w http.ResponseWriter, r *http.Request } } +// VerifyPayment operation middleware +func (sh *strictHandler) VerifyPayment(w http.ResponseWriter, r *http.Request, identifier string, nonce string) { + var request VerifyPaymentRequestObject + + request.Identifier = identifier + request.Nonce = nonce + + var body VerifyPaymentJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.VerifyPayment(ctx, request.(VerifyPaymentRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "VerifyPayment") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(VerifyPaymentResponseObject); ok { + if err := validResponse.VisitVerifyPaymentResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // GetSchemas operation middleware func (sh *strictHandler) GetSchemas(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetSchemasParams) { var request GetSchemasRequestObject @@ -6670,39 +6715,6 @@ func (sh *strictHandler) GetPaymentSettings(w http.ResponseWriter, r *http.Reque } } -// VerifyPayment operation middleware -func (sh *strictHandler) VerifyPayment(w http.ResponseWriter, r *http.Request, paymentOptionID uuid.UUID) { - var request VerifyPaymentRequestObject - - request.PaymentOptionID = paymentOptionID - - var body VerifyPaymentJSONRequestBody - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) - return - } - request.Body = &body - - handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.VerifyPayment(ctx, request.(VerifyPaymentRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "VerifyPayment") - } - - response, err := handler(r.Context(), w, r, request) - - if err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(VerifyPaymentResponseObject); ok { - if err := validResponse.VisitVerifyPaymentResponse(w); err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } - } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) - } -} - // GetQrFromStore operation middleware func (sh *strictHandler) GetQrFromStore(w http.ResponseWriter, r *http.Request, params GetQrFromStoreParams) { var request GetQrFromStoreRequestObject diff --git a/internal/api/payment.go b/internal/api/payment.go index 8028d35aa..9d7a8eb55 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -156,7 +156,18 @@ func (s *Server) GetPaymentSettings(_ context.Context, _ GetPaymentSettingsReque // VerifyPayment is the controller to verify payment func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequestObject) (VerifyPaymentResponseObject, error) { - isPaid, err := s.paymentService.VerifyPayment(ctx, request.PaymentOptionID, request.Body) + const base10 = 10 + issuerDID, err := w3c.ParseDID(request.Identifier) + if err != nil { + log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) + return VerifyPayment400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } + nonce, ok := new(big.Int).SetString(request.Nonce, base10) + if !ok { + log.Error(ctx, "parsing nonce on verify payment", "err", err, "nonce", request.Nonce) + return VerifyPayment400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("invalid nonce: <%s>", request.Nonce)}}, nil + } + isPaid, err := s.paymentService.VerifyPayment(ctx, *issuerDID, nonce, request.Body.TxHash) if err != nil { log.Error(ctx, "can't process payment message", "err", err) return VerifyPayment400JSONResponse{N400JSONResponse{Message: "can't process payment message"}}, nil diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 7725eed42..852b3dce6 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -29,6 +29,7 @@ type PaymentRequestItem struct { ID uuid.UUID Nonce big.Int PaymentRequestID uuid.UUID + PaymentOptionID payments.OptionConfigIDType // The numeric id that identify a payment option in payments config file. Payment protocol.PaymentRequestInfoDataItem } diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index fddaceaab..316fd3924 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -2,6 +2,7 @@ package ports import ( "context" + "math/big" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" @@ -26,7 +27,7 @@ type PaymentService interface { CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq) (*domain.PaymentRequest, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) GetSettings() payments.Config - VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, message *protocol.PaymentMessage) (bool, error) + VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *big.Int, txHash string) (bool, error) CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config *domain.PaymentOptionConfig) (uuid.UUID, error) GetPaymentOptions(ctx context.Context, issuerDID *w3c.DID) ([]domain.PaymentOption, error) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 52a84f824..28b35be27 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -128,8 +128,8 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay ID: uuid.New(), Nonce: *nonce, PaymentRequestID: paymentRequest.ID, - - Payment: data, + PaymentOptionID: chainConfig.PaymentOptionID, + Payment: data, } paymentRequest.Payments = append(paymentRequest.Payments, item) } @@ -161,16 +161,18 @@ func (p *payment) GetSettings() payments.Config { } // VerifyPayment verifies a payment -// TODO: Total refactor! Reimplement from scratch!!!! -func (p *payment) VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, message *protocol.PaymentMessage) (bool, error) { - if len(message.Body.Payments) != 1 { - return false, fmt.Errorf("expected one payment, got %d", len(message.Body.Payments)) +func (p *payment) VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *big.Int, txHash string) (bool, error) { + paymentReqItem, err := p.paymentsStore.GetPaymentRequestItem(ctx, issuerDID, nonce) + if err != nil { + return false, fmt.Errorf("failed to get payment request: %w", err) } - option, err := p.paymentsStore.GetPaymentOptionByID(ctx, nil, paymentOptionID) - if err != nil { - return false, fmt.Errorf("failed to get payment option: %w", err) + setting, found := p.settings[paymentReqItem.PaymentOptionID] + if !found { + log.Error(ctx, "chain not found in configuration", "paymentOptionID", paymentReqItem.PaymentOptionID) + return false, fmt.Errorf("payment Option <%d> not found in payment configuration", paymentReqItem.PaymentOptionID) } + contractAddress := setting.PaymentRails // TODO: Load rpc from network resolvers client, err := ethclient.Dial("https://polygon-amoy.g.alchemy.com/v2/DHvucvBBzrBhaHzmjrMp24PGbl7vwee6") @@ -178,139 +180,19 @@ func (p *payment) VerifyPayment(ctx context.Context, paymentOptionID uuid.UUID, return false, fmt.Errorf("failed to connect to ethereum client: %w", err) } - // contractAddress := common.HexToAddress("0xF8E49b922D5Fb00d3EdD12bd14064f275726D339") - contractAddress, err := contractAddressFromPayment(&message.Body.Payments[0], p.settings) - if err != nil { - return false, fmt.Errorf("failed to get contract address from payment: %w", err) - } - instance, err := eth.NewPaymentContract(*contractAddress, client) - if err != nil { - return false, err - } - - // TODO: Iterate over all payments? Right now we only support one payment - nonce, err := nonceFromPayment(&message.Body.Payments[0]) - if err != nil { - log.Error(ctx, "failed to get nonce from payment request info data", "err", err) - return false, err - } - - recipientAddr, err := recipientAddressFromPayment(&message.Body.Payments[0], option) + instance, err := eth.NewPaymentContract(contractAddress, client) if err != nil { - log.Error(ctx, "failed to get recipient address from payment", "err", err) return false, err } // TODO: pending, canceled, success, failed - isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, *recipientAddr, nonce) + isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, setting.PaymentOption.ContractAddress, nonce) if err != nil { return false, err } return isPaid, nil } -func contractAddressFromPayment(data *protocol.Payment, config payments.Config) (*common.Address, error) { - /* - var sChainID string - switch data.Type() { - case protocol.Iden3PaymentCryptoV1Type: - return nil, nil - case protocol.Iden3PaymentRailsV1Type: - d := data.Data() - t, ok := d.(*protocol.Iden3PaymentRailsV1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") - } - sChainID = t.PaymentData.ChainID - case protocol.Iden3PaymentRailsERC20V1Type: - t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") - } - sChainID = t.PaymentData.ChainID - default: - return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) - } - chainID, err := strconv.Atoi(sChainID) - if err != nil { - return nil, fmt.Errorf("failed to parse chain id: %w", err) - } - c, found := config[chainID] - if !found { - return nil, fmt.Errorf("chain id not found in settings: %d", chainID) - } - addr := common.HexToAddress(c.MCPayment) - return &addr, nil - - */ - return &common.Address{}, nil -} - -func recipientAddressFromPayment(data *protocol.Payment, option *domain.PaymentOption) (*common.Address, error) { - /* - var address common.Address - switch data.Type() { - case protocol.Iden3PaymentCryptoV1Type: - address = common.Address{} - case protocol.Iden3PaymentRailsV1Type: - t, ok := data.Data().(*protocol.Iden3PaymentRailsV1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") - } - for _, chain := range option.Config.Chains { - if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { - address = common.HexToAddress(chain.Recipient) - break - } - } - case protocol.Iden3PaymentRailsERC20V1Type: - t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") - } - for _, chain := range option.Config.Chains { - if strconv.Itoa(chain.ChainId) == t.PaymentData.ChainID { - address = common.HexToAddress(chain.Recipient) - break - } - } - default: - return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) - } - return &address, nil - - */ - return &common.Address{}, nil -} - -func nonceFromPayment(data *protocol.Payment) (*big.Int, error) { - const base10 = 10 - var nonce string - switch data.Type() { - case protocol.Iden3PaymentCryptoV1Type: - nonce = "" - case protocol.Iden3PaymentRailsV1Type: - t, ok := data.Data().(*protocol.Iden3PaymentRailsV1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsRequestV1") - } - nonce = t.Nonce - case protocol.Iden3PaymentRailsERC20V1Type: - t, ok := data.Data().(*protocol.Iden3PaymentRailsERC20V1) - if !ok { - return nil, fmt.Errorf("failed to cast payment request data to Iden3PaymentRailsERC20RequestV1") - } - nonce = t.Nonce - default: - return nil, fmt.Errorf("unsupported payment request data type: %s", data.Type()) - } - bigIntNonce, ok := new(big.Int).SetString(nonce, base10) - if !ok { - return nil, fmt.Errorf("failed to parse nonce creating big int: %s", nonce) - } - return bigIntNonce, nil -} - func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, chainConfig *domain.PaymentOptionConfigItem, nonce *big.Int) (protocol.PaymentRequestInfoDataItem, error) { const defaultExpirationDate = 1 * time.Hour expirationTime := time.Now().Add(defaultExpirationDate) diff --git a/internal/db/schema/migrations/202411131724000_payment_options.sql b/internal/db/schema/migrations/202411131724000_payment_options.sql index bfbe03fca..c1a6dad13 100644 --- a/internal/db/schema/migrations/202411131724000_payment_options.sql +++ b/internal/db/schema/migrations/202411131724000_payment_options.sql @@ -32,6 +32,7 @@ CREATE TABLE payment_request_items id UUID PRIMARY KEY NOT NULL, nonce numeric NOT NULL, payment_request_id UUID NOT NULL REFERENCES payment_requests (id), + payment_option_id int NOT NULL, payment_request_info jsonb NOT NULL /* protocol.PaymentRequestInfo */ ); diff --git a/internal/eth/util.go b/internal/eth/util.go deleted file mode 100644 index 18ae3ccc3..000000000 --- a/internal/eth/util.go +++ /dev/null @@ -1,19 +0,0 @@ -package eth - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/params" -) - -// ToWei converts an ETH amount (float64) tg wei (*big.Int). -func ToWei(amount float64) *big.Int { - ethValue := new(big.Float).SetFloat64(amount) - weiMultiplier := new(big.Float).SetInt64(params.Ether) - weiValue := new(big.Float).Mul(ethValue, weiMultiplier) - - weiInt := new(big.Int) - weiValue.Int(weiInt) - - return weiInt -} diff --git a/internal/kms/local_storage_eth_key_provider.go b/internal/kms/local_storage_eth_key_provider.go index 8ca1e4df1..7dd5a9b1a 100644 --- a/internal/kms/local_storage_eth_key_provider.go +++ b/internal/kms/local_storage_eth_key_provider.go @@ -101,8 +101,13 @@ func (ls *localStorageEthKeyProvider) Sign(ctx context.Context, keyID KeyID, dat } sig, err := crypto.Sign(data, privKey) - sig[64] += 27 - return sig, err + if err != nil { + return nil, err + } + if len(sig) > 65 { // nolint:mnd // Checking for safe signature length + sig[64] += 27 + } + return sig, nil } func (ls *localStorageEthKeyProvider) LinkToIdentity(ctx context.Context, keyID KeyID, identity w3c.DID) (KeyID, error) { diff --git a/internal/kms/vault_eth_key_provider.go b/internal/kms/vault_eth_key_provider.go index 6e6226a15..631e8ed54 100644 --- a/internal/kms/vault_eth_key_provider.go +++ b/internal/kms/vault_eth_key_provider.go @@ -101,8 +101,13 @@ func (v *vaultETHKeyProvider) Sign(_ context.Context, keyID KeyID, data []byte) } sig, err := crypto.Sign(data, privKey) - sig[64] += 27 - return sig, errors.WithStack(err) + if err != nil { + return nil, errors.WithStack(err) + } + if len(sig) > 65 { // nolint:mnd // Checking for safe signature length + sig[64] += 27 + } + return sig, nil } func (v *vaultETHKeyProvider) ListByIdentity(_ context.Context, identity w3c.DID) ([]KeyID, error) { diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go index 3c5f33faf..7fa24d844 100644 --- a/internal/repositories/payment.go +++ b/internal/repositories/payment.go @@ -39,8 +39,8 @@ INTO payment_requests (id, credentials, description, issuer_did, recipient_did, VALUES ($1, $2, $3, $4, $5, $6, $7);` insertPaymentRequestItem = ` INSERT -INTO payment_request_items (id, nonce, payment_request_id, payment_request_info) -VALUES ($1, $2, $3, $4);` +INTO payment_request_items (id, nonce, payment_request_id, payment_option_id, payment_request_info) +VALUES ($1, $2, $3, $4, $5);` ) tx, err := p.conn.Pgx.Begin(ctx) @@ -54,7 +54,7 @@ VALUES ($1, $2, $3, $4);` return uuid.Nil, fmt.Errorf("could not insert payment request: %w", err) } for _, item := range req.Payments { - _, err = tx.Exec(ctx, insertPaymentRequestItem, item.ID, item.Nonce.String(), item.PaymentRequestID, item.Payment) + _, err = tx.Exec(ctx, insertPaymentRequestItem, item.ID, item.Nonce.String(), item.PaymentRequestID, item.PaymentOptionID, item.Payment) if err != nil { return uuid.Nil, fmt.Errorf("could not insert payment request item: %w", err) } @@ -68,7 +68,7 @@ VALUES ($1, $2, $3, $4);` // GetPaymentRequestByID returns a payment request by ID func (p *payment) GetPaymentRequestByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentRequest, error) { const query = ` -SELECT pr.id, pr.issuer_did, pr.recipient_did, pr.payment_option_id, pr.created_at, pri.id, pri.nonce, pri.payment_request_id, pri.payment_request_info +SELECT pr.id, pr.issuer_did, pr.recipient_did, pr.payment_option_id, pr.created_at, pri.id, pri.nonce, pri.payment_request_id, pri.payment_request_info, pri.payment_option_id FROM payment_requests pr LEFT JOIN payment_request_items pri ON pr.id = pri.payment_request_id WHERE pr.issuer_did = $1 AND pr.id = $2;` @@ -94,6 +94,7 @@ WHERE pr.issuer_did = $1 AND pr.id = $2;` &sNonce, &item.PaymentRequestID, &paymentRequestInfoBytes, + &item.PaymentOptionID, ); err != nil { return nil, fmt.Errorf("could not scan payment request: %w", err) } @@ -132,12 +133,12 @@ func (p *payment) GetAllPaymentRequests(ctx context.Context, issuerDID w3c.DID) // GetPaymentRequestItem returns a payment request item func (p *payment) GetPaymentRequestItem(ctx context.Context, issuerDID w3c.DID, nonce *big.Int) (*domain.PaymentRequestItem, error) { const query = ` -SELECT id, nonce, payment_request_id, payment_request_info +SELECT id, nonce, payment_request_id, payment_request_info, payment_option_id FROM payment_request_items LEFT JOIN payment_requests ON payment_requests.id = payment_request_items.payment_request_id WHERE payment_requests.issuer_did = $1 AND nonce = $2;` var item domain.PaymentRequestItem - err := p.conn.Pgx.QueryRow(ctx, query, issuerDID.String(), nonce).Scan(&item.ID, &item.Nonce, &item.PaymentRequestID, &item.Payment) + err := p.conn.Pgx.QueryRow(ctx, query, issuerDID.String(), nonce).Scan(&item.ID, &item.Nonce, &item.PaymentRequestID, &item.Payment, &item.PaymentOptionID) if err != nil { return nil, fmt.Errorf("could not get payment request item: %w", err) } diff --git a/internal/repositories/payment_request_fixture.go b/internal/repositories/payment_request_fixture.go index 566f08441..8d21e1c08 100644 --- a/internal/repositories/payment_request_fixture.go +++ b/internal/repositories/payment_request_fixture.go @@ -14,20 +14,22 @@ import ( "github.com/stretchr/testify/require" "github.com/polygonid/sh-id-platform/internal/core/domain" + "github.com/polygonid/sh-id-platform/internal/payments" ) // CreatePaymentRequest creates a new payment request fixture to be used on tests func (f *Fixture) CreatePaymentRequest(t *testing.T, issuerDID, recipientDID w3c.DID, paymentOptionID uuid.UUID, nPayments int) *domain.PaymentRequest { t.Helper() - var payments []domain.PaymentRequestItem + var paymentList []domain.PaymentRequestItem paymentRequestID := uuid.New() for i := 0; i < nPayments; i++ { nonce := big.NewInt(rand.Int63()) - payments = append(payments, domain.PaymentRequestItem{ + paymentList = append(paymentList, domain.PaymentRequestItem{ ID: uuid.New(), Nonce: *nonce, PaymentRequestID: paymentRequestID, + PaymentOptionID: payments.OptionConfigIDType(i + 1), Payment: protocol.Iden3PaymentRailsRequestV1{ Nonce: "25", Type: protocol.Iden3PaymentRailsRequestV1Type, @@ -66,7 +68,7 @@ func (f *Fixture) CreatePaymentRequest(t *testing.T, issuerDID, recipientDID w3c IssuerDID: issuerDID, RecipientDID: recipientDID, PaymentOptionID: paymentOptionID, - Payments: payments, + Payments: paymentList, CreatedAt: time.Now(), } diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go index 537ce39c1..0edea7d84 100644 --- a/internal/repositories/payment_test.go +++ b/internal/repositories/payment_test.go @@ -238,6 +238,7 @@ func TestPayment_SavePaymentRequest(t *testing.T) { Amount: "11", // etc... }, + PaymentOptionID: 6, }, ) } @@ -283,6 +284,7 @@ func TestPayment_GetPaymentRequestByID(t *testing.T) { assert.Equal(t, expectedPayment.Nonce, paymentRequest.Payments[i].Nonce) assert.Equal(t, expectedPayment.PaymentRequestID, paymentRequest.Payments[i].PaymentRequestID) assert.Equal(t, expectedPayment.Payment, paymentRequest.Payments[i].Payment) + assert.NotZero(t, paymentRequest.Payments[i].PaymentOptionID) } }) } From a4df756e84a0463352f6395a2201710f295a1697 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 10 Dec 2024 14:20:49 +0200 Subject: [PATCH 48/66] fix signature len check --- internal/kms/local_storage_eth_key_provider.go | 2 +- internal/kms/vault_eth_key_provider.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/kms/local_storage_eth_key_provider.go b/internal/kms/local_storage_eth_key_provider.go index 7dd5a9b1a..2b391937c 100644 --- a/internal/kms/local_storage_eth_key_provider.go +++ b/internal/kms/local_storage_eth_key_provider.go @@ -104,7 +104,7 @@ func (ls *localStorageEthKeyProvider) Sign(ctx context.Context, keyID KeyID, dat if err != nil { return nil, err } - if len(sig) > 65 { // nolint:mnd // Checking for safe signature length + if len(sig) >= 65 { // nolint:mnd // Checking for safe signature length sig[64] += 27 } return sig, nil diff --git a/internal/kms/vault_eth_key_provider.go b/internal/kms/vault_eth_key_provider.go index 631e8ed54..9ca73b4bd 100644 --- a/internal/kms/vault_eth_key_provider.go +++ b/internal/kms/vault_eth_key_provider.go @@ -104,7 +104,7 @@ func (v *vaultETHKeyProvider) Sign(_ context.Context, keyID KeyID, data []byte) if err != nil { return nil, errors.WithStack(err) } - if len(sig) > 65 { // nolint:mnd // Checking for safe signature length + if len(sig) >= 65 { // nolint:mnd // Checking for safe signature length sig[64] += 27 } return sig, nil From 783217e976f3099f7b5437d525981072db66eba9 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 10 Dec 2024 16:37:38 +0100 Subject: [PATCH 49/66] fix: payments repo GetPaymentRequestItem fixed and tested --- internal/core/domain/payment.go | 1 + .../202411131724000_payment_options.sql | 1 + internal/repositories/payment.go | 50 ++++++++++++++++--- internal/repositories/payment_test.go | 20 ++++++++ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 852b3dce6..371dfa932 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -30,6 +30,7 @@ type PaymentRequestItem struct { Nonce big.Int PaymentRequestID uuid.UUID PaymentOptionID payments.OptionConfigIDType // The numeric id that identify a payment option in payments config file. + SigningKeyID string Payment protocol.PaymentRequestInfoDataItem } diff --git a/internal/db/schema/migrations/202411131724000_payment_options.sql b/internal/db/schema/migrations/202411131724000_payment_options.sql index c1a6dad13..dce61453e 100644 --- a/internal/db/schema/migrations/202411131724000_payment_options.sql +++ b/internal/db/schema/migrations/202411131724000_payment_options.sql @@ -33,6 +33,7 @@ CREATE TABLE payment_request_items nonce numeric NOT NULL, payment_request_id UUID NOT NULL REFERENCES payment_requests (id), payment_option_id int NOT NULL, + signing_key text NOT NULL, payment_request_info jsonb NOT NULL /* protocol.PaymentRequestInfo */ ); diff --git a/internal/repositories/payment.go b/internal/repositories/payment.go index 7fa24d844..fa56528e0 100644 --- a/internal/repositories/payment.go +++ b/internal/repositories/payment.go @@ -39,8 +39,8 @@ INTO payment_requests (id, credentials, description, issuer_did, recipient_did, VALUES ($1, $2, $3, $4, $5, $6, $7);` insertPaymentRequestItem = ` INSERT -INTO payment_request_items (id, nonce, payment_request_id, payment_option_id, payment_request_info) -VALUES ($1, $2, $3, $4, $5);` +INTO payment_request_items (id, nonce, payment_request_id, payment_option_id, payment_request_info, signing_key) +VALUES ($1, $2, $3, $4, $5, $6);` ) tx, err := p.conn.Pgx.Begin(ctx) @@ -49,12 +49,27 @@ VALUES ($1, $2, $3, $4, $5);` } defer func() { _ = tx.Rollback(ctx) }() - _, err = tx.Exec(ctx, insertPaymentRequest, req.ID, req.Credentials, req.Description, req.IssuerDID.String(), req.RecipientDID.String(), req.PaymentOptionID, req.CreatedAt) + _, err = tx.Exec(ctx, insertPaymentRequest, + req.ID, + req.Credentials, + req.Description, + req.IssuerDID.String(), + req.RecipientDID.String(), + req.PaymentOptionID, + req.CreatedAt, + ) if err != nil { return uuid.Nil, fmt.Errorf("could not insert payment request: %w", err) } for _, item := range req.Payments { - _, err = tx.Exec(ctx, insertPaymentRequestItem, item.ID, item.Nonce.String(), item.PaymentRequestID, item.PaymentOptionID, item.Payment) + _, err = tx.Exec(ctx, insertPaymentRequestItem, + item.ID, + item.Nonce.String(), + item.PaymentRequestID, + item.PaymentOptionID, + item.Payment, + item.SigningKeyID, + ) if err != nil { return uuid.Nil, fmt.Errorf("could not insert payment request item: %w", err) } @@ -68,7 +83,7 @@ VALUES ($1, $2, $3, $4, $5);` // GetPaymentRequestByID returns a payment request by ID func (p *payment) GetPaymentRequestByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.PaymentRequest, error) { const query = ` -SELECT pr.id, pr.issuer_did, pr.recipient_did, pr.payment_option_id, pr.created_at, pri.id, pri.nonce, pri.payment_request_id, pri.payment_request_info, pri.payment_option_id +SELECT pr.id, pr.issuer_did, pr.recipient_did, pr.payment_option_id, pr.created_at, pri.id, pri.nonce, pri.payment_request_id, pri.payment_request_info, pri.payment_option_id, pri.signing_key FROM payment_requests pr LEFT JOIN payment_request_items pri ON pr.id = pri.payment_request_id WHERE pr.issuer_did = $1 AND pr.id = $2;` @@ -95,6 +110,7 @@ WHERE pr.issuer_did = $1 AND pr.id = $2;` &item.PaymentRequestID, &paymentRequestInfoBytes, &item.PaymentOptionID, + &item.SigningKeyID, ); err != nil { return nil, fmt.Errorf("could not scan payment request: %w", err) } @@ -133,15 +149,33 @@ func (p *payment) GetAllPaymentRequests(ctx context.Context, issuerDID w3c.DID) // GetPaymentRequestItem returns a payment request item func (p *payment) GetPaymentRequestItem(ctx context.Context, issuerDID w3c.DID, nonce *big.Int) (*domain.PaymentRequestItem, error) { const query = ` -SELECT id, nonce, payment_request_id, payment_request_info, payment_option_id -FROM payment_request_items +SELECT payment_request_items.id, nonce, payment_request_id, payment_request_info, payment_request_items.payment_option_id +FROM payment_request_items LEFT JOIN payment_requests ON payment_requests.id = payment_request_items.payment_request_id WHERE payment_requests.issuer_did = $1 AND nonce = $2;` var item domain.PaymentRequestItem - err := p.conn.Pgx.QueryRow(ctx, query, issuerDID.String(), nonce).Scan(&item.ID, &item.Nonce, &item.PaymentRequestID, &item.Payment, &item.PaymentOptionID) + var sNonce string + var paymentRequestInfoBytes []byte + err := p.conn.Pgx.QueryRow(ctx, query, issuerDID.String(), nonce.String()).Scan( + &item.ID, + &sNonce, + &item.PaymentRequestID, + &paymentRequestInfoBytes, + &item.PaymentOptionID, + ) if err != nil { return nil, fmt.Errorf("could not get payment request item: %w", err) } + item.Payment, err = p.paymentRequestItem(paymentRequestInfoBytes) + if err != nil { + return nil, fmt.Errorf("could not unmarshal payment request info: %w", err) + } + const base10 = 10 + nonceInt, ok := new(big.Int).SetString(sNonce, base10) + if !ok { + return nil, fmt.Errorf("could not parse nonce: %w", err) + } + item.Nonce = *nonceInt return &item, nil } diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go index 0edea7d84..9a140c613 100644 --- a/internal/repositories/payment_test.go +++ b/internal/repositories/payment_test.go @@ -288,3 +288,23 @@ func TestPayment_GetPaymentRequestByID(t *testing.T) { } }) } + +func TestPayment_GetPaymentRequestItem(t *testing.T) { + ctx := context.Background() + fixture := NewFixture(storage) + issuerID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qQHtVzfwjywqosK24XT7um3R1Ym5L1GJTbijjcxMq") + require.NoError(t, err) + + fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) + repo := NewPayment(*storage) + + payymentOptionID, err := repo.SavePaymentOption(ctx, domain.NewPaymentOption(*issuerID, "name", "description", &domain.PaymentOptionConfig{})) + require.NoError(t, err) + expected := fixture.CreatePaymentRequest(t, *issuerID, *issuerID, payymentOptionID, 10) + + t.Run("Get payment request item", func(t *testing.T) { + paymentRequestItem, err := repo.GetPaymentRequestItem(ctx, *issuerID, &expected.Payments[0].Nonce) + require.NoError(t, err) + assert.Equal(t, expected.Payments[0].ID, paymentRequestItem.ID) + }) +} From ede1e0c24437fafd3a5f2b6da840c2a434570506 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 10 Dec 2024 16:50:44 +0100 Subject: [PATCH 50/66] fix: merge conflict --- go.mod | 4 +++- go.sum | 71 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/go.mod b/go.mod index a55154288..26ea1e128 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.27.31 github.com/aws/aws-sdk-go-v2/credentials v1.17.30 github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 - github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.6 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.2 github.com/caarlos0/env/v11 v11.2.2 github.com/ethereum/go-ethereum v1.14.12 @@ -128,6 +127,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 // indirect github.com/ethereum/c-kzg-4844 v1.0.1 // indirect github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect @@ -289,6 +289,7 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cobra v1.8.1 // indirect @@ -318,6 +319,7 @@ require ( github.com/vmihailenco/go-tinylfu v0.2.2 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect github.com/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect diff --git a/go.sum b/go.sum index 1e810c9b8..3e13b0a96 100644 --- a/go.sum +++ b/go.sum @@ -59,18 +59,18 @@ github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8ger github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= -github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8= -github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= +github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2/config v1.27.31 h1:kxBoRsjhT3pq0cKthgj6RU6bXTm/2SgdoUMyrVw0rAI= github.com/aws/aws-sdk-go-v2/config v1.27.31/go.mod h1:z04nZdSWFPaDwK3DdJOG2r+scLQzMYuJeW0CujEm9FM= github.com/aws/aws-sdk-go-v2/credentials v1.17.30 h1:aau/oYFtibVovr2rDt8FHlU17BTicFEMAi29V1U+L5Q= github.com/aws/aws-sdk-go-v2/credentials v1.17.30/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI= @@ -79,14 +79,16 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 h1:tJ5RnkHC github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18/go.mod h1:++NHzT+nAF7ZPrHPsA+ENvsXkOO8wEu+C6RXltAG4/c= github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 h1:XUomV7SiclZl1QuXORdGcfFqHxEHET7rmNGtxTfNB+M= github.com/aws/aws-sdk-go-v2/service/kms v1.35.5/go.mod h1:A5CS0VRmxxj2YKYLCY08l/Zzbd01m6JZn0WzxgT1OCA= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.2 h1:Rrqru2wYkKQCS2IM5/JrgKUQIoNTqA6y/iuxkjzxC6M= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.2/go.mod h1:QuCURO98Sqee2AXmqDNxKXYFm2OEDAVAPApMqO0Vqnc= github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 h1:zCsFCKvbj25i7p1u94imVoO447I/sFv8qq+lGJhRN0c= github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac= github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 h1:OMsEmCyz2i89XwRwPouAJvhj81wINh+4UK+k/0Yo/q8= github.com/aws/aws-sdk-go-v2/service/sts v1.30.5/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0= -github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= -github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -139,6 +141,9 @@ github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/ckaznocha/intrange v0.2.1 h1:M07spnNEQoALOJhwrImSrJLaxwuiQK+hA2DeajBlwYk= github.com/ckaznocha/intrange v0.2.1/go.mod h1:7NEhVyf8fzZO5Ds7CRaqPEm52Ut83hsTiL5zbER/HYk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= @@ -191,6 +196,9 @@ github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42 github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= +github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w= +github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM= @@ -249,7 +257,6 @@ github.com/go-redis/cache/v8 v8.4.4/go.mod h1:JM6CkupsPvAu/LYEVGQy6UB4WDAzQSXkR0 github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -339,6 +346,7 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -405,6 +413,7 @@ github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXei github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi v0.0.0-20230911113809-c58b7e7a69b0 h1:Fu1/tAINi9FzZ0nKoEVOGXWzL1l15tR1loJx5sQEQ8k= github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi v0.0.0-20230911113809-c58b7e7a69b0/go.mod h1:8fkd2xyUG/V7ovpvZRyD2LyK2zZ4ALbgf5vJGyhzKdg= github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48 h1:/g4rru+OM5WAhVNXEpowKH+vWZLGjgpk5+O8Stu4QBo= @@ -415,8 +424,6 @@ github.com/iden3/driver-did-iden3 v0.0.7 h1:qDbVpEwbk25TCpb1Fw/9+vFiEWRdKTwSYNgW github.com/iden3/driver-did-iden3 v0.0.7/go.mod h1:V0zeeDNxrU/yKRFPgVfntMhBfmDL+GxcvHhW+Fdcz9I= github.com/iden3/go-circuits/v2 v2.4.0 h1:m+7uYtrvJKuc+gVhbXDXl1BJQyK7sWdW7OWttM3R/8I= github.com/iden3/go-circuits/v2 v2.4.0/go.mod h1:k0uYx/ZdZPiDEIy7kI3MAixnREKcc7NdCKDRw8Q+iFA= -github.com/iden3/go-iden3-auth/v2 v2.5.1 h1:2+YPVsYpbTl+eKzVDqEeiiVoAD+c6s/TYPo/oWbVjwg= -github.com/iden3/go-iden3-auth/v2 v2.5.1/go.mod h1:8DYjax0IdfzN+LUvCuGUJkjMPgq7o0byZIrZGdhIxjM= github.com/iden3/go-iden3-auth/v2 v2.6.0 h1:R+QD/H1zhtO9+uvZ73Z9CVU7Omx8xf8ckgv4Syv4IsI= github.com/iden3/go-iden3-auth/v2 v2.6.0/go.mod h1:s6t4ierMRafmJPxHSfwDW3Mh5+ceNbUrtbdP1EVoqfI= github.com/iden3/go-iden3-core v1.0.2 h1:HwNDFeqcUv4ybZj5tH+58JKWKarn/qqBpNCqTLxGP0Y= @@ -490,8 +497,6 @@ github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUO github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= @@ -499,15 +504,12 @@ github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCM github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus= -github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= @@ -524,8 +526,6 @@ github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjz github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk= github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -629,8 +629,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= @@ -683,8 +681,6 @@ github.com/nunnatsa/ginkgolinter v0.18.3/go.mod h1:BE1xyB/PNtXXG1azrvrqJW5eFH0hS github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 h1:rICjNsHbPP1LttefanBPnwsSwl09SqhCO7Ee623qR84= -github.com/oapi-codegen/oapi-codegen/v2 v2.3.0/go.mod h1:4k+cJeSq5ntkwlcpQSxLxICCxQzCL772o30PxdibRt4= github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q= github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= @@ -694,15 +690,20 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/olomix/go-test-pg v1.0.2 h1:4ey3mFBhPx93PdgyshOJI1WrQzqzkWEnb0wL/7UbFMI= github.com/olomix/go-test-pg v1.0.2/go.mod h1:rHMame/S99rnU5YhVYFa3rr0riKqBYe8xsMDP4YzeHA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= @@ -732,8 +733,6 @@ github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEn github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/pressly/goose/v3 v3.22.0 h1:wd/7kNiPTuNAztWun7iaB98DrhulbWPrzMAaw2DEZNw= -github.com/pressly/goose/v3 v3.22.0/go.mod h1:yJM3qwSj2pp7aAaCvso096sguezamNb2OBgxCnh/EYg= github.com/pressly/goose/v3 v3.23.0 h1:57hqKos8izGek4v6D5+OXBa+Y4Rq8MU//+MmnevdpVA= github.com/pressly/goose/v3 v3.23.0/go.mod h1:rpx+D9GX/+stXmzKa+uh1DkjPnNVMdiOCV9iLdle4N8= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= @@ -795,6 +794,8 @@ github.com/securego/gosec/v2 v2.21.4 h1:Le8MSj0PDmOnHJgUATjD96PaXRvCpKC+DGJvwyy0 github.com/securego/gosec/v2 v2.21.4/go.mod h1:Jtb/MwRQfRxCXyCm1rfM1BEiiiTfUOdyzzAhlr6lUTA= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= @@ -823,6 +824,8 @@ github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCp github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/speakeasy-api/openapi-overlay v0.9.0 h1:Wrz6NO02cNlLzx1fB093lBlYxSI54VRhy1aSutx0PQg= +github.com/speakeasy-api/openapi-overlay v0.9.0/go.mod h1:f5FloQrHA7MsxYg9djzMD5h6dxrHjVVByWKh7an8TRc= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= @@ -897,8 +900,6 @@ github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZy github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U= github.com/uudashr/iface v1.2.1 h1:vHHyzAUmWZ64Olq6NZT3vg/z1Ws56kyPdBOd5kTXDF8= github.com/uudashr/iface v1.2.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= -github.com/valkey-io/valkey-go v1.0.45 h1:d2ksu+FvKEy9pU9CCMZ94ABTLm2kNHU0jxEJZRqpFA4= -github.com/valkey-io/valkey-go v1.0.45/go.mod h1:BXlVAPIL9rFQinSFM+N32JfWzfCaUAqBpZkc4vPY6fM= github.com/valkey-io/valkey-go v1.0.51 h1:qioDrTBplnkWLK5TrUq0rkuIv7HUL/RPJU/79wB93Lg= github.com/valkey-io/valkey-go v1.0.51/go.mod h1:BXlVAPIL9rFQinSFM+N32JfWzfCaUAqBpZkc4vPY6fM= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -912,6 +913,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= +github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -973,8 +976,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= @@ -1011,6 +1012,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= @@ -1028,8 +1030,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1045,6 +1045,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1059,6 +1060,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1071,8 +1073,6 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1095,8 +1095,6 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= @@ -1164,11 +1162,13 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1185,9 +1185,8 @@ modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= -modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= -modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= modernc.org/sqlite v1.34.1 h1:u3Yi6M0N8t9yKRDwhXcyp1eS5/ErhPTBggxWFuR6Hfk= +modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From 7f5ea11d726607511c50279cd757bc16c5506738 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 10 Dec 2024 19:28:42 +0200 Subject: [PATCH 51/66] fix IsPaymentDone (check Recipient address) --- internal/core/services/payment.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 28b35be27..94319f860 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -174,6 +174,24 @@ func (p *payment) VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *b } contractAddress := setting.PaymentRails + options, err := p.paymentsStore.GetAllPaymentOptions(ctx, issuerDID) + if err != nil { + return false, err + } + + var paymentOptionConfigItem *domain.PaymentOptionConfigItem + for _, option := range options { + for _, item := range option.Config.Config { + if item.PaymentOptionID == paymentReqItem.PaymentOptionID { + paymentOptionConfigItem = &item + break + } + } + } + if paymentOptionConfigItem == nil { + return false, fmt.Errorf("payment option config item not found") + } + // TODO: Load rpc from network resolvers client, err := ethclient.Dial("https://polygon-amoy.g.alchemy.com/v2/DHvucvBBzrBhaHzmjrMp24PGbl7vwee6") if err != nil { @@ -186,7 +204,7 @@ func (p *payment) VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *b } // TODO: pending, canceled, success, failed - isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, setting.PaymentOption.ContractAddress, nonce) + isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, paymentOptionConfigItem.Recipient, nonce) if err != nil { return false, err } From 2fd434542b0144667704d9ebf103c26b587668d6 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 11 Dec 2024 11:56:56 +0100 Subject: [PATCH 52/66] feat: Use RPC resolver from network resolver --- internal/core/domain/payment.go | 10 ++++ internal/core/services/payment.go | 74 +++++++++++++++----------- internal/kms/local_eth_key_provider.go | 3 -- internal/kms/vault_eth_key_provider.go | 3 -- internal/network/resolver.go | 33 ++++++++---- internal/repositories/payment_test.go | 2 +- 6 files changed, 76 insertions(+), 49 deletions(-) diff --git a/internal/core/domain/payment.go b/internal/core/domain/payment.go index 371dfa932..8021fccb1 100644 --- a/internal/core/domain/payment.go +++ b/internal/core/domain/payment.go @@ -63,6 +63,16 @@ type PaymentOptionConfig struct { Config []PaymentOptionConfigItem `json:"Config"` } +// GetByID runs over all items in configuration and returns one with matching ID +func (c *PaymentOptionConfig) GetByID(paymentOptionID payments.OptionConfigIDType) *PaymentOptionConfigItem { + for _, item := range c.Config { + if item.PaymentOptionID == paymentOptionID { + return &item + } + } + return nil +} + // PaymentOptionConfigItem is an item in Payment option config type PaymentOptionConfigItem struct { PaymentOptionID payments.OptionConfigIDType `json:"paymentOptionId"` diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 94319f860..b246adea8 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -5,7 +5,6 @@ import ( "crypto/ecdsa" "crypto/rand" "encoding/hex" - "errors" "fmt" "math/big" "strconv" @@ -15,7 +14,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" @@ -129,6 +127,7 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay Nonce: *nonce, PaymentRequestID: paymentRequest.ID, PaymentOptionID: chainConfig.PaymentOptionID, + SigningKeyID: chainConfig.SigningKeyID, Payment: data, } paymentRequest.Payments = append(paymentRequest.Payments, item) @@ -172,45 +171,51 @@ func (p *payment) VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *b log.Error(ctx, "chain not found in configuration", "paymentOptionID", paymentReqItem.PaymentOptionID) return false, fmt.Errorf("payment Option <%d> not found in payment configuration", paymentReqItem.PaymentOptionID) } - contractAddress := setting.PaymentRails - options, err := p.paymentsStore.GetAllPaymentOptions(ctx, issuerDID) + payOptConfItem, err := p.paymentOptionConfigItem(ctx, issuerDID, paymentReqItem) if err != nil { - return false, err - } - - var paymentOptionConfigItem *domain.PaymentOptionConfigItem - for _, option := range options { - for _, item := range option.Config.Config { - if item.PaymentOptionID == paymentReqItem.PaymentOptionID { - paymentOptionConfigItem = &item - break - } - } - } - if paymentOptionConfigItem == nil { - return false, fmt.Errorf("payment option config item not found") + log.Error(ctx, "failed to get payment option config", "err", err) + return false, fmt.Errorf("failed to get payment option config: %w", err) } - // TODO: Load rpc from network resolvers - client, err := ethclient.Dial("https://polygon-amoy.g.alchemy.com/v2/DHvucvBBzrBhaHzmjrMp24PGbl7vwee6") + client, err := p.networkResolver.GetEthClientByChainID(setting.ChainID) if err != nil { - return false, fmt.Errorf("failed to connect to ethereum client: %w", err) + log.Error(ctx, "failed to get ethereum client from resolvers", "err", err, "key", paymentReqItem.SigningKeyID) + return false, fmt.Errorf("failed to get ethereum client from resolvers settings for key <%s>", paymentReqItem.SigningKeyID) } - instance, err := eth.NewPaymentContract(contractAddress, client) + instance, err := eth.NewPaymentContract(setting.PaymentRails, client.GetEthereumClient()) if err != nil { return false, err } // TODO: pending, canceled, success, failed - isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, paymentOptionConfigItem.Recipient, nonce) + isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, payOptConfItem.Recipient, nonce) if err != nil { return false, err } return isPaid, nil } +// paymentOptionConfigItem finds the payment option config item used to pay using the payment id stored in PaymentRequest database +func (p *payment) paymentOptionConfigItem(ctx context.Context, issuerDID w3c.DID, item *domain.PaymentRequestItem) (*domain.PaymentOptionConfigItem, error) { + paymentReq, err := p.paymentsStore.GetPaymentRequestByID(ctx, issuerDID, item.PaymentRequestID) + if err != nil { + return nil, fmt.Errorf("failed to get payment request: %w", err) + } + + option, err := p.paymentsStore.GetPaymentOptionByID(ctx, &issuerDID, paymentReq.PaymentOptionID) + if err != nil { + return nil, fmt.Errorf("failed to get payment option: %w", err) + } + + configItem := option.Config.GetByID(item.PaymentOptionID) + if configItem == nil { + return nil, fmt.Errorf("payment option config item for id <%d> not found", item.PaymentOptionID) + } + return configItem, nil +} + func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, chainConfig *domain.PaymentOptionConfigItem, nonce *big.Int) (protocol.PaymentRequestInfoDataItem, error) { const defaultExpirationDate = 1 * time.Hour expirationTime := time.Now().Add(defaultExpirationDate) @@ -222,10 +227,7 @@ func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, return nil, err } - signerAddress, err := p.getAddress(ctx, kms.KeyID{ - Type: kms.KeyTypeEthereum, - ID: chainConfig.SigningKeyID, - }) + signerAddress, err := p.getSignerAddress(ctx, chainConfig.SigningKeyID) if err != nil { log.Error(ctx, "failed to retrieve signer address", "err", err) return nil, err @@ -399,14 +401,22 @@ func (p *payment) paymentRequestSignature( log.Error(ctx, "failed to sign typed data hash", "err", err, "keyId", keyID) return nil, err } + + const recoveryIdOffset = 64 + if len(signature) > recoveryIdOffset { + if signature[recoveryIdOffset] <= 1 { + signature[recoveryIdOffset] += 27 + } + } + return signature, nil } -func (p *payment) getAddress(ctx context.Context, k kms.KeyID) (common.Address, error) { - if p.kms == nil { - return common.Address{}, errors.Join(errors.New("the signer is read-only")) - } - bytesPubKey, err := p.kms.PublicKey(k) +func (p *payment) getSignerAddress(ctx context.Context, signingKeyID string) (common.Address, error) { + bytesPubKey, err := p.kms.PublicKey(kms.KeyID{ + Type: kms.KeyTypeEthereum, + ID: signingKeyID, + }) if err != nil { return common.Address{}, err } diff --git a/internal/kms/local_eth_key_provider.go b/internal/kms/local_eth_key_provider.go index e3bf8956e..b384bbf02 100644 --- a/internal/kms/local_eth_key_provider.go +++ b/internal/kms/local_eth_key_provider.go @@ -100,9 +100,6 @@ func (ls *localEthKeyProvider) Sign(ctx context.Context, keyID KeyID, data []byt if err != nil { return nil, err } - if len(sig) >= 65 { // nolint:mnd // Checking for safe signature length - sig[64] += 27 - } return sig, nil } diff --git a/internal/kms/vault_eth_key_provider.go b/internal/kms/vault_eth_key_provider.go index 9ca73b4bd..c9433dad3 100644 --- a/internal/kms/vault_eth_key_provider.go +++ b/internal/kms/vault_eth_key_provider.go @@ -104,9 +104,6 @@ func (v *vaultETHKeyProvider) Sign(_ context.Context, keyID KeyID, data []byte) if err != nil { return nil, errors.WithStack(err) } - if len(sig) >= 65 { // nolint:mnd // Checking for safe signature length - sig[64] += 27 - } return sig, nil } diff --git a/internal/network/resolver.go b/internal/network/resolver.go index f2c84f018..fcf4c3089 100644 --- a/internal/network/resolver.go +++ b/internal/network/resolver.go @@ -49,11 +49,12 @@ type ResolverClientConfig struct { // Resolver holds the resolver type Resolver struct { - ethereumClients map[resolverPrefix]ResolverClientConfig - rhsSettings map[resolverPrefix]RhsSettings - supportedContracts map[string]*abi.State - stateResolvers map[string]pubsignals.StateResolver - supportedNetworks []SupportedNetworks + ethereumClientsByChainID map[string]ResolverClientConfig + ethereumClients map[resolverPrefix]ResolverClientConfig + rhsSettings map[resolverPrefix]RhsSettings + supportedContracts map[string]*abi.State + stateResolvers map[string]pubsignals.StateResolver + supportedNetworks []SupportedNetworks } // SupportedNetworks holds the chain and networks supoprted @@ -103,6 +104,7 @@ func NewResolver(ctx context.Context, cfg config.Configuration, kms *kms.KMS, re } ethereumClients := make(map[resolverPrefix]ResolverClientConfig) + ethereumClientsByChainID := make(map[string]ResolverClientConfig) rhsSettings := make(map[resolverPrefix]RhsSettings) supportedContracts := make(map[string]*abi.State) stateResolvers := make(map[string]pubsignals.StateResolver) @@ -148,6 +150,7 @@ func NewResolver(ctx context.Context, cfg config.Configuration, kms *kms.KMS, re } ethereumClients[resolverPrefix(resolverPrefixKey)] = *resolverClientConfig + ethereumClientsByChainID[networkSettings.ChainID] = *resolverClientConfig settings := networkSettings.RhsSettings settings.Iden3CommAgentStatus = strings.TrimSuffix(cfg.ServerUrl, "/") @@ -182,11 +185,12 @@ func NewResolver(ctx context.Context, cfg config.Configuration, kms *kms.KMS, re log.Info(ctx, "resolver settings", "settings:", printer.String()) return &Resolver{ - ethereumClients: ethereumClients, - rhsSettings: rhsSettings, - supportedContracts: supportedContracts, - stateResolvers: stateResolvers, - supportedNetworks: supportedNetworks, + ethereumClients: ethereumClients, + ethereumClientsByChainID: ethereumClientsByChainID, + rhsSettings: rhsSettings, + supportedContracts: supportedContracts, + stateResolvers: stateResolvers, + supportedNetworks: supportedNetworks, }, nil } @@ -199,6 +203,15 @@ func (r *Resolver) GetEthClient(resolverPrefixKey string) (*eth.Client, error) { return resolverClientConfig.client, nil } +// GetEthClientByChainID returns the eth client by chain id. +func (r *Resolver) GetEthClientByChainID(chainID int) (*eth.Client, error) { + resolverClientConfig, ok := r.ethereumClientsByChainID[fmt.Sprintf("%d", chainID)] + if !ok { + return nil, fmt.Errorf("ethClient not found for chainID: %d", chainID) + } + return resolverClientConfig.client, nil +} + // GetContractAddress returns the contract address func (r *Resolver) GetContractAddress(resolverPrefixKey string) (*common.Address, error) { resolverClientConfig, ok := r.ethereumClients[resolverPrefix(resolverPrefixKey)] diff --git a/internal/repositories/payment_test.go b/internal/repositories/payment_test.go index 9a140c613..73cb5f296 100644 --- a/internal/repositories/payment_test.go +++ b/internal/repositories/payment_test.go @@ -292,7 +292,7 @@ func TestPayment_GetPaymentRequestByID(t *testing.T) { func TestPayment_GetPaymentRequestItem(t *testing.T) { ctx := context.Background() fixture := NewFixture(storage) - issuerID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qQHtVzfwjywqosK24XT7um3R1Ym5L1GJTbijjcxMq") + issuerID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qX7mBeonrp5u7GapztqjYZTdLsv9XBhyXgjYQGjgi") require.NoError(t, err) fixture.CreateIdentity(t, &domain.Identity{Identifier: issuerID.String()}) From 9007c700d2eae7fb1b04f583b54c9489e18b53e1 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 11 Dec 2024 16:20:52 +0100 Subject: [PATCH 53/66] feat: Verify payment handles failed and cancelled transactions --- internal/api/payment.go | 11 ++---- internal/api/responses.go | 17 ++++++++ internal/core/ports/payment_service.go | 19 ++++++++- internal/core/services/payment.go | 55 +++++++++++++++++++++----- 4 files changed, 83 insertions(+), 19 deletions(-) diff --git a/internal/api/payment.go b/internal/api/payment.go index 9d7a8eb55..807d1b645 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -167,15 +167,12 @@ func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequest log.Error(ctx, "parsing nonce on verify payment", "err", err, "nonce", request.Nonce) return VerifyPayment400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("invalid nonce: <%s>", request.Nonce)}}, nil } - isPaid, err := s.paymentService.VerifyPayment(ctx, *issuerDID, nonce, request.Body.TxHash) + status, err := s.paymentService.VerifyPayment(ctx, *issuerDID, nonce, request.Body.TxHash) if err != nil { - log.Error(ctx, "can't process payment message", "err", err) - return VerifyPayment400JSONResponse{N400JSONResponse{Message: "can't process payment message"}}, nil + log.Error(ctx, "can't verify payment", "err", err, "nonce", request.Nonce, "txID", request.Body.TxHash) + return VerifyPayment400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("can't verify payment: <%s>", err.Error())}}, nil } - if !isPaid { - return VerifyPayment200JSONResponse{Status: PaymentStatusStatusPending}, nil - } - return VerifyPayment200JSONResponse{Status: PaymentStatusStatusSuccess}, nil + return toVerifyPaymentResponse(status) } func newPaymentOptionConfig(config *PaymentOptionConfig) (*domain.PaymentOptionConfig, error) { diff --git a/internal/api/responses.go b/internal/api/responses.go index 61779359f..a437683cf 100644 --- a/internal/api/responses.go +++ b/internal/api/responses.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "fmt" "net/http" "github.com/iden3/go-schema-processor/v2/verifiable" @@ -9,6 +10,7 @@ import ( "github.com/polygonid/sh-id-platform/internal/common" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/pagination" + "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/schema" "github.com/polygonid/sh-id-platform/internal/timeapi" ) @@ -363,3 +365,18 @@ func toCreatePaymentRequestResponse(payReq *domain.PaymentRequest) CreatePayment Payments: payments, } } + +func toVerifyPaymentResponse(status ports.BlockchainPaymentStatus) (VerifyPaymentResponseObject, error) { + switch status { + case ports.BlockchainPaymentStatusPending: + return VerifyPayment200JSONResponse{Status: PaymentStatusStatusPending}, nil + case ports.BlockchainPaymentStatusSuccess: + return VerifyPayment200JSONResponse{Status: PaymentStatusStatusSuccess}, nil + case ports.BlockchainPaymentStatusCancelled: + return VerifyPayment200JSONResponse{Status: PaymentStatusStatusCanceled}, nil + case ports.BlockchainPaymentStatusFailed: + return VerifyPayment200JSONResponse{Status: PaymentStatusStatusFailed}, nil + default: + return VerifyPayment400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("unknown blockchain payment status <%d>", status)}}, nil + } +} diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index 316fd3924..bf41a59de 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -13,6 +13,22 @@ import ( "github.com/polygonid/sh-id-platform/internal/payments" ) +// BlockchainPaymentStatus represents the status of a payment +type BlockchainPaymentStatus int + +const ( + // BlockchainPaymentStatusPending - Payment is still pending + BlockchainPaymentStatusPending BlockchainPaymentStatus = iota + // BlockchainPaymentStatusCancelled - Payment was cancelled + BlockchainPaymentStatusCancelled + // BlockchainPaymentStatusSuccess - Payment was successful + BlockchainPaymentStatusSuccess + // BlockchainPaymentStatusFailed - Payment has failed + BlockchainPaymentStatusFailed + // BlockchainPaymentStatusUnknown - Payment status is unknown. Something went wrong + BlockchainPaymentStatusUnknown +) + // CreatePaymentRequestReq is the request for PaymentService.CreatePaymentRequest type CreatePaymentRequestReq struct { IssuerDID w3c.DID @@ -27,8 +43,7 @@ type PaymentService interface { CreatePaymentRequest(ctx context.Context, req *CreatePaymentRequestReq) (*domain.PaymentRequest, error) CreatePaymentRequestForProposalRequest(ctx context.Context, proposalRequest *protocol.CredentialsProposalRequestMessage) (*comm.BasicMessage, error) GetSettings() payments.Config - VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *big.Int, txHash string) (bool, error) - + VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *big.Int, txHash string) (BlockchainPaymentStatus, error) CreatePaymentOption(ctx context.Context, issuerDID *w3c.DID, name, description string, config *domain.PaymentOptionConfig) (uuid.UUID, error) GetPaymentOptions(ctx context.Context, issuerDID *w3c.DID) ([]domain.PaymentOption, error) GetPaymentOptionByID(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) (*domain.PaymentOption, error) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index b246adea8..d1b03fb82 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -160,41 +160,76 @@ func (p *payment) GetSettings() payments.Config { } // VerifyPayment verifies a payment -func (p *payment) VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *big.Int, txHash string) (bool, error) { +func (p *payment) VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *big.Int, txHash string) (ports.BlockchainPaymentStatus, error) { paymentReqItem, err := p.paymentsStore.GetPaymentRequestItem(ctx, issuerDID, nonce) if err != nil { - return false, fmt.Errorf("failed to get payment request: %w", err) + return ports.BlockchainPaymentStatusPending, fmt.Errorf("failed to get payment request: %w", err) } setting, found := p.settings[paymentReqItem.PaymentOptionID] if !found { log.Error(ctx, "chain not found in configuration", "paymentOptionID", paymentReqItem.PaymentOptionID) - return false, fmt.Errorf("payment Option <%d> not found in payment configuration", paymentReqItem.PaymentOptionID) + return ports.BlockchainPaymentStatusPending, fmt.Errorf("payment Option <%d> not found in payment configuration", paymentReqItem.PaymentOptionID) } payOptConfItem, err := p.paymentOptionConfigItem(ctx, issuerDID, paymentReqItem) if err != nil { log.Error(ctx, "failed to get payment option config", "err", err) - return false, fmt.Errorf("failed to get payment option config: %w", err) + return ports.BlockchainPaymentStatusPending, fmt.Errorf("failed to get payment option config: %w", err) } client, err := p.networkResolver.GetEthClientByChainID(setting.ChainID) if err != nil { log.Error(ctx, "failed to get ethereum client from resolvers", "err", err, "key", paymentReqItem.SigningKeyID) - return false, fmt.Errorf("failed to get ethereum client from resolvers settings for key <%s>", paymentReqItem.SigningKeyID) + return ports.BlockchainPaymentStatusPending, fmt.Errorf("failed to get ethereum client from resolvers settings for key <%s>", paymentReqItem.SigningKeyID) } instance, err := eth.NewPaymentContract(setting.PaymentRails, client.GetEthereumClient()) if err != nil { - return false, err + return ports.BlockchainPaymentStatusPending, err } - // TODO: pending, canceled, success, failed - isPaid, err := instance.IsPaymentDone(&bind.CallOpts{Context: ctx}, payOptConfItem.Recipient, nonce) + status, err := p.verifyPaymentOnBlockchain(ctx, client, instance, payOptConfItem.Recipient, nonce, txHash) if err != nil { - return false, err + log.Error(ctx, "failed to verify payment on blockchain", "err", err, "txHash", txHash, "nonce", nonce) + return ports.BlockchainPaymentStatusPending, err } - return isPaid, nil + return status, nil +} + +func (p *payment) verifyPaymentOnBlockchain( + ctx context.Context, + client *eth.Client, + contract *eth.PaymentContract, + recipient common.Address, + nonce *big.Int, + txID string, +) (ports.BlockchainPaymentStatus, error) { + _, isPending, err := client.GetTransactionByID(ctx, txID) + if err != nil { + if err.Error() == "not found" { + return ports.BlockchainPaymentStatusCancelled, nil + } + return ports.BlockchainPaymentStatusUnknown, err + } + if isPending { + return ports.BlockchainPaymentStatusPending, nil + } + receipt, err := client.GetTransactionReceiptByID(ctx, txID) + if err != nil { + return ports.BlockchainPaymentStatusUnknown, err + } + if receipt.Status == 1 { + isPaid, err := contract.IsPaymentDone(&bind.CallOpts{Context: ctx}, recipient, nonce) + if err != nil { + return ports.BlockchainPaymentStatusPending, nil + } + if isPaid { + return ports.BlockchainPaymentStatusSuccess, nil + } + return ports.BlockchainPaymentStatusPending, nil + } + return ports.BlockchainPaymentStatusFailed, nil } // paymentOptionConfigItem finds the payment option config item used to pay using the payment id stored in PaymentRequest database From 2cc84ff096a139fd5359a9e1b02a35094e2b7c0f Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Fri, 13 Dec 2024 13:11:19 +0200 Subject: [PATCH 54/66] update ethereumClientsByChainID --- internal/core/services/payment.go | 3 ++- internal/network/resolver.go | 15 ++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index d1b03fb82..b00ba5a37 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/uuid" + core "github.com/iden3/go-iden3-core/v2" "github.com/iden3/go-iden3-core/v2/w3c" comm "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" @@ -178,7 +179,7 @@ func (p *payment) VerifyPayment(ctx context.Context, issuerDID w3c.DID, nonce *b return ports.BlockchainPaymentStatusPending, fmt.Errorf("failed to get payment option config: %w", err) } - client, err := p.networkResolver.GetEthClientByChainID(setting.ChainID) + client, err := p.networkResolver.GetEthClientByChainID(core.ChainID(setting.ChainID)) if err != nil { log.Error(ctx, "failed to get ethereum client from resolvers", "err", err, "key", paymentReqItem.SigningKeyID) return ports.BlockchainPaymentStatusPending, fmt.Errorf("failed to get ethereum client from resolvers settings for key <%s>", paymentReqItem.SigningKeyID) diff --git a/internal/network/resolver.go b/internal/network/resolver.go index fcf4c3089..6764021a3 100644 --- a/internal/network/resolver.go +++ b/internal/network/resolver.go @@ -49,7 +49,7 @@ type ResolverClientConfig struct { // Resolver holds the resolver type Resolver struct { - ethereumClientsByChainID map[string]ResolverClientConfig + ethereumClientsByChainID map[core.ChainID]ResolverClientConfig ethereumClients map[resolverPrefix]ResolverClientConfig rhsSettings map[resolverPrefix]RhsSettings supportedContracts map[string]*abi.State @@ -104,7 +104,7 @@ func NewResolver(ctx context.Context, cfg config.Configuration, kms *kms.KMS, re } ethereumClients := make(map[resolverPrefix]ResolverClientConfig) - ethereumClientsByChainID := make(map[string]ResolverClientConfig) + ethereumClientsByChainID := make(map[core.ChainID]ResolverClientConfig) rhsSettings := make(map[resolverPrefix]RhsSettings) supportedContracts := make(map[string]*abi.State) stateResolvers := make(map[string]pubsignals.StateResolver) @@ -150,7 +150,12 @@ func NewResolver(ctx context.Context, cfg config.Configuration, kms *kms.KMS, re } ethereumClients[resolverPrefix(resolverPrefixKey)] = *resolverClientConfig - ethereumClientsByChainID[networkSettings.ChainID] = *resolverClientConfig + chainID, err := core.GetChainID(core.Blockchain(chainName), core.NetworkID(networkName)) + if err != nil { + log.Error(ctx, "cannot get chain ID from blockchain and network", "err", err, "blockchain", chainName, "networl", networkName) + return nil, err + } + ethereumClientsByChainID[chainID] = *resolverClientConfig settings := networkSettings.RhsSettings settings.Iden3CommAgentStatus = strings.TrimSuffix(cfg.ServerUrl, "/") @@ -204,8 +209,8 @@ func (r *Resolver) GetEthClient(resolverPrefixKey string) (*eth.Client, error) { } // GetEthClientByChainID returns the eth client by chain id. -func (r *Resolver) GetEthClientByChainID(chainID int) (*eth.Client, error) { - resolverClientConfig, ok := r.ethereumClientsByChainID[fmt.Sprintf("%d", chainID)] +func (r *Resolver) GetEthClientByChainID(chainID core.ChainID) (*eth.Client, error) { + resolverClientConfig, ok := r.ethereumClientsByChainID[chainID] if !ok { return nil, fmt.Errorf("ethClient not found for chainID: %d", chainID) } From 83b63bb9cc9ee17a1e4ee3a2eae826c10c434c4f Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 16 Dec 2024 09:26:03 +0100 Subject: [PATCH 55/66] chore: Remove protocol.Iden3PaymentRequestCryptoV1Type as we do not support it. --- internal/core/services/payment.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index b00ba5a37..0526d70c6 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -300,9 +300,6 @@ func (p *payment) paymentInfo(ctx context.Context, setting payments.ChainConfig, TokenAddress: setting.PaymentOption.ContractAddress.String(), Proof: paymentProof(&setting, signature, signerAddress), }, nil - - case protocol.Iden3PaymentRequestCryptoV1Type: - return &protocol.Iden3PaymentRequestCryptoV1{}, nil default: return nil, fmt.Errorf("unsupported payment option type: %s", setting.PaymentOption.Type) } From daaeebc111c02bbe476d0a2b605ed1a3d6abcb28 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 16 Dec 2024 09:43:51 +0100 Subject: [PATCH 56/66] chore: Remove hardcoded private key from test. --- .../kms/aws_secret_storage_provider_test.go | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/internal/kms/aws_secret_storage_provider_test.go b/internal/kms/aws_secret_storage_provider_test.go index 3c074ca1d..09db2e2b1 100644 --- a/internal/kms/aws_secret_storage_provider_test.go +++ b/internal/kms/aws_secret_storage_provider_test.go @@ -2,8 +2,10 @@ package kms import ( "context" + "encoding/hex" "testing" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -143,9 +145,14 @@ func Test_searchPrivateKey(t *testing.T) { t.Run("should get private key for BJJ", func(t *testing.T) { did := randomDID(t) - privateKey := "9d7abdd5a43573ab9b623c50b9fc8f4357329d3009fe0fc22c8931161d98a03d" + + privKey, err := crypto.GenerateKey() + require.NoError(t, err) + privKeyBytes := crypto.FromECDSA(privKey) + privateKey := hex.EncodeToString(privKeyBytes) + id := getKeyID(&did, KeyTypeBabyJubJub, "BJJ:2290140c920a31a596937095f18a9ae15c1fe7091091be485f353968a4310380") - err := awsStorageProvider.SaveKeyMaterial(ctx, map[string]string{ + err = awsStorageProvider.SaveKeyMaterial(ctx, map[string]string{ jsonKeyType: string(KeyTypeBabyJubJub), jsonKeyData: privateKey, }, id) @@ -164,9 +171,14 @@ func Test_searchPrivateKey(t *testing.T) { t.Run("should get private key for ETH", func(t *testing.T) { did := randomDID(t) - privateKey := "9d7abdd5a43573ab9b623c50b9fc8f4357329d3009fe0fc22c8931161d98a03d" + + privKey, err := crypto.GenerateKey() + require.NoError(t, err) + privKeyBytes := crypto.FromECDSA(privKey) + privateKey := hex.EncodeToString(privKeyBytes) + id := getKeyID(&did, KeyTypeEthereum, "ETH:2290140c920a31a596937095f18a9ae15c1fe7091091be485f353968a4310380") - err := awsStorageProvider.SaveKeyMaterial(ctx, map[string]string{ + err = awsStorageProvider.SaveKeyMaterial(ctx, map[string]string{ jsonKeyType: string(KeyTypeEthereum), jsonKeyData: privateKey, }, id) @@ -185,9 +197,14 @@ func Test_searchPrivateKey(t *testing.T) { t.Run("should get private key for BJJ | from eth identity", func(t *testing.T) { did := randomDID(t) - privateKey := "9d7abdd5a43573ab9b623c50b9fc8f4357329d3009fe0fc22c8931161d98a03d" + + privKey, err := crypto.GenerateKey() + require.NoError(t, err) + privKeyBytes := crypto.FromECDSA(privKey) + privateKey := hex.EncodeToString(privKeyBytes) + id := did.String() + "/BJJ:f6eb5b16318de6054ccc30047d9ba395c954e78b6f1ba0a8f52a6e46b7f2500f" - err := awsStorageProvider.SaveKeyMaterial(ctx, map[string]string{ + err = awsStorageProvider.SaveKeyMaterial(ctx, map[string]string{ jsonKeyType: string(KeyTypeBabyJubJub), jsonKeyData: privateKey, }, id) From b2b7137961e6c37e290c3f4b8a450bf03fa780c1 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Mon, 16 Dec 2024 10:13:37 +0100 Subject: [PATCH 57/66] chore: Fix context.Background and remove unneeded commented test code. --- cmd/platform/main.go | 2 +- internal/api/payment_test.go | 144 ----------------------------------- 2 files changed, 1 insertion(+), 145 deletions(-) diff --git a/cmd/platform/main.go b/cmd/platform/main.go index ad6cb3e2b..43a5e0b2f 100644 --- a/cmd/platform/main.go +++ b/cmd/platform/main.go @@ -147,7 +147,7 @@ func main() { return } - paymentSettings, err := payments.SettingsFromConfig(context.Background(), &cfg.Payments) + paymentSettings, err := payments.SettingsFromConfig(ctx, &cfg.Payments) if err != nil { log.Error(ctx, "failed to load payment settings", "err", err) return diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index bbda5df82..fb9d4f119 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -677,147 +677,3 @@ func TestServer_CreatePaymentRequest(t *testing.T) { }) } } - -// TODO: Review this test!! -func TestServer_VerifyPayment(t *testing.T) { - /* - const ( - method = "polygonid" - blockchain = "polygon" - network = "amoy" - BJJ = "BJJ" - ) - ctx := context.Background() - server := newTestServer(t, nil) - handler := getHandler(ctx, server) - - iden, err := server.Services.identity.Create(ctx, "polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ}) - require.NoError(t, err) - issuerDID, err := w3c.ParseDID(iden.Identifier) - require.NoError(t, err) - - receiverDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") - require.NoError(t, err) - - _ = receiverDID - - // Creating an ethereum key - signingKeyID, err := keyStore.CreateKey(kms.KeyTypeEthereum, issuerDID) - require.NoError(t, err) - - // Creating a payment config using previously created key - config := domain.PaymentOptionConfig{ - Chains: []domain.PaymentOptionConfigChain{ - { - ChainId: 1101, - Recipient: "0x1101...", - SigningKeyID: signingKeyID.ID, - Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ - Amount: 0.5, - Currency: "ETH", - }, - Iden3PaymentRailsERC20RequestV1: nil, - }, - { - ChainId: 137, - Recipient: "0x137...", - SigningKeyID: signingKeyID.ID, - Iden3PaymentRailsRequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsRequestV1{ - Amount: 0.01, - Currency: "POL", - }, - Iden3PaymentRailsERC20RequestV1: &domain.PaymentOptionConfigChainIden3PaymentRailsERC20RequestV1{ - USDT: struct { - Amount float64 `json:"Amount"` - }{ - Amount: 5.2, - }, - USDC: struct { - Amount float64 `json:"Amount"` - }{ - Amount: 4.3, - }, - }, - }, - }, - } - - paymentOptionID, err := server.Services.payments.CreatePaymentOption(ctx, issuerDID, "Cinema ticket single", "Payment Option explanation", &config) - require.NoError(t, err) - - type expected struct { - httpCode int - msg string - } - - for _, tc := range []struct { - name string - issuerDID w3c.DID - auth func() (string, string) - PaymentOptionID uuid.UUID - body protocol.PaymentMessage - expected expected - }{ - { - name: "Happy Path", - auth: authOk, - issuerDID: *issuerDID, - PaymentOptionID: paymentOptionID, - body: protocol.PaymentMessage{ - ID: uuid.New().String(), - Typ: "application/iden3comm-plain-json", - Type: "https://iden3-communication.io/credentials/0.1/payment", - ThreadID: uuid.New().String(), - From: "did:iden3:polygon:mumbai:x3HstHLj2rTp6HHXk2WczYP7w3rpCsRbwCMeaQ2H2", - To: "did:polygonid:polygon:mumbai:2qJUZDSCFtpR8QvHyBC4eFm6ab9sJo5rqPbcaeyGC4", - Body: protocol.PaymentMessageBody{ - Payments: []protocol.Payment{protocol.NewPaymentRails(protocol.Iden3PaymentRailsV1{ - Nonce: "123", - Type: "Iden3PaymentRailsV1", - Context: protocol.NewPaymentContextString("https://schema.iden3.io/core/jsonld/payment.jsonld"), - PaymentData: struct { - TxID string `json:"txId"` - ChainID string `json:"chainId"` - }{ - TxID: "0x123", - ChainID: "137", - }, - })}, - }, - }, - expected: expected{ - httpCode: http.StatusOK, - }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - rr := httptest.NewRecorder() - payload, err := json.Marshal(tc.body) - require.NoError(t, err) - url := fmt.Sprintf("/v2/payment/verify/%s", tc.PaymentOptionID) - req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(payload)) - assert.NoError(t, err) - req.SetBasicAuth(tc.auth()) - - handler.ServeHTTP(rr, req) - require.Equal(t, tc.expected.httpCode, rr.Code) - - switch tc.expected.httpCode { - case http.StatusCreated: - var response VerifyPayment200JSONResponse - require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) - - case http.StatusBadRequest: - var response VerifyPayment400JSONResponse - require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) - assert.Equal(t, tc.expected.msg, response.Message) - case http.StatusInternalServerError: - var response VerifyPayment500JSONResponse - require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) - assert.Equal(t, tc.expected.msg, response.Message) - } - }) - } - - */ -} From c40490838cfdd4f739c446b64ae1fcc1a6372ad9 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 17 Dec 2024 10:09:38 +0100 Subject: [PATCH 58/66] feat: standardise lower case letters in payment api responses feat: Remove unneded level paymentOption.config.config --- api/api.yaml | 31 +++++++++++-------------------- internal/api/api.gen.go | 12 +++++------- internal/api/payment.go | 12 ++++++------ internal/api/payment_test.go | 13 ++++++------- internal/api/responses.go | 10 ++++------ 5 files changed, 32 insertions(+), 46 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index eb4e9d88c..dcc084796 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -2241,42 +2241,33 @@ components: $ref: '#/components/schemas/TimeUTC' PaymentOptionConfig: - type: object - required: - - config - properties: - config: - type: array - items: - $ref: '#/components/schemas/PaymentOptionConfigItem' + type: array + items: + $ref: '#/components/schemas/PaymentOptionConfigItem' PaymentOptionConfigItem: type: object required: - - paymentOptionId + - paymentOptionID - amount - - Recipient - - SigningKeyId + - recipient + - signingKeyID properties: - paymentOptionId: + paymentOptionID: type: integer amount: type: string - Recipient: + recipient: type: string - SigningKeyId: + signingKeyID: type: string PaymentOptionRequest: type: object required: - - id - - issuerID - name - description - config - - modifiedAt - - createdAt properties: name: type: string @@ -2328,7 +2319,7 @@ components: - recipientDID - paymentOptionID - payments - - CreatedAt + - createdAt properties: id: type: string @@ -2358,7 +2349,7 @@ components: type: array items: $ref: '#/components/schemas/PaymentRequestItem' - CreatedAt: + createdAt: type: string format: date-time diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 703376dcb..2e088f20e 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -295,7 +295,7 @@ type CreatePaymentRequest struct { // CreatePaymentRequestResponse defines model for CreatePaymentRequestResponse. type CreatePaymentRequestResponse struct { - CreatedAt time.Time `json:"CreatedAt"` + CreatedAt time.Time `json:"createdAt"` Credentials []struct { Context string `json:"context"` Type string `json:"type"` @@ -500,16 +500,14 @@ type PaymentOption struct { } // PaymentOptionConfig defines model for PaymentOptionConfig. -type PaymentOptionConfig struct { - Config []PaymentOptionConfigItem `json:"config"` -} +type PaymentOptionConfig = []PaymentOptionConfigItem // PaymentOptionConfigItem defines model for PaymentOptionConfigItem. type PaymentOptionConfigItem struct { - Recipient string `json:"Recipient"` - SigningKeyId string `json:"SigningKeyId"` Amount string `json:"amount"` - PaymentOptionId int `json:"paymentOptionId"` + PaymentOptionID int `json:"paymentOptionID"` + Recipient string `json:"recipient"` + SigningKeyID string `json:"signingKeyID"` } // PaymentOptionRequest defines model for PaymentOptionRequest. diff --git a/internal/api/payment.go b/internal/api/payment.go index 807d1b645..83fd12f1c 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -52,7 +52,7 @@ func (s *Server) CreatePaymentOption(ctx context.Context, request CreatePaymentO log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) return CreatePaymentOption400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil } - payOptConf, err := newPaymentOptionConfig(&request.Body.Config) + payOptConf, err := newPaymentOptionConfig(request.Body.Config) if err != nil { log.Error(ctx, "creating payment option config", "err", err) return CreatePaymentOption400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("invalid config: %s", err)}}, nil @@ -175,12 +175,12 @@ func (s *Server) VerifyPayment(ctx context.Context, request VerifyPaymentRequest return toVerifyPaymentResponse(status) } -func newPaymentOptionConfig(config *PaymentOptionConfig) (*domain.PaymentOptionConfig, error) { +func newPaymentOptionConfig(config PaymentOptionConfig) (*domain.PaymentOptionConfig, error) { const base10 = 10 cfg := &domain.PaymentOptionConfig{ - Config: make([]domain.PaymentOptionConfigItem, len(config.Config)), + Config: make([]domain.PaymentOptionConfigItem, len(config)), } - for i, item := range config.Config { + for i, item := range config { if !common.IsHexAddress(item.Recipient) { return nil, fmt.Errorf("invalid recipient address: %s", item.Recipient) } @@ -190,10 +190,10 @@ func newPaymentOptionConfig(config *PaymentOptionConfig) (*domain.PaymentOptionC } cfg.Config[i] = domain.PaymentOptionConfigItem{ - PaymentOptionID: payments.OptionConfigIDType(item.PaymentOptionId), + PaymentOptionID: payments.OptionConfigIDType(item.PaymentOptionID), Amount: *amount, Recipient: common.HexToAddress(item.Recipient), - SigningKeyID: item.SigningKeyId, + SigningKeyID: item.SigningKeyID, } } return cfg, nil diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index fb9d4f119..5179116aa 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -25,8 +25,7 @@ import ( ) const paymentOptionConfigurationTesting = ` -{ - "Config": [ + [ { "paymentOptionId": 1, "amount": "500000000000000000", @@ -39,8 +38,7 @@ const paymentOptionConfigurationTesting = ` "Recipient": "0x53d284357ec70cE289D6D64134DfAc8E511c8a3D", "SigningKeyId": "pubId" } - ] -} +] ` func TestServer_GetPaymentSettings(t *testing.T) { @@ -187,14 +185,14 @@ func TestServer_GetPaymentOption(t *testing.T) { var config PaymentOptionConfig require.NoError(t, json.Unmarshal([]byte(paymentOptionConfigurationTesting), &config)) domainConfig := domain.PaymentOptionConfig{} - for _, item := range config.Config { + for _, item := range config { amount, ok := new(big.Int).SetString(item.Amount, 10) require.True(t, ok) domainConfig.Config = append(domainConfig.Config, domain.PaymentOptionConfigItem{ - PaymentOptionID: payments.OptionConfigIDType(item.PaymentOptionId), + PaymentOptionID: payments.OptionConfigIDType(item.PaymentOptionID), Amount: *amount, Recipient: common.HexToAddress(item.Recipient), - SigningKeyID: item.SigningKeyId, + SigningKeyID: item.SigningKeyID, }) } optionID, err := server.Services.payments.CreatePaymentOption( @@ -386,6 +384,7 @@ func TestServer_GetPaymentOptions(t *testing.T) { switch tc.expected.httpCode { case http.StatusOK: var response GetPaymentOptions200JSONResponse + fmt.Println(rr.Body.String()) require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) assert.Equal(t, tc.expected.count, len(response.Items)) // Check that 10 items are returned assert.Equal(t, 1, int(response.Meta.Page)) diff --git a/internal/api/responses.go b/internal/api/responses.go index a437683cf..2b46f6d40 100644 --- a/internal/api/responses.go +++ b/internal/api/responses.go @@ -317,15 +317,13 @@ func toPaymentOption(opt *domain.PaymentOption) (PaymentOption, error) { } func toPaymentOptionConfig(config domain.PaymentOptionConfig) PaymentOptionConfig { - cfg := PaymentOptionConfig{ - Config: make([]PaymentOptionConfigItem, len(config.Config)), - } + cfg := make([]PaymentOptionConfigItem, len(config.Config)) for i, item := range config.Config { - cfg.Config[i] = PaymentOptionConfigItem{ - PaymentOptionId: int(item.PaymentOptionID), + cfg[i] = PaymentOptionConfigItem{ + PaymentOptionID: int(item.PaymentOptionID), Amount: item.Amount.String(), Recipient: item.Recipient.String(), - SigningKeyId: item.SigningKeyID, + SigningKeyID: item.SigningKeyID, } } return cfg From 84ffd934d495ed07b34ef75e098eeb4a07e75617 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Tue, 17 Dec 2024 13:54:43 +0100 Subject: [PATCH 59/66] feat: k8s configuration --- k8s/helm/templates/issuer-node-api-configmap.yaml | 4 +++- k8s/helm/values.yaml | 1 + k8s/testing/branches.txt | 2 +- k8s/testing/values.yaml | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/k8s/helm/templates/issuer-node-api-configmap.yaml b/k8s/helm/templates/issuer-node-api-configmap.yaml index 6f94f9372..2b365f848 100644 --- a/k8s/helm/templates/issuer-node-api-configmap.yaml +++ b/k8s/helm/templates/issuer-node-api-configmap.yaml @@ -32,5 +32,7 @@ data: ISSUER_CREDENTIAL_STATUS_PUBLISHING_KEY_PATH : {{ .Values.apiIssuerNode.configMap.issuerCredentialStatusPublishingKeyPath | quote }} ISSUER_RESOLVER_FILE : {{ .Values.issuerResolverFile | quote }} ISSUER_KMS_PROVIDER_LOCAL_STORAGE_FILE_PATH: {{ .Values.apiIssuerNode.configMap.issuerKMSProviderLocalStorageFilePath | quote }} + ISSUER_PAYMENTS_SETTINGS_FILE: {{ .Values.apiIssuerNode.configMap.issuerPaymentsSettingsFile | quote }} - \ No newline at end of file + + ISSUER_PAYMENTS_SETTINGS_FILE \ No newline at end of file diff --git a/k8s/helm/values.yaml b/k8s/helm/values.yaml index 2d4032bd0..a2b15b32f 100644 --- a/k8s/helm/values.yaml +++ b/k8s/helm/values.yaml @@ -68,6 +68,7 @@ apiIssuerNode: issuerCredentialStatusPublishingKeyPath: pbkey issuerIpfsGatewayUrl: https://gateway.pinata.cloud issuerKMSProviderLocalStorageFilePath: /localstoragekeys + issuerPaymentsSettingsFile: "ODAwMDI6CiAgUGF5bWVudFJhaWxzOiAweEY4RTQ5YjkyMkQ1RmIwMGQzRWREMTJiZDE0MDY0ZjI3NTcyNkQzMzkKICBQYXltZW50T3B0aW9uczoKICAgIC0gSUQ6IDEKICAgICAgTmFtZTogQW1veU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogMgogICAgICBOYW1lOiBBbW95IFVTRFQKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4MkZFNDA3NDk4MTJGQUMzOWEwRjM4MDY0OWVGNTlFMDFiY2NmM2ExQQogICAgICBGZWF0dXJlczogW10KICAgIC0gSUQ6IDMKICAgICAgTmFtZTogQW1veSBVU0RDCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDJGRTQwNzQ5ODEyRkFDMzlhMEYzODA2NDllRjU5RTAxYmNjZjNhMUEKICAgICAgRmVhdHVyZXM6CiAgICAgICAgLSBFSVAtMjYxMgo1OTE0MToKICBQYXltZW50UmFpbHM6IDB4NDBFM0VGMjIxQUE5M0Y2RmU5OTdjOWIwMzkzMzIyODIzQmIyMDdkMwogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNAogICAgICBOYW1lOiBMaW5lYVNlcG9saWFOYXRpdmUKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNSZXF1ZXN0VjEKICAgIC0gSUQ6IDUKICAgICAgTmFtZTogTGluZWEgU2Vwb2xpYSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweGIwMTAxYzFGZmRkMTIxM0I4ODZGZWJlRjZGMDc0NDJlNDg5OTBjOUMKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA2CiAgICAgIE5hbWU6IExpbmVhIFNlcG9saWEgVVNEQwogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc0VSQzIwUmVxdWVzdFYxCiAgICAgIENvbnRyYWN0QWRkcmVzczogMHhiMDEwMWMxRmZkZDEyMTNCODg2RmViZUY2RjA3NDQyZTQ4OTkwYzlDCiAgICAgIEZlYXR1cmVzOgogICAgICAgIC0gRUlQLTI2MTIKMjQ0MjoKICBQYXltZW50UmFpbHM6IDB4MDljMjY5ZTc0ZDhCNDdjOTg1MzdBY2Q2Q2JFZTgwNTY4MDZGNGM3MAogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNwogICAgICBOYW1lOiBaa0V2bU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogOAogICAgICBOYW1lOiBaa0V2bSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ3VycmVuY3k6IFVTRFQKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDk4NmNhRTZBRGNGNWRhMmExNTE0YWZjNzMxN0ZCZGVFMEI0MDQ4RGIKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA5CiAgICAgIE5hbWU6IFprRXZtIFVTREMKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4OTg2Y2FFNkFEY0Y1ZGEyYTE1MTRhZmM3MzE3RkJkZUUwQjQwNDhEYgogICAgICBGZWF0dXJlczoKICAgICAgICAtIEVJUC0yNjEyCg==" notificationsIssuerNode: deployment: diff --git a/k8s/testing/branches.txt b/k8s/testing/branches.txt index 5f8221642..9975325ae 100644 --- a/k8s/testing/branches.txt +++ b/k8s/testing/branches.txt @@ -1 +1 @@ -add_key_endpoint=add-key-endpoint \ No newline at end of file +feat/multichain-payment=multichain-payment \ No newline at end of file diff --git a/k8s/testing/values.yaml b/k8s/testing/values.yaml index e4f3207d6..5f25985d0 100644 --- a/k8s/testing/values.yaml +++ b/k8s/testing/values.yaml @@ -68,6 +68,7 @@ apiIssuerNode: issuerVaultUserpassAuthEnabled: "true" issuerCredentialStatusPublishingKeyPath: pbkey issuerIpfsGatewayUrl: https://gateway.pinata.cloud + issuerPaymentsSettingsFile: "ODAwMDI6CiAgUGF5bWVudFJhaWxzOiAweEY4RTQ5YjkyMkQ1RmIwMGQzRWREMTJiZDE0MDY0ZjI3NTcyNkQzMzkKICBQYXltZW50T3B0aW9uczoKICAgIC0gSUQ6IDEKICAgICAgTmFtZTogQW1veU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogMgogICAgICBOYW1lOiBBbW95IFVTRFQKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4MkZFNDA3NDk4MTJGQUMzOWEwRjM4MDY0OWVGNTlFMDFiY2NmM2ExQQogICAgICBGZWF0dXJlczogW10KICAgIC0gSUQ6IDMKICAgICAgTmFtZTogQW1veSBVU0RDCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDJGRTQwNzQ5ODEyRkFDMzlhMEYzODA2NDllRjU5RTAxYmNjZjNhMUEKICAgICAgRmVhdHVyZXM6CiAgICAgICAgLSBFSVAtMjYxMgo1OTE0MToKICBQYXltZW50UmFpbHM6IDB4NDBFM0VGMjIxQUE5M0Y2RmU5OTdjOWIwMzkzMzIyODIzQmIyMDdkMwogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNAogICAgICBOYW1lOiBMaW5lYVNlcG9saWFOYXRpdmUKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNSZXF1ZXN0VjEKICAgIC0gSUQ6IDUKICAgICAgTmFtZTogTGluZWEgU2Vwb2xpYSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweGIwMTAxYzFGZmRkMTIxM0I4ODZGZWJlRjZGMDc0NDJlNDg5OTBjOUMKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA2CiAgICAgIE5hbWU6IExpbmVhIFNlcG9saWEgVVNEQwogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc0VSQzIwUmVxdWVzdFYxCiAgICAgIENvbnRyYWN0QWRkcmVzczogMHhiMDEwMWMxRmZkZDEyMTNCODg2RmViZUY2RjA3NDQyZTQ4OTkwYzlDCiAgICAgIEZlYXR1cmVzOgogICAgICAgIC0gRUlQLTI2MTIKMjQ0MjoKICBQYXltZW50UmFpbHM6IDB4MDljMjY5ZTc0ZDhCNDdjOTg1MzdBY2Q2Q2JFZTgwNTY4MDZGNGM3MAogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNwogICAgICBOYW1lOiBaa0V2bU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogOAogICAgICBOYW1lOiBaa0V2bSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ3VycmVuY3k6IFVTRFQKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDk4NmNhRTZBRGNGNWRhMmExNTE0YWZjNzMxN0ZCZGVFMEI0MDQ4RGIKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA5CiAgICAgIE5hbWU6IFprRXZtIFVTREMKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4OTg2Y2FFNkFEY0Y1ZGEyYTE1MTRhZmM3MzE3RkJkZUUwQjQwNDhEYgogICAgICBGZWF0dXJlczoKICAgICAgICAtIEVJUC0yNjEyCg==" notificationsIssuerNode: deployment: From 0463ede1635a0b2b9b047a68ddb7509b43509f20 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 18 Dec 2024 12:33:42 +0100 Subject: [PATCH 60/66] feat: Refresh schema context on the fly if not present. feat: Pass schema id in Create PaymentRequest message endpoint. feat: Change response in Create PaymentRequest message endpoint. --- api/api.yaml | 67 ++++-------- cmd/platform/main.go | 2 +- internal/api/api.gen.go | 26 ++--- internal/api/main_test.go | 2 +- internal/api/payment.go | 12 +-- internal/api/payment_test.go | 102 +++++++++--------- internal/api/responses.go | 24 ++--- internal/api/schemas.go | 10 +- internal/api/schemas_test.go | 16 +-- internal/core/domain/schema.go | 9 +- internal/core/ports/payment_service.go | 2 +- internal/core/ports/schema_repository.go | 1 + internal/core/services/payment.go | 24 +++-- internal/core/services/schema.go | 36 +++++++ ...000_add_context_field_to_schemas_table.sql | 9 ++ internal/repositories/link.go | 2 + internal/repositories/schema-inmemory.go | 4 + internal/repositories/schema.go | 46 ++++++-- 18 files changed, 222 insertions(+), 172 deletions(-) create mode 100644 internal/db/schema/migrations/202412171212000_add_context_field_to_schemas_table.sql diff --git a/api/api.yaml b/api/api.yaml index dcc084796..dab399e17 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1871,6 +1871,7 @@ components: - bigInt - url - type + - contextURL - createdAt - version properties: @@ -1894,6 +1895,8 @@ components: type: string x-omitempty: false example: KYCCountryOfResidenceCredential + contextURL: + type: string createdAt: $ref: '#/components/schemas/TimeUTC' x-omitempty: false @@ -2279,29 +2282,23 @@ components: CreatePaymentRequest: type: object required: - - option - - credentials + - optionID + - schemaID - description - userDID properties: - option: + optionID: + type: string + x-go-type: uuid.UUID + x-go-type-import: + name: uuid + path: github.com/google/uuid + schemaID: type: string x-go-type: uuid.UUID x-go-type-import: name: uuid path: github.com/google/uuid - credentials: - type: array - items: - type: object - required: [ type, context ] - properties: - type: - type: string - example: "AML" - context: - type: string - example: "" description: type: string example: "Payment for AML" @@ -2313,8 +2310,6 @@ components: type: object required: - id - - credentials - - description - issuerDID - recipientDID - paymentOptionID @@ -2324,20 +2319,6 @@ components: id: type: string format: uuid - credentials: - type: array - items: - type: object - required: [ type, context ] - properties: - type: - type: string - example: "AML" - context: - type: string - example: "" - description: - type: string issuerDID: type: string recipientDID: @@ -2348,29 +2329,17 @@ components: payments: type: array items: - $ref: '#/components/schemas/PaymentRequestItem' + $ref: '#/components/schemas/PaymentRequestInfo' createdAt: type: string format: date-time - PaymentRequestItem: + PaymentRequestInfo: type: object - required: - - id - - nonce - - paymentRequestID - - payment - properties: - id: - type: string - format: uuid - nonce: - type: string - paymentRequestID: - type: string - format: uuid - payment: - type: null + x-go-type: protocol.PaymentRequestInfo + x-go-type-import: + name: protocol + path: github.com/iden3/iden3comm/v2/protocol TimeUTC: type: string diff --git a/cmd/platform/main.go b/cmd/platform/main.go index 43a5e0b2f..1103e0c4d 100644 --- a/cmd/platform/main.go +++ b/cmd/platform/main.go @@ -159,7 +159,7 @@ func main() { proofService := services.NewProver(circuitsLoaderService) schemaService := services.NewSchema(schemaRepository, schemaLoader) linkService := services.NewLinkService(storage, claimsService, qrService, claimsRepository, linkRepository, schemaRepository, schemaLoader, sessionRepository, ps, identityService, *networkResolver, cfg.UniversalLinks) - paymentService := services.NewPaymentService(paymentsRepo, *networkResolver, paymentSettings, keyStore) + paymentService := services.NewPaymentService(paymentsRepo, *networkResolver, schemaService, paymentSettings, keyStore) transactionService, err := gateways.NewTransaction(*networkResolver) if err != nil { diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 2e088f20e..88eacf56e 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -284,27 +284,19 @@ type CreateLinkRequest struct { // CreatePaymentRequest defines model for CreatePaymentRequest. type CreatePaymentRequest struct { - Credentials []struct { - Context string `json:"context"` - Type string `json:"type"` - } `json:"credentials"` Description string `json:"description"` - Option uuid.UUID `json:"option"` + OptionID uuid.UUID `json:"optionID"` + SchemaID uuid.UUID `json:"schemaID"` UserDID string `json:"userDID"` } // CreatePaymentRequestResponse defines model for CreatePaymentRequestResponse. type CreatePaymentRequestResponse struct { - CreatedAt time.Time `json:"createdAt"` - Credentials []struct { - Context string `json:"context"` - Type string `json:"type"` - } `json:"credentials"` - Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` Id openapi_types.UUID `json:"id"` IssuerDID string `json:"issuerDID"` PaymentOptionID openapi_types.UUID `json:"paymentOptionID"` - Payments []PaymentRequestItem `json:"payments"` + Payments []PaymentRequestInfo `json:"payments"` RecipientDID string `json:"recipientDID"` } @@ -526,13 +518,8 @@ type PaymentOptionsPaginated struct { Meta PaginatedMetadata `json:"meta"` } -// PaymentRequestItem defines model for PaymentRequestItem. -type PaymentRequestItem struct { - Id openapi_types.UUID `json:"id"` - Nonce string `json:"nonce"` - Payment interface{} `json:"payment"` - PaymentRequestID openapi_types.UUID `json:"paymentRequestID"` -} +// PaymentRequestInfo defines model for PaymentRequestInfo. +type PaymentRequestInfo = protocol.PaymentRequestInfo // PaymentStatus defines model for PaymentStatus. type PaymentStatus struct { @@ -591,6 +578,7 @@ type RevokeClaimResponse struct { // Schema defines model for Schema. type Schema struct { BigInt string `json:"bigInt"` + ContextURL string `json:"contextURL"` CreatedAt TimeUTC `json:"createdAt"` Description *string `json:"description"` Hash string `json:"hash"` diff --git a/internal/api/main_test.go b/internal/api/main_test.go index 28c4b8d85..60fba2e28 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -346,7 +346,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { identityService := services.NewIdentity(keyStore, repos.identity, repos.idenMerkleTree, repos.identityState, mtService, qrService, repos.claims, repos.revocation, repos.connection, st, nil, repos.sessions, pubSub, *networkResolver, rhsFactory, revocationStatusResolver) connectionService := services.NewConnection(repos.connection, repos.claims, st) schemaService := services.NewSchema(repos.schemas, schemaLoader) - paymentService := services.NewPaymentService(repos.payments, *networkResolver, paymentSettings, keyStore) + paymentService := services.NewPaymentService(repos.payments, *networkResolver, schemaService, paymentSettings, keyStore) mediaTypeManager := services.NewMediaTypeManager( map[iden3comm.ProtocolMessage][]string{ diff --git a/internal/api/payment.go b/internal/api/payment.go index 83fd12f1c..6dcbe4279 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-core/v2/w3c" - "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/core/ports" @@ -123,21 +122,14 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment log.Error(ctx, "parsing user did", "err", err, "did", request.Body.UserDID) return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "invalid userDID"}}, nil } - if len(request.Body.Credentials) == 0 { - log.Error(ctx, "create payment request: empty credentials") - return CreatePaymentRequest400JSONResponse{N400JSONResponse{Message: "empty credentials"}}, nil - } req := &ports.CreatePaymentRequestReq{ IssuerDID: *issuerDID, UserDID: *userDID, - OptionID: request.Body.Option, + SchemaID: request.Body.SchemaID, + OptionID: request.Body.OptionID, Description: request.Body.Description, } - req.Credentials = make([]protocol.PaymentRequestInfoCredentials, len(request.Body.Credentials)) - for i, cred := range request.Body.Credentials { - req.Credentials[i] = protocol.PaymentRequestInfoCredentials{Type: cred.Type, Context: cred.Context} - } payReq, err := s.paymentService.CreatePaymentRequest(ctx, req) if err != nil { diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index 5179116aa..ee24a2cbd 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -384,7 +384,6 @@ func TestServer_GetPaymentOptions(t *testing.T) { switch tc.expected.httpCode { case http.StatusOK: var response GetPaymentOptions200JSONResponse - fmt.Println(rr.Body.String()) require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) assert.Equal(t, tc.expected.count, len(response.Items)) // Check that 10 items are returned assert.Equal(t, 1, int(response.Meta.Page)) @@ -507,6 +506,8 @@ func TestServer_CreatePaymentRequest(t *testing.T) { blockchain = "polygon" network = "amoy" BJJ = "BJJ" + url = "https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json" + schemaType = "KYCCountryOfResidenceCredential" ) ctx := context.Background() server := newTestServer(t, nil) @@ -520,7 +521,9 @@ func TestServer_CreatePaymentRequest(t *testing.T) { receiverDID, err := w3c.ParseDID("did:polygonid:polygon:amoy:2qRYvPBNBTkPaHk1mKBkcLTequfAdsHzXv549ktnL5") require.NoError(t, err) - _ = receiverDID + iReq := ports.NewImportSchemaRequest(url, schemaType, nil, "1.0", nil) + schema, err := server.schemaService.ImportSchema(ctx, *issuerDID, iReq) + require.NoError(t, err) // Creating an ethereum key signingKeyID, err := keyStore.CreateKey(kms.KeyTypeEthereum, issuerDID) @@ -535,6 +538,12 @@ func TestServer_CreatePaymentRequest(t *testing.T) { Recipient: common.Address{}, SigningKeyID: signingKeyID.ID, }, + { + PaymentOptionID: 2, + Amount: *amount, + Recipient: common.Address{}, + SigningKeyID: signingKeyID.ID, + }, }, } @@ -575,62 +584,58 @@ func TestServer_CreatePaymentRequest(t *testing.T) { auth: authOk, issuerDID: *issuerDID, body: CreatePaymentRequestJSONRequestBody{ - UserDID: receiverDID.String(), - Option: uuid.New(), - Credentials: []struct { - Context string `json:"context"` - Type string `json:"type"` - }{ - { - Context: "context", - Type: "type", - }, - }, + UserDID: receiverDID.String(), + OptionID: uuid.New(), + SchemaID: schema.ID, }, expected: expected{ httpCode: http.StatusBadRequest, - msg: "can't create payment-request: payment option not found", + msg: "can't create payment-request: failed to get payment option: payment option not found", + }, + }, + { + name: "Not existing schema", + auth: authOk, + issuerDID: *issuerDID, + body: CreatePaymentRequestJSONRequestBody{ + UserDID: receiverDID.String(), + OptionID: paymentOptionID, + SchemaID: uuid.New(), + }, + expected: expected{ + httpCode: http.StatusBadRequest, + msg: "can't create payment-request: failed to get schema: schema not found", }, }, - { name: "Happy Path", auth: authOk, issuerDID: *issuerDID, body: CreatePaymentRequestJSONRequestBody{ UserDID: receiverDID.String(), - Option: paymentOptionID, + OptionID: paymentOptionID, + SchemaID: schema.ID, Description: "Payment Request", - Credentials: []struct { - Context string `json:"context"` - Type string `json:"type"` - }{ - { - Context: "context", - Type: "type", - }, - }, }, expected: expected{ httpCode: http.StatusCreated, resp: CreatePaymentRequestResponse{ - IssuerDID: issuerDID.String(), - RecipientDID: receiverDID.String(), - Credentials: []struct { - Context string `json:"context"` - Type string `json:"type"` - }{ - { - Context: "context", - Type: "type", - }, - }, - Description: "Payment Request", - Payments: []PaymentRequestItem{ + CreatedAt: time.Now(), + IssuerDID: issuerDID.String(), + PaymentOptionID: paymentOptionID, + Payments: []PaymentRequestInfo{ { - Payment: protocol.Iden3PaymentRailsERC20RequestV1{}, + Credentials: []protocol.PaymentRequestInfoCredentials{ + { + Type: "KYCCountryOfResidenceCredential", + Context: "https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json", + }, + }, + Description: "lala", + Data: protocol.PaymentRequestInfoData{}, }, }, + RecipientDID: receiverDID.String(), }, }, }, @@ -655,15 +660,16 @@ func TestServer_CreatePaymentRequest(t *testing.T) { assert.Equal(t, tc.expected.resp.IssuerDID, response.IssuerDID) assert.Equal(t, tc.expected.resp.RecipientDID, response.RecipientDID) assert.InDelta(t, time.Now().UnixMilli(), response.CreatedAt.UnixMilli(), 10) - assert.Equal(t, tc.expected.resp.Description, response.Description) - assert.Equal(t, tc.expected.resp.Credentials, response.Credentials) - assert.Equal(t, len(tc.expected.resp.Payments), len(response.Payments)) - for i := range tc.expected.resp.Payments { - assert.NotEqual(t, big.Int{}, response.Payments[i].Nonce) - assert.NotEqual(t, uuid.Nil, response.Payments[i].PaymentRequestID) - assert.NotEqual(t, uuid.Nil, response.Payments[i].Id) - // TODO: Fix it assert.Equal(t, tc.expected.resp.Payments[i].Payment, response.Payments[i].Payment) - } + /* + assert.Equal(t, len(tc.expected.resp.Payments), len(response.Payments)) + for i := range tc.expected.resp.Payments { + assert.NotEqual(t, big.Int{}, response.Payments[i].Nonce) + assert.NotEqual(t, uuid.Nil, response.Payments[i].PaymentRequestID) + assert.NotEqual(t, uuid.Nil, response.Payments[i].Id) + // TODO: Fix it assert.Equal(t, tc.expected.resp.Payments[i].Payment, response.Payments[i].Payment) + } + + */ case http.StatusBadRequest: var response CreatePaymentRequest400JSONResponse require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) diff --git a/internal/api/responses.go b/internal/api/responses.go index 2b46f6d40..5d5d89010 100644 --- a/internal/api/responses.go +++ b/internal/api/responses.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/iden3/go-schema-processor/v2/verifiable" + "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/common" "github.com/polygonid/sh-id-platform/internal/core/domain" @@ -138,6 +139,7 @@ func schemaResponse(s *domain.Schema) Schema { return Schema{ Id: s.ID.String(), Type: s.Type, + ContextURL: s.ContextURL, Url: s.URL, BigInt: s.Hash.BigInt().String(), Hash: string(hash), @@ -343,25 +345,23 @@ func toCreatePaymentRequestResponse(payReq *domain.PaymentRequest) CreatePayment Type: cred.Type, } } - payments := make([]PaymentRequestItem, len(payReq.Payments)) - for i, payment := range payReq.Payments { - payments[i] = PaymentRequestItem{ - Id: payment.ID, - Nonce: payment.Nonce.String(), - PaymentRequestID: payment.PaymentRequestID, - Payment: payment.Payment, - } + payment := PaymentRequestInfo{ + Credentials: payReq.Credentials, + Description: payReq.Description, + } + payment.Data = make([]protocol.PaymentRequestInfoDataItem, len(payReq.Payments)) + for i, pay := range payReq.Payments { + payment.Data[i] = pay.Payment } - return CreatePaymentRequestResponse{ + resp := CreatePaymentRequestResponse{ CreatedAt: payReq.CreatedAt, - Credentials: creds, - Description: payReq.Description, Id: payReq.ID, IssuerDID: payReq.IssuerDID.String(), RecipientDID: payReq.RecipientDID.String(), PaymentOptionID: payReq.PaymentOptionID, - Payments: payments, + Payments: []PaymentRequestInfo{payment}, } + return resp } func toVerifyPaymentResponse(status ports.BlockchainPaymentStatus) (VerifyPaymentResponseObject, error) { diff --git a/internal/api/schemas.go b/internal/api/schemas.go index 45dc71964..80a925673 100644 --- a/internal/api/schemas.go +++ b/internal/api/schemas.go @@ -43,13 +43,15 @@ func (s *Server) GetSchema(ctx context.Context, request GetSchemaRequestObject) return GetSchema400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil } schema, err := s.schemaService.GetByID(ctx, *issuerDID, request.Id) - if errors.Is(err, services.ErrSchemaNotFound) { - log.Error(ctx, "schema not found", "id", request.Id) - return GetSchema404JSONResponse{N404JSONResponse{Message: "schema not found"}}, nil - } if err != nil { + if errors.Is(err, services.ErrSchemaNotFound) { + log.Error(ctx, "schema not found", "id", request.Id) + return GetSchema404JSONResponse{N404JSONResponse{Message: "schema not found"}}, nil + } log.Error(ctx, "loading schema", "err", err, "id", request.Id) + return GetSchema500JSONResponse{N500JSONResponse{Message: err.Error()}}, nil } + return GetSchema200JSONResponse(schemaResponse(schema)), nil } diff --git a/internal/api/schemas_test.go b/internal/api/schemas_test.go index 0261384ca..89726cc75 100644 --- a/internal/api/schemas_test.go +++ b/internal/api/schemas_test.go @@ -33,7 +33,7 @@ func TestServer_GetSchema(t *testing.T) { s := &domain.Schema{ ID: uuid.New(), IssuerDID: *issuerDID, - URL: "https://domain.org/this/is/an/url", + URL: "http://localhost:8080/json/exampleMultidepth.json", Type: "schemaType", Words: domain.SchemaWordsFromString("attr1, attr2, attr3"), CreatedAt: time.Now(), @@ -88,12 +88,13 @@ func TestServer_GetSchema(t *testing.T) { expected: expected{ httpCode: http.StatusOK, schema: &Schema{ - BigInt: s.Hash.BigInt().String(), - CreatedAt: TimeUTC(s.CreatedAt), - Hash: string(sHash), - Id: s.ID.String(), - Type: s.Type, - Url: s.URL, + BigInt: s.Hash.BigInt().String(), + CreatedAt: TimeUTC(s.CreatedAt), + ContextURL: "http://localhost:8080/json-ld/exampleMultidepth.jsonld", + Hash: string(sHash), + Id: s.ID.String(), + Type: s.Type, + Url: s.URL, }, }, }, @@ -114,6 +115,7 @@ func TestServer_GetSchema(t *testing.T) { assert.Equal(t, tc.expected.schema.Id, response.Id) assert.Equal(t, tc.expected.schema.BigInt, response.BigInt) assert.Equal(t, tc.expected.schema.Type, response.Type) + assert.Equal(t, tc.expected.schema.ContextURL, response.ContextURL) assert.Equal(t, tc.expected.schema.Url, response.Url) assert.Equal(t, tc.expected.schema.Hash, response.Hash) assert.InDelta(t, time.Time(tc.expected.schema.CreatedAt).UnixMilli(), time.Time(response.CreatedAt).UnixMilli(), 1000) diff --git a/internal/core/domain/schema.go b/internal/core/domain/schema.go index 43978042a..024cc8dff 100644 --- a/internal/core/domain/schema.go +++ b/internal/core/domain/schema.go @@ -23,14 +23,6 @@ const ( // SchemaFormat type type SchemaFormat string -const ( - // JSONLD JSON-LD schema format - JSONLD SchemaFormat = "json-ld" - - // JSON JSON schema format - JSON SchemaFormat = "json" -) - // SchemaWords is a collection of schema attributes type SchemaWords []string @@ -61,6 +53,7 @@ type Schema struct { IssuerDID w3c.DID URL string Type string + ContextURL string Title *string Description *string Version string diff --git a/internal/core/ports/payment_service.go b/internal/core/ports/payment_service.go index bf41a59de..013995f58 100644 --- a/internal/core/ports/payment_service.go +++ b/internal/core/ports/payment_service.go @@ -34,8 +34,8 @@ type CreatePaymentRequestReq struct { IssuerDID w3c.DID UserDID w3c.DID OptionID uuid.UUID + SchemaID uuid.UUID Description string - Credentials []protocol.PaymentRequestInfoCredentials } // PaymentService is the interface implemented by the payment service diff --git a/internal/core/ports/schema_repository.go b/internal/core/ports/schema_repository.go index b1cbdcb48..3e74e0ddf 100644 --- a/internal/core/ports/schema_repository.go +++ b/internal/core/ports/schema_repository.go @@ -14,4 +14,5 @@ type SchemaRepository interface { Save(ctx context.Context, schema *domain.Schema) error GetByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.Schema, error) GetAll(ctx context.Context, issuerDID w3c.DID, query *string) ([]domain.Schema, error) + Update(ctx context.Context, s *domain.Schema) error } diff --git a/internal/core/services/payment.go b/internal/core/services/payment.go index 0526d70c6..5c1c85e86 100644 --- a/internal/core/services/payment.go +++ b/internal/core/services/payment.go @@ -33,15 +33,17 @@ import ( type payment struct { networkResolver network.Resolver settings payments.Config + schemaService ports.SchemaService paymentsStore ports.PaymentRepository kms kms.KMSType } // NewPaymentService creates a new payment service -func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, settings *payments.Config, kms kms.KMSType) ports.PaymentService { +func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, schemaSrv ports.SchemaService, settings *payments.Config, kms kms.KMSType) ports.PaymentService { return &payment{ networkResolver: resolver, settings: *settings, + schemaService: schemaSrv, paymentsStore: payOptsRepo, kms: kms, } @@ -93,14 +95,24 @@ func (p *payment) CreatePaymentRequest(ctx context.Context, req *ports.CreatePay option, err := p.paymentsStore.GetPaymentOptionByID(ctx, &req.IssuerDID, req.OptionID) if err != nil { log.Error(ctx, "failed to get payment option", "err", err, "issuerDID", req.IssuerDID, "optionID", req.OptionID) - return nil, err + return nil, fmt.Errorf("failed to get payment option: %w", err) + } + schema, err := p.schemaService.GetByID(ctx, req.IssuerDID, req.SchemaID) + if err != nil { + log.Error(ctx, "failed to get schema", "err", err, "issuerDID", req.IssuerDID, "schemaID", req.SchemaID) + return nil, fmt.Errorf("failed to get schema: %w", err) } paymentRequest := &domain.PaymentRequest{ - ID: uuid.New(), - IssuerDID: req.IssuerDID, - RecipientDID: req.UserDID, - Credentials: req.Credentials, + ID: uuid.New(), + IssuerDID: req.IssuerDID, + RecipientDID: req.UserDID, + Credentials: []protocol.PaymentRequestInfoCredentials{ + { + Context: schema.ContextURL, + Type: schema.Type, + }, + }, Description: req.Description, PaymentOptionID: req.OptionID, CreatedAt: time.Now(), diff --git a/internal/core/services/schema.go b/internal/core/services/schema.go index c8bfef9ee..490c6ba7d 100644 --- a/internal/core/services/schema.go +++ b/internal/core/services/schema.go @@ -3,6 +3,7 @@ package services import ( "context" "errors" + "fmt" "time" "github.com/google/uuid" @@ -35,6 +36,35 @@ func (s *schema) GetByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) ( if err != nil { return nil, err } + schemaID := schema.ID + schema, err = s.fixSchemaContext(ctx, schema) + if err != nil { + log.Error(ctx, "fixing schema context", "err", err, "schema", schemaID) + return nil, fmt.Errorf("fixing schema context: %w", err) + } + return schema, nil +} + +// fixSchemaContext updates the schema context url if it is empty. This will happen in old installations +// that did not have the context url stored in the database +// There is no action in DB if the context url is already stored +func (s *schema) fixSchemaContext(ctx context.Context, schema *domain.Schema) (*domain.Schema, error) { + if schema.ContextURL == "" { + remoteSchema, err := jsonschema.Load(ctx, schema.URL, s.loader) + if err != nil { + log.Error(ctx, "loading jsonschema", "err", err, "jsonschema", schema.URL) + return nil, ErrLoadingSchema + } + contextUrl, err := remoteSchema.JSONLdContext() + if err != nil { + log.Error(ctx, "getting jsonld context", "err", err, "jsonschema", schema.URL) + return nil, ErrProcessSchema + } + schema.ContextURL = contextUrl + if err := s.repo.Update(ctx, schema); err != nil { + return nil, fmt.Errorf("updating schema: %w", err) + } + } return schema, nil } @@ -61,12 +91,18 @@ func (s *schema) ImportSchema(ctx context.Context, did w3c.DID, req *ports.Impor log.Error(ctx, "hashing schema", "err", err, "jsonschema", req.URL) return nil, ErrProcessSchema } + contextUrl, err := remoteSchema.JSONLdContext() + if err != nil { + log.Error(ctx, "getting jsonld context", "err", err, "jsonschema", req.URL) + return nil, ErrProcessSchema + } schema := &domain.Schema{ ID: uuid.New(), IssuerDID: did, URL: req.URL, Type: req.SType, + ContextURL: contextUrl, Version: req.Version, Title: req.Title, Description: req.Description, diff --git a/internal/db/schema/migrations/202412171212000_add_context_field_to_schemas_table.sql b/internal/db/schema/migrations/202412171212000_add_context_field_to_schemas_table.sql new file mode 100644 index 000000000..1230841ae --- /dev/null +++ b/internal/db/schema/migrations/202412171212000_add_context_field_to_schemas_table.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE schemas ADD COLUMN context_url text NOT NULL DEFAULT ''; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE schemas DROP COLUMN context_url; +-- +goose StatementEnd \ No newline at end of file diff --git a/internal/repositories/link.go b/internal/repositories/link.go index 4c71fe93c..f0011791a 100644 --- a/internal/repositories/link.go +++ b/internal/repositories/link.go @@ -158,6 +158,7 @@ SELECT links.id, schemas.issuer_id as schema_issuer_id, schemas.url, schemas.type, + schemas.context_url, schemas.hash, schemas.words, schemas.created_at @@ -223,6 +224,7 @@ WHERE links.issuer_id = $1 &schema.IssuerID, &schema.URL, &schema.Type, + &schema.ContextURL, &schema.Hash, &schema.Words, &schema.CreatedAt, diff --git a/internal/repositories/schema-inmemory.go b/internal/repositories/schema-inmemory.go index f7d896e38..d5c67544d 100644 --- a/internal/repositories/schema-inmemory.go +++ b/internal/repositories/schema-inmemory.go @@ -13,6 +13,10 @@ type schemaInMemory struct { schemas map[uuid.UUID]domain.Schema } +func (s *schemaInMemory) Update(ctx context.Context, schema *domain.Schema) error { + return s.Save(ctx, schema) +} + // NewSchemaInMemory returns schemaRepository implemented in memory convenient for testing func NewSchemaInMemory() *schemaInMemory { return &schemaInMemory{schemas: make(map[uuid.UUID]domain.Schema)} diff --git a/internal/repositories/schema.go b/internal/repositories/schema.go index 264e38807..39915e848 100644 --- a/internal/repositories/schema.go +++ b/internal/repositories/schema.go @@ -27,6 +27,7 @@ type dbSchema struct { IssuerID string URL string Type string + ContextURL string Version string Title *string Description *string @@ -46,7 +47,7 @@ func NewSchema(conn db.Storage) *schema { // Save stores a new entry in schemas table func (r *schema) Save(ctx context.Context, s *domain.Schema) error { - const insertSchema = `INSERT INTO schemas (id, issuer_id, url, type, hash, words, created_at,version,title,description) VALUES($1, $2::text, $3::text, $4::text, $5::text, $6::text, $7, $8::text,$9::text,$10::text);` + const insertSchema = `INSERT INTO schemas (id, issuer_id, url, type, context_url, hash, words, created_at, version, title, description) VALUES($1, $2, $3, $4, $5, $6, $7, $8,$9,$10,$11);` hash, err := s.Hash.MarshalText() if err != nil { return err @@ -58,6 +59,7 @@ func (r *schema) Save(ctx context.Context, s *domain.Schema) error { s.IssuerDID.String(), s.URL, s.Type, + s.ContextURL, string(hash), r.toFullTextSearchDocument(s.Type, s.Words), s.CreatedAt, @@ -72,7 +74,38 @@ func (r *schema) Save(ctx context.Context, s *domain.Schema) error { return err } + return nil +} +func (r *schema) Update(ctx context.Context, s *domain.Schema) error { + const updateSchema = ` +UPDATE schemas +SET issuer_id=$2, url=$3, type=$4, context_url=$5, hash=$6, words=$7, created_at=$8, version=$9, title=$10, description=$11 +WHERE schemas.id = $1;` + hash, err := s.Hash.MarshalText() + if err != nil { + return err + } + cmd, err := r.conn.Pgx.Exec( + ctx, + updateSchema, + s.ID, + s.IssuerDID.String(), + s.URL, + s.Type, + s.ContextURL, + string(hash), + r.toFullTextSearchDocument(s.Type, s.Words), + s.CreatedAt, + s.Version, + s.Title, + s.Description) + if err != nil { + return err + } + if cmd.RowsAffected() == 0 { + return ErrSchemaDoesNotExist + } return nil } @@ -89,7 +122,7 @@ func (r *schema) GetAll(ctx context.Context, issuerDID w3c.DID, query *string) ( var err error var rows pgx.Rows sqlArgs := make([]interface{}, 0) - sqlQuery := `SELECT id, issuer_id, url, type, words, hash, created_at,version,title,description + sqlQuery := `SELECT id, issuer_id, url, type, context_url, words, hash, created_at,version,title,description FROM schemas WHERE issuer_id=$1` sqlArgs = append(sqlArgs, issuerDID.String()) @@ -110,7 +143,7 @@ func (r *schema) GetAll(ctx context.Context, issuerDID w3c.DID, query *string) ( schemaCol := make([]domain.Schema, 0) s := dbSchema{} for rows.Next() { - if err := rows.Scan(&s.ID, &s.IssuerID, &s.URL, &s.Type, &s.Words, &s.Hash, &s.CreatedAt, &s.Version, &s.Title, &s.Description); err != nil { + if err := rows.Scan(&s.ID, &s.IssuerID, &s.URL, &s.Type, &s.ContextURL, &s.Words, &s.Hash, &s.CreatedAt, &s.Version, &s.Title, &s.Description); err != nil { return nil, err } item, err := toSchemaDomain(&s) @@ -124,14 +157,14 @@ func (r *schema) GetAll(ctx context.Context, issuerDID w3c.DID, query *string) ( // GetByID searches and returns an schema by id func (r *schema) GetByID(ctx context.Context, issuerDID w3c.DID, id uuid.UUID) (*domain.Schema, error) { - const byID = `SELECT id, issuer_id, url, type, words, hash, created_at,version,title,description + const byID = `SELECT id, issuer_id, url, type, context_url, words, hash, created_at,version,title,description FROM schemas WHERE issuer_id = $1 AND id=$2` s := dbSchema{} row := r.conn.Pgx.QueryRow(ctx, byID, issuerDID.String(), id) - err := row.Scan(&s.ID, &s.IssuerID, &s.URL, &s.Type, &s.Words, &s.Hash, &s.CreatedAt, &s.Version, &s.Title, &s.Description) - if err == pgx.ErrNoRows { + err := row.Scan(&s.ID, &s.IssuerID, &s.URL, &s.Type, &s.ContextURL, &s.Words, &s.Hash, &s.CreatedAt, &s.Version, &s.Title, &s.Description) + if errors.Is(err, pgx.ErrNoRows) { return nil, ErrSchemaDoesNotExist } if err != nil { @@ -154,6 +187,7 @@ func toSchemaDomain(s *dbSchema) (*domain.Schema, error) { IssuerDID: *issuerDID, URL: s.URL, Type: s.Type, + ContextURL: s.ContextURL, Hash: schemaHash, Words: domain.SchemaWordsFromString(s.Words), CreatedAt: s.CreatedAt, From 71fb3d75054b57209fb30bd4e289185369f15ed5 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 18 Dec 2024 12:40:11 +0100 Subject: [PATCH 61/66] fix: merge conflict --- internal/api/api.gen.go | 709 +++++++++++++++++++++++++++++++++++++- internal/api/main_test.go | 4 +- internal/api/server.go | 52 +-- 3 files changed, 736 insertions(+), 29 deletions(-) diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 88eacf56e..016b2c973 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -60,7 +60,7 @@ const ( // Defines values for DisplayMethodType. const ( - Iden3BasicDisplayMethodv2 DisplayMethodType = "Iden3BasicDisplayMethodv2" + Iden3BasicDisplayMethodV1 DisplayMethodType = "Iden3BasicDisplayMethodV1" ) // Defines values for GetIdentitiesResponseCredentialStatusType. @@ -147,6 +147,16 @@ const ( GetCredentialOfferParamsTypeUniversalLink GetCredentialOfferParamsType = "universalLink" ) +// Defines values for GetAllDisplayMethodsParamsSort. +const ( + CreatedAt GetAllDisplayMethodsParamsSort = "created_at" + MinusCreatedAt GetAllDisplayMethodsParamsSort = "-created_at" + MinusName GetAllDisplayMethodsParamsSort = "-name" + MinusType GetAllDisplayMethodsParamsSort = "-type" + Name GetAllDisplayMethodsParamsSort = "name" + Type GetAllDisplayMethodsParamsSort = "type" +) + // Defines values for GetStateTransactionsParamsFilter. const ( GetStateTransactionsParamsFilterAll GetStateTransactionsParamsFilter = "all" @@ -237,6 +247,15 @@ type CreateCredentialResponse struct { Id string `json:"id"` } +// CreateDisplayMethodRequest defines model for CreateDisplayMethodRequest. +type CreateDisplayMethodRequest struct { + Name string `json:"name"` + + // Type Display method type (Iden3BasicDisplayMethodV1 is default value) + Type *string `json:"type,omitempty"` + Url string `json:"url"` +} + // CreateIdentityRequest defines model for CreateIdentityRequest. type CreateIdentityRequest struct { CredentialStatusType *CreateIdentityRequestCredentialStatusType `json:"credentialStatusType,omitempty"` @@ -342,6 +361,20 @@ type DisplayMethod struct { // DisplayMethodType defines model for DisplayMethod.Type. type DisplayMethodType string +// DisplayMethodEntity defines model for DisplayMethodEntity. +type DisplayMethodEntity struct { + Id uuid.UUID `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Url string `json:"url"` +} + +// DisplayMethodPaginated defines model for DisplayMethodPaginated. +type DisplayMethodPaginated struct { + Items []DisplayMethodEntity `json:"items"` + Meta PaginatedMetadata `json:"meta"` +} + // GenericErrorMessage defines model for GenericErrorMessage. type GenericErrorMessage struct { Message string `json:"message"` @@ -797,6 +830,25 @@ type GetCredentialOfferParams struct { // GetCredentialOfferParamsType defines parameters for GetCredentialOffer. type GetCredentialOfferParamsType string +// GetAllDisplayMethodsParams defines parameters for GetAllDisplayMethods. +type GetAllDisplayMethodsParams struct { + Page *uint `form:"page,omitempty" json:"page,omitempty"` + + // MaxResults Number of items to fetch on each page. Minimum is 10. Default is 50. No maximum by the moment. + MaxResults *uint `form:"max_results,omitempty" json:"max_results,omitempty"` + Sort *[]GetAllDisplayMethodsParamsSort `form:"sort,omitempty" json:"sort,omitempty"` +} + +// GetAllDisplayMethodsParamsSort defines parameters for GetAllDisplayMethods. +type GetAllDisplayMethodsParamsSort string + +// UpdateDisplayMethodJSONBody defines parameters for UpdateDisplayMethod. +type UpdateDisplayMethodJSONBody struct { + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + Url *string `json:"url,omitempty"` +} + // GetSchemasParams defines parameters for GetSchemas. type GetSchemasParams struct { // Query Query string to do full text search in schema types and attributes. @@ -868,6 +920,12 @@ type CreateLinkQrCodeCallbackTextRequestBody = CreateLinkQrCodeCallbackTextBody // ActivateLinkJSONRequestBody defines body for ActivateLink for application/json ContentType. type ActivateLinkJSONRequestBody ActivateLinkJSONBody +// CreateDisplayMethodJSONRequestBody defines body for CreateDisplayMethod for application/json ContentType. +type CreateDisplayMethodJSONRequestBody = CreateDisplayMethodRequest + +// UpdateDisplayMethodJSONRequestBody defines body for UpdateDisplayMethod for application/json ContentType. +type UpdateDisplayMethodJSONRequestBody UpdateDisplayMethodJSONBody + // CreatePaymentRequestJSONRequestBody defines body for CreatePaymentRequest for application/json ContentType. type CreatePaymentRequestJSONRequestBody = CreatePaymentRequest @@ -972,6 +1030,21 @@ type ServerInterface interface { // Get Credentials Offer // (GET /v2/identities/{identifier}/credentials/{id}/offer) GetCredentialOffer(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id PathClaim, params GetCredentialOfferParams) + // Get All Display Methods + // (GET /v2/identities/{identifier}/display-method) + GetAllDisplayMethods(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetAllDisplayMethodsParams) + // Create Display Method + // (POST /v2/identities/{identifier}/display-method) + CreateDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) + // Delete Display Method + // (DELETE /v2/identities/{identifier}/display-method/{id}) + DeleteDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) + // Get Display Method + // (GET /v2/identities/{identifier}/display-method/{id}) + GetDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) + // Update Display Method + // (PATCH /v2/identities/{identifier}/display-method/{id}) + UpdateDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) // Create Payment Request // (POST /v2/identities/{identifier}/payment-request) CreatePaymentRequest(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) @@ -1209,6 +1282,36 @@ func (_ Unimplemented) GetCredentialOffer(w http.ResponseWriter, r *http.Request w.WriteHeader(http.StatusNotImplemented) } +// Get All Display Methods +// (GET /v2/identities/{identifier}/display-method) +func (_ Unimplemented) GetAllDisplayMethods(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetAllDisplayMethodsParams) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Create Display Method +// (POST /v2/identities/{identifier}/display-method) +func (_ Unimplemented) CreateDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Delete Display Method +// (DELETE /v2/identities/{identifier}/display-method/{id}) +func (_ Unimplemented) DeleteDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Get Display Method +// (GET /v2/identities/{identifier}/display-method/{id}) +func (_ Unimplemented) GetDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Update Display Method +// (PATCH /v2/identities/{identifier}/display-method/{id}) +func (_ Unimplemented) UpdateDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + w.WriteHeader(http.StatusNotImplemented) +} + // Create Payment Request // (POST /v2/identities/{identifier}/payment-request) func (_ Unimplemented) CreatePaymentRequest(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { @@ -2443,6 +2546,215 @@ func (siw *ServerInterfaceWrapper) GetCredentialOffer(w http.ResponseWriter, r * handler.ServeHTTP(w, r) } +// GetAllDisplayMethods operation middleware +func (siw *ServerInterfaceWrapper) GetAllDisplayMethods(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + // Parameter object where we will unmarshal all parameters from the context + var params GetAllDisplayMethodsParams + + // ------------- Optional query parameter "page" ------------- + + err = runtime.BindQueryParameter("form", true, false, "page", r.URL.Query(), ¶ms.Page) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "page", Err: err}) + return + } + + // ------------- Optional query parameter "max_results" ------------- + + err = runtime.BindQueryParameter("form", true, false, "max_results", r.URL.Query(), ¶ms.MaxResults) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "max_results", Err: err}) + return + } + + // ------------- Optional query parameter "sort" ------------- + + err = runtime.BindQueryParameter("form", false, false, "sort", r.URL.Query(), ¶ms.Sort) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "sort", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetAllDisplayMethods(w, r, identifier, params) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// CreateDisplayMethod operation middleware +func (siw *ServerInterfaceWrapper) CreateDisplayMethod(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.CreateDisplayMethod(w, r, identifier) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// DeleteDisplayMethod operation middleware +func (siw *ServerInterfaceWrapper) DeleteDisplayMethod(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + // ------------- Path parameter "id" ------------- + var id Id + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DeleteDisplayMethod(w, r, identifier, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetDisplayMethod operation middleware +func (siw *ServerInterfaceWrapper) GetDisplayMethod(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + // ------------- Path parameter "id" ------------- + var id Id + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetDisplayMethod(w, r, identifier, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// UpdateDisplayMethod operation middleware +func (siw *ServerInterfaceWrapper) UpdateDisplayMethod(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "identifier" ------------- + var identifier PathIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "identifier", chi.URLParam(r, "identifier"), &identifier, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identifier", Err: err}) + return + } + + // ------------- Path parameter "id" ------------- + var id Id + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BasicAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.UpdateDisplayMethod(w, r, identifier, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // CreatePaymentRequest operation middleware func (siw *ServerInterfaceWrapper) CreatePaymentRequest(w http.ResponseWriter, r *http.Request) { @@ -3242,6 +3554,21 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/v2/identities/{identifier}/credentials/{id}/offer", wrapper.GetCredentialOffer) }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v2/identities/{identifier}/display-method", wrapper.GetAllDisplayMethods) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/v2/identities/{identifier}/display-method", wrapper.CreateDisplayMethod) + }) + r.Group(func(r chi.Router) { + r.Delete(options.BaseURL+"/v2/identities/{identifier}/display-method/{id}", wrapper.DeleteDisplayMethod) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v2/identities/{identifier}/display-method/{id}", wrapper.GetDisplayMethod) + }) + r.Group(func(r chi.Router) { + r.Patch(options.BaseURL+"/v2/identities/{identifier}/display-method/{id}", wrapper.UpdateDisplayMethod) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/v2/identities/{identifier}/payment-request", wrapper.CreatePaymentRequest) }) @@ -4573,6 +4900,223 @@ func (response GetCredentialOffer500JSONResponse) VisitGetCredentialOfferRespons return json.NewEncoder(w).Encode(response) } +type GetAllDisplayMethodsRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Params GetAllDisplayMethodsParams +} + +type GetAllDisplayMethodsResponseObject interface { + VisitGetAllDisplayMethodsResponse(w http.ResponseWriter) error +} + +type GetAllDisplayMethods200JSONResponse DisplayMethodPaginated + +func (response GetAllDisplayMethods200JSONResponse) VisitGetAllDisplayMethodsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetAllDisplayMethods400JSONResponse struct{ N400JSONResponse } + +func (response GetAllDisplayMethods400JSONResponse) VisitGetAllDisplayMethodsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetAllDisplayMethods404JSONResponse struct{ N404JSONResponse } + +func (response GetAllDisplayMethods404JSONResponse) VisitGetAllDisplayMethodsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type GetAllDisplayMethods500JSONResponse struct{ N500JSONResponse } + +func (response GetAllDisplayMethods500JSONResponse) VisitGetAllDisplayMethodsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type CreateDisplayMethodRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Body *CreateDisplayMethodJSONRequestBody +} + +type CreateDisplayMethodResponseObject interface { + VisitCreateDisplayMethodResponse(w http.ResponseWriter) error +} + +type CreateDisplayMethod201JSONResponse UUIDResponse + +func (response CreateDisplayMethod201JSONResponse) VisitCreateDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(201) + + return json.NewEncoder(w).Encode(response) +} + +type CreateDisplayMethod400JSONResponse struct{ N400JSONResponse } + +func (response CreateDisplayMethod400JSONResponse) VisitCreateDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type CreateDisplayMethod500JSONResponse struct{ N500JSONResponse } + +func (response CreateDisplayMethod500JSONResponse) VisitCreateDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteDisplayMethodRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Id Id `json:"id"` +} + +type DeleteDisplayMethodResponseObject interface { + VisitDeleteDisplayMethodResponse(w http.ResponseWriter) error +} + +type DeleteDisplayMethod200JSONResponse GenericMessage + +func (response DeleteDisplayMethod200JSONResponse) VisitDeleteDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteDisplayMethod400JSONResponse struct{ N400JSONResponse } + +func (response DeleteDisplayMethod400JSONResponse) VisitDeleteDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteDisplayMethod404JSONResponse struct{ N404JSONResponse } + +func (response DeleteDisplayMethod404JSONResponse) VisitDeleteDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteDisplayMethod500JSONResponse struct{ N500JSONResponse } + +func (response DeleteDisplayMethod500JSONResponse) VisitDeleteDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type GetDisplayMethodRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Id Id `json:"id"` +} + +type GetDisplayMethodResponseObject interface { + VisitGetDisplayMethodResponse(w http.ResponseWriter) error +} + +type GetDisplayMethod200JSONResponse DisplayMethodEntity + +func (response GetDisplayMethod200JSONResponse) VisitGetDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetDisplayMethod400JSONResponse struct{ N400JSONResponse } + +func (response GetDisplayMethod400JSONResponse) VisitGetDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetDisplayMethod404JSONResponse struct{ N404JSONResponse } + +func (response GetDisplayMethod404JSONResponse) VisitGetDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type GetDisplayMethod500JSONResponse struct{ N500JSONResponse } + +func (response GetDisplayMethod500JSONResponse) VisitGetDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateDisplayMethodRequestObject struct { + Identifier PathIdentifier `json:"identifier"` + Id Id `json:"id"` + Body *UpdateDisplayMethodJSONRequestBody +} + +type UpdateDisplayMethodResponseObject interface { + VisitUpdateDisplayMethodResponse(w http.ResponseWriter) error +} + +type UpdateDisplayMethod200JSONResponse GenericMessage + +func (response UpdateDisplayMethod200JSONResponse) VisitUpdateDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateDisplayMethod400JSONResponse struct{ N400JSONResponse } + +func (response UpdateDisplayMethod400JSONResponse) VisitUpdateDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateDisplayMethod404JSONResponse struct{ N404JSONResponse } + +func (response UpdateDisplayMethod404JSONResponse) VisitUpdateDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateDisplayMethod500JSONResponse struct{ N500JSONResponse } + +func (response UpdateDisplayMethod500JSONResponse) VisitUpdateDisplayMethodResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type CreatePaymentRequestRequestObject struct { Identifier PathIdentifier `json:"identifier"` Body *CreatePaymentRequestJSONRequestBody @@ -5359,6 +5903,21 @@ type StrictServerInterface interface { // Get Credentials Offer // (GET /v2/identities/{identifier}/credentials/{id}/offer) GetCredentialOffer(ctx context.Context, request GetCredentialOfferRequestObject) (GetCredentialOfferResponseObject, error) + // Get All Display Methods + // (GET /v2/identities/{identifier}/display-method) + GetAllDisplayMethods(ctx context.Context, request GetAllDisplayMethodsRequestObject) (GetAllDisplayMethodsResponseObject, error) + // Create Display Method + // (POST /v2/identities/{identifier}/display-method) + CreateDisplayMethod(ctx context.Context, request CreateDisplayMethodRequestObject) (CreateDisplayMethodResponseObject, error) + // Delete Display Method + // (DELETE /v2/identities/{identifier}/display-method/{id}) + DeleteDisplayMethod(ctx context.Context, request DeleteDisplayMethodRequestObject) (DeleteDisplayMethodResponseObject, error) + // Get Display Method + // (GET /v2/identities/{identifier}/display-method/{id}) + GetDisplayMethod(ctx context.Context, request GetDisplayMethodRequestObject) (GetDisplayMethodResponseObject, error) + // Update Display Method + // (PATCH /v2/identities/{identifier}/display-method/{id}) + UpdateDisplayMethod(ctx context.Context, request UpdateDisplayMethodRequestObject) (UpdateDisplayMethodResponseObject, error) // Create Payment Request // (POST /v2/identities/{identifier}/payment-request) CreatePaymentRequest(ctx context.Context, request CreatePaymentRequestRequestObject) (CreatePaymentRequestResponseObject, error) @@ -6305,6 +6864,154 @@ func (sh *strictHandler) GetCredentialOffer(w http.ResponseWriter, r *http.Reque } } +// GetAllDisplayMethods operation middleware +func (sh *strictHandler) GetAllDisplayMethods(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetAllDisplayMethodsParams) { + var request GetAllDisplayMethodsRequestObject + + request.Identifier = identifier + request.Params = params + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetAllDisplayMethods(ctx, request.(GetAllDisplayMethodsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetAllDisplayMethods") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetAllDisplayMethodsResponseObject); ok { + if err := validResponse.VisitGetAllDisplayMethodsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// CreateDisplayMethod operation middleware +func (sh *strictHandler) CreateDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { + var request CreateDisplayMethodRequestObject + + request.Identifier = identifier + + var body CreateDisplayMethodJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.CreateDisplayMethod(ctx, request.(CreateDisplayMethodRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "CreateDisplayMethod") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(CreateDisplayMethodResponseObject); ok { + if err := validResponse.VisitCreateDisplayMethodResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteDisplayMethod operation middleware +func (sh *strictHandler) DeleteDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + var request DeleteDisplayMethodRequestObject + + request.Identifier = identifier + request.Id = id + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.DeleteDisplayMethod(ctx, request.(DeleteDisplayMethodRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteDisplayMethod") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(DeleteDisplayMethodResponseObject); ok { + if err := validResponse.VisitDeleteDisplayMethodResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetDisplayMethod operation middleware +func (sh *strictHandler) GetDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + var request GetDisplayMethodRequestObject + + request.Identifier = identifier + request.Id = id + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetDisplayMethod(ctx, request.(GetDisplayMethodRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetDisplayMethod") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetDisplayMethodResponseObject); ok { + if err := validResponse.VisitGetDisplayMethodResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// UpdateDisplayMethod operation middleware +func (sh *strictHandler) UpdateDisplayMethod(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, id Id) { + var request UpdateDisplayMethodRequestObject + + request.Identifier = identifier + request.Id = id + + var body UpdateDisplayMethodJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.UpdateDisplayMethod(ctx, request.(UpdateDisplayMethodRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UpdateDisplayMethod") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(UpdateDisplayMethodResponseObject); ok { + if err := validResponse.VisitUpdateDisplayMethodResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // CreatePaymentRequest operation middleware func (sh *strictHandler) CreatePaymentRequest(w http.ResponseWriter, r *http.Request, identifier PathIdentifier) { var request CreatePaymentRequestRequestObject diff --git a/internal/api/main_test.go b/internal/api/main_test.go index 45962ca9a..f3d220023 100644 --- a/internal/api/main_test.go +++ b/internal/api/main_test.go @@ -245,7 +245,7 @@ type servicex struct { identity ports.IdentityService schema ports.SchemaService links ports.LinkService - payments ports.PaymentService + payments ports.PaymentService qrs ports.QrStoreService displayMethod ports.DisplayMethodService } @@ -372,7 +372,7 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer { credentials: claimsService, identity: identityService, links: linkService, - payments: paymentService, + payments: paymentService, qrs: qrService, schema: schemaService, displayMethod: displayMethodService, diff --git a/internal/api/server.go b/internal/api/server.go index 1425e5d02..e8d85dc10 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -17,38 +17,38 @@ import ( // Server implements StrictServerInterface and holds the implementation of all API controllers // This is the glue to the API autogenerated code type Server struct { - cfg *config.Configuration - accountService ports.AccountService - claimService ports.ClaimService - connectionsService ports.ConnectionService - health *health.Status - identityService ports.IdentityService - linkService ports.LinkService - networkResolver network.Resolver - packageManager *iden3comm.PackageManager - publisherGateway ports.Publisher - qrService ports.QrStoreService - schemaService ports.SchemaService - paymentService ports.PaymentService + cfg *config.Configuration + accountService ports.AccountService + claimService ports.ClaimService + connectionsService ports.ConnectionService + health *health.Status + identityService ports.IdentityService + linkService ports.LinkService + networkResolver network.Resolver + packageManager *iden3comm.PackageManager + publisherGateway ports.Publisher + qrService ports.QrStoreService + schemaService ports.SchemaService + paymentService ports.PaymentService displayMethodService ports.DisplayMethodService } // NewServer is a Server constructor func NewServer(cfg *config.Configuration, identityService ports.IdentityService, accountService ports.AccountService, connectionsService ports.ConnectionService, claimsService ports.ClaimService, qrService ports.QrStoreService, publisherGateway ports.Publisher, packageManager *iden3comm.PackageManager, networkResolver network.Resolver, health *health.Status, schemaService ports.SchemaService, linkService ports.LinkService, paymentService ports.PaymentService, displayMethodService ports.DisplayMethodService) *Server { return &Server{ - cfg: cfg, - accountService: accountService, - claimService: claimsService, - connectionsService: connectionsService, - health: health, - identityService: identityService, - linkService: linkService, - networkResolver: networkResolver, - publisherGateway: publisherGateway, - packageManager: packageManager, - qrService: qrService, - schemaService: schemaService, - paymentService: paymentService, + cfg: cfg, + accountService: accountService, + claimService: claimsService, + connectionsService: connectionsService, + health: health, + identityService: identityService, + linkService: linkService, + networkResolver: networkResolver, + publisherGateway: publisherGateway, + packageManager: packageManager, + qrService: qrService, + schemaService: schemaService, + paymentService: paymentService, displayMethodService: displayMethodService, } } From 7f2c4b67c2c5c1a329544ec7d850930cfccf148f Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 18 Dec 2024 12:53:21 +0100 Subject: [PATCH 62/66] fix: configmap --- internal/api/payment_test.go | 2 +- k8s/helm/templates/issuer-node-api-configmap.yaml | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index ee24a2cbd..ab3c8198f 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -659,7 +659,7 @@ func TestServer_CreatePaymentRequest(t *testing.T) { assert.NotEqual(t, uuid.Nil, response.Id) assert.Equal(t, tc.expected.resp.IssuerDID, response.IssuerDID) assert.Equal(t, tc.expected.resp.RecipientDID, response.RecipientDID) - assert.InDelta(t, time.Now().UnixMilli(), response.CreatedAt.UnixMilli(), 10) + assert.InDelta(t, time.Now().UnixMilli(), response.CreatedAt.UnixMilli(), 100) /* assert.Equal(t, len(tc.expected.resp.Payments), len(response.Payments)) for i := range tc.expected.resp.Payments { diff --git a/k8s/helm/templates/issuer-node-api-configmap.yaml b/k8s/helm/templates/issuer-node-api-configmap.yaml index 2b365f848..085a4411a 100644 --- a/k8s/helm/templates/issuer-node-api-configmap.yaml +++ b/k8s/helm/templates/issuer-node-api-configmap.yaml @@ -32,7 +32,4 @@ data: ISSUER_CREDENTIAL_STATUS_PUBLISHING_KEY_PATH : {{ .Values.apiIssuerNode.configMap.issuerCredentialStatusPublishingKeyPath | quote }} ISSUER_RESOLVER_FILE : {{ .Values.issuerResolverFile | quote }} ISSUER_KMS_PROVIDER_LOCAL_STORAGE_FILE_PATH: {{ .Values.apiIssuerNode.configMap.issuerKMSProviderLocalStorageFilePath | quote }} - ISSUER_PAYMENTS_SETTINGS_FILE: {{ .Values.apiIssuerNode.configMap.issuerPaymentsSettingsFile | quote }} - - - ISSUER_PAYMENTS_SETTINGS_FILE \ No newline at end of file + ISSUER_PAYMENTS_SETTINGS_FILE: {{ .Values.apiIssuerNode.configMap.issuerPaymentsSettingsFile | quote }} \ No newline at end of file From f5bb512f9100a1bd3d6a27d0533b9b1999b0382e Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 18 Dec 2024 13:05:28 +0100 Subject: [PATCH 63/66] chore: payment configuration for devel --- api/spec.html | 2 +- k8s/helm/values.yaml | 2 +- k8s/testing/values.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/spec.html b/api/spec.html index b679b4c4c..f653c5747 100644 --- a/api/spec.html +++ b/api/spec.html @@ -1,7 +1,7 @@ - Privado ID - Issuer Node + Privado ID - Issuer Node API diff --git a/k8s/helm/values.yaml b/k8s/helm/values.yaml index a2b15b32f..e74966ded 100644 --- a/k8s/helm/values.yaml +++ b/k8s/helm/values.yaml @@ -68,7 +68,7 @@ apiIssuerNode: issuerCredentialStatusPublishingKeyPath: pbkey issuerIpfsGatewayUrl: https://gateway.pinata.cloud issuerKMSProviderLocalStorageFilePath: /localstoragekeys - issuerPaymentsSettingsFile: "ODAwMDI6CiAgUGF5bWVudFJhaWxzOiAweEY4RTQ5YjkyMkQ1RmIwMGQzRWREMTJiZDE0MDY0ZjI3NTcyNkQzMzkKICBQYXltZW50T3B0aW9uczoKICAgIC0gSUQ6IDEKICAgICAgTmFtZTogQW1veU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogMgogICAgICBOYW1lOiBBbW95IFVTRFQKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4MkZFNDA3NDk4MTJGQUMzOWEwRjM4MDY0OWVGNTlFMDFiY2NmM2ExQQogICAgICBGZWF0dXJlczogW10KICAgIC0gSUQ6IDMKICAgICAgTmFtZTogQW1veSBVU0RDCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDJGRTQwNzQ5ODEyRkFDMzlhMEYzODA2NDllRjU5RTAxYmNjZjNhMUEKICAgICAgRmVhdHVyZXM6CiAgICAgICAgLSBFSVAtMjYxMgo1OTE0MToKICBQYXltZW50UmFpbHM6IDB4NDBFM0VGMjIxQUE5M0Y2RmU5OTdjOWIwMzkzMzIyODIzQmIyMDdkMwogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNAogICAgICBOYW1lOiBMaW5lYVNlcG9saWFOYXRpdmUKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNSZXF1ZXN0VjEKICAgIC0gSUQ6IDUKICAgICAgTmFtZTogTGluZWEgU2Vwb2xpYSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweGIwMTAxYzFGZmRkMTIxM0I4ODZGZWJlRjZGMDc0NDJlNDg5OTBjOUMKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA2CiAgICAgIE5hbWU6IExpbmVhIFNlcG9saWEgVVNEQwogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc0VSQzIwUmVxdWVzdFYxCiAgICAgIENvbnRyYWN0QWRkcmVzczogMHhiMDEwMWMxRmZkZDEyMTNCODg2RmViZUY2RjA3NDQyZTQ4OTkwYzlDCiAgICAgIEZlYXR1cmVzOgogICAgICAgIC0gRUlQLTI2MTIKMjQ0MjoKICBQYXltZW50UmFpbHM6IDB4MDljMjY5ZTc0ZDhCNDdjOTg1MzdBY2Q2Q2JFZTgwNTY4MDZGNGM3MAogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNwogICAgICBOYW1lOiBaa0V2bU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogOAogICAgICBOYW1lOiBaa0V2bSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ3VycmVuY3k6IFVTRFQKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDk4NmNhRTZBRGNGNWRhMmExNTE0YWZjNzMxN0ZCZGVFMEI0MDQ4RGIKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA5CiAgICAgIE5hbWU6IFprRXZtIFVTREMKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4OTg2Y2FFNkFEY0Y1ZGEyYTE1MTRhZmM3MzE3RkJkZUUwQjQwNDhEYgogICAgICBGZWF0dXJlczoKICAgICAgICAtIEVJUC0yNjEyCg==" + issuerPaymentsSettingsFile: "ODAwMDI6CiAgUGF5bWVudFJhaWxzOiAweEY4RTQ5YjkyMkQ1RmIwMGQzRWREMTJiZDE0MDY0ZjI3NTcyNkQzMzkKICBQYXltZW50T3B0aW9uczoKICAgIC0gSUQ6IDEKICAgICAgTmFtZTogQW1veU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogMgogICAgICBOYW1lOiBBbW95IFVTRFQKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4NzFkY2M4RGM1RWIxMzgwMDNkMzU3MTI1NTQ1OEJjNTY5MmE2MGVENAogICAgICBGZWF0dXJlczogW10KICAgIC0gSUQ6IDMKICAgICAgTmFtZTogQW1veSBVU0RDCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDcxZGNjOERjNUViMTM4MDAzZDM1NzEyNTU0NThCYzU2OTJhNjBlRDQKICAgICAgRmVhdHVyZXM6CiAgICAgICAgLSBFSVAtMjYxMgo1OTE0MToKICBQYXltZW50UmFpbHM6IDB4NDBFM0VGMjIxQUE5M0Y2RmU5OTdjOWIwMzkzMzIyODIzQmIyMDdkMwogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNAogICAgICBOYW1lOiBMaW5lYVNlcG9saWFOYXRpdmUKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNSZXF1ZXN0VjEKICAgIC0gSUQ6IDUKICAgICAgTmFtZTogTGluZWEgU2Vwb2xpYSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweGIwMTAxYzFGZmRkMTIxM0I4ODZGZWJlRjZGMDc0NDJlNDg5OTBjOUMKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA2CiAgICAgIE5hbWU6IExpbmVhIFNlcG9saWEgVVNEQwogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc0VSQzIwUmVxdWVzdFYxCiAgICAgIENvbnRyYWN0QWRkcmVzczogMHhiMDEwMWMxRmZkZDEyMTNCODg2RmViZUY2RjA3NDQyZTQ4OTkwYzlDCiAgICAgIEZlYXR1cmVzOgogICAgICAgIC0gRUlQLTI2MTIKMjQ0MjoKICBQYXltZW50UmFpbHM6IDB4MDljMjY5ZTc0ZDhCNDdjOTg1MzdBY2Q2Q2JFZTgwNTY4MDZGNGM3MAogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNwogICAgICBOYW1lOiBaa0V2bU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogOAogICAgICBOYW1lOiBaa0V2bSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ3VycmVuY3k6IFVTRFQKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDk4NmNhRTZBRGNGNWRhMmExNTE0YWZjNzMxN0ZCZGVFMEI0MDQ4RGIKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA5CiAgICAgIE5hbWU6IFprRXZtIFVTREMKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4OTg2Y2FFNkFEY0Y1ZGEyYTE1MTRhZmM3MzE3RkJkZUUwQjQwNDhEYgogICAgICBGZWF0dXJlczoKICAgICAgICAtIEVJUC0yNjEy" notificationsIssuerNode: deployment: diff --git a/k8s/testing/values.yaml b/k8s/testing/values.yaml index 5f25985d0..6bc541ff3 100644 --- a/k8s/testing/values.yaml +++ b/k8s/testing/values.yaml @@ -68,7 +68,7 @@ apiIssuerNode: issuerVaultUserpassAuthEnabled: "true" issuerCredentialStatusPublishingKeyPath: pbkey issuerIpfsGatewayUrl: https://gateway.pinata.cloud - issuerPaymentsSettingsFile: "ODAwMDI6CiAgUGF5bWVudFJhaWxzOiAweEY4RTQ5YjkyMkQ1RmIwMGQzRWREMTJiZDE0MDY0ZjI3NTcyNkQzMzkKICBQYXltZW50T3B0aW9uczoKICAgIC0gSUQ6IDEKICAgICAgTmFtZTogQW1veU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogMgogICAgICBOYW1lOiBBbW95IFVTRFQKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4MkZFNDA3NDk4MTJGQUMzOWEwRjM4MDY0OWVGNTlFMDFiY2NmM2ExQQogICAgICBGZWF0dXJlczogW10KICAgIC0gSUQ6IDMKICAgICAgTmFtZTogQW1veSBVU0RDCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDJGRTQwNzQ5ODEyRkFDMzlhMEYzODA2NDllRjU5RTAxYmNjZjNhMUEKICAgICAgRmVhdHVyZXM6CiAgICAgICAgLSBFSVAtMjYxMgo1OTE0MToKICBQYXltZW50UmFpbHM6IDB4NDBFM0VGMjIxQUE5M0Y2RmU5OTdjOWIwMzkzMzIyODIzQmIyMDdkMwogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNAogICAgICBOYW1lOiBMaW5lYVNlcG9saWFOYXRpdmUKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNSZXF1ZXN0VjEKICAgIC0gSUQ6IDUKICAgICAgTmFtZTogTGluZWEgU2Vwb2xpYSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweGIwMTAxYzFGZmRkMTIxM0I4ODZGZWJlRjZGMDc0NDJlNDg5OTBjOUMKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA2CiAgICAgIE5hbWU6IExpbmVhIFNlcG9saWEgVVNEQwogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc0VSQzIwUmVxdWVzdFYxCiAgICAgIENvbnRyYWN0QWRkcmVzczogMHhiMDEwMWMxRmZkZDEyMTNCODg2RmViZUY2RjA3NDQyZTQ4OTkwYzlDCiAgICAgIEZlYXR1cmVzOgogICAgICAgIC0gRUlQLTI2MTIKMjQ0MjoKICBQYXltZW50UmFpbHM6IDB4MDljMjY5ZTc0ZDhCNDdjOTg1MzdBY2Q2Q2JFZTgwNTY4MDZGNGM3MAogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNwogICAgICBOYW1lOiBaa0V2bU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogOAogICAgICBOYW1lOiBaa0V2bSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ3VycmVuY3k6IFVTRFQKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDk4NmNhRTZBRGNGNWRhMmExNTE0YWZjNzMxN0ZCZGVFMEI0MDQ4RGIKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA5CiAgICAgIE5hbWU6IFprRXZtIFVTREMKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4OTg2Y2FFNkFEY0Y1ZGEyYTE1MTRhZmM3MzE3RkJkZUUwQjQwNDhEYgogICAgICBGZWF0dXJlczoKICAgICAgICAtIEVJUC0yNjEyCg==" + issuerPaymentsSettingsFile: "ODAwMDI6CiAgUGF5bWVudFJhaWxzOiAweEY4RTQ5YjkyMkQ1RmIwMGQzRWREMTJiZDE0MDY0ZjI3NTcyNkQzMzkKICBQYXltZW50T3B0aW9uczoKICAgIC0gSUQ6IDEKICAgICAgTmFtZTogQW1veU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogMgogICAgICBOYW1lOiBBbW95IFVTRFQKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4NzFkY2M4RGM1RWIxMzgwMDNkMzU3MTI1NTQ1OEJjNTY5MmE2MGVENAogICAgICBGZWF0dXJlczogW10KICAgIC0gSUQ6IDMKICAgICAgTmFtZTogQW1veSBVU0RDCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDcxZGNjOERjNUViMTM4MDAzZDM1NzEyNTU0NThCYzU2OTJhNjBlRDQKICAgICAgRmVhdHVyZXM6CiAgICAgICAgLSBFSVAtMjYxMgo1OTE0MToKICBQYXltZW50UmFpbHM6IDB4NDBFM0VGMjIxQUE5M0Y2RmU5OTdjOWIwMzkzMzIyODIzQmIyMDdkMwogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNAogICAgICBOYW1lOiBMaW5lYVNlcG9saWFOYXRpdmUKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNSZXF1ZXN0VjEKICAgIC0gSUQ6IDUKICAgICAgTmFtZTogTGluZWEgU2Vwb2xpYSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweGIwMTAxYzFGZmRkMTIxM0I4ODZGZWJlRjZGMDc0NDJlNDg5OTBjOUMKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA2CiAgICAgIE5hbWU6IExpbmVhIFNlcG9saWEgVVNEQwogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc0VSQzIwUmVxdWVzdFYxCiAgICAgIENvbnRyYWN0QWRkcmVzczogMHhiMDEwMWMxRmZkZDEyMTNCODg2RmViZUY2RjA3NDQyZTQ4OTkwYzlDCiAgICAgIEZlYXR1cmVzOgogICAgICAgIC0gRUlQLTI2MTIKMjQ0MjoKICBQYXltZW50UmFpbHM6IDB4MDljMjY5ZTc0ZDhCNDdjOTg1MzdBY2Q2Q2JFZTgwNTY4MDZGNGM3MAogIFBheW1lbnRPcHRpb25zOgogICAgLSBJRDogNwogICAgICBOYW1lOiBaa0V2bU5hdGl2ZQogICAgICBUeXBlOiBJZGVuM1BheW1lbnRSYWlsc1JlcXVlc3RWMQogICAgLSBJRDogOAogICAgICBOYW1lOiBaa0V2bSBVU0RUCiAgICAgIFR5cGU6IElkZW4zUGF5bWVudFJhaWxzRVJDMjBSZXF1ZXN0VjEKICAgICAgQ3VycmVuY3k6IFVTRFQKICAgICAgQ29udHJhY3RBZGRyZXNzOiAweDk4NmNhRTZBRGNGNWRhMmExNTE0YWZjNzMxN0ZCZGVFMEI0MDQ4RGIKICAgICAgRmVhdHVyZXM6IFtdCiAgICAtIElEOiA5CiAgICAgIE5hbWU6IFprRXZtIFVTREMKICAgICAgVHlwZTogSWRlbjNQYXltZW50UmFpbHNFUkMyMFJlcXVlc3RWMQogICAgICBDb250cmFjdEFkZHJlc3M6IDB4OTg2Y2FFNkFEY0Y1ZGEyYTE1MTRhZmM3MzE3RkJkZUUwQjQwNDhEYgogICAgICBGZWF0dXJlczoKICAgICAgICAtIEVJUC0yNjEy" notificationsIssuerNode: deployment: From 97ea05d5fe433dd57aece56152474a1abe192999 Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 18 Dec 2024 16:03:27 +0100 Subject: [PATCH 64/66] feat: Return payments configuration --- api/api.yaml | 13 ++++++++++--- internal/api/api.gen.go | 12 ++++++++---- internal/api/payment.go | 4 +--- internal/payments/settings.go | 2 +- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 9d7c77bc6..701a1b1c4 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1503,9 +1503,9 @@ paths: /v2/payment/settings: get: - summary: Return payment settings + summary: Payments Configuration operationId: GetPaymentSettings - description: Get the payment settings + description: Returns the server payments configuration tags: - Payment security: @@ -1516,7 +1516,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PaymentOptionConfig' + $ref: '#/components/schemas/PaymentsConfiguration' /v2/identities/{identifier}/payment/options: get: @@ -2391,6 +2391,13 @@ components: type: string enum: [ success, failed, pending, canceled ] + PaymentsConfiguration: + type: object + x-go-type: payments.Config + x-go-type-import: + name: payments + path: github.com/polygonid/sh-id-platform/internal/payments + PaymentOption: type: object required: diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 016b2c973..3df79c674 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -18,6 +18,7 @@ import ( "github.com/oapi-codegen/runtime" strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" openapi_types "github.com/oapi-codegen/runtime/types" + payments "github.com/polygonid/sh-id-platform/internal/payments" timeapi "github.com/polygonid/sh-id-platform/internal/timeapi" ) @@ -567,6 +568,9 @@ type PaymentVerifyRequest struct { TxHash string `json:"txHash"` } +// PaymentsConfiguration defines model for PaymentsConfiguration. +type PaymentsConfiguration = payments.Config + // PublishIdentityStateResponse defines model for PublishIdentityStateResponse. type PublishIdentityStateResponse struct { ClaimsTreeRoot *string `json:"claimsTreeRoot,omitempty"` @@ -1084,7 +1088,7 @@ type ServerInterface interface { // Get Identity State Transactions // (GET /v2/identities/{identifier}/state/transactions) GetStateTransactions(w http.ResponseWriter, r *http.Request, identifier PathIdentifier, params GetStateTransactionsParams) - // Return payment settings + // Payments Configuration // (GET /v2/payment/settings) GetPaymentSettings(w http.ResponseWriter, r *http.Request) // Get QrCode from store @@ -1390,7 +1394,7 @@ func (_ Unimplemented) GetStateTransactions(w http.ResponseWriter, r *http.Reque w.WriteHeader(http.StatusNotImplemented) } -// Return payment settings +// Payments Configuration // (GET /v2/payment/settings) func (_ Unimplemented) GetPaymentSettings(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotImplemented) @@ -5661,7 +5665,7 @@ type GetPaymentSettingsResponseObject interface { VisitGetPaymentSettingsResponse(w http.ResponseWriter) error } -type GetPaymentSettings200JSONResponse PaymentOptionConfig +type GetPaymentSettings200JSONResponse PaymentsConfiguration func (response GetPaymentSettings200JSONResponse) VisitGetPaymentSettingsResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") @@ -5957,7 +5961,7 @@ type StrictServerInterface interface { // Get Identity State Transactions // (GET /v2/identities/{identifier}/state/transactions) GetStateTransactions(ctx context.Context, request GetStateTransactionsRequestObject) (GetStateTransactionsResponseObject, error) - // Return payment settings + // Payments Configuration // (GET /v2/payment/settings) GetPaymentSettings(ctx context.Context, request GetPaymentSettingsRequestObject) (GetPaymentSettingsResponseObject, error) // Get QrCode from store diff --git a/internal/api/payment.go b/internal/api/payment.go index 6dcbe4279..848d89c1e 100644 --- a/internal/api/payment.go +++ b/internal/api/payment.go @@ -141,9 +141,7 @@ func (s *Server) CreatePaymentRequest(ctx context.Context, request CreatePayment // GetPaymentSettings is the controller to get payment settings func (s *Server) GetPaymentSettings(_ context.Context, _ GetPaymentSettingsRequestObject) (GetPaymentSettingsResponseObject, error) { - // TODO: Implement - // return GetPaymentSettings200JSONResponse(s.paymentService.GetSettings()), nil - return GetPaymentSettings200JSONResponse{}, nil + return GetPaymentSettings200JSONResponse(s.paymentService.GetSettings()), nil } // VerifyPayment is the controller to verify payment diff --git a/internal/payments/settings.go b/internal/payments/settings.go index b1bb7ba5c..ab7dfd037 100644 --- a/internal/payments/settings.go +++ b/internal/payments/settings.go @@ -36,7 +36,7 @@ type PaymentOptionConfig struct { Name string Type protocol.PaymentRequestType ContractAddress common.Address - Features []protocol.PaymentFeatures + Features []protocol.PaymentFeatures `json:"features,omitempty"` } // configDTO is the data transfer object for the configuration. It maps to payment configuration file From 9f63af59baf8bb5225c027a48fd49de4cbeeb68d Mon Sep 17 00:00:00 2001 From: x1m3 Date: Wed, 18 Dec 2024 17:33:02 +0100 Subject: [PATCH 65/66] feat: Rename field PaymentRequestMessage.RecipientDID to userDID --- api/api.yaml | 4 ++-- internal/api/api.gen.go | 2 +- internal/api/payment_test.go | 4 ++-- internal/api/responses.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/api.yaml b/api/api.yaml index 701a1b1c4..f9982d824 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -2496,7 +2496,7 @@ components: required: - id - issuerDID - - recipientDID + - userDID - paymentOptionID - payments - createdAt @@ -2506,7 +2506,7 @@ components: format: uuid issuerDID: type: string - recipientDID: + userDID: type: string paymentOptionID: type: string diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 3df79c674..3d613184b 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -317,7 +317,7 @@ type CreatePaymentRequestResponse struct { IssuerDID string `json:"issuerDID"` PaymentOptionID openapi_types.UUID `json:"paymentOptionID"` Payments []PaymentRequestInfo `json:"payments"` - RecipientDID string `json:"recipientDID"` + UserDID string `json:"userDID"` } // Credential defines model for Credential. diff --git a/internal/api/payment_test.go b/internal/api/payment_test.go index ab3c8198f..d5315a5fc 100644 --- a/internal/api/payment_test.go +++ b/internal/api/payment_test.go @@ -635,7 +635,7 @@ func TestServer_CreatePaymentRequest(t *testing.T) { Data: protocol.PaymentRequestInfoData{}, }, }, - RecipientDID: receiverDID.String(), + UserDID: receiverDID.String(), }, }, }, @@ -658,7 +658,7 @@ func TestServer_CreatePaymentRequest(t *testing.T) { require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response)) assert.NotEqual(t, uuid.Nil, response.Id) assert.Equal(t, tc.expected.resp.IssuerDID, response.IssuerDID) - assert.Equal(t, tc.expected.resp.RecipientDID, response.RecipientDID) + assert.Equal(t, tc.expected.resp.UserDID, response.UserDID) assert.InDelta(t, time.Now().UnixMilli(), response.CreatedAt.UnixMilli(), 100) /* assert.Equal(t, len(tc.expected.resp.Payments), len(response.Payments)) diff --git a/internal/api/responses.go b/internal/api/responses.go index 5d5d89010..c5dcb63de 100644 --- a/internal/api/responses.go +++ b/internal/api/responses.go @@ -357,7 +357,7 @@ func toCreatePaymentRequestResponse(payReq *domain.PaymentRequest) CreatePayment CreatedAt: payReq.CreatedAt, Id: payReq.ID, IssuerDID: payReq.IssuerDID.String(), - RecipientDID: payReq.RecipientDID.String(), + UserDID: payReq.RecipientDID.String(), PaymentOptionID: payReq.PaymentOptionID, Payments: []PaymentRequestInfo{payment}, } From 3062fec6865400b0e059445a7f2e84aa880c50b7 Mon Sep 17 00:00:00 2001 From: martinsaporiti Date: Mon, 23 Dec 2024 15:09:21 -0300 Subject: [PATCH 66/66] chore: update migration name for payments --- ...00_payment_options.sql => 202412231508172_payment_options.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/db/schema/migrations/{202411131724000_payment_options.sql => 202412231508172_payment_options.sql} (100%) diff --git a/internal/db/schema/migrations/202411131724000_payment_options.sql b/internal/db/schema/migrations/202412231508172_payment_options.sql similarity index 100% rename from internal/db/schema/migrations/202411131724000_payment_options.sql rename to internal/db/schema/migrations/202412231508172_payment_options.sql