GithubHelp home page GithubHelp logo

truelayer / truelayer-dotnet Goto Github PK

View Code? Open in Web Editor NEW
10.0 22.0 11.0 3.17 MB

The official TrueLayer .NET library provides convenient access to TrueLayer APIs from applications built with .NET.

Home Page: https://docs.truelayer.com

License: MIT License

C# 99.94% Shell 0.06%
payments sdk-dotnet do-not-auto-tag library

truelayer-dotnet's Introduction

TrueLayer.NET

NuGet NuGet License

Build Coverage Status Quality Gate Status

The official TrueLayer .NET client provides convenient access to TrueLayer APIs from applications built with .NET.

The library currently supports .NET Standard 2.1, .NET 5.0 and .NET 6.0.

Installation

Using the .NET Core command-line interface (CLI) tools:

dotnet add package TrueLayer.Client

Using the NuGet Command Line Interface (CLI)

nuget install TrueLayer.Client

Using the Package Manager Console:

Install-Package TrueLayer.Client

From within Visual Studio:

  1. Open the Solution Explorer.
  2. Right-click on a project within your solution.
  3. Click on Manage NuGet Packages...
  4. Click on the Browse tab and search for "TrueLayer".
  5. Click on the TrueLayer package, select the appropriate version in the right-tab and click Install.

Pre-release Packages

Pre-release packages can be downloaded from GitHub Packages.

dotnet add package TrueLayer.Client --prerelease --source https://nuget.pkg.github.com/TrueLayer/index.json

More information on using GitHub packages with .NET.

Documentation

For a comprehensive list of examples, check out the API documentation.

Usage

Prerequisites

First sign up for a developer account. Follow the instructions to set up a new application and obtain your Client ID and Secret. Once the application has been created you must add your application redirected URIs in order to test your integration end-to-end.

Next, generate a signing key pair used to sign API requests.

To generate a private key, run:

docker run --rm -v ${PWD}:/out -w /out -it alpine/openssl ecparam -genkey -name secp521r1 -noout -out ec512-private-key.pem

To obtain the public key, run:

docker run --rm -v ${PWD}:/out -w /out -it alpine/openssl ec -in ec512-private-key.pem -pubout -out ec512-public-key.pem

Navigate to the Payments settings section of the TrueLayer console and upload your public key. Obtain the Key Id.

Configure Settings

Add your Client ID, Secret and Signing Key ID to appsettings.json or any other supported configuration provider.

{
  "TrueLayer": {
    "ClientId": "your id",
    "ClientSecret": "your secret",
    "UseSandbox": true,
    "Payments": {
      "SigningKey": {
        "KeyId": "85eeb2da-702c-4f4b-bf9a-e98af5fd47c3"
      }
    }
  }
}

Initialize TrueLayer.NET

Register the TrueLayer client in Startup.cs or Program.cs (.NET 6.0):

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    services.AddTrueLayer(configuration, options =>
    {
        if (options.Payments?.SigningKey != null)
        {
            // For demo purposes only. Private key should be stored securely
            options.Payments.SigningKey.PrivateKey = File.ReadAllText("ec512-private-key.pem");
        }
    });
}

Alternatively you can create a class that implements IConfigureOptions<TrueLayerOptions> if you have more complex configuration requirements.

Make a payment

Inject ITrueLayerClient into your classes:

public class MyService
{
    private readonly ITrueLayerClient _client;

    public MyService(ITrueLayerClient client)
    {
        _client = client;
    }

    public async Task<ActionResult> MakePayment()
    {
        var paymentRequest = new CreatePaymentRequest(
            amountInMinor: amount.ToMinorCurrencyUnit(2),
            currency: Currencies.GBP,
            paymentMethod: new PaymentMethod.BankTransfer(
                new Provider.UserSelected
                {
                    Filter = new ProviderFilter
                    {
                        ProviderIds = new[] { "mock-payments-gb-redirect" }
                    }
                },
                new Beneficiary.ExternalAccount(
                    "TrueLayer",
                    "truelayer-dotnet",
                    new AccountIdentifier.SortCodeAccountNumber("567890", "12345678")
                )
            ),
            user: new PaymentUserRequest("Jane Doe", "[email protected]", "0123456789")
        );

        var apiResponse = await _client.Payments.CreatePayment(
            paymentRequest,
            idempotencyKey: Guid.NewGuid().ToString()
        );

        if (!apiResponse.IsSuccessful)
        {
            return HandleFailure(
                apiResponse.StatusCode,
                // Includes details of any errors
                apiResponse.Problem
            )
        }

        // Pass the ResourceToken to the TrueLayer Web or Mobile SDK

        // or, redirect to the TrueLayer Hosted Payment Page
        string hostedPaymentPageUrl = _client.Payments.CreateHostedPaymentPageLink(
            apiResponse.Data!.Id,
            apiResponse.Data!.ResourceToken,
            new Uri("https://redirect.yourdomain.com"));

        return Redirect(hostedPaymentPageUrl);
    }
}

For more examples see the API documentation. Advanced customization options and documentation for contributors can be found in the Wiki.

Retrieve provider details

Inject ITrueLayerClient into your classes:

public class MyService
{
    private readonly ITrueLayerClient _client;

    public MyService(ITrueLayerClient client)
    {
        _client = client;
    }

    public async Task<ActionResult> GetProvider(string id)
    {
        var apiResponse = await _client.PaymentsProviders.GetPaymentsProvider(id);

        if (!apiResponse.IsSuccessful)
        {
            return HandleFailure(
                apiResponse.StatusCode,
                // Includes details of any errors
                apiResponse.Problem
            )
        }

        return OkObjectResult(apiResponse.Data.Id);
    }
}

For more examples see the API documentation. Advanced customization options and documentation for contributors can be found in the Wiki.

Make a payout

Inject ITrueLayerClient into your classes:

public class MyService
{
    private readonly ITrueLayerClient _client;

    public MyService(ITrueLayerClient client)
    {
        _client = client;
    }

    public async Task<ActionResult> MakePayout()
    {
        var payoutRequest = new CreatePayoutRequest(
            merchantAccountId: "VALID_MERCHANT_ID",
            amountInMinor: amount.ToMinorCurrencyUnit(2),
            currency: Currencies.GBP,
            beneficiary: new Beneficiary.ExternalAccount(
                "TrueLayer",
                "truelayer-dotnet",
                new SchemeIdentifier.Iban("VALID_IBAN")
            )
        );

        var apiResponse = await _client.Payments.CreatePayout(
            payoutRequest,
            idempotencyKey: Guid.NewGuid().ToString()
        );

        if (!apiResponse.IsSuccessful)
        {
            return HandleFailure(
                apiResponse.StatusCode,
                // Includes details of any errors
                apiResponse.Problem
            )
        }


        return Accepted(apiResponse.Data.Id);
    }
}

For more examples see the API documentation. Advanced customization options and documentation for contributors can be found in the Wiki.

Building locally

This project uses Cake to build, test and publish packages.

Run build.sh (Mac/Linux) or build.ps1 (Windows) To build and test the project.

This will output NuGet packages and coverage reports in the artifacts directory.

Library Documentation

The library API documentation is built using DocFx. To build and serve the docs locally run:

./build.sh --target ServeDocs

This will serve the docs on http://localhost:8080.

Contributing

Contributions are always welcome!

Please adhere to this project's code of conduct.

License

MIT

truelayer-dotnet's People

Contributors

couper4 avatar dili91 avatar lindronics avatar talento90 avatar tl-antonio-valentini avatar tl-ben-foster avatar tl-david-hayden avatar tl-facundo-aita avatar tl-flavio-barinas avatar tl-kieran-allen avatar tl-lucas-lima avatar tl-mauro-franchi avatar tl-roberto-mancinelli avatar tl-tai-tang avatar tl-wajid-malik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

truelayer-dotnet's Issues

Improve account identifier handling

The varying schema per account identifier type is a pain to work with:

        public class AccountIdentifier
        {
            public AccountIdentifier(string type)
            {
                Type = type.NotNullOrWhiteSpace(nameof(type));
            }

            /// <summary>
            /// The type of account identifier
            /// </summary>
            public string Type { get; }
            
            /// <summary>
            /// 6 digit sort code (no spaces or dashes)
            /// </summary>
            public string? SortCode { get; set; }
            
            /// <summary>
            /// 8 digit account number
            /// </summary>
            public string? AccountNumber { get; set; }
            
            /// <summary>
            /// Valid International Bank Account Number (no spaces). 
            /// Consists of a 2 letter country code, followed by 2 check digits, and then by up to 30 alphanumeric characters (also known as the BBAN).
            /// </summary>
            public string? Iban { get; set; }
            
            /// <summary>
            /// Valid Basic Bank Account Number (no spaces). Consists of up to 30 alphanumeric characters, with a fixed length per country. 
            /// </summary>
            public string? Bban { get; set; }
            
            /// <summary>
            /// Valid Polish NRB (no spaces). Consists of 2 check digits, followed by an 8 digit bank branch number, and then by a 16 digit bank account number. 
            /// Equivalent to a Polish IBAN with the country code removed.
            /// </summary>
            public string? Nrb { get; set; }
        }

We could provide a factory method to map a generic number parameter to the correct property based on the type

Remove `Functionality` from ApiClient

ApiClient is designed to be as agnostic as possible to the underlying APIs it calls. We currently pass a Functionality enum to support URI selection. This should be moved to the corresponding Client class so that we avoid having to change ApiClient every time we extend the SDK.

Implement auth token caching

We can implement token caching to avoid obtaining a token for every API call (which will also give Auth API a breather)

Handle dynamic Payment request objects

A number of the payment request fields can be of differing types each with a unique set of fields. We can either adopt pseudo union types (class with every possible field) where this complexity leaks to the client of create static helpers / factories that leverage generic to handle the differing schemas.

I'd lean towards the latter as it's a better experience for the developer.

Review configuration against other APIs

We need to review whether the configuration is flexible enough to support all of TrueLayer's APIs. I know Payments and Data share the same Client ID/Secret but is it possible that these will conflict with other APIs (Pay Direct?).

It may be beneficial to model the configuration based on the feature:

{
    "TrueLayer": {
        "UseSandbox": true,
        "Payments": {
            "ClientId": "xxx"
        }
    }
}

Complete remaining PayDirect features

  • Full Deposit details schema (currently missing a few fields)
  • Providers endpoint (see if we can leverage model from payments)
  • Query transactions
  • Webhook Payloads
  • Set up or update automated account sweeping
  • Get automated account sweeping details
  • Disable automated account sweeping

SDK design guidelines

Covering:

  • Use of records
    • Classes for request objects, or anything that is mutable
    • Records for response objects, or anything that is immutable
  • When fields should be nullable, init, use of constructors
    • Constructors for any mandatory API fields (use acceptance tests to validate compatibility)
    • Fields should be null if they can be null / are optional
    • Use of nullable
    • Init should be used in cases for response objects where the field should be set on construction but we are using public setters (may not be necessary if we use shorthand record initialisers)
  • When multiple types per file are allowed and impact on user experience
    • Multiple types per file can be used when the types are nested
    • Nested types can be used when the type is only ever used within the context of the parent type
    • To avoid naming conflicts when nesting types with properties e.g. you can use the Request or Response suffix for example, public AccountRequest Account {get;set;} equally a Details or Summary suffix may be fair.
    • In cases, where nested types are used within nested types, choose the depth that makes sense to preserve the heirarchy
    • If the type should be shared outside of the context of the top level type, it should be moved but note that this is a breaking change
  • Naming conventions

  • Record type constructor injection warning - tests should validate mapping
  • Naming of request/response objects vs resources e.g. InitiateDepositRequest/Response vs Deposit
  • When should an enum be used vs constants (e.g. strings on response - backwards compatibility)

Naming changes

We've agreed that backend clients will be named "Libraries" rather than "SDKs". Given that Library is overly generic I would suggest the following:

  • Assembly: TrueLayerSDK -> TrueLayer.Client

When referring to the library we called it the "TrueLayer .NET Client" or "TrueLayer Client" when the .NET is inferred.

Drop custom HttpClientFactory

If we're happy to target .NET Core 3.0 onwards we can leverage HttpClientFactory that is included in .NET Standard 2.1

Clients should be lazily initialised

We will also need to move validation outside of the DI wireup as features that require specific options may not be used by the developer e.g. Payout signing key id

Add UserAgent

Add the user agent

name: truelayer-sdk-net
version: The assembly version

Abstract obtaining authentication tokens

For APIs leveraging client-credentials authentication, it should be possible to obtain these automatically on behalf of the developer without an explicit call. We can also implement token caching to avoid obtaining a token for every API call (which will also give Auth API a breather)

Resources

GitHub Actions / CI

Set up build script to run on GitHub actions. Probably worth waiting to move the repo since it will require the setup of secrets etc.

Implement http error handling

We should find a way to handle errors coming from TL apis.
The following code is commented out at the moment. You can find it here.

var content = await httpResponse.Content.ReadAsStringAsync();
if (httpResponse.StatusCode == Unprocessable)
{
    var error = await DeserializeJsonAsync<ErrorResponse>(httpResponse);
    throw new TruelayerValidationException(error, httpResponse.StatusCode, requestId);
}

URLs should be configurable

It should be possible to override URLs, specifically so that we can test the SDK against other environments.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.