Skip to content

payments: Bitnob adapter, and the KYB reality in both setups - #1013

Merged
ralyodio merged 1 commit into
masterfrom
worktree-bitnob-adapter
Sep 6, 2026
Merged

payments: Bitnob adapter, and the KYB reality in both setups#1013
ralyodio merged 1 commit into
masterfrom
worktree-bitnob-adapter

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Adds @profullstack/sh1pt-payment-bitnob — African payouts over the rail the recipient actually uses: NIP in Nigeria, M-Pesa in Kenya and Tanzania, MTN MoMo in Ghana and Uganda, PayShap or EFT in South Africa.

Why both this and TransFi

Onboarding time, and the setup steps now say so rather than leaving it to be discovered mid-integration:

TransFi Bitnob
KYB review 1–14 business days 24–48 hours
Then keys production keys within 24h of verification dedicated API keys on approval
Submit to compliance@transfi.com in-dashboard
Covers 41 corridors 6 African corridors

So Bitnob is the fast route to one African corridor in production; TransFi is the breadth partner for the other 36. TransFi's steps now also name the compliance address, what UBOs must provide, and that the webhook secret arrives before KYB clears — so the integration can be proven while verification runs.

The guard that matters

requireEnvironment. Bitnob serves sandbox and production from ONE base URL — the key alone decides which world you are in. Unlike every other adapter here, there is no host to eyeball and nothing in a request or response that reveals a sandbox key reached production. It would quote invented prices to real customers and look entirely healthy doing it.

So the environment is:

  • required, never defaulted — the assumption that costs money is "probably production" on a sandbox key
  • checked before credentials are even read
  • accompanied by an error that explains the consequence, not just the rule, because a guard nobody understands gets deleted by the next person in a hurry

Sandbox keys are issued instantly and are for testing only. Production access needs KYB at both vendors; there is no shortcut at either.

Consistent with the house pattern

payout() validates its arguments and then refuses, exactly as transfi does: the request shape is unverified, and a fabricated transfer id would report money as sent when nothing left the account.

The signing scheme is verified from Bitnob's published docs — CLIENT_ID:TIMESTAMP:NONCE:PAYLOAD, HMAC-SHA256 keyed with the client secret, hex, across four X-Auth-* headers — and pinned to exact bytes by a test, since a subtly wrong canonical string fails as a 401 that reads as a bad credential rather than as our bug.

Testing

17 tests on this adapter, 111 across packages/payments, typecheck clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KcjnyR2gpRBzWYGobUvuHT

Adds @profullstack/sh1pt-payment-bitnob — African payouts over the rail the
recipient actually uses: NIP in Nigeria, M-Pesa in Kenya and Tanzania, MTN MoMo
in Ghana and Uganda, PayShap or EFT in South Africa.

The reason to have both this and transfi is onboarding time, and the setup steps
now say so instead of leaving it to be discovered. Bitnob's KYB review is
typically 24-48 hours; TransFi's is 1-14 business days with production keys
issued within 24h of verification. So Bitnob is the fast route to one African
corridor in production, and TransFi is the breadth partner for the other 36.
TransFi's steps also now name compliance@transfi.com, what UBOs must provide,
and that the webhook secret arrives before KYB clears so the integration can be
proven while verification runs.

The guard that matters here is `requireEnvironment`. Bitnob serves sandbox and
production from ONE base URL — the key alone decides which world you are in —
so unlike every other adapter there is no host to eyeball and nothing in a
request or response that reveals a sandbox key has been deployed to production.
It would quote invented prices to real customers and look entirely healthy doing
it. So the environment is required, never defaulted, checked before credentials
are even read, and the error explains the consequence rather than just stating
the rule, because a guard nobody understands gets deleted by the next person in
a hurry.

Sandbox keys are issued instantly and are for testing only. Production access
needs KYB at both vendors; there is no shortcut at either.

payout() validates and then refuses, as with transfi: the request shape is
unverified and a fabricated transfer id would report money as sent when nothing
left the account.

17 tests on this adapter, 111 across packages/payments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KcjnyR2gpRBzWYGobUvuHT
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

49 finding(s)

HIGH/CRITICAL: 1 | MEDIUM: 14 | LOW: 34

Severity Rule Location
HIGH js-host-header-trust packages/bots/wechat/src/index.ts:405
MEDIUM redos-nested-quantifier packages/actions-fleet-core/src/action-pack/schema.ts:3
MEDIUM redos-nested-quantifier packages/core/src/setup-helpers.ts:583
MEDIUM redos-nested-quantifier packages/policy/src/rules/bundle-id.ts:3
MEDIUM sql-string-concatenation packages/targets/deploy-wordpress/src/index.ts:154
MEDIUM redos-nested-quantifier packages/targets/desktop-linux/src/index.ts:19
MEDIUM redos-nested-quantifier packages/targets/desktop-mac/src/index.ts:15
MEDIUM redos-nested-quantifier packages/targets/desktop-steamos/src/index.ts:28
MEDIUM redos-nested-quantifier packages/targets/mobile-android/src/index.ts:9
MEDIUM redos-nested-quantifier packages/targets/mobile-ios/src/index.ts:11
MEDIUM redos-nested-quantifier packages/targets/tv-androidtv/src/index.ts:14
MEDIUM redos-nested-quantifier packages/targets/tv-firetv/src/index.ts:13
MEDIUM redos-nested-quantifier packages/targets/tv-tvos/src/index.ts:14
MEDIUM redos-nested-quantifier packages/targets/tv-webos/src/index.ts:26
MEDIUM js-unescaped-html-sink sites/sh1pt.com/app/blog/[slug]/page.tsx:76
LOW secret-generic-credential packages/affiliates/skimlinks/src/index.test.ts:25
LOW secret-generic-credential packages/affiliates/skimlinks/src/index.test.ts:71
LOW secret-generic-api-key packages/affiliates/sovrn/src/index.ts:28
LOW secret-generic-credential packages/agent-providers/opencode/src/__tests__/opencode.test.ts:99
LOW js-nosql-injection packages/ai/amazon-bedrock/src/index.test.ts:121
LOW secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:9
LOW secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:10
LOW secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:11
LOW secret-generic-credential packages/bridges/matrix/src/index.ts:58
LOW secret-generic-credential packages/bridges/matrix/src/index.ts:59
LOW secret-generic-credential packages/bridges/slack/src/index.test.ts:259
LOW secret-generic-credential packages/captcha/captchasolver/src/index.ts:34
LOW secret-generic-credential packages/cli/src/commands/secrets.ts:189
LOW secret-generic-credential packages/cloud/linode/src/index.ts:15
LOW secret-generic-credential packages/observability/sentry/src/index.ts:15
LOW secret-generic-credential packages/outreach/producthunt/src/index.ts:103
LOW secret-generic-credential packages/promo/posthog/src/index.ts:23
LOW secret-generic-credential packages/scanners/threatcrush/test/scan-output.txt:35
LOW secret-generic-credential packages/scanners/threatcrush/test/scan-output.txt:40
LOW secret-database-url packages/scanners/threatcrush/test/scan-output.txt:54
LOW secret-generic-credential packages/security/snyk/src/index.ts:26
LOW secret-generic-credential packages/social/hashnode/src/index.ts:4
LOW secret-generic-credential packages/social/linkedin/src/index.ts:3
LOW secret-generic-credential packages/social/linkedin/src/index.ts:4
LOW secret-generic-credential packages/social/medium/src/index.ts:4
LOW secret-generic-credential packages/social/snapchat/src/index.ts:5
LOW secret-generic-credential packages/social/tiktok/src/index.ts:5
LOW secret-generic-credential packages/targets/plugin-vscode/src/index.test.ts:115
LOW secret-generic-credential packages/targets/registry-ans/src/index.test.ts:79
LOW secret-generic-credential packages/targets/registry-ans/src/index.ts:49
LOW secret-generic-credential packages/targets/sdk-pypi/src/index.test.ts:49
LOW secret-generic-credential packages/vcs/gitlab/src/index.test.ts:96
LOW secret-generic-credential sites/sh1pt.com/supabase/config.toml:303
LOW secret-generic-credential sites/sh1pt.com/supabase/config.toml:335

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 63fdfc8 into master Sep 6, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant