Puntjes.Sdk
2.2.1
dotnet add package Puntjes.Sdk --version 2.2.1
NuGet\Install-Package Puntjes.Sdk -Version 2.2.1
<PackageReference Include="Puntjes.Sdk" Version="2.2.1" />
<PackageVersion Include="Puntjes.Sdk" Version="2.2.1" />
<PackageReference Include="Puntjes.Sdk" />
paket add Puntjes.Sdk --version 2.2.1
#r "nuget: Puntjes.Sdk, 2.2.1"
#:package Puntjes.Sdk@2.2.1
#addin nuget:?package=Puntjes.Sdk&version=2.2.1
#tool nuget:?package=Puntjes.Sdk&version=2.2.1
Puntjes.Sdk
The official .NET client for the Puntjes loyalty API. It runs on .NET 8 and .NET 10.
dotnet add package Puntjes.Sdk
Quick start
Create your API credentials in the Puntjes admin portal. The secret is shown once.
using Puntjes.Sdk;
using Puntjes.Sdk.Generated.Models;
var puntjes = PuntjesClientFactory.Create(
Environment.GetEnvironmentVariable("PUNTJES_CLIENT_ID")!,
Environment.GetEnvironmentVariable("PUNTJES_CLIENT_SECRET")!);
// A customer scans their card at the till.
var customer = (await puntjes.Customers.Lookup.GetAsync(c => c.QueryParameters.Identifier = scannedCard))?.Data;
Console.WriteLine(customer?.WalletBalance);
// Record the purchase. Amounts are in cents.
var transaction = (await puntjes.Transactions.PostAsync(new CreateTransactionData
{
Identifier = scannedCard,
TotalAmount = 4200,
IdempotencyKey = $"order-{orderId}",
}))?.Data;
Console.WriteLine(transaction?.PointsEarned);
PuntjesClientFactory.Create returns a client generated from the Puntjes OpenAPI document, so it has every call the
API has. A call follows the URL: puntjes.Customers["42"].Wallet.GetAsync() is GET /customers/42/wallet. Read the
Data part of the answer.
examples/QuickStart is a complete program: dotnet run --project examples/QuickStart -- <card or email>.
Options
var puntjes = PuntjesClientFactory.Create(id, secret, new PuntjesOptions
{
BaseUrl = "https://puntjes.app", // with or without /api/v1
MaxRetries = 2, // 0 turns retries off
Timeout = TimeSpan.FromSeconds(30), // per attempt
TokenStore = myDistributedStore, // an ITokenStore; memory by default
});
What the SDK does for you
- Authentication. It asks for a
client_credentialstoken on the first call, keeps it until 30 seconds before it expires, and asks once more when the API rejects it. Parallel calls share one request for a token. AnITokenStorekeeps each token under a key made from the host and the credentials, so one shared store (a Redis cache, for example) can serve the clients of many shops without mixing their tokens. - Retries. A GET, PUT, PATCH or DELETE, and a POST that carries an
idempotency_key, is sent again after a network failure, a 5xx answer orRATE_LIMITED. It waits 0.5 s, then 1 s, or whatRetry-Aftersays. A POST without a key is never sent twice, so a purchase is never recorded twice.PLAN_LIMIT_EXCEEDEDis never retried. An attempt that takes longer thanTimeoutcounts as a network failure. A failed token request is retried the same way. - Idempotency keys.
IdempotencyKey.New()makes one. Send the same key again to repeat a call safely.
Errors
Every error is a PuntjesException, with a subclass per kind:
| Exception | When |
|---|---|
AuthenticationException |
401: wrong credentials or a rejected token |
ForbiddenException |
403 |
NotFoundException |
404 |
ConflictException |
409 |
ValidationException |
422 VALIDATION_ERROR; ErrorsFor("field") gives the messages |
RateLimitException |
429 RATE_LIMITED; RetryAfter says how long to wait |
PlanLimitExceededException |
429 PLAN_LIMIT_EXCEEDED: the shop's plan is used up |
ServerException |
5xx |
TransportException |
No answer from the API, or an answer that is not from the API |
A 422 for a business rule, such as INSUFFICIENT_BALANCE, is a plain PuntjesException.
using Puntjes.Sdk.Exceptions;
using Puntjes.Sdk.Generated.Errors;
try
{
await puntjes.Redemptions.PostAsync(body);
}
catch (PuntjesException e) when (e.ErrorCode == ErrorCode.InsufficientBalance)
{
Console.WriteLine($"Not enough points. Request {e.RequestId}");
}
ErrorCode is null for a code that is newer than your SDK; RawCode always holds the code as the API sent it.
Pages
await foreach (var product in Paginator.AllAsync<ProductData>(async (page, ct) =>
{
var answer = await puntjes.Products.GetAsync(c => c.QueryParameters.Page = page, ct);
return (answer?.Data?.Data, answer?.Data?.Meta);
}))
{
Console.WriteLine(product.Name);
}
Apple wallet pass
byte[] pkpass = await WalletPass.AppleAsync(puntjes, customerId);
How this package is made
The code under src/Puntjes.Sdk/Generated is generated by Kiota from
spec/api.json. Never edit it by hand: CI regenerates it and fails on any difference.
scripts/generate.shregenerates it, with Kiota and the .NET SDK pinned by image digest.- When Puntjes publishes a new API spec,
.github/workflows/regenerate.ymlopens a PR with the new code. A minor or patch spec merges itself when CI is green; a major one waits for a person. .github/workflows/release.ymlruns release-please. Its release PR merges itself when CI is green, and the tag publishes the package to nuget.org.
nuget.org trusted publishing
The publish job has no stored API key. nuget.org trades the job's GitHub token for a key that lives one hour. The
owner of the puntjesapp account on nuget.org sets this up once:
- On nuget.org, open Trusted Publishing and add a policy for GitHub Actions.
- Repository owner
PuntjesApp, repositorypuntjes-dotnet, workflow filerelease.yml, no environment. - In this repository, set the variable
NUGET_USERtopuntjesapp.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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. |
-
net10.0
- Microsoft.Kiota.Abstractions (>= 2.1.2)
- Microsoft.Kiota.Http.HttpClientLibrary (>= 2.1.2)
- Microsoft.Kiota.Serialization.Json (>= 2.1.2)
- Microsoft.Kiota.Serialization.Text (>= 2.1.2)
-
net8.0
- Microsoft.Kiota.Abstractions (>= 2.1.2)
- Microsoft.Kiota.Http.HttpClientLibrary (>= 2.1.2)
- Microsoft.Kiota.Serialization.Json (>= 2.1.2)
- Microsoft.Kiota.Serialization.Text (>= 2.1.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.