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

# Go Boilerplate

> Clone a minimal Go server that creates Dodo Payments checkout sessions, verifies webhooks, and opens the Customer Portal with the dodopayments-go SDK.

<Card title="GitHub Repository" icon="github" href="https://github.com/dodopayments/go-boilerplate">
  Minimal Go + Dodo Payments boilerplate
</Card>

## Overview

The Go boilerplate is a minimal Go server that sells your Dodo Payments products from a pricing page. It creates checkout sessions, verifies and handles webhooks, and opens the Customer Portal. Clone it as the starting point for your own Go backend.

<Info>
  The boilerplate needs Go 1.24.4 or later, the version set in its `go.mod`. It uses a `cmd`, `internal`, and `templates` layout, renders the pricing page with Go HTML templates, and calls the Dodo Payments API through the [`dodopayments-go`](/developer-resources/sdks/go) SDK.
</Info>

### Features

* **Quick Setup**: Clone the repository, add your API keys to `.env`, and start the server with `make run`.
* **Payment Integration**: A checkout flow that creates checkout sessions with the `dodopayments-go` SDK.
* **Modern UI**: A dark-themed pricing page built with Go HTML templates and Tailwind CSS.
* **Webhook Handling**: Verifies the signature of each webhook before it processes the event.
* **Customer Portal**: Self-serve subscription management through the Customer Portal.
* **Go Best Practices**: A clean project layout with `cmd`, `internal`, and `templates`.
* **Pre-filled Checkout**: Passes the customer's name and email to checkout, so the customer doesn't type them again.

## Prerequisites

Before you begin, you need:

* **Go 1.24.4 or later**. Check your version with `go version`.
* **A Dodo Payments account**, to create an API key and a webhook signing key in the dashboard.
* **At least one product**, created under **Products** in the dashboard.

## Quick Start

<Steps>
  <Step title="Clone the Repository">
    ```bash theme={null}
    git clone https://github.com/dodopayments/go-boilerplate.git
    cd go-boilerplate
    ```
  </Step>

  <Step title="Install Dependencies">
    ```bash theme={null}
    make install
    ```

    `make install` runs `go mod download` and then `go mod tidy`. To download the modules without `make`, run:

    ```bash theme={null}
    go mod download
    ```
  </Step>

  <Step title="Get API Credentials">
    Sign up at [Dodo Payments](https://dodopayments.com/), then copy both keys from the dashboard:

    * **API Key:** [Developer → API Keys](https://app.dodopayments.com/developer/api-keys)
    * **Webhook Key:** [Developer → Webhooks](https://app.dodopayments.com/developer/webhooks). Each webhook endpoint has its own signing key. To create an endpoint that reaches your local server, see [Testing Webhooks Locally](#testing-webhooks-locally).

    <Tip>
      Create both keys in test mode while you develop. To switch to test mode, turn off the **Live Mode** switch in the dashboard sidebar.
    </Tip>
  </Step>

  <Step title="Configure Environment Variables">
    Create a `.env` file in the project root from the template:

    ```bash theme={null}
    cp .env.example .env
    ```

    Set these values in `.env`:

    ```bash .env theme={null}
    DODO_PAYMENTS_API_KEY=your_api_key_here
    DODO_PAYMENTS_WEBHOOK_KEY=your_webhook_signing_key_here
    DODO_PAYMENTS_RETURN_URL=http://localhost:8000
    DODO_PAYMENTS_ENVIRONMENT=test_mode
    PORT=8000
    ```

    The server reads these variables at startup:

    | Variable | Required | Default | Purpose |
    | - | - | - | - |
    | `DODO_PAYMENTS_API_KEY` | Yes | None | Authenticates requests to the Dodo Payments API. |
    | `DODO_PAYMENTS_WEBHOOK_KEY` | Yes | None | Verifies the signature of incoming webhooks. |
    | `DODO_PAYMENTS_RETURN_URL` | No | `http://localhost:8080` | The URL that checkout sends the customer to after payment. |
    | `DODO_PAYMENTS_ENVIRONMENT` | No | `test_mode` | `test_mode` or `live_mode`. |
    | `PORT` | No | `8000` | The port the server listens on. |

    The server exits at startup if either required key is missing. `.env.example` sets `PORT` and `DODO_PAYMENTS_RETURN_URL` to port `8080`. This page uses port `8000`, so set both to `8000` as shown, or replace `8000` with `8080` in the commands on this page.

    <Warning>
      Never commit your `.env` file to version control. The repository's `.gitignore` already excludes it.
    </Warning>
  </Step>

  <Step title="Add Your Products">
    Replace the sample product in `internal/lib/products.go` with your products. Copy each product ID from **Products** in the dashboard:

    ```go theme={null}
    var Products = []Product{
        {
            ProductID:   "pdt_001", // Replace with your product ID
            Name:        "Basic Plan",
            Description: "Get access to basic features and support",
            Price:       9999, // in cents
            Features: []string{
                "Access to basic features",
                "Email support",
                "1 Team member",
                "Basic analytics",
            },
        },
        // ... add more products
    }
    ```

    `Price` sets only the price that the pricing page displays, in the smallest currency unit: `9999` displays as \$99.99. Checkout charges the price of the product in Dodo Payments.
  </Step>

  <Step title="Run the Development Server">
    ```bash theme={null}
    make run
    ```

    `make run` builds the server into `bin/server` and starts it. To run the server without building a binary first, run:

    ```bash theme={null}
    go run cmd/server/main.go
    ```

    Open [http://localhost:8000](http://localhost:8000) to see your pricing page.

    <Check>
      You see a dark-themed pricing page that lists your products, ready to purchase.
    </Check>
  </Step>
</Steps>

## Project Structure

The repository has this layout:

```text theme={null}
go-boilerplate/
├── cmd/
│   └── server/             # Application entry point (main.go)
├── internal/
│   ├── api/                # API handlers: checkout.go, portal.go, webhook.go
│   ├── core/               # Configuration loading (config.go)
│   └── lib/                # Shared logic: products.go, customers.go
├── templates/              # HTML templates: base.html, index.html
├── Makefile                # Build and run commands
├── go.mod                  # Go module definition
├── go.sum                  # Dependency checksums
├── .env.example            # Environment template
└── README.md
```

## API Endpoints

The boilerplate includes the following pre-configured endpoints:

| Endpoint | Method | Description |
| - | - | - |
| `/` | GET | Pricing page that lists the products in `internal/lib/products.go`. |
| `/api/checkout` | POST | Creates a checkout session. Send `product_id`, plus optional `quantity` (default `1`) and `customer` (`email`, `name`, `phone`). Returns `session_id` and `checkout_url`. |
| `/api/webhook` | POST | Verifies and handles Dodo Payments webhooks. Returns `401` when the signature check fails. |
| `/api/customer-portal` | POST | Returns a Customer Portal link in `portal_url`. Send `customer_id`, or `email` with an optional `name`. With only an email, the handler looks up the customer and creates one if none exists. |
| `/health` | GET | Health check. Returns `OK`. |

## Customization

### Update Product Information

Edit `internal/lib/products.go` to change:

* Product IDs (from **Products** in your Dodo Payments dashboard)
* Names
* Pricing shown on the pricing page
* Features
* Descriptions

```go theme={null}
var Products = []Product{
    {
        ProductID:   "pdt_001", // Replace with your product ID
        Name:        "Basic Plan",
        Description: "Get access to basic features and support",
        Price:       9999,
        Features: []string{
            "Access to basic features",
            "Email support",
            "1 Team member",
            "Basic analytics",
        },
    },
}
```

The pricing page template adds a `/mo` suffix to every price and shows **Custom** instead of a price when `Price` is `100000` or more. To change this, edit `templates/index.html`.

### Pre-fill Customer Data

In `templates/index.html`, the `handleCheckout` function sends hardcoded customer data to `/api/checkout`. Replace it with your signed-in user's data:

```javascript theme={null}
const customerData = {
    name: "John Doe",       // Replace with actual logged-in user's name
    email: "john@example.com"  // Replace with actual logged-in user's email
};
```

The `handlePortal` function reuses this customer data, and falls back to the same sample name and email. In a production app, inject these values from your authentication system in both functions.

## Webhook Events

`internal/api/webhook.go` verifies each request with `client.Webhooks.Unwrap` and the key in `DODO_PAYMENTS_WEBHOOK_KEY`, then routes the event by its `type`. These events have a handler, and each handler logs the event data:

| Event | Description |
| - | - |
| `payment.succeeded` | Triggered when a payment is successful |
| `payment.failed` | Triggered when a payment attempt fails |
| `subscription.active` | Triggered when a subscription becomes active |
| `subscription.renewed` | Triggered when a subscription renews for the next billing period |
| `subscription.updated` | Triggered when any subscription field changes |
| `subscription.cancelled` | Triggered when a subscription is cancelled |

The handler also accepts `subscription.on_hold`, `subscription.failed`, `subscription.expired`, and `subscription.plan_changed` without any action, and logs every other event type as unhandled. It responds with `200` to every verified event. For all event types, see the [Webhook Event Guide](/developer-resources/webhooks/intents/webhook-events-guide).

Add your business logic to the handler functions to:

* Update user permissions in your database
* Send confirmation emails
* Provision access to digital products
* Track analytics and metrics

## Testing Webhooks Locally

Dodo Payments can't reach `localhost`. To receive webhooks during development, expose your local server with a tunnel such as [ngrok](https://ngrok.com/):

```bash theme={null}
ngrok http 8000
```

In the [Dodo Payments Dashboard](https://app.dodopayments.com/developer/webhooks), add an endpoint with the forwarding URL that ngrok prints, followed by `/api/webhook`:

```text theme={null}
https://your-ngrok-url.ngrok.io/api/webhook
```

Copy the endpoint's signing key into `DODO_PAYMENTS_WEBHOOK_KEY`, then restart the server.

## Deployment

### Build for Production

`make build` compiles the server into `bin/server`:

```bash theme={null}
make build
```

To build and start the binary without `make`, run:

```bash theme={null}
go build -o bin/server cmd/server/main.go
./bin/server
```

### Deploy to Vercel

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/dodopayments/go-boilerplate)

After you deploy, add the variables from your `.env` file to the Vercel project settings, because `.env` isn't in the repository. Then set your webhook endpoint in the dashboard to `https://yourdomain.com/api/webhook`.

### Docker

Create a `Dockerfile` in the project root. The build stage must use Go 1.24.4 or later to match `go.mod`:

```dockerfile theme={null}
FROM golang:1.24-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o bin/server cmd/server/main.go

FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/bin/server .
COPY --from=builder /app/templates ./templates

EXPOSE 8000
CMD ["./server"]
```

The final image copies `templates/` next to the binary, because the server loads the templates from the working directory. Build and run the image:

```bash theme={null}
docker build -t go-dodo .
docker run -p 8000:8000 --env-file .env go-dodo
```

The container listens on the `PORT` value from `.env`, so keep `PORT=8000` to match the port mapping.

### Production Considerations

<Warning>
  Before you deploy to production:

  * Set `DODO_PAYMENTS_ENVIRONMENT` to `live_mode`.
  * Use a live mode API key from the dashboard.
  * Point the webhook endpoint at your production domain, and use that endpoint's signing key.
  * Set `DODO_PAYMENTS_RETURN_URL` to a page on your production domain.
  * Serve every endpoint over HTTPS.
</Warning>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Build errors or missing dependencies">
    Check that `go version` reports Go 1.24.4 or later, then download the modules again:

    ```bash theme={null}
    go mod tidy
    go mod download
    ```
  </Accordion>

  <Accordion title="Checkout session creation fails">
    **Common causes:**

    * The product ID is invalid. Check that it exists under **Products** in the same mode as your API key.
    * The API key or `DODO_PAYMENTS_ENVIRONMENT` in `.env` is wrong. A test mode key needs `test_mode`.
    * For the exact error, check the server logs. The handler logs every failed request before it returns `500`.
  </Accordion>

  <Accordion title="Webhooks not receiving events">
    For local testing, expose your server with [ngrok](https://ngrok.com):

    ```bash theme={null}
    ngrok http 8000
    ```

    Set the webhook URL in your [Dodo Payments dashboard](https://app.dodopayments.com/developer/webhooks) to the ngrok URL. Then set `DODO_PAYMENTS_WEBHOOK_KEY` in `.env` to that endpoint's signing key. If the server logs `webhook verification failed`, the key doesn't match the endpoint.
  </Accordion>

  <Accordion title="Templates not loading">
    The server loads `templates/base.html` and `templates/index.html` from the working directory. Start the server from the project root, or change the template paths in `cmd/server/main.go`.
  </Accordion>
</AccordionGroup>

## Learn More

<CardGroup cols={2}>
  <Card title="Go SDK" icon="golang" href="/developer-resources/sdks/go">
    Complete Go SDK documentation
  </Card>

  <Card title="Webhooks Documentation" icon="webhook" href="/developer-resources/webhooks">
    Learn about all webhook events and best practices
  </Card>

  <Card title="Checkout Sessions" icon="credit-card" href="/developer-resources/checkout-session">
    Deep dive into checkout session configuration
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Complete Dodo Payments API documentation
  </Card>
</CardGroup>

## Support

For help with the boilerplate:

* Ask questions in the [Discord community](https://discord.gg/bYqAp4ayYh).
* Check the [GitHub repository](https://github.com/dodopayments/go-boilerplate) for issues and updates.
* Contact the [support team](mailto:support@dodopayments.com).


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