SolidNetsEasyClient 0.1.1
dotnet add package SolidNetsEasyClient --version 0.1.1
NuGet\Install-Package SolidNetsEasyClient -Version 0.1.1
<PackageReference Include="SolidNetsEasyClient" Version="0.1.1" />
paket add SolidNetsEasyClient --version 0.1.1
#r "nuget: SolidNetsEasyClient, 0.1.1"
// Install SolidNetsEasyClient as a Cake Addin #addin nuget:?package=SolidNetsEasyClient&version=0.1.1 // Install SolidNetsEasyClient as a Cake Tool #tool nuget:?package=SolidNetsEasyClient&version=0.1.1
What is it?
This is a client for making type safe requests to the Easy Payment API endpoint.
Status
Current development status is: under development, somewhat functional library. Since Nexi and Nets have had a merger, this library uses a mixture of naming references to Nets and Nexi.
Quickstart
Add the package to your project:
$ `dotnet add package SolidNetsEasyClient`
Register the service in the startup process:
// Register nets services
builder.Services
.AddNetsEasy(options =>
{
// Configure all the options here...
// MUST set these either through appsettings.json or via env vars:
options.ApiKey = "my-api-key";
options.TermsUrl = "https://my-site/terms-and-conditions";
options.PrivacyPolicyUrl = "https://my-site/privacy-policy";
// If embedded checkout:
options.IntegrationType = Integration.EmbeddedCheckout;
options.CheckoutUrl = "https://exact.url.to/checkout";
options.CheckoutKey = "my-checkout-key";
// If hosted on nexi:
options.IntegrationType = Integration.HostedPaymentPage;
options.ReturnUrl = "https://redirect-on-success.to/my-page";
options.CancelUrl = "https://redirect-on-cancel.to/my-other-page";
});
Use the payment builder to construct a payment object, or make a PaymentRequest
yourself:
Order order = new Order { /* With order items */};
NetsPaymentBuilder builder; // Inject by dependency injection
PaymentRequest payment = builder.CreatePayment(order)
.WithPrivateCustomer(
customerId: "myInternalCustomerId",
firstName: "John",
lastName: "Doe",
email: "john@doe.com"
)
.MerchantHandlesCustomerData()
.AddCustomer()
.ChargeImmidiately()
.AddWebhook("https://my-site/callback/charge", EventName.ChargeCreated, "authorizationheader123")
.AddWebhook("https://my-site/callback", EventName.PaymentCreated, "authorizationheader123")
.Build();
Then start a checkout session using the http client NexiClient
or the typed IPaymentClient
.
NexiClient client; // Inject by dependency injection
PaymentResult? payment = await client.StartCheckoutPayment(payment);
If using the EmbeddedCheckout
- see this documentation for more information: https://developer.nexigroup.com/nexi-checkout/en-EU/docs/web-integration/integrate-checkout-on-your-website-embedded/
If using the HostedPaymentPage
- see this documentation for more information: https://developer.nexigroup.com/nexi-checkout/en-EU/docs/web-integration/integrate-checkout-on-your-website-hosted/
Embedded checkout
The overall steps for the embedded checkout is as follows:
- Customer creates an order
- Your backend receives the order
- Initiate a checkout by contacting Nexi/Nets (backend)
- Get paymentId from Nets (backend)
- Make the frontend use paymentId and your checkoutKey
- Customer inputs their payment details
- Nets receives the payment details
- Nets charges the customer
- Nets calls your endpoint via webhooks
The customer never leaves your checkout page, the Nets integration is displayed in an iframe by using the Nets Checkout JS SDK.
The benefit is the possibility for a more streamlined customer experience, by only having the customer in 1 place and being able to customize the look and feel of your checkout page.
The drawback is more work for the developer, and the result is only as good as the developer makes it.
Hosted checkout
- Customer creates an order
- Your backend receives the order
- Initiate checkout by contacting Nexi/Nets (backend)
- Redirect customer to Nets
- Customer inputs their payment details on Nets
- Nets redirects customer to your page
- Nets charges the customer
- Nets calls your endpoint via webhooks
The customer will leave your page and be redirected upon the start and end of their customer journey.
The benefit is a simpler developer flow and maybe easier to customize some parts. The base checkout is already developed by Nets/Nexi.
The drawback is the customer redirection, and limited customization.
MVC
TODO
Minimal Api
TODO
Webhooks
The webhook notifications, will be directed to the endpoint you specified in the AddWebhook
builder method.
It is highly recommended to use these for processing customer payments. The callback webhook will be an http POST
request with an authorization header as specified on the payment request.
For example, you can either use the generic interface IWebhook<WebhookData>
to catch any webhook callbacks OR you can specify the expected callback type (e.g) ChargeCreated
.
[HttpPost("/callback")]
public IResult Method(IWebhook<WebhookData> payload)
{
if (payload is PaymentCreated paymentCreated)
{
// Handle payment creation..
}
else if (payload is ChargeFailed chargeFailed)
{
// etc.
}
}
// Route: https://my-site/callback/charge
public IResult Method(ChargeCreation payload)
{
// Handle charge creation..
}
Nets event name | Description (from Nets Easy) | DTO |
---|---|---|
payment.created | A payment has been created. | PaymentCreated |
payment.reservation.created | The amount of the payment has been reserved. | ReservationCreatedV1 |
payment.reservation.created.v2 | The amount of the payment has been reserved. | ReservationCreatedV2 |
payment.reservation.failed | A reservation attempt has failed. | ReservationFailed |
payment.checkout.completed | The customer has completed the checkout. | CheckoutCompleted |
payment.charge.created.v2 | The customer has successfully been charged, partially or fully. | ChargeCreated |
payment.charge.failed | A charge attempt has failed. | ChargeFailed |
payment.refund.initiated.v2 | A refund has been initiated. | RefundInitiated |
payment.refund.failed | A refund attempt has failed. | RefundFailed |
payment.refund.completed | A refund has successfully been completed. | RefundCompleted |
payment.cancel.created | A reservation has been canceled. | PaymentCancelled |
payment.cancel.failed | A cancellation has failed. | PaymentCancellationFailed |
Proxy
You must remember to configure the forwarded headers, if you want ASP.NET Core to work with proxy servers and load balancers.
Documentation reference: https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-8.0
Summary
Configure the forwarded headers for X-Forwarded-For
header field.
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
Then insert the middleware as the first item in the pipeline:
app.UseForwardedHeaders();
IP-filter
The SolidNetsEasyIPFilterAttribute
can both whitelist and blacklist ips or ip-ranges (using the CIDR-format). Thus minimally ensuring that any webhook requests are coming from an accepted source.
For the minimal API you can use the endpoint filter WebhookFilter
to ensure that the IP is on the whitelist.
Or just use the MapNetsWebhook
extension method, which also ensures the response to be 200 OK and listens on POST requests.
Terminology for Nexi-Nets
According to my own understanding.
Term | Definition |
---|---|
Merchant | You, the seller. |
Consumer | The customer. |
Checkout | The time when the customer is inputting their payment details. |
Payment | A request from merchant to the consumer, to get paid. |
Charge | When money has been transfered thus finalizing the payment. |
Order | The items the consumer has bought. |
Subscription | A completely regular recurring payment, which cannot change in either Amount or in Time. E.g. Always 50 DKK on the last day of the month. Has an exact end date. |
Unscheduled subscription | A variable recurring payment, which can change in Amount and in Time. Consumer MUST agree to the terms that Nexi/Nets stores the consumer's payment details. Has no end date. |
Features
Use the client to make and manage payments, from the backend. Remember that the you still need a frontend for a customer to input payment details.
Client
Using the NexiClient
you must ensure that the http client is not used in a singleton.
See the guidelines here: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines
The ASP.NET Core will use the HttpClient
and dispose it after use, but the HttpMessageHandler
will be reused depending on their lifetimes.
The pooling ensures that port and socket exhaustion is limited.
The lifetime restriction ensures that new DNS changes can be updated.
There is only 1 client with 4 typed interfaces.
IPaymentClient
- handles the checkout, and payment.IChargeClient
- handles the charge and refund of a payment.ISubscriptionClient
- handles the subscriptions, bulk charging and bulk verification.IUnscheduledSubscriptionClient
- handles unscheduled subscriptions, bulk charging and bulk verification of cards.
Roadmap
- Handle payments, subscriptions and webhooks in a type safe and easy to use way.
- Create nuget package
- Add unit tests
- Add easy to use configuration for handling API keys and other client settings
- Add example site
- Consolidate the various Models into related models
- Add more unit tests
- Proof read README
- Add options section that explains each option
- Add Nets Terminology for vocabulary such as: Payment, checkout, charge...
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. |
-
net8.0
- IPAddressRange (>= 6.0.0)
- ISO3166 (>= 1.0.4)
- Microsoft.Extensions.Http (>= 8.0.0)
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 |
---|---|---|
0.1.1 | 424 | 5/16/2024 |
0.1.0 | 95 | 5/16/2024 |
0.0.24 | 186 | 9/6/2023 |
0.0.23 | 151 | 9/6/2023 |
0.0.22 | 216 | 4/27/2023 |
0.0.21 | 308 | 2/22/2023 |
0.0.20 | 254 | 2/22/2023 |
0.0.19 | 284 | 2/16/2023 |
0.0.18 | 297 | 2/11/2023 |
0.0.16 | 279 | 2/11/2023 |
0.0.15 | 286 | 2/10/2023 |
0.0.14 | 302 | 1/31/2023 |
0.0.13 | 298 | 1/30/2023 |
0.0.12 | 339 | 1/22/2023 |
0.0.11 | 335 | 1/21/2023 |
0.0.9 | 362 | 12/10/2022 |
0.0.8 | 323 | 12/9/2022 |
0.0.7 | 360 | 11/28/2022 |
0.0.6 | 355 | 11/24/2022 |
0.0.5 | 382 | 11/11/2022 |
0.0.4 | 403 | 11/2/2022 |
0.0.3 | 418 | 10/26/2022 |