> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yativo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK

> Install, configure and use the official Yativo Fiat SDK for Go

The official Go library for the Yativo Fiat API. It sends your [API key](/yativo-fiat/authentication) on every request, adds idempotency keys to writes, retries temporary failures safely, raises typed errors and verifies [webhooks](/yativo-fiat/webhooks).

> **Package**
>
> `github.com/Yativo/yativo-fiat-go`

> **Requires**
>
> Go 1.18 or newer

> **[Source](https://github.com/Yativo/yativo-fiat-go)**
>
> Yativo/yativo-fiat-go

***

## Install

```bash theme={null}
# New project? Create a module first, in an empty folder:
mkdir my-shop && cd my-shop
go mod init my-shop

# Then add the SDK:
go get github.com/Yativo/yativo-fiat-go
```

## Configure

You need an API key with your server's IP address on its [IP whitelist](/yativo-fiat/ip-allowlist). Put the key and secret in environment variables, never in code:

```bash .env theme={null}
YATIVO_API_KEY=yativo_pk_your_key_here
YATIVO_API_SECRET=yativo_sk_your_secret_here
YATIVO_ENVIRONMENT=sandbox
```

Export the variables in your shell (`set -a; . ./.env; set +a`) or in your process manager, container or hosting settings.

The client reads these variables automatically. You can also pass them yourself:

```go theme={null}
client, err := yativo.New(
	os.Getenv("YATIVO_API_KEY"),
	os.Getenv("YATIVO_API_SECRET"),
	yativo.WithEnvironment(yativo.Sandbox), // or yativo.Production
)
```

> [!WARNING]
>
> Use the SDK on your server only. A browser or mobile app would expose your API secret.

## Make your first request

```go main.go theme={null}
package main

import (
	"context"
	"fmt"
	"log"

	yativo "github.com/Yativo/yativo-fiat-go"
)

func main() {
	// Empty strings mean: read YATIVO_API_KEY and YATIVO_API_SECRET from the environment.
	client, err := yativo.New("", "")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	resp, err := client.IpWhitelist.Check(ctx)
	if err != nil {
		log.Fatal(err)
	}
	var check struct {
		Result string `json:"result"`
	}
	_ = resp.Decode(&check)
	fmt.Println("Key check:", check.Result) // "pass" means you're good to go

	resp, err = client.Wallets.Balance(ctx, nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Balances:", string(resp.Data))
}
```

```bash theme={null}
set -a; . ./.env; set +a
go run .
```

You should see `Key check: pass` and your balances. `pass` means the key works from this server.

***

## Accept a payment

A [deposit](/yativo-fiat/deposits) gives your customer a hosted page to pay with a local method. You're credited in the wallet you choose.

**1. Find a payment method**

List the methods for your customer's country (three-letter code) and currency. The method's `id` is the deposit's `gateway`.

```go theme={null}
resp, err := client.PaymentMethods.Payin(ctx, yativo.Query{"country": "BRA", "currency": "BRL"})
var methods []struct {
	ID           int    `json:"id"`
	MethodName   string `json:"method_name"`
	BaseCurrency string `json:"base_currency"`
}
err = resp.Decode(&methods)
```

**2. Create the deposit**

`amount` is in the method's currency. `currency` is the wallet you're credited in: one of the method's `base_currency` values. Send your order ID as the idempotency key so a retry never creates a second deposit.

```go theme={null}
resp, err := client.Deposits.Create(ctx, map[string]any{
	"gateway":      22,    // the payment method id from step 1
	"amount":       250,   // how much the customer pays, in the method's currency (BRL here)
	"currency":     "USD", // which of your wallets gets the money (one of base_currency)
	"redirect_url": "https://your-shop.com/orders/1001", // where the customer goes afterwards
}, yativo.WithIdempotencyKey("order-1001")) // your order id: retrying never charges twice
if err != nil {
	return err
}

var created yativo.DepositCreated
_ = resp.Decode(&created)
// Save created.DepositData.ID with your order, then send the customer here:
http.Redirect(w, r, created.DepositURL, http.StatusSeeOther)
```

**3. Confirm before you fulfil**

When the customer comes back, or a `deposit.updated` webhook arrives, read the deposit from the API. A redirect back to your site isn't proof of payment.

```go theme={null}
resp, err := client.Deposits.Retrieve(ctx, savedDepositID)
var deposit yativo.Deposit
if err == nil && resp.Decode(&deposit) == nil && deposit.Status == "success" {
	// the money arrived: ship the order
}
```

A deposit's `status` is `pending`, `processing`, `success`, `failed`, `cancelled` or `expired`.

## Handle webhooks

Register your URL once and store the secret it returns as `YATIVO_WEBHOOK_SECRET`. Your account has one webhook URL.

```go theme={null}
resp, err := client.WebhookEndpoints.Create(ctx, map[string]any{"url": "https://your-shop.com/webhooks/yativo"})
var endpoint yativo.WebhookEndpoint
_ = resp.Decode(&endpoint)
fmt.Println(endpoint.Secret) // copy this into YATIVO_WEBHOOK_SECRET. Don't share it.
```

The SDK checks the `Signature` header against the raw body and routes each event to your code. It answers `200` when your code succeeds, `401` for a bad signature and `500` when your code fails, so Yativo retries.

```go theme={null}
client, _ := yativo.New("", "") // also reads YATIVO_WEBHOOK_SECRET

client.Webhooks.On(yativo.EventDepositUpdated, func(ctx context.Context, e *yativo.WebhookEvent) error {
	var p struct {
		ID string `json:"id"`
	}
	if err := e.DecodePayload(&p); err != nil {
		return err
	}
	// Ask Yativo directly instead of trusting the message.
	resp, err := client.Deposits.Retrieve(ctx, p.ID)
	if err != nil {
		return err // answers 500, so Yativo tries again later
	}
	var d yativo.Deposit
	if resp.Decode(&d) == nil && d.Status == "success" {
		// mark the order paid
	}
	return nil
})

http.Handle("/webhooks/yativo", client.Webhooks) // checks the signature, answers 200/401/500
log.Fatal(http.ListenAndServe(":3000", nil))
```

Other frameworks:

```go theme={null}
// Using another router? It's a plain http.Handler:
router.Handle("/webhooks/yativo", client.Webhooks)          // chi, gorilla/mux
e.POST("/webhooks/yativo", echo.WrapHandler(client.Webhooks)) // echo

// Or verify one delivery yourself:
event, err := yativo.ConstructEvent(rawBody, r.Header.Get("Signature"), secret)
```

> [!TIP]
>
> Answer within 3 seconds and do slow work in the background. The same event can arrive more than once, so skip ones you've already handled. See [Webhooks](/yativo-fiat/webhooks) for every event.

## Card Checkout

Card Checkout gives your buyer a hosted page to pay by card, Apple Pay or Google Pay. Your account needs Card Checkout access, and the selling customer needs an approved `card_checkout` endorsement.

```go theme={null}
resp, err := client.CardCheckout.CreateOrder(ctx, map[string]any{
	"customer_id":    "your-selling-customer-id", // must have an approved card_checkout endorsement
	"amount":         "49.99",                    // a string with 2 decimals
	"currency":       "USD",
	"description":    "Pro plan, 1 month",
	"reference":      "INV-10023", // your invoice number (must be unique)
	"customer_email": "buyer@example.com",
	"success_url":    "https://your-shop.com/thanks",
	"cancel_url":     "https://your-shop.com/cart",
}, yativo.WithIdempotencyKey("INV-10023"))

var order yativo.CardCheckoutOrder
_ = resp.Decode(&order)
// Send the buyer to order.CheckoutURL exactly as it is (it's signed).
```

Treat the order as paid only after the `card_checkout.order.paid` webhook, confirmed by reading the order. The link works for 60 minutes.

## Send a payout

```go theme={null}
// 1. Which details does this payout method need? (account number, bank, ...)
form, err := client.PaymentMethods.PayoutForm(ctx, methodID, nil)

// 2. Save the person you're paying
resp, err := client.Beneficiaries.Create(ctx, map[string]any{
	"gateway_id": methodID, "currency": "BRL", "nickname": "Supplier Ltda",
	"payment_data": paymentData, // the fields from the form
})
var b struct{ ID int `json:"id"` }
_ = resp.Decode(&b)

// 3. Lock a rate
resp, err = client.Rates.Quote(ctx, map[string]any{
	"from_currency": "USD", "to_currency": "BRL", "amount": 500,
	"method_id": methodID, "method_type": "payout",
})
var quote struct{ QuoteID string `json:"quote_id"` }
_ = resp.Decode(&quote)

// 4. Send it
resp, err = client.Payouts.Create(ctx, map[string]any{
	"debit_wallet": "USD", "payment_method_id": b.ID, "quote_id": quote.QuoteID,
}, yativo.WithIdempotencyKey("invoice-778"))
```

Progress arrives through the `payout.updated` webhook. See [Payouts](/yativo-fiat/payouts).

***

## Everyday tasks

| To… | Call |
| - | - |
| See your balances | `client.Wallets.Balance(ctx, nil)` |
| Create a customer | `client.Customers.Create(ctx, body)` |
| Look up a customer | `client.Customers.Retrieve(ctx, customerID, nil)` |
| Collect a payment | `client.Deposits.Create(ctx, body)` |
| Check a payment | `client.Deposits.Retrieve(ctx, depositID)` |
| List payments | `client.Deposits.List(ctx, yativo.Query{"per_page": "50"})` |
| Give a customer a bank account | `client.VirtualAccounts.Create(ctx, body)` |
| Get an exchange rate | `client.Rates.Quote(ctx, body)` |
| Pay someone | `client.Payouts.Create(ctx, body)` |
| Card payment link | `client.CardCheckout.CreateOrder(ctx, body)` |
| Move money between your wallets | `client.Swaps.Create(ctx, body)` |
| See your transactions | `client.Transactions.List(ctx, nil)` |
| Resend a missed webhook | `client.Events.Resend(ctx, eventID, nil)` |

Every endpoint is wrapped. The full method list is in [docs/REFERENCE.md](https://github.com/Yativo/yativo-fiat-go/blob/main/docs/REFERENCE.md). To call an endpoint the SDK doesn't wrap yet, with the same authentication and retries:

```go theme={null}
resp, err := client.Do(ctx, "GET", "/some/new/endpoint", yativo.Query{"page": "1"}, nil)
```

## Errors

```go theme={null}
_, err := client.Deposits.Create(ctx, map[string]any{"gateway": 22, "currency": "USD"}) // amount is missing

var apiErr *yativo.Error
var connErr *yativo.ConnectionError
switch {
case yativo.IsType(err, yativo.ErrValidation):
	fmt.Println("Please fix:", err.(*yativo.Error).Fields)
case errors.As(err, &apiErr):
	fmt.Println(apiErr.HTTPStatus, apiErr.Code, apiErr.Message)
case errors.As(err, &connErr):
	fmt.Println("Could not reach Yativo:", connErr)
}
```

API failures are `*yativo.Error` with `Type` (`ErrBadRequest`, `ErrAuthentication`, `ErrPermissionDenied`, `ErrNotFound`, `ErrConflict`, `ErrValidation`, `ErrRateLimit`, `ErrServer`), `HTTPStatus`, `Code`, `Message` and `Fields`. Network failures are `*yativo.ConnectionError`. Use `yativo.IsNotFound(err)` and `yativo.IsType(err, yativo.ErrValidation)`.

Network errors, `429` and `5xx` are retried twice with backoff, using the same idempotency key. Other errors are never retried. See [Troubleshooting](/yativo-fiat/sdks/overview#troubleshooting) for what each common error means.

## Pagination

```go theme={null}
// One page
resp, err := client.Deposits.List(ctx, yativo.Query{"currency": "BRL", "per_page": "50"})
var deposits []yativo.Deposit
_ = resp.Decode(&deposits)
fmt.Println(resp.Pagination.LastPage)

// Every page, one item at a time
err = client.Paginate(ctx,
	func(q yativo.Query) (*yativo.Response, error) { return client.Deposits.List(ctx, q) },
	yativo.Query{"per_page": "100"},
	func(item json.RawMessage) error {
		var d yativo.Deposit
		_ = json.Unmarshal(item, &d)
		fmt.Println(d.ID, d.Status)
		return nil
	})
```

## Options

| Option | Default | What it does |
| - | - | - |
| `yativo.New(key, secret)` | `YATIVO_API_KEY`, `YATIVO_API_SECRET` | Your API key and secret |
| `WithEnvironment` | `YATIVO_ENVIRONMENT` or `Production` | `yativo.Production` or `yativo.Sandbox` |
| `WithBaseURL` | `YATIVO_BASE_URL` | Use a different URL (must be `https`, except `localhost`) |
| `WithTimeout` | `30s` | Time to wait for each request |
| `WithMaxRetries` | `2` | Retries for network errors, 429 and 5xx |
| `WithWebhookSecret` | `YATIVO_WEBHOOK_SECRET` | Used by `client.Webhooks` |
| `WithHTTPClient` | `http.Client` | Your own client, e.g. for a proxy |

## Test in sandbox

Use sandbox keys with `YATIVO_ENVIRONMENT=sandbox`. No real money moves. Card Checkout test cards: `4242 4242 4242 4242` succeeds and `4000 0000 0000 0002` is declined, with any future expiry and any CVC. Send yourself sample webhooks:

```go theme={null}
client, _ := yativo.New("", "", yativo.WithEnvironment(yativo.Sandbox))
_, err := client.Sandbox.SimulateWebhook(ctx, "payin", nil) // sends deposit.created and deposit.updated
```

## More

> **[Runnable examples](https://github.com/Yativo/yativo-fiat-go/tree/main/examples)**
>
> A quick start, a complete checkout server with webhooks, and card payments.

> **[Every method](https://github.com/Yativo/yativo-fiat-go/blob/main/docs/REFERENCE.md)**
>
> All endpoints as Go calls.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.