{"id":50712731,"url":"https://github.com/interledger/mockgatehub","last_synced_at":"2026-06-09T16:32:25.554Z","repository":{"id":351558638,"uuid":"1138840881","full_name":"interledger/mockgatehub","owner":"interledger","description":"An experimental mock service provider for Gatehub","archived":false,"fork":false,"pushed_at":"2026-04-15T13:48:13.000Z","size":8194,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T15:29:39.623Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/interledger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-21T07:24:49.000Z","updated_at":"2026-04-15T13:46:29.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/interledger/mockgatehub","commit_stats":null,"previous_names":["interledger/mockgatehub"],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/interledger/mockgatehub","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/interledger%2Fmockgatehub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/interledger%2Fmockgatehub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/interledger%2Fmockgatehub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/interledger%2Fmockgatehub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/interledger","download_url":"https://codeload.github.com/interledger/mockgatehub/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/interledger%2Fmockgatehub/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34116457,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-09T16:32:21.462Z","updated_at":"2026-06-09T16:32:25.549Z","avatar_url":"https://github.com/interledger.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MockGatehub\n\nEXPERIMENTAL\n\nA lightweight Go mock of the GateHub API for local development and testing of wallet applications that integrate with GateHub.\n\nOfficial GateHub documentation can be found [here](https://docs.gatehub.net/api-documentation/c3OPAp5dM191CDAdwyYS).\n\n## Overview\n\nMockGatehub provides a drop-in replacement for GateHub's sandbox environment, enabling developers to:\n\n- Develop and test wallet integrations without real GateHub credentials\n- Run locally without external API dependencies\n- Test multi-currency operations (11 supported currencies)\n- Verify KYC flows with a realistic iframe and server-side approval\n- Test webhook delivery mechanisms\n- UNSTABLE - Test card issuance, transactions, and 3DS challenge flows\n- UNSTABLE - Configure transaction fees for deposit/withdrawal testing\n\n## Features\n\n- **Full API Coverage**: Authentication, KYC, wallets, transactions, rates, fees, and cards\n- **Multi-Currency Support**: XRP, USD, EUR, GBP, ZAR, MXN, SGD, CAD, EGG, PEB, PKR\n- **Realistic KYC Flow**: Iframe-based form with `action_required` → `accepted` lifecycle\n- **Card Services**: Full card lifecycle — issuance, lock/unlock/block, limits, transactions, 3DS challenges\n- **Webhook Delivery**: Redis-backed job queue with configurable delay, retry, and HMAC signing\n- **Configurable Fees**: Runtime-adjustable deposit and withdrawal fee percentages via admin API\n- **Dual Storage**: In-memory (development) and Redis (runtime) backends\n- **HMAC Authentication**: Enforced by default, matching real GateHub signature validation\n- **Pre-seeded Users**: Test users with balances ready to use\n- **BDD Test Suite**: Comprehensive Gherkin feature files with godog E2E tests\n\n## Quick Start\n\n### Running with Docker Compose\n\n```bash\ndocker compose up -d mockgatehub\n```\n\nThe service will be available at `http://localhost:8080`\n\n### Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MOCKGATEHUB_PORT` | `8080` | HTTP server port |\n| `LOG_LEVEL` | `info` | Log level (`debug`, `info`, `warn`, `error`) |\n| `MOCKGATEHUB_REDIS_URL` | — | Redis connection URL (enables Redis storage) |\n| `MOCKGATEHUB_REDIS_DB` | `0` | Redis database number |\n| `MOCKGATEHUB_ENFORCE_AUTHENTICATION` | `true` | Enable HMAC signature validation |\n| `MOCKGATEHUB_VALID_CREDENTIALS` | `local-test-app-id:local-test-app-secret` | Comma-separated `appId:secret` pairs |\n| `WEBHOOK_URL` | — | Application webhook endpoint URL (fallback if no org config) |\n| `WEBHOOK_SECRET` | `mock-secret` | Secret for signing outgoing webhooks |\n| `WEBHOOK_MIN_DELAY_SEC` | `0.05` | Minimum seconds before webhooks become eligible for delivery |\n| `DEFAULT_ORGANIZATION_ID` | `default-org` | Organization ID for callback routing |\n\n\u003e **Note**: The webhook queue always requires Redis, even when using in-memory storage for application data.\n\n### Pre-seeded Test Users\n\nTwo test users are automatically created at startup:\n\n| | User 1 | User 2 |\n|-|--------|--------|\n| **Email** | `testuser1@mockgatehub.local` | `testuser2@mockgatehub.local` |\n| **User ID** | `00000000-0000-0000-0000-000000000001` | `00000000-0000-0000-0000-000000000002` |\n| **Balance** | 10,000 USD | 10,000 EUR |\n| **KYC State** | `action_required` | `action_required` |\n\n## API Endpoints\n\n### Health \u0026 Utility\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/health` | Service health status |\n| `GET` | `/api/user-currencies` | Currencies with non-zero balances for a user |\n\n### Authentication (`/auth/v1/`)\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/tokens` | Generate iframe access token |\n| `POST` | `/users/managed` | Create managed user |\n| `GET` | `/users/managed` | Get managed user by email |\n| `PUT` | `/users/managed/email` | Update user email |\n| `PATCH` | `/users/organization/{orgID}` | Update organization configuration |\n\n### Identity / KYC (`/id/v1/`)\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/users/{userID}` | Get user state and profile |\n| `POST` | `/users/{userID}/hubs/{gatewayID}` | Start KYC process |\n| `PUT` | `/hubs/{gatewayID}/users/{userID}` | Update KYC state |\n| `POST` | `/hubs/{gatewayID}/users/{userID}/overrideRiskLevel` | Override risk level |\n\n### Iframe Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/iframe/onboarding` | Serve KYC iframe HTML |\n| `POST` | `/iframe/submit` | KYC iframe form submission |\n| `GET` | `/` | Serve deposit/withdrawal iframe |\n| `POST` | `/transaction/complete` | Iframe transaction completion callback |\n\n### Wallets \u0026 Transactions (`/core/v1/`)\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/users/{userID}` | Get user wallets (auto-creates if none) |\n| `POST` | `/users/{userID}/wallets` | Create new wallet |\n| `GET` | `/users/{userID}/wallets/{walletID}` | Get wallet details |\n| `GET` | `/wallets/{walletID}/balances` | Get multi-currency balance |\n| `POST` | `/transactions` | Create deposit/hosted/withdrawal transaction |\n| `GET` | `/transactions/{txID}` | Get transaction details |\n\n### Rates (`/rates/v1/`)\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/rates/current` | Get current exchange rates |\n| `GET` | `/liquidity_provider/vaults` | Get vault UUIDs |\n\n### Fee Configuration (Admin)\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/admin/fees` | Get current fee percentages |\n| `PUT` | `/admin/fees` | Set deposit/withdrawal fee percentages (0–100) |\n\n### Cards (`/cards/v1/`)\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/customers` | Create customer |\n| `POST` | `/customers/managed` | Create managed customer with account and card |\n| `POST` | `/customers/{customerID}/addresses` | Add delivery address |\n| `GET` | `/customers/{customerID}/addresses` | List delivery addresses |\n| `POST` | `/cards` | Create card |\n| `GET` | `/cards/{customerID}` | List cards for customer |\n| `GET` | `/cards/{cardID}/card` | Get card details |\n| `DELETE` | `/cards/{cardID}/card` | Delete (soft-delete) card |\n| `PUT` | `/cards/{cardID}/lock` | Lock card temporarily |\n| `PUT` | `/cards/{cardID}/unlock` | Unlock card |\n| `PUT` | `/cards/{cardID}/block` | Block card permanently |\n| `GET` | `/cards/{cardID}/limits` | Get card spending limits |\n| `PUT` | `/cards/{cardID}/limits` | Update card spending limits |\n| `POST` | `/token/card-data` | Generate card data token |\n| `POST` | `/accounts/{accountID}/cards` | Order additional card |\n| `POST` | `/cards/{cardID}/plastic` | Order physical plastic card |\n| `POST` | `/transactions` | Create card transaction |\n| `GET` | `/transactions/{txID}` | Get card transaction |\n| `GET` | `/cards/{cardID}/transactions` | List card transactions |\n| `GET` | `/transaction/pending-confirmations` | List pending 3DS challenges |\n| `POST` | `/test/3ds/challenge` | Create 3DS challenge (testing) |\n| `POST` | `/transaction/{txID}` | Confirm/decline 3DS challenge |\n| `GET` | `/card-applications/{appID}/card-products` | Get card product catalog |\n\n## Supported Currencies\n\n| Currency | Code | Vault UUID |\n|----------|------|------------|\n| US Dollar | USD | `450d2156-132a-4d3f-88c5-74822547658d` |\n| Euro | EUR | `a09a0a2c-1a3a-44c5-a1b9-603a6eea9341` |\n| British Pound | GBP | `992b932d-7e9e-44b0-90ea-b82a530b6784` |\n| South African Rand | ZAR | `f1c412ce-5e2b-4737-9121-b7c11d6c3f93` |\n| Mexican Peso | MXN | `426c2e30-111e-4273-92b3-508445a6bb58` |\n| Singapore Dollar | SGD | `e2914c33-2e57-49a5-ac06-25c006497b3d` |\n| Canadian Dollar | CAD | `bd5af6fe-5d92-4b20-9bd4-1baa52b7a02e` |\n| EGG (Test) | EGG | `9a550347-799e-4c10-9142-f1a2e1c084e7` |\n| PEB (Test) | PEB | `0ba2b0d1-b7a2-416c-a4ac-1cb3e5281300` |\n| Pakistani Rupee | PKR | `2868b4e5-7178-4945-8ec5-8208fac2a22d` |\n| XRP | XRP | `6e1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b` |\n\n## Webhook Events\n\nMockGatehub delivers webhooks via a Redis-backed job queue with configurable minimum delay, 10 retry attempts, and 30-second fixed retry backoff. Webhooks are signed with `X-GH-Webhook-Signature` using HMAC-SHA256.\n\n### Supported Event Types\n\n| Event | Trigger |\n|-------|---------|\n| `id.verification.accepted` | KYC approved (iframe submit) |\n| `id.verification.rejected` | KYC rejected |\n| `id.verification.action_required` | KYC requires action |\n| `core.deposit.completed` | Deposit/hosted transfer completed |\n| `cards.card.created` | Card created |\n| `cards.transaction.event` | Card transaction event |\n| `cards.3ds.auth_3ds_confirmation` | 3DS challenge confirmation |\n\n### Webhook Payload Format\n\n```json\n{\n  \"uuid\": \"webhook-uuid\",\n  \"timestamp\": \"1768920404045\",\n  \"event_type\": \"core.deposit.completed\",\n  \"user_uuid\": \"user-id\",\n  \"environment\": \"sandbox\",\n  \"data\": { ... }\n}\n```\n\n\u003e The `timestamp` is milliseconds since epoch as a string. The `environment` is always `\"sandbox\"`.\n\n## KYC Flow\n\n1. **Start KYC** (`POST /id/v1/users/{userID}/hubs/{gatewayID}`) — sets user to `action_required`\n2. **Load iframe** (`GET /iframe/onboarding?bearer={token}`) — serves KYC form from `web/kyc-iframe.html`\n3. **Submit form** (`POST /iframe/submit`) — accepts `multipart/form-data` or URL-encoded, updates user to `accepted`, triggers `id.verification.accepted` webhook\n4. **Parent notification** — iframe posts `{ type: 'OnboardingCompleted', value: '{\"applicantStatus\":\"submitted\"}' }` to the parent window\n\nThe `bearer` token is required. The `user_id` form field is optional — if omitted, it is resolved from the token-to-user mapping created via `/auth/v1/tokens`.\n\n### Optional 2FA TOTP Verification\n\nThe KYC iframe includes an optional **\"Trigger 2FA TOTP verification\"** checkbox. When checked:\n\n1. User enters a TOTP code in the revealed input field\n2. On form submit, MockGateHub calls the integrator's 2FA endpoint before completing KYC:\n   ```\n   POST {org.apiBaseUrl}/v1/users/managed/{userId}/2fa\n   {\"action\": \"VERIFY\", \"code\": \"{entered_code}\"}\n   ```\n3. If integrator returns `{\"success\": true}` → KYC proceeds normally\n4. If integrator returns `{\"success\": false}` or non-2xx → form submission fails with error\n5. If no organization `apiBaseUrl` is configured → form submission fails with clear error\n\nThis requires the organization configuration to be set via `PATCH /auth/v1/users/organization/{orgID}` with a valid `apiBaseUrl`.\n\n## Transaction Lifecycle\n\n1. `POST /core/v1/transactions` creates a transaction in `pending` state (status `1`)\n2. A pending webhook (`core.deposit.completed`) fires immediately\n3. After a ~2-second delay, the transaction is marked `completed` (status `100`), the balance is credited, and a completed webhook fires\n4. If no webhook URL is configured, the transaction completes synchronously\n\n**Transaction types**: `0` = Withdrawal, `1` = Deposit (external), `2` = Hosted transfer\n\n**Fee behavior**: Deposit fees reduce the credited amount. Withdrawal fees increase the total deducted. Hosted transfers always have 0% fee.\n\n## Authentication\n\nAll API requests require HMAC-SHA256 signatures by default, matching real GateHub behavior.\n\n### Default Test Credentials\n\n- **App ID**: `local-test-app-id`\n- **Secret**: `local-test-app-secret`\n\n### Request Signing\n\nInclude these headers on every request:\n\n- `x-gatehub-app-id` — application identifier\n- `x-gatehub-timestamp` — Unix timestamp (seconds)\n- `x-gatehub-signature` — `HMAC-SHA256(timestamp|method|full_url|body, secret)`, hex-encoded\n\n```bash\nTIMESTAMP=$(date +%s)\nBODY='{\"email\":\"user@example.com\"}'\nSIGNATURE=$(echo -n \"${TIMESTAMP}POST/auth/v1/tokens${BODY}\" | openssl dgst -sha256 -hmac \"local-test-app-secret\" -hex | cut -d' ' -f2)\n\ncurl -X POST http://localhost:8080/auth/v1/tokens \\\n  -H \"x-gatehub-app-id: local-test-app-id\" \\\n  -H \"x-gatehub-timestamp: $TIMESTAMP\" \\\n  -H \"x-gatehub-signature: $SIGNATURE\" \\\n  -H \"Content-Type: application/json\" \\\n  -d \"$BODY\"\n```\n\n### Public Endpoints (No Auth Required)\n\n`/health`, `/`, `/iframe/onboarding`, `/iframe/submit`, `/transaction/complete`, `/api/user-currencies`, `/admin/fees`\n\n### Disable Authentication\n\n```bash\nMOCKGATEHUB_ENFORCE_AUTHENTICATION=false\n```\n\n\u003e **Warning**: Only use this in development. Always enable authentication for realistic testing.\n\n## Development\n\n### Prerequisites\n\n- Go 1.24+\n- Docker \u0026 Docker Compose\n- Redis (required for webhook queue, optional for app storage)\n\n### Building\n\n```bash\ngo build -o mockgatehub ./cmd/mockgatehub\n```\n\n### Running Locally\n\n```bash\n# With Redis for webhooks (required)\nMOCKGATEHUB_REDIS_URL=redis://localhost:6379 ./mockgatehub\n\n# Disable auth for quick iteration\nMOCKGATEHUB_ENFORCE_AUTHENTICATION=false MOCKGATEHUB_REDIS_URL=redis://localhost:6379 ./mockgatehub\n```\n\n### Testing\n\n```bash\n# All tests (unit + E2E)\nmake test\n\n# Unit tests only\nmake unit-tests\n\n# BDD E2E tests (requires Docker)\nmake e2e-tests\n\n# Coverage report\nmake coverage\n```\n\n## Architecture\n\n```\ncmd/mockgatehub/          # Application entry point and route setup\ninternal/\n  ├── auth/               # HMAC signature validation + HTTP middleware\n  ├── config/             # Environment variable configuration\n  ├── consts/             # Constants (currencies, vault IDs, rates, statuses)\n  ├── handler/            # HTTP handlers (auth, identity, core, cards, fees, rates)\n  ├── logger/             # Zap structured logging\n  ├── models/             # Domain models and API DTOs\n  ├── storage/            # Storage layer (interface + memory/Redis implementations)\n  ├── utils/              # Utilities (UUID, address generation)\n  └── webhook/            # Redis-backed webhook queue, worker, and delivery\nfeatures/                 # Gherkin BDD feature files\ntest/integration/         # Go integration tests\ntestenv/                  # Godog E2E test runner with Docker Compose\nweb/                      # Static HTML (KYC iframe, deposit/withdraw iframe)\ndocs/                     # GateHub API reference documentation\n```\n\n## Limitations\n\n- **Sandbox Only**: Designed for development, not production use\n- **Happy Paths**: Focuses on successful flows; limited error simulation\n- **Redis Required**: Webhook queue always needs Redis, even with in-memory app storage\n\n## Troubleshooting\n\n### Container won't start\n\n```bash\ndocker compose logs mockgatehub\n```\n\n### Check Redis connection\n\n```bash\nredis-cli -n 1 KEYS \"balance:*\"\n```\n\n### Test health endpoint\n\n```bash\ncurl http://localhost:8080/health\n```\n\n### View webhook delivery logs\n\n```bash\ndocker compose logs \u003cyour-app-service\u003e | grep webhook\n```\n\n## Contributing\n\nSee [AGENTS.md](AGENTS.md) for AI agent development guidelines.\n\n## License\n\nMaintained by the Interledger Foundation.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finterledger%2Fmockgatehub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finterledger%2Fmockgatehub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finterledger%2Fmockgatehub/lists"}