Skip to content

gocanto/payment-gateway

Repository files navigation

Payment Gateway

Lean payment orchestration API backed by a JSON-persisted store that mimics SQLite behaviour. The service exposes Stripe-inspired payment flows over HTTP with request logging, webhook verification, and a simple sweeper for stale authorisations.

Architecture and Trade-offs

Click here

What lives in this repository?

The codebase is split into a handful of small packages:

Package Purpose
internal/config Loads .env files and exposes runtime configuration such as bind address, Stripe keys, and environment mode.
internal/endpoint HTTP router plus request handlers for intents, captures, refunds, checkout sessions, webhooks, and health checks.
internal/payments Core domain service that orchestrates repository updates and calls into the payment gateway abstraction. Includes a fake gateway for local use and the Stripe-backed implementation for production.
internal/store JSON-backed persistence layer that mirrors a subset of SQLite behaviour and stores transactions plus webhook payloads.
internal/jobs Background checkout tracker that periodically polls Stripe (or the fake gateway) to keep transactions in sync after hosted checkout flows.
internal/types Serializable data models shared across the service boundary.

cmd/server/main.go wires everything together: it loads configuration, opens the store, picks the correct gateway (FakeGateway for non-production environments), and starts the HTTP server along with the authorization sweeper job.

Transactions move through a handful of statuses defined in internal/types: created → authorized → captured/refunded or voided/expired/failed. Metadata is stored as JSON on each transaction and webhook payloads are persisted for auditability.

Environment variables

Variable Required Description
STRIPE_SECRET_KEY Used for authenticating with Stripe's REST API. The server will exit if it is not provided.
STRIPE_WEBHOOK_SECRET Optional secret used to verify incoming Stripe webhook signatures.
APP_BASE_URL Base URL used to resolve default success/cancel URLs for checkout sessions and determine the listen port. Defaults to http://localhost:8080.
APP_ENV Controls runtime behaviour (local, production). local enables the fake gateway and bypasses Stripe.

Configuration values are loaded from a .env file at the repository root using github.com/joho/godotenv. Duplicate .env.example if you need to customise values locally. When APP_ENV=local the server automatically switches to the in-memory fake gateway so you can exercise the API without real Stripe credentials.

Getting started

make format        # gofmt -w -s .
make test-all      # go test ./... inside a golang:1.25 container
make test-coverage # go test ./... inside a golang:1.25 container
make build         # compile ./cmd/server into ./bin/app with the dockerised Go toolchain
make run           # execute the built binary (requires prior make build)

To run the service with Docker, build and start the containerised server:

docker compose up --build

The application listens on port 8080 inside the container and is published to port 8004 on the host. Once the container is healthy you can access the API at http://localhost:8004.

Running locally without Docker

  1. Copy .env.example to .env and set APP_ENV=local. Leave the Stripe keys blank to exercise the fake gateway.
  2. Install Go 1.22 or newer.
  3. Run make build && make run.
  4. Issue HTTP requests against http://localhost:8080 using curl or your favourite API client.

API surface

All endpoints live under /v1 and accept/return JSON. Every JSON response includes a request_id field that can be used to correlate server logs with API calls.

Method Path Description
POST /v1/payments/intent Create a payment intent and transaction record.
POST /v1/payments/confirm Confirm an intent with a payment method.
POST /v1/payments/capture Capture an authorised payment (optionally partial).
POST /v1/payments/refund Refund a captured payment (optionally partial).
POST /v1/payments/void Void an uncaptured payment.
POST /v1/payments List all transactions in chronological order.
POST /v1/payments/{id} Retrieve a transaction by ID.
POST /v1/checkout/session Create a Stripe Checkout session for a transaction.
POST /v1/stripe/webhook Receive Stripe webhooks and sync transaction status.

A simple health probe is available via POST /healthz.

API usage examples (curl)

The fake gateway (APP_ENV=local) lets you exercise the full lifecycle without touching real Stripe systems. The commands below use realistic but fake identifiers so you can copy/paste them as-is to understand request payloads. They target http://localhost:8080, which is the default when running the binary directly. If you're using Docker, replace the host and port with http://localhost:8004.

BASE_URL=${BASE_URL:-http://localhost:8080}
# python3 -m json.tool pretty-prints the JSON responses shown below.

# 1. Create a payment intent (returns transaction + client secret)
curl -sS -X POST "${BASE_URL}/v1/payments/intent" \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 4200,
    "currency": "usd",
    "capture_method": "manual",
    "merchant_order_id": "order-1001",
    "metadata": {"customer_id": "cust_demo_42"}
  }' | python3 -m json.tool

# 2. Confirm the intent with a fake payment method
curl -sS -X POST "${BASE_URL}/v1/payments/confirm" \
  -H 'Content-Type: application/json' \
  -d '{
    "tx_id": "txn_demo_1001",
    "payment_method": "pm_card_visa"
  }' | python3 -m json.tool

# 3. Capture the authorised payment (omit amount to capture in full)
curl -sS -X POST "${BASE_URL}/v1/payments/capture" \
  -H 'Content-Type: application/json' \
  -d '{
    "tx_id": "txn_demo_1001",
    "amount": 2100
  }' | python3 -m json.tool

# 4. Refund a portion of the captured payment
curl -sS -X POST "${BASE_URL}/v1/payments/refund" \
  -H 'Content-Type: application/json' \
  -d '{
    "tx_id": "txn_demo_1001",
    "amount": 1000
  }' | python3 -m json.tool

# 5. Void another authorisation
curl -sS -X POST "${BASE_URL}/v1/payments/void" \
  -H 'Content-Type: application/json' \
  -d '{"tx_id": "txn_demo_2001"}' | python3 -m json.tool

# 6. Create a checkout session (returns hosted page URL)
curl -sS -X POST "${BASE_URL}/v1/checkout/session" \
  -H 'Content-Type: application/json' \
  -d '{
    "tx_id": "txn_demo_1001",
    "amount": 4200,
    "currency": "usd",
    "merchant_order_id": "order-1001",
    "success_url": "https://merchant.example/success",
    "cancel_url": "https://merchant.example/cancel"
  }' | python3 -m json.tool

# 7. List every recorded transaction
curl -sS -X POST "${BASE_URL}/v1/payments" | python3 -m json.tool

# 8. Fetch a transaction to inspect its status
curl -sS -X POST "${BASE_URL}/v1/payments/txn_demo_1001" | python3 -m json.tool

# 9. Simulate a Stripe webhook callback (signature check disabled when secret is empty)
curl -sS -X POST "${BASE_URL}/v1/stripe/webhook" \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "evt_demo_1",
    "type": "payment_intent.succeeded",
    "data": {"object": {"id": "pi_fake_1"}}
  }' | python3 -m json.tool

The repository also includes executable helpers under ssh/ that mirror each example (including list_transactions.sh) so you can run them without copy/pasting JSON payloads. Override BASE_URL when targeting a remote deployment, for example BASE_URL=http://localhost:8004 ./ssh/create_intent.sh when hitting Docker. To run the full sequence with colour-coded output, execute make emulate-payment or run the orchestrator directly via BASE_URL=http://localhost:8004 ./ssh/init.sh.

Webhook testing against a real Stripe account can be performed with Stripe CLI by forwarding events to /v1/stripe/webhook and ensuring STRIPE_WEBHOOK_SECRET matches your CLI signing secret.

Contributing

Please feel free to fork this repository to contribute to it by submitting a functionalities/bugs-fixes pull request to enhance it.

How can I thank you?

There are many ways you would be able to support my open source work. There is not a right one to choose, so the choice is yours.

Nevertheless 😀, I would propose the following

  • ⬆️ Follow me on Twitter.
  • ⭐ Star the repository.
  • 🤝 Open a pull request to fix/improve the codebase.
  • ✍️ Open a pull request to improve the documentation.
  • ☕ Buy me a coffee?

Thank you for reading this far. 😊

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages