### Setup and Run Frontend
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Commands to install frontend dependencies, generate GraphQL types, and start the Next.js development server.
```bash
cd client
npm install
npm run codegen
npm run dev
```
--------------------------------
### Example Configuration File
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
An example YAML file demonstrating how to provide configuration values for the application. These values can be overridden by environment variables.
```yaml
environment: development
# Other configuration variables
```
--------------------------------
### Self-Hosting Setup
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Command to start the Revline application in a self-hosted configuration using a sample Docker Compose file.
```bash
docker-compose -f sample-docker-compose.yml up -d
```
--------------------------------
### Configure Backend
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Steps to configure the Go backend by copying an example configuration file to `server/config.yaml`.
```bash
cp server/config.example.yaml server/config.yaml
```
--------------------------------
### Run Development Server
Source: https://github.com/dan6erbond/revline/blob/main/website/README.md
Commands to start the Next.js development server. This allows for hot-reloading and immediate feedback as you edit your code. The server typically runs on http://localhost:3000.
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
--------------------------------
### Basic Uber FX Application Setup
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
A minimal Go program demonstrating the basic structure for an application using Uber's FX dependency injection framework. It initializes and runs the FX application.
```go
import "go.uber.org/fx"
func main() {
fx.New().Run()
}
```
--------------------------------
### Run Revline Backend Server
Source: https://github.com/dan6erbond/revline/blob/main/server/README.md
Starts the Revline backend application. This command compiles and runs the main Go program, initializing the HTTP server, GraphQL endpoint, and other services.
```bash
go run main.go
```
--------------------------------
### Configure Frontend Environment
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Instructions to set up the frontend environment variables by copying an example file to `client/.env.local`.
```bash
cp client/.env.example client/.env.local
```
--------------------------------
### Start Development Infrastructure
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Command to start the necessary infrastructure services for local development, including Postgres and MinIO, using Docker Compose.
```bash
docker-compose up
```
--------------------------------
### Integrate HTTP Module in Main Application
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
This Go snippet shows how to integrate the previously defined HTTP FX module into the main application. By calling `fx.New` with the `httpfx.Module` and then `Run()`, the application starts, and the HTTP server is initialized and launched via the FX lifecycle hooks.
```go
fx.New(
httpfx.Module,
).Run()
```
--------------------------------
### Install Dependencies
Source: https://github.com/dan6erbond/revline/blob/main/client/README.md
Command to install project dependencies for the Revline frontend. This step is crucial after cloning the repository to ensure all necessary packages are downloaded and installed.
```bash
cd client
npm install
```
--------------------------------
### Install Go Dependencies
Source: https://github.com/dan6erbond/revline/blob/main/server/README.md
Installs or updates project dependencies using Go modules. This command ensures all required libraries are fetched and managed correctly.
```bash
go mod tidy
```
--------------------------------
### GQLGen Handler Setup
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Defines custom HTTP route handlers for GQLGen's GraphQL endpoint and Playground, adhering to a familiar http.HandlerFunc pattern for integration with a custom HTTP server.
```go
type NewServerResult struct {
fx.Out
Routes []httpfx.Route `group:"routes,flatten"`
}
type PlaygroundHandlerFunc func(http.ResponseWriter, *http.Request)
func (f PlaygroundHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
f(w, r)
}
func (h PlaygroundHandlerFunc) Pattern() string {
return "/playground"
}
func (h PlaygroundHandlerFunc) Methods() []string {
return []string{"GET"}
}
type GraphqlHandlerFunc struct{ *handler.Server }
func (h GraphqlHandlerFunc) Pattern() string {
return "/graphql"
}
func (h GraphqlHandlerFunc) Methods() []string {
return []string{}
}
func NewServer(resolver *graph.Resolver, entClient *ent.Client) NewServerResult {
srv := handler.New(graph.NewExecutableSchema(graph.Config{
Resolvers: resolver,
Directives: graph.DirectiveRoot{
LoggedIn: directives.LoggedIn(),
},
}))
srv.Use(entgql.Transactioner{TxOpener: entClient})
return NewServerResult{Routes: []httpfx.Route{
PlaygroundHandlerFunc(playground.Handler("Revline", "/graphql")),
GraphqlHandlerFunc{srv},
}}
}
```
--------------------------------
### Start Development Server
Source: https://github.com/dan6erbond/revline/blob/main/client/README.md
Command to launch the local development server for the Revline frontend. This command compiles the application and makes it accessible at a specified local URL, typically http://localhost:3000.
```bash
npm run dev
```
--------------------------------
### Configure Environment Variables
Source: https://github.com/dan6erbond/revline/blob/main/client/README.md
Instructions to set up the local environment configuration for the Revline frontend. It involves copying an example environment file and configuring essential variables like the GraphQL endpoint and authentication providers.
```bash
cp .env.example .env.local
```
--------------------------------
### Create Chi Router with Middleware and Routes
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
This Go function acts as an FX provider to create and configure a Chi router. It applies standard middleware like RequestID, RealIP, Recoverer, and Timeout, along with CORS configuration. It also registers custom routes based on the `Route` interface and starts the HTTP server on a configured host and port.
```go
package httpfx
import (
"fmt"
"net/http"
"time"
"github.com/Dan6erbond/revline/internal"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
"go.uber.org/fx"
"go.uber.org/zap"
)
var NewRouterParamTags = fx.ParamTags("", "", "", `group:"routes"`, `group:"middlewares"`)
type Route interface {
http.Handler
Pattern() string
Methods() []string
}
func NewRouter(lc fx.Lifecycle, logger *zap.Logger, config internal.Config, routes []Route, mws []func(http.Handler) http.Handler) *chi.Mux {
outer := chi.NewRouter()
router.Use(middleware.RequestID)
router.Use(middleware.RealIP)
router.Use(middleware.Recoverer)
router.Use(middleware.Timeout(60 * time.Second))
router.Use(cors.Handler(cors.Options{
AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "Content-Length", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300,
Debug: config.Environment == "development",
}))
router.Use(mws...)
router.Get("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
for _, route := range routes {
if len(route.Methods()) == 0 {
router.Handle(route.Pattern(), route)
continue
}
for _, m := range route.Methods() {
switch m {
case "GET":
router.Get(route.Pattern(), route.ServeHTTP)
case "POST":
router.Post(route.Pattern(), route.ServeHTTP)
case "PUT":
router.Put(route.Pattern(), route.ServeHTTP)
case "DELETE":
router.Delete(route.Pattern(), route.ServeHTTP)
case "PATCH":
router.Patch(route.Pattern(), route.ServeHTTP)
case "OPTIONS":
router.Options(route.Pattern(), route.ServeHTTP)
case "HEAD":
router.Head(route.Pattern(), route.ServeHTTP)
default:
logger.Warn("Unsupported method for route", zap.String("method", m))
}
}
}
lc.Append(fx.StartHook(func() {
go http.ListenAndServe(fmt.Sprintf("%s:%d", config.Host, config.Port), router)
}))
return router
}
```
--------------------------------
### React Frontend for Handling Stripe Onboarding Status
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
Manages user feedback after the Stripe onboarding process using React hooks and toasts. It checks for 'success' or 'canceled' query parameters to display appropriate messages to the user, guiding them on the next steps.
```typescript
const router = useRouter();
const success = getQueryParam(router.query.success);
const canceled = getQueryParam(router.query.canceled);
useEffect(() => {
if (success) {
addToast({
title: "Onboarding complete",
description:
"Your payout account has been successfully linked. You're now ready to receive payments and manage transfers.",
color: "success",
});
} else if (canceled) {
addToast({
title: "Onboarding canceled",
description:
"You didn't finish setting up your payout account. Without a connected account, you won’t be able to receive payments. Try again when you're ready.",
color: "warning",
});
}
}, [canceled, success]);
```
--------------------------------
### Initialize Go Module
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Initializes a new Go module for the project. This command sets up the go.mod file, which tracks dependencies for the Go project.
```shell
go mod init github.com/dan6erbond/revline
```
--------------------------------
### Configure and Supply App Configuration with FX
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Configures Viper to read application settings from a YAML file and environment variables, then unmarshals it into a Config struct. The configuration is then supplied to the FX dependency injection container.
```go
func main() {
var config internal.Config
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AutomaticEnv()
internal.SetDefaults()
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("fatal error config file: %v", err)
}
if err = viper.Unmarshal(&config); err != nil {
log.Fatalf("unable to decode into struct, %v", err)
}
fx.New(
fx.Supply(config),
).Run()
}
```
--------------------------------
### Define HTTP FX Module
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
This Go code defines an FX module for the HTTP server. It uses `fx.Provide` to register the `NewRouter` function as a dependency and `fx.Invoke` to ensure the router is created and its startup hook is registered within the FX application lifecycle.
```go
package httpfx
import (
"github.com/go-chi/chi"
"go.uber.org/fx"
)
var Module = fx.Module("http",
fx.Provide(
fx.Annotate(
NewRouter,
NewRouterParamTags,
),
),
fx.Invoke(func(router *chi.Mux) {}),
)
```
--------------------------------
### Run Backend Server
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Command to compile and run the Go backend application from the `server/` directory.
```bash
cd server
go run main.go
```
--------------------------------
### Implement Go Resolver for CreateCar Mutation
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Provides the Go implementation for the `createCar` GraphQL mutation. It retrieves user context, sets the owner ID from the context, and uses the Ent client to create and save a new car.
```go
// CreateCar is the resolver for the createCar field.
func (r *mutationResolver) CreateCar(ctx context.Context, input ent.CreateCarInput) (*ent.Car, error) {
user := auth.ForContext(ctx)
input.OwnerID = &user.ID
return r.entClient.Car.Create().SetInput(input).Save(ctx)
}
```
--------------------------------
### Clone Revline Repository
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Instructions to clone the Revline project from GitHub and navigate into the project directory.
```bash
git clone https://github.com/dan6erbond/revline.git
cd revline
```
--------------------------------
### Define Configuration Struct and Viper Defaults
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Defines a Go struct to hold application configuration and sets default values using the Viper library. This struct is intended to be populated from environment variables or configuration files.
```go
package internal
import "github.com/spf13/viper"
type Config struct {
DatabaseURL string
Host string
Port uint
Environment string
// Other configuration variables
}
func SetDefaults() {
viper.SetDefault("host", "localhost")
viper.SetDefault("port", 4000)
viper.SetDefault("environment", "development")
}
```
--------------------------------
### Ent Code Generation with entgql
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Configures Ent code generation using the entgql extension to generate GraphQL schemas, enable Relay specifications, and integrate with the Go backend.
```go
package main
import (
"log"
"entgo.io/contrib/entgql"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
)
func main() {
ex, err := entgql.NewExtension(
entgql.WithSchemaGenerator(),
entgql.WithSchemaPath("../graph/ent.graphqls"),
entgql.WithWhereInputs(true),
entgql.WithRelaySpec(true),
// entgql.WithNodeDescriptor(false),
)
if err != nil {
log.Fatalf("creating entgql extension: %v", err)
}
if err := entc.Generate("./schema", &gen.Config{}, entc.Extensions(ex), entc.FeatureNames("entql", "privacy", "schema/snapshot", "sql/execquery")); err != nil {
log.Fatalf("running ent codegen: %v", err)
}
}
```
--------------------------------
### GQLGen Module Definition
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Defines the fx.Module for GQLGen integration, providing the new resolver and server components to the Uber FX application.
```go
package graphfx
import (
"github.com/Dan6erbond/revline/graph"
"go.uber.org/fx"
)
var Module = fx.Module("graph",
fx.Provide(
graph.NewResolver,
NewServer,
),
)
```
--------------------------------
### Implement Go Resolver for CreateFuelUp Mutation
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Provides the Go implementation for the `CreateFuelUp` GraphQL mutation. It utilizes a transactional Ent client to ensure atomicity when creating a new fuel-up record, incorporating any extended input fields.
```go
// CreateFuelUp is the resolver for the createFuelUp field.
func (r *mutationResolver) CreateFuelUp(ctx context.Context, input ent.CreateFuelUpInput) (*ent.FuelUp, error) {
c := ent.FromContext(ctx)
return c.FuelUp.Create().SetInput(input).Save(ctx)
}
```
--------------------------------
### Extend GraphQL CreateFuelUpInput with OdometerKm
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Extends the Ent-generated `CreateFuelUpInput` type to include an optional `odometerKm` field. This allows capturing odometer readings directly within the fuel-up creation input.
```gql
extend input CreateFuelUpInput {
odometerKm: Float
}
```
--------------------------------
### Repository Structure
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Outlines the directory structure of the Revline project, separating the Next.js frontend and Go backend, along with Docker configuration files.
```bash
revline/
├── client/ # Next.js frontend with Apollo Client
├── server/ # Go backend using Fx, gqlgen, ent
├── docker-compose.yml # Dev infra (Postgres, MinIO only)
├── sample-docker-compose.yml # Self-hosting reference
└── README.md
```
--------------------------------
### Implement Go Resolver for CreateFuelUpInput OdometerKm Field
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Implements the resolver for the extended `odometerKm` field within `CreateFuelUpInput`. If `odometerKm` is provided, it creates a corresponding `OdometerReading` record using the Ent client and associates its ID with the input.
```go
// OdometerKm is the resolver for the odometerKm field.
func (r *createFuelUpInputResolver) OdometerKm(ctx context.Context, obj *ent.CreateFuelUpInput, data *float64) error {
if data != nil {
c := ent.FromContext(ctx)
or, err := c.OdometerReading.Create().
SetCarID(obj.CarID).
SetReadingKm(*data).
SetReadingTime(obj.OccurredAt).
SetNotes("Created by fuel-up").
Save(ctx)
if err != nil {
return err
}
obj.OdometerReadingID = &or.ID
return err
}
return nil
}
```
--------------------------------
### Revline Frontend Folder Layout
Source: https://github.com/dan6erbond/revline/blob/main/client/README.md
Provides an overview of the directory structure for the Revline frontend client application. It details the organization of assets, public files, and the main source code within the 'src' directory, including components, pages, and utility files.
```bash
client/
├── assets/ # Used in OG images and API routes
├── public/ # Logos, static placeholders, etc.
└── src/
├── apollo-client/ # Apollo setup, auth handling, upload link
├── app/ # App Router - used for public/marketing pages
├── auth/ # Auth.js configuration
├── components/ # Reusable UI components
├── contexts/ # React contexts
├── gql/ # Auto-generated GraphQL types
├── hooks/ # Custom hooks
├── literals/ # UI literals & constants
├── pages/ # Pages Router - main CSR app shell
├── styles/ # Global styles
├── utils/ # Helpers and utility functions
└── middleware.ts # Auth protection for non-public routes
```
--------------------------------
### Implement Go Resolver for Car AverageConsumption Field
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Provides a placeholder Go resolver function for the `averageConsumptionLitersPerKm` field added to the `Car` type. Currently, it panics indicating that the implementation is not yet complete.
```go
// AverageConsumptionLitersPerKm is the resolver for the averageConsumptionLitersPerKm field.
func (r *carResolver) AverageConsumptionLitersPerKm(ctx context.Context, obj *ent.Car) (float64, error) {
panic("Not implemented: Car - averageConsumptionLitersPerKm")
}
```
--------------------------------
### MinIO Access Details
Source: https://github.com/dan6erbond/revline/blob/main/README.md
Credentials and URL for accessing the MinIO object storage console, used for local media storage.
```APIDOC
MinIO Console:
URL: http://localhost:9001
Access Key: minioadmin
Secret Key: minioadmin
```
--------------------------------
### React Frontend for Initiating Stripe Onboarding
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
Handles the user interaction for connecting a Stripe account in the React frontend. It orchestrates calls to `createConnectAccount` and `linkConnectAccount` mutations, redirecting the user to Stripe's onboarding flow upon successful account linking.
```typescript
}
onPress={() => {
let promise: Promise<
FetchResult | undefined
>;
if (data?.me.stripeAccountID) {
promise = mutateLinkConnectAccount();
} else {
promise = mutateCreateConnectAccount().then(({ data }) => {
if (!data?.createConnectAccount) return;
return mutateLinkConnectAccount();
});
}
promise.then((res) => {
if (!res?.data?.linkConnectAccount) return;
window.location.href = res.data.linkConnectAccount;
});
}}
isLoading={isCreatingConnectAccount || isLinkingConnectAccount}
>
Connect Stripe Account
```
--------------------------------
### Generate GQLGen GraphQL Code
Source: https://github.com/dan6erbond/revline/blob/main/server/README.md
Executes the GQLGen code generation for the GraphQL API. This command generates Go code for GraphQL schema, resolvers, and models based on the defined `.graphqls` files.
```bash
go generate ./graph
```
--------------------------------
### GraphQL Mutations for Stripe Connect
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
Defines the GraphQL mutations required for managing Stripe Connect accounts within the Revline platform. These mutations handle account creation, linking, and generating login links for express onboarding.
```APIDOC
extend type Mutation {
createConnectAccount: User! @loggedIn
linkConnectAccount: String! @loggedIn
createExpressLoginLink: String! @loggedIn
}
```
--------------------------------
### GraphQL Query Schema Extension
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Extends the GraphQL schema to define a `car` query, specifying its input (ID) and output type.
```gql
extend type Query {
car(id: ID!): Car!
}
```
--------------------------------
### GraphQL Car Query Resolver
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Implements the `car` query resolver in Go, fetching a `Car` entity from the Ent client using a parsed UUID.
```go
// Car is the resolver for the car field.
func (r *queryResolver) Car(ctx context.Context, id string) (*ent.Car, error) {
uid, err := uuid.Parse(id)
if err != nil {
return nil, err
}
return r.entClient.Car.Get(ctx, uid)
}
```
--------------------------------
### Go Resolver for Linking Stripe Connect Account
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
Implements the `linkConnectAccount` GraphQL mutation in Go. This function generates a Stripe Account Link URL, directing the user to Stripe's onboarding page. It uses the user's existing Stripe Account ID and specifies return URLs for success and cancellation.
```go
// LinkConnectAccount is the resolver for the linkConnectAccount field.
func (r *mutationResolver) LinkConnectAccount(ctx context.Context) (string, error) {
user := auth.ForContext(ctx)
baseURL, err := url.Parse(r.config.PublicURL)
if err != nil {
return "", err
}
var (
successURL = baseURL.JoinPath("/affiliate")
cancelURL = baseURL.JoinPath("/affiliate")
)
successURL.RawQuery = "success=true"
cancelURL.RawQuery = "canceled=true"
accountLink, err := accountlink.New(&stripe.AccountLinkParams{
Account: user.StripeAccountID,
ReturnURL: stripe.String(successURL.String()),
RefreshURL: stripe.String(cancelURL.String()),
Type: stripe.String("account_onboarding"),
})
if err != nil {
return "", err
}
return accountLink.URL, err
}
```
--------------------------------
### Define GraphQL CreateCar Mutation
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Extends the GraphQL Mutation type to include a new mutation for creating a car. It specifies the input type and the return type, along with an authentication directive.
```gql
extend type Mutation {
createCar(input: CreateCarInput!): Car! @loggedIn
}
```
--------------------------------
### Handle Stripe Connect Webhook (Go)
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
This Go function sets up an HTTP handler to process Stripe Connect webhook events. It verifies the Stripe signature, reads and parses the event data, specifically handling the 'account.updated' event to update user capabilities in the database. Dependencies include Stripe Go SDK, Zap logger, and an ORM client (ent).
```go
package main
import (
"encoding/json"
"io"
"net/http"
"go.uber.org/zap"
"github.com/stripe/stripe-go/v74"
"github.com/stripe/stripe-go/v74/webhook"
"your_project/ent"
"your_project/internal"
)
func ConnectWebhook(config internal.Config, entClient *ent.Client, logger *zap.Logger) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handleError := func(message string, code int, err error) {
http.Error(w, err.Error(), code)
logger.Warn(message, zap.Error(err))
}
const MaxBodyBytes = int64(65536)
r.Body = http.MaxBytesReader(w, r.Body, MaxBodyBytes)
body, err := io.ReadAll(r.Body)
if err != nil {
handleError("Error reading request body", http.StatusBadRequest, err)
return
}
event, err := webhook.ConstructEvent(body, r.Header.Get("Stripe-Signature"), config.Stripe.ConnectWebhookSecret)
if err != nil {
handleError("Error verifying webhook signature", http.StatusBadRequest, err)
return
}
if event.Type == "account.updated" {
var account stripe.Account
if err := json.Unmarshal(event.Data.Raw, &account); err != nil {
handleError("Error unmarshaling event data", http.StatusBadRequest, err)
return
}
// Assuming User model has a StripeAccountID field and a SetStripeAccountCapabilities method
user, err := entClient.User.Query().Where(
user.StripeAccountID(account.ID),
).First(r.Context())
if err != nil {
handleError("Error retrieving user by Stripe account ID", http.StatusInternalServerError, err)
return
}
var capabilities map[string]string
capabilitiesJSON, err := json.Marshal(account.Capabilities)
if err != nil {
handleError("Failed to marshal account capabilities", http.StatusInternalServerError, err)
return
}
if err := json.Unmarshal(capabilitiesJSON, &capabilities); err != nil {
handleError("Failed to unmarshal account capabilities", http.StatusInternalServerError, err)
return
}
if _, err := user.Update().SetStripeAccountCapabilities(capabilities).Save(r.Context()); err != nil {
handleError("Failed to update user Stripe account capabilities", http.StatusInternalServerError, err)
return
}
}
w.WriteHeader(http.StatusOK)
})
}
```
--------------------------------
### Generate Ent ORM Code
Source: https://github.com/dan6erbond/revline/blob/main/server/README.md
Runs the Ent ORM code generation process for the backend. This command generates Go code based on the Ent schema definitions, including database interactions and GraphQL extensions.
```bash
go generate ./ent
```
--------------------------------
### Ent Generate Directive
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Specifies the command to run the custom Ent code generation script (`entc.go`) using Go modules.
```go
package ent
//go:generate go run -mod=mod entc.go
```
--------------------------------
### Process Stripe Webhook for Subscription Creation
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
This Go function handles Stripe's 'checkout.session.completed' webhook event. It parses the session, locks the subscriptions table, finds the associated checkout session, determines the subscription tier, and creates a new subscription record in the database, including affiliate codes.
```go
func Webhook(config internal.Config, entClient *ent.Client, logger *zap.Logger) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Parse and validate webhook payload
switch event.Type {
case "checkout.session.completed":
tx, err := entClient.Tx(r.Context())
var session stripe.CheckoutSession
if err := json.Unmarshal(event.Data.Raw, &session); err != nil {
rollback(tx, "Error unmarshaling event data", http.StatusBadRequest, err)
return
}
if _, err := tx.ExecContext(r.Context(), "LOCK TABLE subscriptions IN ACCESS EXCLUSIVE MODE"); err != nil {
rollback(tx, "Error locking subscriptions table", http.StatusBadRequest, err)
return
}
uid, err := uuid.Parse(session.ClientReferenceID)
if err != nil {
rollback(tx, "Error parsing checkout session reference UUID", http.StatusInternalServerError, err)
return
}
checkoutSession, err := tx.CheckoutSession.Query().
Where(checkoutsession.Or(
checkoutsession.IDEQ(uid),
checkoutsession.StripeSessionIDEQ(session.ID),
)).
WithUser().
First(r.Context())
if err != nil {
rollback(tx, "Error finding checkout session", http.StatusInternalServerError, err)
return
}
var tier = subscriptionq.TierDiy
if checkoutSession.StripePriceID == config.Stripe.Products.Enthusiast.Prices.Monthly.ID {
tier = subscriptionq.TierEnthusiast
}
if _, err = tx.Subscription.Create().
SetCheckoutSession(checkoutSession).
SetStripeSubscriptionID(session.Subscription.ID).
SetStatus(subscriptionq.StatusActive).
SetTier(tier).
SetUser(checkoutSession.Edges.User).
SetNillableAffiliate6moCode(checkoutSession.Affiliate6moCode).
SetNillableAffiliate12moCode(checkoutSession.Affiliate12moCode).
Save(r.Context()); err != nil {
rollback(tx, "Error saving subscription", http.StatusInternalServerError, err)
return
}
if err := tx.Commit(); err != nil {
rollback(tx, "Error committing transaction", http.StatusInternalServerError, err)
return
}
}
})
}
```
--------------------------------
### Go Resolver for Creating Stripe Connect Account
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
Implements the `createConnectAccount` GraphQL mutation in Go. This function creates a new Stripe Connect account of type 'express' for the logged-in user and updates the user record with the Stripe account ID and generated affiliate codes.
```go
// CreateConnectAccount is the resolver for the createConnectAccount field.
func (r *mutationResolver) CreateConnectAccount(ctx context.Context) (*ent.User, error) {
user := auth.ForContext(ctx)
account, err := account.New(&stripe.AccountParams{
Controller: &stripe.AccountControllerParams{
StripeDashboard: &stripe.AccountControllerStripeDashboardParams{
Type: stripe.String("express"),
},
Fees: &stripe.AccountControllerFeesParams{
Payer: stripe.String("application"),
},
Losses: &stripe.AccountControllerLossesParams{
Payments: stripe.String("application"),
},
},
})
if err != nil {
return nil, err
}
if _, err := user.Update().
SetStripeAccountID(account.ID).
SetAffiliate6moCode(petname.Generate(3, "-")).
SetAffiliate12moCode(petname.Generate(3, "-")).
Save(ctx); err != nil {
return nil, err
}
return user, err
}
```
--------------------------------
### Add GraphQL AverageConsumption Field to Car Type
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/go-gqlgen-ent-uber-fx.mdx
Extends the existing `Car` GraphQL type to include a new field, `averageConsumptionLitersPerKm`. This field is intended to be calculated at runtime.
```gql
extend type Car {
averageConsumptionLitersPerKm: Float!
}
```
--------------------------------
### Generate GraphQL Types
Source: https://github.com/dan6erbond/revline/blob/main/client/README.md
Command to generate TypeScript types for GraphQL queries and mutations. This process uses the GraphQL schema from the backend to create type-safe data structures, enhancing developer experience and reducing runtime errors.
```bash
npm run codegen
```
--------------------------------
### Handle Stripe Invoice Paid Event for Affiliate Payouts
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
Processes Stripe's 'invoice.paid' event to identify and pay affiliate commissions. It retrieves subscription details, checks for affiliate codes, fetches expanded invoice and payment intent data, calculates the commission amount (20% for 6-month codes, 30% for 12-month codes), and initiates a Stripe transfer to the affiliate's Connect account.
```go
var invoice stripe.Invoice
if err := json.Unmarshal(event.Data.Raw, &invoice); err != nil {
handleError("Error unmarshaling event data", http.StatusBadRequest, err)
return
}
id := invoice.Lines.Data[0].Parent.SubscriptionItemDetails.Subscription
sub, err := entClient.Subscription.Query().
Where(subscriptionq.StripeSubscriptionID(id)).
First(r.Context())
if err != nil {
handleError("Error retrieving subscription by Stripe ID", http.StatusInternalServerError, err)
return
}
if sub.Affiliate6moCode != nil || sub.Affiliate12moCode != nil {
expandedInvoice, err := stripeInvoice.Get(invoice.ID, &stripe.InvoiceParams{
Expand: stripe.StringSlice([]string{"payments.data.payment.payment_intent"}),
})
if err != nil {
handleError("Error retrieving expanded invoice", http.StatusInternalServerError, err)
return
}
paymentIntent, err := paymentintent.Get(expandedInvoice.Payments.Data[0].Payment.PaymentIntent.ID, &stripe.PaymentIntentParams{
Expand: stripe.StringSlice([]string{"latest_charge"}),
})
if err != nil {
handleError("Error retrieving expanded payment intent", http.StatusInternalServerError, err)
return
}
var (
affiliatePartner *ent.User
amount int64
)
if sub.Affiliate6moCode != nil {
if time.Unix(invoice.Created, 0).After(sub.CreateTime.AddDate(0, 7, 0)) {
w.WriteHeader(http.StatusOK)
return
}
affiliatePartner, err = entClient.User.Query().Where(
userq.Affiliate6moCodeEQ(*sub.Affiliate6moCode),
).First(r.Context())
amount = int64(float64(expandedInvoice.Lines.Data[0].Amount) * 0.2)
} else {
if time.Unix(invoice.Created, 0).After(sub.CreateTime.AddDate(0, 13, 0)) {
w.WriteHeader(http.StatusOK)
return
}
affiliatePartner, err = entClient.User.Query().Where(
userq.Affiliate12moCodeEQ(*sub.Affiliate12moCode),
).First(r.Context())
amount = int64(float64(expandedInvoice.Lines.Data[0].Amount) * 0.3)
}
if err != nil {
handleError("Error finding affiliate partner", http.StatusInternalServerError, err)
return
}
params := &stripe.TransferParams{
Amount: stripe.Int64(amount),
Currency: stripe.String(expandedInvoice.Lines.Data[0].Currency),
Destination: affiliatePartner.StripeAccountID,
SourceTransaction: &paymentIntent.LatestCharge.ID,
}
if _, err := transfer.New(params); err != nil {
handleError("Failed to transfer affiliate payout", http.StatusInternalServerError, err)
return
}
}
w.WriteHeader(http.StatusOK)
```
--------------------------------
### Check Account Capabilities in Frontend (TSX)
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
This TypeScript/React snippet demonstrates how to conditionally render UI elements based on fetched user data, specifically checking Stripe account capabilities. It accesses `stripeAccountCapabilities.transfers` to determine if the 'transfers' capability is 'active', influencing whether to show an affiliate code or an onboarding button.
```tsx
data?.me.stripeAccountID && data.me.stripeAccountCapabilities?.transfers === "active"
```
--------------------------------
### Validate Affiliate Code in Backend Request
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
Backend Go code to validate an incoming affiliate code against user records in the database. It queries for matching 6-month or 12-month affiliate codes and associates the found partner with the checkout session.
```go
if input.Affiliate != nil {
affiliatePartner, err = r.entClient.User.Query().Where(
userq.Or(
userq.Affiliate6moCodeEQ(*input.Affiliate),
userq.Affiliate12moCodeEQ(*input.Affiliate),
),
).First(ctx)
if err != nil {
return "", err
}
if *affiliatePartner.Affiliate6moCode == *input.Affiliate {
checkoutSessionCreate.SetAffiliate6moCode(*affiliatePartner.Affiliate6moCode)
} else if *affiliatePartner.Affiliate12moCode == *input.Affiliate {
checkoutSessionCreate.SetAffiliate12moCode(*affiliatePartner.Affiliate12moCode)
}
}
```
--------------------------------
### Retrieve Affiliate Code from Cookie for Checkout
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
This function retrieves the affiliate code from the document's cookies and includes it in the checkout mutation variables. It handles decoding and parsing the cookie string.
```javascript
const checkout = () => {
const decodedCookie = decodeURIComponent(document.cookie);
const ca = decodedCookie.split(";");
const affiliate = ca
.find((c) => c.trim().startsWith("affiliate="))
?.substring(11);
mutate({
variables: {
input: { tier: SubscriptionTier.Diy, affiliate },
},
}).then(
({ data }) =>
data?.createCheckoutSession &&
(window.location.href = data?.createCheckoutSession)
);
}
```
--------------------------------
### Set Affiliate Cookie in Next.js Pages Router
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
This snippet demonstrates how to capture an affiliate code from the router's query parameters and set it as a cookie, suitable for applications using the Next.js Pages Router.
```javascript
const router = useRouter();
const href = useHref();
useEffect(() => {
if (getQueryParam(router.query.affiliate)) {
document.cookie = `affiliate=${router.query.affiliate}`;
}
}, [router.query]);
```
--------------------------------
### Set Affiliate Cookie in Next.js App Router
Source: https://github.com/dan6erbond/revline/blob/main/website/src/app/blog/posts/stripe-affiliate-go-nextjs.mdx
This component reads the 'affiliate' query parameter from the URL and stores it in a document cookie. It's designed for Next.js App Router and should be placed in a high-level layout.
```tsx
"use client";
import { useEffect } from "react";
import { useSearchParams } from "next/navigation";
export default function AffiliateCookie() {
const query = useSearchParams();
useEffect(() => {
if (query?.get("affiliate")) {
document.cookie = `affiliate=${query.get("affiliate")}`;
}
}, [query]);
return null;
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.