Go Further with Open Payments

Written by Blair Currey

Why Open Payments in Go?

The Open Payments standard is reshaping how applications initiate, manage, and complete digital transactions — enabling truly interoperable financial systems across different wallets, services, and financial institutions.

The Interledger Foundation has been steadily expanding SDK support across languages — Node led the way, followed by PHP and Rust. But one language was notably missing: Go.

According to the 2025 Stack Overflow Developer Survey, Go ranks among the top backend languages, sitting alongside TypeScript and Python in developer adoption. Perhaps more tellingly, the survey notes that “Python developers aspire to use Rust and Go as the path to high-performance systems programming” — reflecting Go’s growing role in cloud infrastructure, microservices, and performance-critical backends.

Today, we’re excited to close that gap with Open Payments Go — an open-source SDK that brings first-class Open Payments support to the Go ecosystem.

What We Built

interledger/open-payments-go is a production-ready Go module that provides complete client support for the Open Payments API.

It includes:

Here’s what it looks like to request a grant and create an incoming payment:

package main
import (
"context"
"log"
"time"
openpayments "github.com/interledger/open-payments-go"
as "github.com/interledger/open-payments-go/generated/authserver"
rs "github.com/interledger/open-payments-go/generated/resourceserver"
)
func main() {
privateKeyBase64 := os.Getenv("BASE64_PRIVATE_KEY")
keyId := os.Getenv("KEY_ID")
// Initialize the authenticated client
client, err := openpayments.NewAuthenticatedClient(
"https://wallet.example.com/alice", // Your wallet address
privateKeyBase64,
keyId,
)
if err != nil {
log.Fatal(err)
}
// Get wallet address info
wallet, err := client.WalletAddress.Get(context.Background(), openpayments.WalletAddressGetParams{
URL: "https://wallet.example.com/alice",
})
if err != nil {
log.Fatal(err)
}
// Build the access request
incomingAccess := as.AccessIncoming{
Type: as.IncomingPayment,
Actions: []as.AccessIncomingActions{
as.AccessIncomingActionsCreate,
as.AccessIncomingActionsRead,
as.AccessIncomingActionsList,
as.AccessIncomingActionsComplete,
},
}
accessItem := as.AccessItem{}
accessItem.FromAccessIncoming(incomingAccess)
// Request a grant
grant, err := client.Grant.Request(context.Background(), openpayments.GrantRequestParams{
URL: *wallet.AuthServer,
RequestBody: as.GrantRequestWithAccessToken{
AccessToken: struct {
Access as.Access `json:"access"`
}{
Access: []as.AccessItem{accessItem},
},
},
})
if err != nil {
log.Fatal(err)
}
// Create an incoming payment
expiresAt := time.Now().Add(24 * time.Hour)
payment, err := client.IncomingPayment.Create(context.Background(), openpayments.IncomingPaymentCreateParams{
BaseURL: *wallet.ResourceServer,
AccessToken: grant.AccessToken.Value,
Payload: rs.CreateIncomingPaymentJSONBody{
WalletAddressSchema: *wallet.Id,
IncomingAmount: &rs.Amount{
Value: "1000",
AssetCode: wallet.AssetCode,
AssetScale: wallet.AssetScale,
},
ExpiresAt: &expiresAt,
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("Created incoming payment: %s", *payment.Id)
}

With Go’s strong typing and the SDK’s clean API design, developers get compile-time safety and IDE autocompletion for the entire Open Payments workflow.

How It Works: Inside the Library

Open Payments Go is designed around Go idioms and best practices, making it feel natural for Go developers while handling the complexity of the Open Payments protocol.

Project Structure

PackagePurpose
openpayments (root)Main client types, service implementations, and public API
generated/authserverTypes generated from the Auth Server OpenAPI spec
generated/resourceserverTypes generated from the Resource Server OpenAPI spec
generated/walletaddressserverTypes generated from the Wallet Address Server OpenAPI spec
httpsignatureutilsHTTP signature creation and validation utilities
test/integrationIntegration tests against Rafiki local and testnet environments

Service-Oriented Architecture

Each Open Payments resource has a dedicated service with methods that map directly to API operations:

Type-Safe Generated Types with OpenAPI Overlays

One challenge we faced was that the upstream OpenAPI specifications didn’t always produce ideal Go types when run through code generators. For example, request bodies for grants and outgoing payments used oneOf unions that resulted in awkward generated type names like PostRequestJSONBody.

To solve this, we leveraged OpenAPI Overlays — a powerful technique for modifying OpenAPI specs without forking them. Our overlay files (authserver.overlay.yaml, resourceserver.overlay.yaml) add explicit type names and restructure unions for better Go ergonomics:

resourceserver.overlay.yaml
actions:
- target: $.components.schemas
update:
CreateOutgoingPaymentWithQuote:
type: object
required: [walletAddress, quoteId]
properties:
walletAddress:
$ref: '#/components/schemas/walletAddress'
quoteId:
type: string
metadata:
type: object
additionalProperties: true
CreateOutgoingPaymentWithAmount:
type: object
required: [walletAddress, incomingPayment, debitAmount]
# ...

Instead of wrestling with anonymous types, you get clear, self-documenting structs:

// Without overlays: confusing generated names
var payload PostRequestJSONBody // What is this?
// With overlays: intent is obvious
var payload rs.CreateOutgoingPaymentWithQuote
payload.WalletAddressSchema = walletAddress
payload.QuoteId = quoteId

This approach keeps us in sync with upstream specs while producing clean, well-named Go types.

HTTP Signatures Made Simple

Open Payments requires HTTP Message Signatures for authenticated requests. The SDK handles this complexity internally through the httpsignatureutils package:

The SDK automatically signs requests when using AuthenticatedClient. Internally, it:

  1. Computes Content-Digest for request bodies
  2. Builds the signature base string per RFC 9421
  3. Signs with your Ed25519 private key
  4. Sets Signature and Signature-Input headers

Why It Matters for the Go Community

Go powers a significant portion of cloud infrastructure, payment systems, and fintech backends. By bringing native Open Payments support to Go, we enable:

Getting Started

Install the SDK:

Terminal window
go get github.com/interledger/open-payments-go

The SDK requires Go 1.21 or later. For development, you’ll also need to initialize the OpenAPI spec submodule:

Terminal window
git submodule update --init

To regenerate types from the specs (if you’re contributing or customizing):

Terminal window
go generate ./generated

What’s Next

We’re actively developing Open Payments Go and have exciting plans ahead:

Resources


We’re excited to bring Open Payments to the Go ecosystem and can’t wait to see what the community builds.

Give it a try, and let us know what you think! 🚀


As we are open source, you can easily check our work on GitHub. If the work mentioned here inspired you, we welcome your contributions. You can join our community slack or participate in the next community call, which takes place each second Wednesday of the month.

If you want to stay updated with all open opportunities and news from the Interledger Foundation, you can subscribe to our newsletter.