paymail

package module
v0.10.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 12, 2024 License: MIT Imports: 19 Imported by: 5

README

go-paymail

Paymail client & server library for Golang

Release Build Status Report codecov Go
Mergify Status Gitpod Ready-to-Code


Table of Contents


Installation

go-paymail requires a supported release of Go.

go get -u github.com/tonicpow/go-paymail

Documentation

View the generated documentation

GoDoc

Features
Package Dependencies

Client Packages:

Server Packages:

Library Deployment

goreleaser for easy binary or library deployment to GitHub and can be installed via: brew install goreleaser.

The .goreleaser.yml file is used to configure goreleaser.

Use make release-snap to create a snapshot version of the release, and finally make release to ship to production.

Makefile Commands

View all makefile commands

make help

List of all current commands:

all                   Runs multiple commands
clean                 Remove previous builds and any test cache data
clean-mods            Remove all the Go mod cache
coverage              Shows the test coverage
diff                  Show the git diff
generate              Runs the go generate command in the base of the repo
godocs                Sync the latest tag with GoDocs
help                  Show this help message
install               Install the application
install-go            Install the application (Using Native Go)
install-releaser      Install the GoReleaser application
lint                  Run the golangci-lint application (install if not found)
release               Full production release (creates release in GitHub)
release               Runs common.release then runs godocs
release-snap          Test the full release (build binaries)
release-test          Full production test release (everything except deploy)
replace-version       Replaces the version in HTML/JS (pre-deploy)
tag                   Generate a new tag and push (tag version=0.0.0)
tag-remove            Remove a tag if found (tag-remove version=0.0.0)
tag-update            Update an existing tag to current commit (tag-update version=0.0.0)
test                  Runs lint and ALL tests
test-ci               Runs all tests via CI (exports coverage)
test-ci-no-race       Runs all tests via CI (no race) (exports coverage)
test-ci-short         Runs unit tests via CI (exports coverage)
test-no-lint          Runs just tests
test-short            Runs vet, lint and tests (excludes integration tests)
test-unit             Runs tests and outputs coverage
uninstall             Uninstall the application (and remove files)
update-linter         Update the golangci-lint package (macOS only)
vet                   Run the Go vet application

Examples & Tests

All unit tests and examples run via GitHub Actions and uses Go version 1.19.x. View the configuration file.

Run all tests (including integration tests)

make test

Run tests (excluding integration tests)

make test-short

Benchmarks

Run the Go benchmarks:

make bench

Code Standards

Read more about this Go project's code standards.


Usage

Checkout all the client examples or server examples!


Maintainers

MrZ
MrZ

Contributing

View the contributing guidelines and follow the code of conduct.

How can I help?

All kinds of contributions are welcome 🙌! The most basic way to show your support is to star 🌟 the project, or to raise issues 💬. You can also support this project by becoming a sponsor on GitHub 👏 or by making a bitcoin donation to ensure this journey continues indefinitely! 🚀

Stars


License

License

Documentation

Overview

Package paymail is Golang library for implementing Paymail as a client or server following the bsvalias specifications: http://bsvalias.org/

If you have any suggestions or comments, please feel free to open an issue on this GitHub repository!

By TonicPow Inc (https://tonicpow.com)

Index

Examples

Constants

View Source
const (
	BRFCBasicAddressResolution         = "759684b1a19a"       // more info: http://bsvalias.org/04-01-basic-address-resolution.html
	BRFCP2PPaymentDestination          = "2a40af698840"       // more info: https://docs.moneybutton.com/docs/paymail/paymail-07-p2p-payment-destination.html
	BRFCP2PPaymentDestinationWithToken = "f792b6eff07a"       // more info: https://docs.moneybutton.com/docs/paymail/paymail-11-p2p-payment-destination-tokens.html
	BRFCP2PTransactions                = "5f1323cddf31"       // more info: https://docs.moneybutton.com/docs/paymail/paymail-06-p2p-transactions.html
	BRFCPaymentDestination             = "paymentDestination" // more info: http://bsvalias.org/04-01-basic-address-resolution.html
	BRFCPayToProtocolPrefix            = "7bd25e5a1fc6"       // more info: http://bsvalias.org/04-04-payto-protocol-prefix.html
	BRFCPki                            = "pki"                // more info: http://bsvalias.org/03-public-key-infrastructure.html
	BRFCPkiAlternate                   = "0c4339ef99c2"       // more info: http://bsvalias.org/03-public-key-infrastructure.html
	BRFCPublicProfile                  = "f12f968c92d6"       // more info: https://github.com/bitcoin-sv-specs/brfc-paymail/pull/7/files
	BRFCReceiverApprovals              = "3d7c2ca83a46"       // more info: http://bsvalias.org/04-03-receiver-approvals.html
	BRFCSenderValidation               = "6745385c3fc0"       // more info: http://bsvalias.org/04-02-sender-validation.html
	BRFCSFPAssetInformation            = "1300361cb2d4"       // more info: https://docs.moneybutton.com/docs/paymail/paymail-08-asset-information.html
	BRFCSFPAuthoriseAction             = "95dddb461bff"       // more info: https://docs.moneybutton.com/docs/sfp/paymail-10-sfp-authorise.html
	BRFCSFPBuildAction                 = "189e32d93d28"       // more info: https://docs.moneybutton.com/docs/sfp/paymail-09-sfp-build.html
	BRFCVerifyPublicKeyOwner           = "a9f510c16bde"       // more info: http://bsvalias.org/05-verify-public-key-owner.html
)

All BRFC IDs that have been used/referenced in the library

View Source
const (
	DefaultBsvAliasVersion = "1.0"      // Default version number for bsvalias
	DefaultPort            = 443        // Default port (from specs)
	DefaultPriority        = 10         // Default priority (from specs)
	DefaultProtocol        = "tcp"      // Default protocol (from specs)
	DefaultServiceName     = "bsvalias" // Default service name (from specs)
	DefaultWeight          = 10         // Default weight (from specs)
	PubKeyLength           = 66         // Required length for a valid PubKey (pki)
)

Public defaults for paymail specs

http://bsvalias.org/02-01-host-discovery.html

Service	  bsvalias
Proto	  tcp
Name	  <domain>.<tld>.
TTL	      3600 (see notes)
Class	  IN
Priority  10
Weight	  10
Port	  443
Target	  <endpoint-discovery-host>

Max SRV Records:  1
View Source
const BRFCKnownSpecifications = `` /* 5092-byte string literal not displayed */

BRFCKnownSpecifications is a running list of all known BRFC specifications

JSON file was converted into a go:var for binary shipment Add your spec at runtime using LoadBRFCs() Add your spec to the list below: https://github.com/tonicpow/go-paymail/issues/new/choose

Variables

This section is empty.

Functions

func ConvertHandle

func ConvertHandle(handle string, isBeta bool) string

ConvertHandle will convert a $handle or 1handle to a paymail address

For HandCash: $handle = handle@handcash.io or handle@beta.handcash.io For RelayX: 1handle = handle@relayx.io

Example

ExampleConvertHandle example using the method ConvertHandle()

See more examples in /examples/

paymail := ConvertHandle("$mr-z", false)
fmt.Println(paymail)
Output:

mr-z@handcash.io

func SanitizePaymail

func SanitizePaymail(paymailAddress string) (alias, domain, address string)

SanitizePaymail will take an input and return the sanitized version (alias@domain.tld)

Alias is the first part of the address (alias @) Domain is the lowercase sanitized version (domain.tld) Address is the full sanitized paymail address (alias@domain.tld)

Example

ExampleSanitizePaymail example using SanitizePaymail()

See more examples in /examples/

alias, domain, address := SanitizePaymail("Paymail@Domain.com")
fmt.Printf("alias: %s domain: %s address: %s", alias, domain, address)
Output:

alias: paymail domain: domain.com address: paymail@domain.com

func UserAgent added in v0.2.3

func UserAgent() string

UserAgent will return the default user agent string

func ValidateDomain

func ValidateDomain(domain string) error

ValidateDomain will do a basic validation on the domain format

This will not check to see if the domain is an active paymail provider This will not check DNS records to make sure the domain is active

Example

ExampleValidateDomain example using ValidateDomain()

See more examples in /examples/

err := ValidateDomain("domain")
if err != nil {
	fmt.Println(err.Error())
} else {
	fmt.Println("example failed")
}
Output:

domain name is invalid: domain

func ValidatePaymail

func ValidatePaymail(paymailAddress string) error

ValidatePaymail will do a basic validation on the paymail format (email address format)

This will not check to see if the paymail address is active via the provider

Example

ExampleValidatePaymail example using ValidatePaymail()

See more examples in /examples/

err := ValidatePaymail("bad@paymail")
if err != nil {
	fmt.Println(err.Error())
} else {
	fmt.Println("example failed")
}
Output:

paymail address failed format validation: email is not a valid address format

func ValidateTimestamp added in v0.0.5

func ValidateTimestamp(timestamp string) error

ValidateTimestamp will test if the timestamp is valid

This is used to validate the "dt" parameter in resolve_address.go Allowing 3 minutes before/after for

Example

ExampleValidateTimestamp example using the method ValidateTimestamp()

See more examples in /examples/

err := ValidateTimestamp(time.Now().UTC().Format(time.RFC3339))
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
} else {
	fmt.Printf("timestamp was valid!")
}
Output:

timestamp was valid!

func Version added in v0.2.3

func Version() string

Version will return the version of the library

Types

type AddressInformation added in v0.4.2

type AddressInformation struct {
	Alias       string `json:"alias"`        // Alias or handle of the paymail
	Avatar      string `json:"avatar"`       // This is the url of the user (public profile)
	Domain      string `json:"domain"`       // Domain of the paymail
	ID          string `json:"id"`           // Global unique identifier
	LastAddress string `json:"last_address"` // This is used as a temp address for now (should be via xPub)
	Name        string `json:"name"`         // This is the name of the user (public profile)
	PrivateKey  string `json:"-"`            // PrivateKey hex encoded
	PubKey      string `json:"pubkey"`       // PublicKey hex encoded
}

AddressInformation is an internal struct for paymail addresses and their corresponding information

type BRFCSpec

type BRFCSpec struct {
	Alias      string `json:"alias,omitempty"`      // Alias is used in the list of capabilities
	Author     string `json:"author"`               // Free-form, could include a name, alias, paymail address, GitHub/social media handle, etc.
	ID         string `json:"id"`                   // Public BRFC ID
	Supersedes string `json:"supersedes,omitempty"` // A BRFC ID (or list of IDs) that this document supersedes
	Title      string `json:"title"`                // Title of the brfc
	URL        string `json:"url,omitempty"`        // Public URL to view the specification
	Version    string `json:"version"`              // No set format; could be a sequence number, publication date, or any other scheme
	Valid      bool   `json:"valid"`                // Validated the ID -> (title,author,version)
}

BRFCSpec is a full BRFC specification document

See more: http://bsvalias.org/01-brfc-specifications.html

func LoadBRFCs added in v0.6.0

func LoadBRFCs(additionalSpecifications string) ([]*BRFCSpec, error)

LoadBRFCs will load the known "default" specifications into structs from JSON

additionSpecifications is appended to the default specs BRFCKnownSpecifications is a local constant of JSON to preload known BRFC ids

Example

ExampleLoadBRFCs example using LoadBRFCs()

See more examples in /examples/

// Load additional specification(s)
additionalSpec := `[{"author": "andy (nChain)","id": "57dd1f54fc67","title": "BRFC Specifications","url": "http://bsvalias.org/01-02-brfc-id-assignment.html","version": "1"}]`
specs, err := LoadBRFCs(additionalSpec)
if err != nil {
	fmt.Printf("error occurred: %s", err.Error())
	return
}
fmt.Printf("total specifications found: %d", len(specs))
Output:

total specifications found: 24

func (*BRFCSpec) Generate

func (b *BRFCSpec) Generate() error

Generate will generate a new BRFC ID from the given specification

See more: http://bsvalias.org/01-02-brfc-id-assignment.html

Example

ExampleBRFCSpec_Generate example using Generate()

See more examples in /examples/

// Start with a new BRFC specification
newBRFC := &BRFCSpec{
	Author:  "MrZ",
	Title:   "New BRFC",
	Version: "1",
}
if err := newBRFC.Generate(); err != nil {
	fmt.Printf("error occurred: %s", err.Error())
} else {
	fmt.Printf("id generated: %s", newBRFC.ID)
}
Output:

id generated: e898079d7d1a

func (*BRFCSpec) Validate

func (b *BRFCSpec) Validate() (valid bool, id string, err error)

Validate will check if the BRFC is valid or not (and set b.Valid)

Returns the ID that was generated to compare against the existing id Returns valid bool for convenience, but also sets b.Valid = true

Example

ExampleBRFCSpec_Validate example using Validate()

See more examples in /examples/

// Start with an existing BRFC specification
newBRFC := &BRFCSpec{
	Author:  "MrZ",
	ID:      "e898079d7d1a",
	Title:   "New BRFC",
	Version: "1",
}
if valid, id, err := newBRFC.Validate(); err != nil {
	fmt.Printf("error occurred: %s", err.Error())
} else if !valid {
	fmt.Printf("id is invalid: %s vs %s", newBRFC.ID, id)
} else {
	fmt.Printf("brfc is valid: %s", id)
}
Output:

brfc is valid: e898079d7d1a

type CapabilitiesPayload added in v0.5.0

type CapabilitiesPayload struct {
	BsvAlias     string                 `json:"bsvalias"`     // Version of the bsvalias
	Capabilities map[string]interface{} `json:"capabilities"` // Raw list of the capabilities
}

CapabilitiesPayload is the actual payload response

func (*CapabilitiesPayload) GetBool added in v0.5.0

func (c *CapabilitiesPayload) GetBool(brfcID, alternateID string) bool

GetBool will perform getValue() but cast to a bool if found

Returns false if not found

Example

ExampleCapabilitiesPayload_GetBool example using GetBool()

See more examples in /examples/

capabilities := &CapabilitiesPayload{
	BsvAlias: DefaultServiceName,
	Capabilities: map[string]interface{}{
		"6745385c3fc0": true,
		"alternate_id": true,
		"0c4339ef99c2": "https://domain.com/" + DefaultServiceName + "/id/{alias}@{domain.tld}",
	},
}

found := capabilities.GetBool("6745385c3fc0", "alternate_id")
fmt.Printf("found value: %v", found)
Output:

found value: true

func (*CapabilitiesPayload) GetString added in v0.5.0

func (c *CapabilitiesPayload) GetString(brfcID, alternateID string) string

GetString will perform getValue() but cast to a string if found

Returns an empty string if not found

Example

ExampleCapabilitiesPayload_GetString example using GetString()

See more examples in /examples/

capabilities := &CapabilitiesPayload{
	BsvAlias: DefaultServiceName,
	Capabilities: map[string]interface{}{
		"6745385c3fc0": false,
		"pki":          "https://domain.com/" + DefaultServiceName + "/id/{alias}@{domain.tld}",
		"0c4339ef99c2": "https://domain.com/" + DefaultServiceName + "/id/{alias}@{domain.tld}",
	},
}

found := capabilities.GetString("pki", "0c4339ef99c2")
fmt.Printf("found value: %v", found)
Output:

found value: https://domain.com/bsvalias/id/{alias}@{domain.tld}

func (*CapabilitiesPayload) Has added in v0.5.0

func (c *CapabilitiesPayload) Has(brfcID, alternateID string) bool

Has will check if a BRFC ID (or alternate) is found in the list of capabilities

Alternate is used for example: "pki" is also BRFC "0c4339ef99c2"

Example

ExampleCapabilitiesPayload_Has example using Has()

See more examples in /examples/

capabilities := &CapabilitiesPayload{
	BsvAlias: DefaultServiceName,
	Capabilities: map[string]interface{}{
		"6745385c3fc0": true,
		"alternate_id": true,
		"0c4339ef99c2": "https://domain.com/" + DefaultServiceName + "/id/{alias}@{domain.tld}",
	},
}

found := capabilities.Has("6745385c3fc0", "alternate_id")
fmt.Printf("found brfc: %v", found)
Output:

found brfc: true

type CapabilitiesResponse added in v0.5.0

type CapabilitiesResponse struct {
	StandardResponse
	CapabilitiesPayload
}

CapabilitiesResponse is the full response returned

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the Paymail client configuration and options

func (*Client) CheckDNSSEC

func (c *Client) CheckDNSSEC(domain string) (result *DNSCheckResult)

CheckDNSSEC will check the DNSSEC for a given domain

Paymail providers should have DNSSEC enabled for their domain

Example

ExampleClient_CheckDNSSEC example using CheckDNSSEC()

See more examples in /examples/

client, _ := NewClient()
results := client.CheckDNSSEC("google.com")
if len(results.ErrorMessage) == 0 {
	fmt.Printf("valid DNSSEC found for: %s", "google.com")
} else {
	fmt.Printf("invalid DNSSEC found for: %s error: %s", "google.com", results.ErrorMessage)
}
Output:

valid DNSSEC found for: google.com

func (*Client) CheckSSL

func (c *Client) CheckSSL(host string) (valid bool, err error)

CheckSSL will do a basic check on the host to see if there is a valid SSL cert

All paymail requests should be via HTTPS and have a valid certificate

Example

ExampleClient_CheckSSL example using CheckSSL()

See more examples in /examples/

client, _ := NewClient()
valid, _ := client.CheckSSL("google.com")
if valid {
	fmt.Printf("valid SSL certificate found for: %s", "google.com")
} else {
	fmt.Printf("invalid SSL certificate found for: %s", "google.com")
}
Output:

valid SSL certificate found for: google.com

func (*Client) GetBRFCs added in v0.2.1

func (c *Client) GetBRFCs() []*BRFCSpec

GetBRFCs will return the list of specs

func (*Client) GetCapabilities

func (c *Client) GetCapabilities(target string, port int) (response *CapabilitiesResponse, err error)

GetCapabilities will return a list of capabilities for a given domain & port

Specs: http://bsvalias.org/02-02-capability-discovery.html

Example

ExampleClient_GetCapabilities example using GetCapabilities()

See more examples in /examples/

// Load the client
client := newTestClient(nil)

mockCapabilities(http.StatusOK)

// Get the capabilities
capabilities, err := client.GetCapabilities(testDomain, DefaultPort)
if err != nil {
	fmt.Printf("error getting capabilities: " + err.Error())
	return
}
fmt.Printf("found %d capabilities", len(capabilities.Capabilities))
Output:

found 3 capabilities

func (*Client) GetOptions added in v0.6.0

func (c *Client) GetOptions() *ClientOptions

GetOptions will return the Client options

func (*Client) GetP2PPaymentDestination added in v0.0.2

func (c *Client) GetP2PPaymentDestination(p2pURL, alias, domain string,
	paymentRequest *PaymentRequest) (response *PaymentDestinationResponse, err error)

GetP2PPaymentDestination will return list of outputs for the P2P transactions to use

Specs: https://docs.moneybutton.com/docs/paymail-07-p2p-payment-destination.html

Example

ExampleClient_GetP2PPaymentDestination example using GetP2PPaymentDestination()

See more examples in /examples/

// Load the client
client := newTestClient(nil)

mockP2PPaymentDestination(http.StatusOK)

// Payment Request
paymentRequest := &PaymentRequest{Satoshis: 100}

// Fire the request
destination, err := client.GetP2PPaymentDestination(
	testServerURL+"p2p-payment-destination/{alias}@{domain.tld}",
	testAlias,
	testDomain,
	paymentRequest,
)
if err != nil {
	fmt.Printf("error occurred in GetP2PPaymentDestination: %s", err.Error())
	return
}
if len(destination.Outputs) > 0 {
	fmt.Printf("payment destination: " + destination.Outputs[0].Script)
}
Output:

payment destination: 76a9143e2d1d795f8acaa7957045cc59376177eb04a3c588ac

func (*Client) GetPKI

func (c *Client) GetPKI(pkiURL, alias, domain string) (response *PKIResponse, err error)

GetPKI will return a valid PKI response for a given alias@domain.tld

Specs: http://bsvalias.org/03-public-key-infrastructure.html

Example

ExampleClient_GetPKI example using GetPKI()

See more examples in /examples/

// Load the client
client := newTestClient(nil)

mockGetPKI(http.StatusOK)

// Get the pki
pki, err := client.GetPKI(testServerURL+"id/{alias}@{domain.tld}", testAlias, testDomain)
if err != nil {
	fmt.Printf("error getting pki: " + err.Error())
	return
}
fmt.Printf("found %s handle with pubkey: %s", pki.Handle, pki.PubKey)
Output:

found mrz@test.com handle with pubkey: 02ead23149a1e33df17325ec7a7ba9e0b20c674c57c630f527d69b866aa9b65b10

func (*Client) GetPublicProfile

func (c *Client) GetPublicProfile(publicProfileURL, alias, domain string) (response *PublicProfileResponse, err error)

GetPublicProfile will return a valid public profile

Specs: https://github.com/bitcoin-sv-specs/brfc-paymail/pull/7/files

Example

ExampleClient_GetPublicProfile example using GetPublicProfile()

See more examples in /examples/

// Load the client
client := newTestClient(nil)

mockGetPublicProfile(http.StatusOK)

// Get profile
profile, err := client.GetPublicProfile(testServerURL+"public-profile/{alias}@{domain.tld}", testAlias, testDomain)
if err != nil {
	fmt.Printf("error getting profile: " + err.Error())
	return
}
fmt.Printf("found profile for: %s", profile.Name)
Output:

found profile for: MrZ

func (*Client) GetResolver added in v0.4.4

func (c *Client) GetResolver() interfaces.DNSResolver

GetResolver will return the internal resolver from the client

func (*Client) GetSRVRecord

func (c *Client) GetSRVRecord(service, protocol, domainName string) (srv *net.SRV, err error)

GetSRVRecord will get the SRV record for a given domain name

Specs: http://bsvalias.org/02-01-host-discovery.html

Example

ExampleClient_GetSRVRecord example using GetSRVRecord()

See more examples in /examples/

client := newTestClient(nil)
srv, _ := client.GetSRVRecord(DefaultServiceName, DefaultProtocol, testDomain)
fmt.Printf("port: %d priority: %d weight: %d target: %s", srv.Port, srv.Priority, srv.Weight, srv.Target)
Output:

port: 443 priority: 10 weight: 10 target: www.test.com

func (*Client) GetUserAgent added in v0.2.2

func (c *Client) GetUserAgent() string

GetUserAgent will return the user agent string of the client

func (*Client) ResolveAddress

func (c *Client) ResolveAddress(resolutionURL, alias, domain string, senderRequest *SenderRequest) (response *ResolutionResponse, err error)

ResolveAddress will return a hex-encoded Bitcoin script if successful

Specs: http://bsvalias.org/04-01-basic-address-resolution.html

Example

ExampleClient_ResolveAddress example using ResolveAddress()

See more examples in /examples/

// Load the client
client := newTestClient(nil)

mockResolveAddress(http.StatusOK)

// Sender Request
senderRequest := &SenderRequest{
	Dt:           time.Now().UTC().Format(time.RFC3339),
	SenderHandle: testAlias + "@" + testDomain,
	SenderName:   testName,
}

// Fire the request
resolution, err := client.ResolveAddress(
	testServerURL+"address/{alias}@{domain.tld}",
	testAlias, testDomain, senderRequest,
)
if err != nil {
	fmt.Printf("error occurred in ResolveAddress: %s", err.Error())
	return
}
fmt.Printf("address found: %s", resolution.Address)
Output:

address found: 1Cat862cjhp8SgLLMvin5gyk5UScasg1P9

func (*Client) SendP2PTransaction added in v0.0.2

func (c *Client) SendP2PTransaction(p2pURL, alias, domain string,
	transaction *P2PTransaction) (response *P2PTransactionResponse, err error)

SendP2PTransaction will submit a transaction hex string (tx_hex) to a paymail provider

Specs: https://docs.moneybutton.com/docs/paymail-06-p2p-transactions.html

Example

ExampleClient_SendP2PTransaction example using SendP2PTransaction()

See more examples in /examples/

// Load the client (using a TestClient for this example since a live transaction is not possible)
client := newTestClient(nil)

// Create mock response (Using a mocked response since a live transaction is not possible)
httpmock.Reset()
httpmock.RegisterResponder(http.MethodPost, testServerURL+"receive-transaction/"+testAlias+"@"+testDomain,
	httpmock.NewStringResponder(http.StatusOK, `{"note":"test note","txid":"f3ddfabf7a7a84cfa20016e61df24dff32953d4023a3002cb5a98d6da4ef9bf1"}`))

// Raw TX
rawTransaction := &P2PTransaction{
	Hex:       "some-raw-hex",
	MetaData:  &P2PMetaData{Note: "test note", Sender: "someone@" + testDomain},
	Reference: "1234567"}

// Fire the request
transaction, err := client.SendP2PTransaction(
	testServerURL+"receive-transaction/{alias}@{domain.tld}", testAlias, testDomain, rawTransaction,
)
if err != nil {
	fmt.Printf("error occurred in SendP2PTransaction: %s", err.Error())
	return
}
fmt.Printf("transaction was successful: %s", transaction.TxID)
Output:

transaction was successful: f3ddfabf7a7a84cfa20016e61df24dff32953d4023a3002cb5a98d6da4ef9bf1

func (*Client) ValidateSRVRecord

func (c *Client) ValidateSRVRecord(ctx context.Context, srv *net.SRV, port, priority, weight uint16) error

ValidateSRVRecord will check for a valid SRV record for paymail following specifications

Specs: http://bsvalias.org/02-01-host-discovery.html

Example

ExampleClient_ValidateSRVRecord example using ValidateSRVRecord()

See more examples in /examples/

client := newTestClient(nil)
err := client.ValidateSRVRecord(
	context.Background(),
	&net.SRV{
		Target:   testDomain,
		Port:     DefaultPort,
		Priority: 1,
		Weight:   DefaultWeight,
	},
	DefaultPort,
	DefaultPriority,
	DefaultWeight,
)
if err != nil {
	fmt.Printf("error: %s", err.Error())
}
Output:

error: srv priority 1 does not match 10

func (*Client) VerifyPubKey

func (c *Client) VerifyPubKey(verifyURL, alias, domain, pubKey string) (response *VerificationResponse, err error)

VerifyPubKey will try to match a handle and pubkey

Specs: https://bsvalias.org/05-verify-public-key-owner.html

Example

ExampleClient_VerifyPubKey example using VerifyPubKey()

See more examples in /examples/

// Load the client
client := newTestClient(nil)

mockVerifyPubKey(http.StatusOK)

// Verify PubKey
verification, err := client.VerifyPubKey(
	testServerURL+"verifypubkey/{alias}@{domain.tld}/{pubkey}",
	testAlias, testDomain, testPubKey,
)
if err != nil {
	fmt.Printf("error getting verification: " + err.Error())
	return
}
fmt.Printf("verified %s handle with pubkey: %s", verification.Handle, verification.PubKey)
Output:

verified mrz@test.com handle with pubkey: 02ead23149a1e33df17325ec7a7ba9e0b20c674c57c630f527d69b866aa9b65b10

func (*Client) WithCustomHTTPClient added in v0.2.0

func (c *Client) WithCustomHTTPClient(client *resty.Client) ClientInterface

WithCustomHTTPClient will overwrite the default client with a custom client.

func (*Client) WithCustomResolver added in v0.2.0

func (c *Client) WithCustomResolver(resolver interfaces.DNSResolver) ClientInterface

WithCustomResolver will allow you to supply a custom dns resolver, useful for testing etc.

type ClientInterface added in v0.6.0

type ClientInterface interface {
	CheckDNSSEC(domain string) (result *DNSCheckResult)
	CheckSSL(host string) (valid bool, err error)
	GetBRFCs() []*BRFCSpec
	GetCapabilities(target string, port int) (response *CapabilitiesResponse, err error)
	GetOptions() *ClientOptions
	GetP2PPaymentDestination(p2pURL, alias, domain string, paymentRequest *PaymentRequest) (response *PaymentDestinationResponse, err error)
	GetPKI(pkiURL, alias, domain string) (response *PKIResponse, err error)
	GetPublicProfile(publicProfileURL, alias, domain string) (response *PublicProfileResponse, err error)
	GetResolver() interfaces.DNSResolver
	GetSRVRecord(service, protocol, domainName string) (srv *net.SRV, err error)
	GetUserAgent() string
	ResolveAddress(resolutionURL, alias, domain string, senderRequest *SenderRequest) (response *ResolutionResponse, err error)
	SendP2PTransaction(p2pURL, alias, domain string, transaction *P2PTransaction) (response *P2PTransactionResponse, err error)
	ValidateSRVRecord(ctx context.Context, srv *net.SRV, port, priority, weight uint16) error
	VerifyPubKey(verifyURL, alias, domain, pubKey string) (response *VerificationResponse, err error)
	WithCustomHTTPClient(client *resty.Client) ClientInterface
	WithCustomResolver(resolver interfaces.DNSResolver) ClientInterface
}

ClientInterface is the Paymail client interface

func NewClient

func NewClient(opts ...ClientOps) (ClientInterface, error)

NewClient creates a new client for all paymail requests

If no options are given, it will use the defaultClientOptions() If no client is supplied it will use a default Resty HTTP client

Example

ExampleNewClient example using NewClient()

See more examples in /examples/

client, err := NewClient()
if err != nil {
	fmt.Printf("error loading client: %s", err.Error())
	return
}
fmt.Printf("loaded client: %s", client.GetOptions().userAgent)
Output:

loaded client: go-paymail: v0.10.2

type ClientOps added in v0.2.0

type ClientOps func(c *ClientOptions)

ClientOps allow functional options to be supplied that overwrite default go-paymail client options.

func WithBRFCSpecs added in v0.2.0

func WithBRFCSpecs(specs []*BRFCSpec) ClientOps

WithBRFCSpecs allows custom specs to be supplied to extend or replace the defaults.

func WithDNSPort added in v0.2.0

func WithDNSPort(port string) ClientOps

WithDNSPort can be supplied with a custom dns port to perform SRV checks on. Default is 53.

func WithDNSTimeout added in v0.2.0

func WithDNSTimeout(timeout time.Duration) ClientOps

WithDNSTimeout can be supplied to overwrite the default dns srv check timeout. The default is 5 seconds.

func WithHTTPTimeout added in v0.2.0

func WithHTTPTimeout(timeout time.Duration) ClientOps

WithHTTPTimeout can be supplied to adjust the default http client timeouts. The http client is used when querying paymail services for capabilities Default timeout is 20 seconds.

func WithNameServer added in v0.2.0

func WithNameServer(ip string) ClientOps

WithNameServer can be supplied to overwrite the default name server used to resolve srv requests. default is 8.8.8.8.

func WithNameServerNetwork added in v0.2.0

func WithNameServerNetwork(network string) ClientOps

WithNameServerNetwork can overwrite the default network protocol to use. The default is udp.

func WithNetwork added in v0.6.1

func WithNetwork(n Network) ClientOps

WithNetwork will set the client's operational network to one provided. Default is mainnet.

func WithRequestTracing added in v0.2.0

func WithRequestTracing() ClientOps

WithRequestTracing will enable tracing. Tracing is disabled by default.

func WithRetryCount added in v0.2.0

func WithRetryCount(retries int) ClientOps

WithRetryCount will overwrite the default retry count for http requests. Default retries is 2.

func WithSSLDeadline added in v0.2.0

func WithSSLDeadline(timeout time.Duration) ClientOps

WithSSLDeadline will overwrite the default ssl deadline. Default is 10 seconds.

func WithSSLTimeout added in v0.2.0

func WithSSLTimeout(timeout time.Duration) ClientOps

WithSSLTimeout will overwrite the default ssl timeout. Default timeout is 10 seconds.

func WithUserAgent added in v0.2.0

func WithUserAgent(userAgent string) ClientOps

WithUserAgent will overwrite the default useragent. Default is go-paymail + version.

type ClientOptions

type ClientOptions struct {
	// contains filtered or unexported fields
}

ClientOptions holds all the configuration for client requests and default resources

type DNSCheckResult

type DNSCheckResult struct {
	Answer       answer    `json:"answer"`
	CheckTime    time.Time `json:"check_time"`
	DNSSEC       bool      `json:"dnssec"`
	Domain       string    `json:"domain,omitempty"`
	ErrorMessage string    `json:"error_message,omitempty"`
	NSEC         nsec      `json:"nsec"`
}

DNSCheckResult struct is returned for the DNS check

type Network added in v0.6.1

type Network byte

Network an alias of the bitcoin networks.

const (
	// Mainnet bitcoin main network.
	Mainnet Network = iota
	// Testnet bitcoin test network.
	Testnet
	// STN bitcoin stress test network.
	STN
)

func (Network) String added in v0.6.1

func (n Network) String() string

String representation of the network.

func (Network) URLSuffix added in v0.6.1

func (n Network) URLSuffix() string

URLSuffix the conventional URL suffix for the network.

type P2PMetaData added in v0.0.7

type P2PMetaData struct {
	Note      string `json:"note,omitempty"`      // A human-readable bit of information about the payment
	PubKey    string `json:"pubkey,omitempty"`    // Public key to validate the signature (if signature is given)
	Sender    string `json:"sender,omitempty"`    // The paymail of the person that originated the transaction
	Signature string `json:"signature,omitempty"` // A signature of the tx id made by the sender
}

P2PMetaData is an object containing data associated with the P2P transaction

type P2PTransaction added in v0.0.2

type P2PTransaction struct {
	Hex       string       `json:"hex"`       // The raw transaction, encoded as a hexadecimal string
	MetaData  *P2PMetaData `json:"metadata"`  // An object containing data associated with the transaction
	Reference string       `json:"reference"` // Reference for the payment (from previous P2P Destination request)
}

P2PTransaction is the request body for the P2P transaction request

type P2PTransactionPayload added in v0.5.0

type P2PTransactionPayload struct {
	Note string `json:"note"` // Some human-readable note
	TxID string `json:"txid"` // The txid of the broadcasted tx
}

P2PTransactionPayload is payload from the request

type P2PTransactionResponse added in v0.0.7

type P2PTransactionResponse struct {
	StandardResponse
	P2PTransactionPayload
}

P2PTransactionResponse is the response to the request

type PKIPayload added in v0.5.0

type PKIPayload struct {
	BsvAlias string `json:"bsvalias"` // Version of Paymail
	Handle   string `json:"handle"`   // The <alias>@<domain>.<tld>
	PubKey   string `json:"pubkey"`   // The related PubKey
}

PKIPayload is the payload from the response

type PKIResponse added in v0.5.0

type PKIResponse struct {
	StandardResponse
	PKIPayload
}

PKIResponse is the result returned

type PaymentDestinationPayload added in v0.5.0

type PaymentDestinationPayload struct {
	Outputs   []*PaymentOutput `json:"outputs"`   // A list of outputs
	Reference string           `json:"reference"` // A reference for the payment, created by the receiver of the transaction
}

PaymentDestinationPayload is the payload from the response

The reference is unique for the payment destination request

type PaymentDestinationResponse added in v0.5.0

type PaymentDestinationResponse struct {
	StandardResponse
	PaymentDestinationPayload
}

PaymentDestinationResponse is the response from the GetP2PPaymentDestination() request

The reference is unique for the payment destination request

type PaymentOutput added in v0.0.7

type PaymentOutput struct {
	Address  string `json:"address,omitempty"`  // Hex encoded locking script
	Satoshis uint64 `json:"satoshis,omitempty"` // Number of satoshis for that output
	Script   string `json:"script"`             // Hex encoded locking script
}

PaymentOutput is returned inside the payment destination response

There can be several outputs in one response based on the amount of satoshis being transferred and the rules in place by the Paymail provider

type PaymentRequest added in v0.0.2

type PaymentRequest struct {
	Satoshis uint64 `json:"satoshis"` // The amount, in Satoshis, that the sender intends to transfer to the receiver
}

PaymentRequest is the request body for the P2P payment request

type PublicProfilePayload added in v0.5.0

type PublicProfilePayload struct {
	Avatar string `json:"avatar"` // A URL that returns a 180x180 image. It can accept an optional parameter `s` to return an image of width and height `s`. The image should be JPEG, PNG, or GIF.
	Name   string `json:"name"`   // A string up to 100 characters long. (name or nickname)
}

PublicProfilePayload is the payload from the response

type PublicProfileResponse added in v0.5.0

type PublicProfileResponse struct {
	StandardResponse
	PublicProfilePayload
}

PublicProfileResponse is the result returned from GetPublicProfile()

type ResolutionPayload added in v0.5.0

type ResolutionPayload struct {
	Address   string `json:"address,omitempty"`   // Legacy BSV address derived from the output script (custom for our Go package)
	Output    string `json:"output"`              // hex-encoded Bitcoin script, which the sender MUST use during the construction of a payment transaction
	Signature string `json:"signature,omitempty"` // This is used if SenderValidation is enforced (signature of "output" value)
}

ResolutionPayload is the payload from the response

type ResolutionResponse added in v0.5.0

type ResolutionResponse struct {
	StandardResponse
	ResolutionPayload
}

ResolutionResponse is the response from the ResolveAddress() request

type SanitisedPaymail added in v0.1.6

type SanitisedPaymail struct {
	Alias, Domain, Address string
}

SanitisedPaymail contains elements of a sanitized paymail address. All elements are lowercase.

func ValidateAndSanitisePaymail added in v0.1.6

func ValidateAndSanitisePaymail(paymail string, isBeta bool) (*SanitisedPaymail, error)

ValidateAndSanitisePaymail will take a paymail address or handle, convert to a paymail address if it's a handle, validate that address, then sanitize it if it is valid. If the address or handle isn't valid, an error will be returned.

type SenderRequest

type SenderRequest struct {
	Amount       uint64 `json:"amount,omitempty"`     // The amount, in Satoshis, that the sender intends to transfer to the receiver
	Dt           string `json:"dt"`                   // (required) ISO-8601 formatted timestamp; see notes
	Purpose      string `json:"purpose,omitempty"`    // Human-readable description of the purpose of the payment
	SenderHandle string `json:"senderHandle"`         // (required) Sender's paymail handle
	SenderName   string `json:"senderName,omitempty"` // Human-readable sender display name
	Signature    string `json:"signature,omitempty"`  // Compact Bitcoin message signature; http://bsvalias.org/04-01-basic-address-resolution.html#signature-field
}

SenderRequest is the request body for the basic address resolution

This is required to make a basic resolution request, and Dt and SenderHandle are required

func (*SenderRequest) Sign added in v0.0.5

func (s *SenderRequest) Sign(privateKey string) (string, error)

Sign will sign the given components in the ResolveAddress() request

Source: https://github.com/moneybutton/paymail-client/blob/master/src/VerifiableMessage.js Specs: http://bsvalias.org/04-01-basic-address-resolution.html#signature-field Additional Specs: http://bsvalias.org/04-02-sender-validation.html

Example

ExampleSenderRequest_Sign example using Sign()

See more examples in /examples/

// Test private key
key := "54035dd4c7dda99ac473905a3d82f7864322b49bab1ff441cc457183b9bd8abd"

// Create the request / message
senderRequest := &SenderRequest{
	Dt:           time.Now().UTC().Format(time.RFC3339),
	SenderHandle: testAlias + "@" + testDomain,
	SenderName:   testName,
	Purpose:      testMessage,
}

// Sign the sender request
signature, err := senderRequest.Sign(key)
if err != nil {
	fmt.Printf("error occurred in sign: %s", err.Error())
	return
} else if len(signature) == 0 {
	fmt.Printf("signature was empty")
	return
}

// Cannot display signature as it changes because of the "dt" field
fmt.Printf("signature created!")
Output:

signature created!

func (*SenderRequest) Verify added in v0.0.5

func (s *SenderRequest) Verify(keyAddress, signature string) error

Verify will verify the given components in the ResolveAddress() request

Source: https://github.com/moneybutton/paymail-client/blob/master/src/VerifiableMessage.js Specs: http://bsvalias.org/04-01-basic-address-resolution.html#signature-field

Example

ExampleSenderRequest_Verify example using Verify()

See more examples in /examples/

// Example sender request
senderRequest := &SenderRequest{
	Dt:           "2020-10-02T16:43:39Z",
	SenderHandle: testAlias + "@" + testDomain,
	SenderName:   testName,
	Purpose:      testMessage,
}

// Try verifying (valid) (using an address and a signature - previously generated for example)
if err := senderRequest.Verify(
	"1LqWAxSaKdXRKATAj7ELk34ioyT1T8gXgU",
	"G70DPE2p8xtCehUjRkQF2gI26kDu59JsQ6KKUmJyHi1XFGkeoIokgzN/kiMy+lujpXOi+C35sZUwgSMqOYRDXPQ=",
); err != nil {
	fmt.Printf("error occurred in Verify: %s", err.Error())
	return
}

fmt.Printf("signature verified!")
Output:

signature verified!

type ServerError added in v0.0.5

type ServerError struct {
	Code    string `json:"code"`    // Shows the corresponding code
	Message string `json:"message"` // Shows the error message returned by the server
}

ServerError is the standard error response from a paymail server

type StandardResponse

type StandardResponse struct {
	Body       []byte          `json:"-"` // Body of the response request
	StatusCode int             `json:"-"` // Status code returned on the request
	Tracing    resty.TraceInfo `json:"-"` // Trace information if enabled on the request
}

StandardResponse is the standard fields returned on all responses

type VerificationPayload added in v0.5.0

type VerificationPayload struct {
	BsvAlias string `json:"bsvalias"` // Version of the bsvalias
	Handle   string `json:"handle"`   // The <alias>@<domain>.<tld>
	Match    bool   `json:"match"`    // If the match was successful or not
	PubKey   string `json:"pubkey"`   // The related PubKey
}

VerificationPayload is the payload from the response

type VerificationResponse added in v0.5.0

type VerificationResponse struct {
	StandardResponse
	VerificationPayload
}

VerificationResponse is the result returned from the VerifyPubKey() request

Directories

Path Synopsis
examples
Package interfaces is the custom interface
Package interfaces is the custom interface
Package server is the server package for Paymail
Package server is the server package for Paymail
Package tester is the testing package
Package tester is the testing package

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL