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

# Payment Service Architecture

> Isolated payment microservice: gRPC API, multi-provider adapters, pricing, and event publishing

> **Last verified:** 2026-02-12 | Commit scope: `fa4aa75`

## Overview

The payment service is an **isolated microservice** with its own database, VPC, and deployment lifecycle.

**Responsibilities:**

* Payment initiation and processing
* Price quotes and pricing configuration
* Webhook handling from payment providers
* Event publishing for payment lifecycle

**Isolation boundaries:**

* Separate Go module (Go Workspace in `hoodcloud/payment-service`)
* Separate PostgreSQL instance
* Network isolation (designed for separate VPC)
* No card data touches our systems (SAQ A compliance)

## Communication Architecture

```mermaid theme={null}
graph TB
    subgraph MainVPC["HoodCloud Control Plane (Main VPC)"]
        APIServer["API Server<br/>+ gRPC Client"]
        NATSConsumer["NATS Consumer<br/>(payment.*)"]
    end

    subgraph PaymentVPC["Payment Service (Isolated VPC)"]
        gRPCServer["gRPC Server<br/>:50051"]
        NATSPublisher["NATS Publisher"]
        WebhookHandler["Webhook Handler"]
        PricingService["Pricing Service"]
        PaymentService["Payment Service"]
        PaymentDB[(PostgreSQL<br/>Payment)]
        PaymentRedis[(Redis)]
    end

    APIServer -->|"gRPC (mTLS)<br/>VPC Peering"| gRPCServer
    NATSPublisher -->|"NATS JetStream<br/>(mTLS + encryption)"| NATSConsumer

    WebhookHandler --> PaymentService
    PricingService --> PaymentService
    gRPCServer --> PaymentService
    PaymentService --> PaymentDB
    PaymentService --> PaymentRedis
```

### Sync: gRPC + mTLS

| gRPC Method       | Description               |
| ----------------- | ------------------------- |
| `InitiatePayment` | Creates payment session   |
| `GetQuote`        | Returns pricing for items |
| `GetPayment`      | Retrieves payment state   |
| `CancelPayment`   | Cancels pending payment   |

**Security:** TLS 1.3 minimum, client certificate verification (mTLS), CN auth interceptor validates client certificate CN against allowed list (`payment-service/internal/server/grpc.go:cnAuthInterceptor`). Standard gRPC health check service (`grpc.health.v1.Health`).

### Async: NATS JetStream

| Event                   | Subject             | Consumer Action                            |
| ----------------------- | ------------------- | ------------------------------------------ |
| `PaymentCompletedEvent` | `payment.completed` | Create subscriptions, trigger provisioning |
| `PaymentFailedEvent`    | `payment.failed`    | Log failure, notify user                   |
| `PaymentCanceledEvent`  | `payment.canceled`  | Update records                             |

**Delivery:** Durable consumer (`main-app-payment`), manual ACK, idempotency via Redis store.

> **See also:** [Workflows — NATS Consumers](./workflows#nats-consumers) for the consumer implementation in the main app.

## Database Schema

Separate PostgreSQL instance with four tables:

| Table                | Purpose                                                                    |
| -------------------- | -------------------------------------------------------------------------- |
| `payments`           | Core payment record (status, amount, provider reference, crypto details)   |
| `payment_line_items` | Line items per payment (chain\_profile\_id, node\_type, duration, pricing) |
| `payment_events`     | Audit trail (actor, event\_type, request\_id, IP, JSONB details)           |
| `customers`          | Minimal user mirror (id matches main app user\_id, wallet\_address, email) |

### Payment Status Flow

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending
    pending --> processing
    pending --> failed
    pending --> canceled
    processing --> completed
    processing --> failed
    completed --> [*]
    failed --> [*]
    canceled --> [*]
```

### Data Isolation

| Data            | Payment Service            | Main App       |
| --------------- | -------------------------- | -------------- |
| User identity   | Mirror (customer\_id only) | Primary        |
| Payment records | Primary                    | Reference only |
| Subscriptions   | Reference only             | Primary        |

## Pricing Service

Config-based (not database-driven) for simplicity and auditability.

**File:** `payment-service/config/pricing.yaml`

```yaml theme={null}
pricing:
  celestia-mocha:
    light:
      1w: 999       # $9.99
      1m: 2999      # $29.99
    full:
      1w: 1999
      1m: 5999

crypto:
  supported_currencies: [USDC, USDT, ETH]
  exchange_rates:
    USDC: "1.0"
    USDT: "1.0"
    ETH: "2500.0"
  quote_validity: 15m

session:
  expiry: 30m
```

Lookup: `PricingService.GetPrice(chainProfileID, nodeType, duration)` in `payment-service/internal/service/pricing.go`.

## Multi-Provider Architecture

Multiple providers active simultaneously, keyed by payment method. Provider selection based on `method` field in payment request. Registered in `map[PaymentMethod]Provider` during startup.

### Provider Adapter Interface

**File:** `payment-service/internal/adapters/provider.go`

```go theme={null}
type Provider interface {
    Name() string
    InitiatePayment(ctx context.Context, req InitiateRequest) (*InitiateResult, error)
    GetPaymentStatus(ctx context.Context, providerRef string) (*StatusResult, error)
    CancelPayment(ctx context.Context, providerRef string) error
    HandleWebhook(ctx context.Context, payload []byte, signature string) (*WebhookResult, error)
}
```

### Available Adapters

| Adapter  | Method   | Status      | Notes                                                    |
| -------- | -------- | ----------- | -------------------------------------------------------- |
| `stripe` | `card`   | Implemented | Stripe Checkout Sessions, webhook signature verification |
| `tempo`  | `crypto` | Implemented | TIP-20 `TransferWithMemo`, payment ID as `bytes32` memo  |
| `mock`   | any      | Implemented | Configurable delay and failure rate for testing          |

### Stripe Adapter

**Package:** `payment-service/internal/adapters/stripe/`

**Flow:** `InitiatePayment` -> Stripe Checkout Session -> user completes payment -> Stripe webhook (`POST /webhooks/stripe`) -> `HandleWebhook` verifies `Stripe-Signature` -> `CompletePayment` -> NATS `payment.completed`.

**Webhook events:** `checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed`, `checkout.session.expired`.

**Config:** `STRIPE_ENABLED=true`. Credentials (`stripe_secret_key`, `stripe_webhook_secret`) stored in Vault.

### Tempo Adapter

**Package:** `payment-service/internal/adapters/tempo/`

**Flow:** `InitiatePayment` returns receiver address + memo (payment ID as hex) -> user calls `transferWithMemo()` on TIP-20 contract -> background watcher detects `TransferWithMemo` event -> `CompletePayment` -> NATS `payment.completed`.

**Config:** `TEMPO_ENABLED=true`, `TEMPO_RECEIVER_ADDRESS`.

### Payment Methods Endpoint

`GET /api/v1/payment-methods` returns active methods based on enabled providers:

```json theme={null}
{"methods": ["card", "crypto"]}
```

## Main App Integration

### Payment Initiation

**File:** `internal/api/handler_payment.go`

`POST /api/v1/payments` initiates a checkout session via gRPC to the payment service.

| Field                       | Required           | Description                                           |
| --------------------------- | ------------------ | ----------------------------------------------------- |
| `items`                     | Yes                | Line items (chain\_profile\_id, node\_type, duration) |
| `subscription_ids`          | No                 | `pending_payment` subscriptions to link               |
| `method`                    | Yes                | `"card"` or `"crypto"`                                |
| `crypto_currency`           | When method=crypto | `"USDC"`, `"USDT"`, or `"ETH"`                        |
| `idempotency_key`           | Yes                | Client-generated key for safe retries                 |
| `return_url` / `cancel_url` | Yes                | Redirect URLs after checkout                          |

**Subscription linking:** Handler validates each subscription ID (exists, owned by caller, status `pending_payment`), initiates payment via gRPC, sets `payment_id` on each subscription via `UpdatePaymentID`.

### gRPC Client

**File:** `internal/grpc/payment_client.go`

Connects via mTLS. Credentials from Vault or file paths.

## gRPC Service Definition

**File:** `payment-service/proto/payment.proto`

```protobuf theme={null}
service PaymentService {
  rpc InitiatePayment(InitiatePaymentRequest) returns (InitiatePaymentResponse);
  rpc GetPayment(GetPaymentRequest) returns (GetPaymentResponse);
  rpc GetQuote(GetQuoteRequest) returns (GetQuoteResponse);
  rpc CancelPayment(CancelPaymentRequest) returns (CancelPaymentResponse);
}
```

## Vault Integration

**Package:** `payment-service/internal/vault/`

Independent Vault client (separate from main app). AppRole authentication with token renewal. Retrieves `PaymentCredentials` (DB password, Redis password, Stripe keys, NATS CTRL account signing seed).

> **See also:** [Vault](../operations/vault) for Vault setup, secret structure, and operations. See [Environment Variables](../operations/environment-variables) for the complete payment service Vault configuration variables.

## Development

```bash theme={null}
cd payment-service
make proto       # Generate protobuf code
make migrate-up  # Run database migrations
make build       # Build binary
make test        # Run tests
```

**Docker Compose:** `infrastructure/docker/docker-compose.payment.yml` with dedicated Caddy reverse proxy for TLS termination.

| Service          | Port  | Description                            |
| ---------------- | ----- | -------------------------------------- |
| caddy-payment    | 443   | HTTPS (TLS termination, Let's Encrypt) |
| postgres-payment | 5433  | Payment database                       |
| payment-service  | 50051 | gRPC API                               |
| payment-service  | 8085  | HTTP health checks                     |
| payment-service  | 9086  | Prometheus metrics                     |

## Related Documents

* [Overview](./overview) — System overview
* [Workflows](./workflows) — NATS payment consumer, subscription lifecycle
* [Domain Model](./domain-model) — Subscription state machine
* [Extending](./extending) — Adding payment providers
* [Environment Variables](../operations/environment-variables) — Complete env var reference
