triple

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: MIT Imports: 7 Imported by: 0

README

triple-go

Go Reference License

A Go client for the Triple transaction data enrichment API: turn raw bank/card transaction strings into clean merchant names, logos, categories, locations, contact details, subscription detection, CO₂ estimates, fraud signals, and payment processor identification.

Zero third-party dependencies — the entire SDK is built on the Go standard library.

Installation

go get github.com/iamkanishka/triple-go

Requires Go 1.25+.

Quick start

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/iamkanishka/triple-go"
	v1 "github.com/iamkanishka/triple-go/enrich/v1"
	"github.com/iamkanishka/triple-go/idgen"
)

func main() {
	client, err := triple.NewClient(os.Getenv("TRIPLE_API_KEY"))
	if err != nil {
		log.Fatal(err)
	}

	amount := 24.99
	enriched, err := client.Enrich.Transaction(context.Background(), v1.Request{
		MerchantName:         "AMZN MKTP UK",
		TransactionType:      v1.TransactionTypeCardTransaction,
		TransactionID:        idgen.GenerateTransactionID(),
		MerchantCountry:      "GBR",
		TransactionAmount:    &amount,
		TransactionCurrency:  "GBP",
		ChannelType:          v1.ChannelTypeECommerce,
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(enriched.VisualEnrichments.MerchantCleanName) // "Amazon"
}

*triple.Client is safe for concurrent use by multiple goroutines, and safe to build more than one of (e.g. one per tenant, or one for sandbox alongside one for production) side by side in the same process — there's no global or package-level state.

API keys starting with tr_test_ are sandbox keys, tr_live_ are production keys — the environment (and therefore which host gets called) is inferred automatically from whichever you pass in.

Configuration

Every option is a functional triple.Option passed to NewClient:

client, err := triple.NewClient(
	apiKey,
	triple.WithReceiveTimeout(15*time.Second),
	triple.WithMaxRetries(5),
)

Or set the environment explicitly instead of relying on key-prefix inference:

client, err := triple.NewClient(apiKey, triple.WithEnvironment(triple.EnvironmentSandbox))

See transport.Config for the full list — timeouts, retry policy, a custom *http.Client passthrough, an optional client-side rate limiter, telemetry hooks, and so on.

Enrichment

Two flavours, matching Triple's two enrichment endpoints:

// Structured — when you have discrete fields
client.Enrich.Transaction(ctx, v1.Request{
	MerchantName:         "AMZN MKTP UK",
	TransactionType:      v1.TransactionTypeCardTransaction,
	TransactionID:        idgen.GenerateTransactionID(),
	MerchantCountry:      "GBR",
	TransactionAmount:    &amount,   // *float64
	TransactionCurrency:  "GBP",
})

// Unstructured — when all you have is a raw description string
client.Enrich.UnstructuredTransaction(ctx, v2.Request{
	TransactionID:       idgen.GenerateTransactionID(),
	Text:                "CRD PUR 4321 NETFLIX.COM 866-5797172 CA",
	TransactionAmount:   &amount,
	TransactionCurrency: "USD",
})

Every input is validated locally before any network call is made — invalid input returns an *apierror.Error with Type == apierror.TypeValidation immediately, with the same field-level error shape (Errors map[string][]string) Triple's own API would return.

The two response shapes differ slightly, matching Triple's own OpenAPI spec: the structured (v1) response wraps every enrichment feature (location, subscriptions, CO₂, fraud, contact, payment processor) in an Enabled-flagged struct, since not every transaction carries every kind of signal — an online purchase, for instance, never has a MerchantLocation. The unstructured (v2) response uses flat, simply nullable structs instead. See enrich/v1 and enrich/v2 for the exact shapes, including a couple of small helpers like (*v1.Subscriptions).Recurring() and (*v1.Fraud).Flagged().

Brands, feedback, stocks, cryptos, and TLS

// Look up a brand directly (e.g. to refresh a cached logo)
client.Brands.Fetch(ctx, "497f6eca-6276-4993-bfeb-53cbbbba6f08")

// Tell Triple when enrichment data is wrong or missing
client.Feedback.Report(ctx, feedback.Request{
	TransactionID: "txn_123",
	Report:        feedback.ReportBrandName,
	ResponseValue: "AMZN MKTP UK",
	Feedback:      "Should be Amazon",
})

// Brokerage data
client.Stocks.Fetch(ctx, "LU1778762911", stocks.WithFormat(resource.FormatSVGLight))
client.Cryptos.Fetch(ctx, "bitcoin")

// Issue an mTLS client certificate (hits Triple's control-plane host)
lifetime := 365
client.TLS.IssueCertificate(ctx, tlscert.CertificateRequest{
	PublicKey: pem,
	Lifetime:  &lifetime,
})

Error handling

Every call returns (result, error). On failure, error is always an *apierror.Error:

enriched, err := client.Enrich.Transaction(ctx, req)
if err != nil {
	var apiErr *apierror.Error
	if errors.As(err, &apiErr) {
		switch apiErr.Type {
		case apierror.TypeValidation:
			// local validation failure — apiErr.Errors is a field -> []message map
			log.Printf("bad enrich payload: %v", apiErr.Errors)
		case apierror.TypeRateLimited:
			// only seen after the client's own retries are exhausted
			log.Printf("rate limited, retry after %v", apiErr.RetryAfter)
		default:
			log.Print(apiErr)
		}
	}
	return
}

apierror.Error distinguishes TypeValidation, TypeUnauthenticated, TypeForbidden, TypeNotFound, TypeRateLimited, TypeServerError, TypeUnexpectedStatus, and TypeNetworkError — see the package docs for the full field list.

Retries

408, 429, 500, 502, 503, and 504 responses (and transport errors) are retried automatically with exponential backoff, honoring Triple's Retry-After header on 429s. Configure or disable this via transport.Config:

triple.NewClient(apiKey, triple.WithMaxRetries(5))

// Disable retries entirely:
triple.NewClient(apiKey, triple.WithShouldRetry(func(*http.Response, error, int) (bool, time.Duration) {
	return false, 0
}))

Telemetry

A telemetry.Hook receives a telemetry.Event around every request attempt (start/stop/error) — handy for logging, metrics, or tracing:

triple.NewClient(apiKey, triple.WithHooks(func(e telemetry.Event) {
	log.Printf("%s %s attempt=%d status=%d duration=%v", e.Method, e.Path, e.Attempt, e.Status, e.Duration)
}))

Optional client-side rate limiting

For bulk workloads (e.g. backfilling historical transactions) where you'd rather avoid 429s in the first place:

limiter := ratelimiter.New(50, time.Second) // 50 requests/second
client, err := triple.NewClient(apiKey, triple.WithRateLimiter(limiter))

This is a single-process token bucket. For multi-instance deployments sharing one rate budget, supply your own transport.RateLimiter implementation (e.g. backed by Redis) instead.

Testing code that calls Triple

Every domain service accepts a *transport.Client, and transport.Config accepts an HTTPClient override — so tests can point the SDK at an httptest.Server with no extra test dependency required:

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(`{"transaction_id":"txn_1"}`))
}))
defer srv.Close()

client, err := triple.NewClient("tr_test_xxx", triple.WithBaseURL(srv.URL))

Sandbox vs. production

Triple provides fully isolated sandbox and production environments (API hosts, dashboards, and databases). Pass a tr_test_* key to hit sandbox, or tr_live_* for production — transport.NewConfig infers this and warns on any mismatch if you also pass WithEnvironment explicitly.

License

MIT. See LICENSE.

Disclaimer

This is a community-maintained client and is not officially affiliated with or endorsed by Triple Technologies. See jointriple.com for the official product and docs.triple.app for the official API reference.

Documentation

Overview

Package triple is a Go client for the Triple (https://jointriple.com) transaction data enrichment API.

Quick start

client, err := triple.NewClient(os.Getenv("TRIPLE_API_KEY"))
if err != nil {
	log.Fatal(err)
}

enriched, err := client.Enrich.Transaction(ctx, v1.Request{
	MerchantName:        "AMZN MKTP UK",
	TransactionType:     v1.TransactionTypeCardTransaction,
	TransactionID:       idgen.GenerateTransactionID(),
	TransactionAmount:   ptr(24.99),
	TransactionCurrency: "GBP",
})

A *Client is safe for concurrent use by multiple goroutines, and safe to build more than one of (e.g. one per tenant, or one for sandbox and one for production) side by side in the same process — there's no global or package-level state.

Configuration

Every option is passed to NewClient as a functional Option — see WithEnvironment, WithBaseURL, WithReceiveTimeout, WithMaxRetries, WithRateLimiter, WithHooks, and so on.

Error handling

Every call returns (result, error). On failure, error is always an *apierror.Error — use errors.As to inspect its Type, Status, Errors (field-level validation messages), and RetryAfter fields.

Domain services

Every resource has its own service, reachable as a field on *Client: Enrich, Brands, Feedback, Stocks, Cryptos, TLS. Each is also usable standalone (e.g. enrich.New(transportClient)) if you want to compose your own client.

Index

Constants

View Source
const (
	EnvironmentProduction = transport.EnvironmentProduction
	EnvironmentSandbox    = transport.EnvironmentSandbox
)

The recognized Environment values, re-exported from package transport.

Variables

View Source
var (
	WithEnvironment    = transport.WithEnvironment
	WithBaseURL        = transport.WithBaseURL
	WithControlBaseURL = transport.WithControlBaseURL
	WithReceiveTimeout = transport.WithReceiveTimeout
	WithConnectTimeout = transport.WithConnectTimeout
	WithShouldRetry    = transport.WithShouldRetry
	WithMaxRetries     = transport.WithMaxRetries
	WithHTTPClient     = transport.WithHTTPClient
	WithHooks          = transport.WithHooks
	WithRateLimiter    = transport.WithRateLimiter
	WithUserAgent      = transport.WithUserAgent
	WithLogger         = transport.WithLogger
)

Functional options, re-exported from package transport for convenience.

Functions

This section is empty.

Types

type Client

type Client struct {

	// Enrich enriches structured and unstructured transactions.
	Enrich *enrich.Service
	// Brands looks up brands by id.
	Brands *brands.Service
	// Feedback reports incorrect or missing enrichment data.
	Feedback *feedback.Service
	// Stocks looks up brokerage products (stocks/funds) by ISIN.
	Stocks *stocks.Service
	// Cryptos looks up cryptocurrencies by slug.
	Cryptos *cryptos.Service
	// TLS issues mTLS client certificates.
	TLS *tlscert.Service
	// contains filtered or unexported fields
}

Client is the entry point to the Triple API. Build one with NewClient.

func NewClient

func NewClient(apiKey string, opts ...Option) (*Client, error)

NewClient builds a Client for the given API key, applying any Options. Environment is inferred from the key's prefix ("tr_test_" for sandbox, "tr_live_" for production) unless overridden with WithEnvironment.

It returns an error if apiKey is empty, or if the environment can't be inferred and wasn't set explicitly.

func NewClientFromConfig

func NewClientFromConfig(cfg transport.Config) *Client

NewClientFromConfig builds a Client from a fully-constructed transport.Config, bypassing NewClient's validation and defaulting. Most callers should use NewClient instead; this is exposed for advanced use (e.g. tests that need full control over the config).

func (*Client) Config

func (c *Client) Config() transport.Config

Config returns the Client's resolved configuration.

func (*Client) Transport

func (c *Client) Transport() *transport.Client

Transport returns the underlying transport.Client, for advanced use: inspecting the resolved transport.Config, or calling an endpoint this SDK doesn't wrap yet via Transport().Do(...).

type Environment

type Environment = transport.Environment

Environment selects which Triple host a Client talks to.

type Option

type Option = transport.ConfigOption

Option configures optional Client settings via NewClient.

Directories

Path Synopsis
Package apierror defines the structured error type returned by every call in the triple-go SDK: local validation failures, Triple API error responses, and transport-level failures all surface as an *Error.
Package apierror defines the structured error type returned by every call in the triple-go SDK: local validation failures, Triple API error responses, and transport-level failures all surface as an *Error.
Package brands provides brand lookups — fetch a brand's canonical name/logo by id.
Package brands provides brand lookups — fetch a brand's canonical name/logo by id.
Package cryptos provides cryptocurrency lookups by slug.
Package cryptos provides cryptocurrency lookups by slug.
Package enrich implements transaction enrichment — the core of the Triple API.
Package enrich implements transaction enrichment — the core of the Triple API.
v1
Package v1 holds the request/response types for the v1 (structured) transaction enrichment endpoint: POST /v1/enrich-transaction/.
Package v1 holds the request/response types for the v1 (structured) transaction enrichment endpoint: POST /v1/enrich-transaction/.
v2
Package v2 holds the request/response types for the v2 (unstructured) transaction enrichment endpoint: POST /v2/enrich-unstructured-transaction/.
Package v2 holds the request/response types for the v2 (unstructured) transaction enrichment endpoint: POST /v2/enrich-unstructured-transaction/.
Package feedback reports incorrect or missing enrichment data back to Triple, helping its models improve over time.
Package feedback reports incorrect or missing enrichment data back to Triple, helping its models improve over time.
Package idgen provides small stateless ID-generation helpers shared across the triple-go SDK.
Package idgen provides small stateless ID-generation helpers shared across the triple-go SDK.
Package ratelimiter provides optional, opt-in client-side rate limiting for the triple-go SDK.
Package ratelimiter provides optional, opt-in client-side rate limiting for the triple-go SDK.
Package resource defines the visual-asset type shared by the stocks and cryptos domains.
Package resource defines the visual-asset type shared by the stocks and cryptos domains.
Package stocks provides brokerage product (stock/fund) lookups by ISIN.
Package stocks provides brokerage product (stock/fund) lookups by ISIN.
Package telemetry defines the observability hooks emitted by the transport around each HTTP call.
Package telemetry defines the observability hooks emitted by the transport around each HTTP call.
Package tlscert issues mTLS client certificates against Triple's control-plane host (a different host from the main enrichment/data API — see transport.Config.ControlBaseURL).
Package tlscert issues mTLS client certificates against Triple's control-plane host (a different host from the main enrichment/data API — see transport.Config.ControlBaseURL).
Package transport provides the low-level HTTP plumbing shared by every domain package in the triple-go SDK: request building, authentication, timeouts, retries with backoff, an optional client-side rate limiter, and telemetry hooks.
Package transport provides the low-level HTTP plumbing shared by every domain package in the triple-go SDK: request building, authentication, timeouts, retries with backoff, an optional client-side rate limiter, and telemetry hooks.
Package validate provides small, composable field validators used by the request types in each domain package.
Package validate provides small, composable field validators used by the request types in each domain package.

Jump to

Keyboard shortcuts

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