CongoBcc.ExchangeRate 1.0.1

dotnet add package CongoBcc.ExchangeRate --version 1.0.1
                    
NuGet\Install-Package CongoBcc.ExchangeRate -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CongoBcc.ExchangeRate" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CongoBcc.ExchangeRate" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="CongoBcc.ExchangeRate" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CongoBcc.ExchangeRate --version 1.0.1
                    
#r "nuget: CongoBcc.ExchangeRate, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CongoBcc.ExchangeRate@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CongoBcc.ExchangeRate&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=CongoBcc.ExchangeRate&version=1.0.1
                    
Install as a Cake Tool

Bcc.ExchangeRate

Fetches the Banque Centrale du Congo's (BCC's) published USD/CDF exchange rate, with a fallback to BCC's official X/Twitter account when the website is unreachable or its markup changes.

Origin

Extracted from an internal Cloud Run Job that scraped BCC and wrote straight into that product's own snapshot table, so other consumers needing the same rate don't have to re-implement, and separately maintain, the same fragile HTML scraper.

What moved here: the fetch + parse + fallback-orchestration logic only (BccExchangeRateClient, BccFetcher, BccRateParser). What stayed behind, deliberately: persistence. Each consumer has its own schema/domain model for storing a rate — baking a schema-specific write into this package would defeat the point of sharing it. Call FetchLatestAsync, then persist the BccExchangeRateResult however your own product does.

Note on naming: the client's original brief called this "BCDC," but the actual central bank being scraped is BCC (Banque Centrale du Congo) — BCDC (Banque Commerciale Du Congo) is a different, private bank. This package is named for the real source.

Note on the nuget.org package id: the C# namespace/assembly is Bcc.ExchangeRate throughout — that's what every using statement below actually is. The nuget.org PackageId is CongoBcc.ExchangeRate instead, because nuget.org rejects Bcc.ExchangeRate outright (409, "The package ID is reserved" — an unrelated company already holds a formal ID Prefix Reservation on "Bcc," see BccCode.*/BccPay.* on nuget.org; nothing to do with the Congolese central bank, just an unlucky collision). PackageId/AssemblyName are independent properties in NuGet, so only the csproj's <PackageId> needed to change — no C# code anywhere references the package id directly.

Usage

using Bcc.ExchangeRate;

using var client = new BccExchangeRateClient(); // or new BccExchangeRateClient(httpClient) with your own HttpClient

var result = await client.FetchLatestAsync(new BccExchangeRateOptions
{
    // XBearerToken = "...", // optional — makes the X fallback actually reliable
});

Console.WriteLine($"1 USD = {result.RateUsdToCdf} CDF (source: {result.SourceDetail})");

FetchLatestAsync throws InvalidOperationException (naming both underlying errors) if neither the website nor the X fallback produced a parseable rate — a missing rate surfaces loudly, it is never silently reported as success.

Building and packing locally

No private registry is wired up yet — for now this ships as a .nupkg dropped into a shared local folder feed that consumers point their own NuGet.Config at:

dotnet pack src/Bcc.ExchangeRate -c Release -o /path/to/local/feed

(GeneratePackageOnBuild is already on, so a plain dotnet build -c Release produces the same package under src/Bcc.ExchangeRate/bin/Release/.)

Revisit: once this is trusted by more consumers, or once a consuming repo's CI needs it without a manual copy step, push it to a real registry instead (GitLab Package Registry is a natural fit if consumers are on gitlab.com) rather than continuing to hand-copy a .nupkg around.

Publishing to nuget.org

CongoBcc.ExchangeRate is unclaimed on nuget.org as of this writing (checked directly against the registry — Bcc.ExchangeRate, the first choice, is not: see the naming note above). nuget.org is public and effectively permanent — anyone can install this package once pushed, and a version can be unlisted (hidden from search/new installs) but never fully deleted. Worth being sure this is the audience you want before the first push, not after — though the package itself has nothing sensitive in it (no secrets, no consumer-specific business logic; see "What moved here" above).

Already done, ready before either publishing path below: the package is MIT-licensed (LICENSE at the repo root, PackageLicenseExpression in the csproj) and includes its own README — nuget.org's "missing license/readme" warnings are gone as of the last dotnet pack.

Path A — GitLab CI/CD, Trusted Publishing (no stored secret, current setup)

This repo's .gitlab-ci.yml publishes via nuget.org's Trusted Publishing — GitLab issues a short-lived OIDC token per pipeline run, nuget.org exchanges it for a 1-hour API key, and that key is what actually pushes. No API key is ever stored in GitLab or committed anywhere. One-time setup, then repeatable for every future version with no credential to rotate:

  1. ✅ Done — this repo lives at thubadigital/bcc-fx-rate.
  2. On nuget.org: your username (top right) → Trusted Publishing → Add a new policy → choose GitLab. The exact field labels on that form weren't spelled out anywhere I could check directly (only a screenshot in Microsoft's own docs, not transcribable text) — match by meaning against GitLab's own OIDC claim names (namespace_path/project_path), which is what nuget.org validates the token against either way:
    • namespace path: thubadigital (the group that owns the project)
    • project path: thubadigital/bcc-fx-rate (the full path)
    • Scopes: Push new packages and package versions, glob CongoBcc.ExchangeRate*
    • Leave environment and ref blank unless you want to restrict which branch/environment can publish. If a field doesn't take the value as shown, it's a labeling difference, not a wrong value — the group/project path above is what identifies this repo either way.
  3. In the GitLab project: Settings → CI/CD → Variables → add NUGET_USERNAME (your nuget.org profile name — not your email address). Not a secret, but keeping it out of the committed YAML means changing accounts later doesn't need a code change.
  4. Tag a release matching the version in Bcc.ExchangeRate.csproj (v1.0.0 for the current one) and push the tag: git tag v1.0.0 && git push origin v1.0.0.
  5. In GitLab → CI/CD → Pipelines, find the pipeline that tag triggered and click Run on the publish_nuget job — it's when: manual deliberately, so nothing publishes to a public, permanent registry as a side effect of an ordinary push.
  6. On the next real version, bump <Version> in the csproj, tag vX.Y.Z to match, push, run the job again — no setup repeated.

A private GitLab repo's Trusted Publishing policy starts in a 7-day "pending" state until the first successful publish provides GitLab's repo/owner IDs to nuget.org (prevents a delete-and-recreate-the-repo attack on the policy) — normal, not an error; just don't let more than 7 days pass without running the job at least once.

Path B — manual push with an API key (no GitLab setup, one-off)

Useful if you want a version out today without wiring up CI/CD Path A above, or as a fallback if the pipeline itself is ever broken:

  1. Create an account at nuget.org (or sign in) and verify your email — pushing is blocked until it's verified.
  2. Generate an API key: account menu → API Keys → Create. Shortest expiration offered, scope Push new packages and package versions, glob CongoBcc.ExchangeRate*. Copy it immediately — shown once.
  3. Push both packages (already built at src/Bcc.ExchangeRate/bin/Release/ — see "Building and packing locally" above; the files are named for the PackageId, not the project folder):
    dotnet nuget push src/Bcc.ExchangeRate/bin/Release/CongoBcc.ExchangeRate.1.0.0.nupkg \
      --api-key <your-api-key> --source https://api.nuget.org/v3/index.json
    
    The matching .snupkg (debug symbols) is discovered and pushed automatically alongside it.
  4. Delete the API key from nuget.org afterward — no reason to leave even a short-lived one active once the push succeeded.

Never commit an API key anywhere (not in this repo, not in either consumer, not in GitLab CI/CD variables) — Path A exists specifically so a stored key is never needed at all.

After either path

It appears at https://www.nuget.org/packages/CongoBcc.ExchangeRate within a few minutes (indexing isn't instant). From there, both consuming repos can drop the local-packages source from their own NuGet.Config and pull straight from nuget.org instead.

Status

The upstream scraper this was extracted from is still staging-only as of the extraction date — BCC's page markup and the regexes matching it (BccRateParser) are not yet proven reliable in production for any consumer. Treat a FetchLatestAsync failure as an expected, not exceptional, occurrence for now, and keep tests/.../BccRateParserTests.cs (which run offline, no network) as the first thing to extend if BCC changes their page format.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 90 9/22/2026
1.0.0 84 9/14/2026