TRP Bridge
The TRP Bridge implements http.Handler objects for each TRP request type and which can be passed to a basic http.Server and easily built into your TRISA node. A minimal example that simply logs incoming requests and return no error is below:
package main
import (
"log"
"net/http"
"github.com/trisacrypto/trisa/pkg/openvasp"
)
// TRPHandler implements both the InquiryHandler and the Confirmation Handler interfaces
type TRPHandler struct{}
// OnInquiry implements the InquiryHandler interface and is used to respond to TRP
// transfer inquiry requests that initiate or conclude the TRP protocol.
func (t *TRPHandler) OnInquiry(in *openvasp.Inquiry) (*openvasp.InquiryResolution, error) {
// TODO: add your Transfer Inquiry code here!
log.Printf(
"received trp inquiry with request identifier %q\n",
in.TRP.RequestIdentifier
)
return nil, nil
}
// OnConfirmation implements the ConfirmationHandler interface and is used to respond to
// TRP callbacks from the beneficiary VASP.
func (t *TRPHandler) OnConfirmation(in *openvasp.Confirmation) error {
// TODO: add your Transfer Confirmation code here!
log.Printf(
"received trp confirmation with request identifier %q\n",
in.TRP.RequestIdentifier
)
return nil
}
func main() {
// Create a new handler object
handler := &TRPHandler{}
// Create a mux to route requests to different paths to different handlers.
mux := http.NewServeMux()
mux.Handle("/transfers", openvasp.TransferInquiry(handler))
mux.Handle("/confirm", openvasp.TransferConfirmation(handler))
log.Printf(
"waiting for TRP requests with API version %s at http://localhost:8080\n",
openvasp.APIVersion
)
// Serve the TRP API server on port 8080. In production applications you would
// likely configure mTLS and TLS termination at the server first.
http.ListenAndServe(":8080", mux)
}Transfer Inquiry
The openvasp.TransferInquiry function accepts an object that implements the openvasp.InquiryHandler interface and returns an http.Handler that wraps the InquiryHandler to handle TRP Transfer Inquiry POST requests. The InquiryHandler is defined as follows:
type InquiryHandler interface {
OnInquiry(*Inquiry) (*InquiryResolution, error)
}The HTTP handler returned performs the following operations when an incoming HTTP POST request is received:
- Validates the incoming TRP request
- Parses the TRP
Inquiryobject along with any extensions. - Calls the handler’s
OnInquirymethod - Returns success or failure based on the returned value of
OnInquiry.
To return a failure condition from the OnInquiry() function, users may return an openvasp.StatusError that specifies the HTTP status code and message to return. This is useful particularly to return 404 errors if the Travel Address is incorrect or no beneficiary account exists at the endpoint. If a generic error is returned, then the handler will return a 500 Internal Server Error along with the err.Error() text.
func (t *TRPHandler) OnInquiry(in *openvasp.Inquiry) (*openvasp.InquiryResolution, error) {
// Lookup Travel Address and return a 404 error if beneficiary is not found.
if notFound {
return nil, &openvasp.StatusError{Code: http.StatusNotFound}
}
}To return a succesful 200 OK response, return an openvasp.InquiryResolution object or simply nil, nil.
The semantics are as follows:
- If the resolution response is
nilor contains just theVersion, then the counterparty expects a subsequentPOSTrequest to the callback in the request. - The inquiry can be automatically approved by returning the
Approvedfield without theVersionor theRejectedfields (these must be zero valued). - The inquiry can be automatically rejected by specifying a
Rejectedreason without theVersionor theApprovedfields (these must be zero valued).
Transfer Confirmation
The openvasp.TransferConfirmation function accepts an object that implements the openvasp.ConfirmationHander interface and returns an http.Handler that wraps the ConfirmationHander to handle TRP Transfer Confirmation POST requests. The ConfirmationHander is defined as follows:
type ConfirmationHandler interface {
OnConfirmation(*Confirmation) error
}When an incoming HTTP POST request is received (e.g. to the callback URL specified in the transfer inquiry), the HTTP handler performs the following operations:
- Validates the incoming TRP request
- Parses the TRP
Confirmationobject - Calls the handler’s
OnConfirmationmethod - Returns a 200 OK response if the error is
nilotherwise returns theStatusErroror a 500 Internal Server Error
Setting up a Server
There are many ways to setup an http.Server in Golang. Feel free to use a the plain vanilla server or a framework like Gin or an advanced muxer like HTTPRouter. The handlers defined in the openvasp package implement the http.HandlerFunc and return an http.Handler object and can be used in most Go frameworks.
The primary consideration is mapping URLs (e.g. multiplexing) and setting up TLS termination and mTLS authentication. TRISA recommends that you terminate TLS and mTLS at a reverse-proxy for effective load balancing. However, by way of example, here is a simple code snippet to demonstrate setting up mTLS in a Go server.
func main() {
// Assuming you have your mTLS certificate and keys stored in cert.pem and key.pem
certs, err := os.ReadFile("cert.pem")
if err != nil {
log.Fatal(err)
}
certPool := x509.NewCertPool()
certPool.AppendCertsFromPem(certs)
server := http.Server{
Addr: ":8080",
Handler: mux,
TLSConfig: &tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
CliantCAs: certPool,
MinVersion: tls.VersionTLS13,
},
},
if er := server.ListenAndServeTLS("cert.pem", "key.pem"); err != nil {
log.Fatal(err)
}
}For more robust mTLS setup, please see the trust package in TRISA.
API Checks
The openvasp.APIChecks middleware validates TRP requests and parses header information from the request. Both the TransferInquiry and the TransferConfirmation handlers implement this middleware.
The checks that are performed include:
- Ensure the HTTP Method is
POSTotherwise a 405 Method Not Allowed error is returned. - Ensure the TRP API Version header is set and the version is compatible with the implemented TRP version otherwise a 400 Bad Request error is returned.
- Ensures that the currently implemented TRP API Version header is set on the outgoing response.
- Checks that there is a request identifier header on the request, otherwise a 400 Bad Request error is returned.
- Ensures that the request identifier is echoed back on the outgoing response.
- Enforces that the
Content-Typeheader is specified and that the content type isapplication/json, otherwise a 415 Unsupported Media Type error is returned.
ParseTRPInfo
The openvasp.ParseTRPInfo function parses TRP-specific headers from the request and adds them to a openvasp.TRPInfo struct that can be used for TRP processing. A mapping of the headers to the parsed fields is as follows:
| Field | Header | Type | Description |
|---|---|---|---|
| Address | string | The Travel Address, LNURL, or URL of the request | |
| APIVersion | api-version | string | Defaults to the APIVersion of the package |
| RequestIdentifier | api-extensions | string | A unique identifier representing the specific transfer (used as the envelope ID) |
| APIExtensions | request-identifier | []string | The comma separated names of any extensions used in the request |