Skip to content

Commit

Permalink
[ACL-255] Add multi Client support (#239)
Browse files Browse the repository at this point in the history
  • Loading branch information
tl-Roberto-Mancinelli authored Jan 15, 2025
1 parent 442f1b4 commit f836fcd
Show file tree
Hide file tree
Showing 51 changed files with 1,012 additions and 810 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ jobs:
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- name: Setup .NET 3.1
uses: actions/setup-dotnet@v2
with:
dotnet-version: 3.1.x
- name: Restore tools
run: dotnet tool restore
- name: Run the build script
Expand All @@ -44,6 +40,9 @@ jobs:
TrueLayer__ClientId: ${{ secrets.TRUELAYER__CLIENTID }}
TrueLayer__ClientSecret: ${{ secrets.TRUELAYER__CLIENTSECRET }}
TrueLayer__Payments__SigningKey__KeyId: ${{ secrets.TRUELAYER__PAYMENTS__SIGNINGKEY__KEYID }}
TrueLayer2__ClientId: ${{ secrets.TRUELAYER__CLIENTID }}
TrueLayer2__ClientSecret: ${{ secrets.TRUELAYER__CLIENTSECRET }}
TrueLayer2__Payments__SigningKey__KeyId: ${{ secrets.TRUELAYER__PAYMENTS__SIGNINGKEY__KEYID }}
with:
cake-version: 5.0.0
target: CI
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ jobs:
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- name: Setup .NET 3.1
uses: actions/setup-dotnet@v2
with:
dotnet-version: 3.1.x
- name: Restore tools
run: dotnet tool restore
- name: Run the build script
Expand All @@ -55,6 +51,9 @@ jobs:
TrueLayer__ClientId: ${{ secrets.TRUELAYER__CLIENTID }}
TrueLayer__ClientSecret: ${{ secrets.TRUELAYER__CLIENTSECRET }}
TrueLayer__Payments__SigningKey__KeyId: ${{ secrets.TRUELAYER__PAYMENTS__SIGNINGKEY__KEYID }}
TrueLayer2__ClientId: ${{ secrets.TRUELAYER__CLIENTID }}
TrueLayer2__ClientSecret: ${{ secrets.TRUELAYER__CLIENTSECRET }}
TrueLayer2__Payments__SigningKey__KeyId: ${{ secrets.TRUELAYER__PAYMENTS__SIGNINGKEY__KEYID }}
with:
cake-version: 5.0.0
target: ${{ github.event.inputs.target || 'Publish' }}
Expand Down
54 changes: 49 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Add your Client ID, Secret and Signing Key ID to `appsettings.json` or any other

### Initialize TrueLayer.NET

Register the TrueLayer client in `Startup.cs` or `Program.cs` (.NET 6.0):
Register the TrueLayer client in `Startup.cs` or `Program.cs` (.NET 9.0/.NET 8.0/.NET 6.0):

```c#
public IConfiguration Configuration { get; }
Expand All @@ -115,13 +115,57 @@ public void ConfigureServices(IServiceCollection services)
// For demo purposes only. Private key should be stored securely
options.Payments.SigningKey.PrivateKey = File.ReadAllText("ec512-private-key.pem");
}
})
// We advice to cache the auth token
.AddAuthTokenInMemoryCaching();
},
// For best performance and reliability we advice to cache the auth token
authCachingStrategy: AuthCachingStrategy.InMemory)

}
```

Alternatively you can create a class that implements `IConfigureOptions<TrueLayerOptions>` if you have more complex configuration requirements.
### Multiple TrueLayer Clients

Use keyed version of TrueLayer client (.NET 9.0/.NET 8.0):

```c#
.AddKeyedTrueLayer("TrueLayerGbp",
configuration,
options =>
{
// For demo purposes only. Private key should be stored securely
var privateKey = File.ReadAllText("ec512-private-key.pem");
if (options.Payments?.SigningKey != null)
{
options.Payments.SigningKey.PrivateKey = privateKey;
}
},
authTokenCachingStrategy: AuthTokenCachingStrategies.InMemory)
.AddKeyedTrueLayer("TrueLayerEur",
configuration,
options =>
{
// For demo purposes only. Private key should be stored securely
var privateKey = File.ReadAllText("ec512-private-key.pem");
if (options.Payments?.SigningKey != null)
{
options.Payments.SigningKey.PrivateKey = privateKey;
}
},
authTokenCachingStrategy: AuthTokenCachingStrategies.InMemory)
```

Use `[FromKeyedServices()]` attribute to retrieve keyed client

```c#
public GbpController([FromKeyedServices("TrueLayerGbp")]ITrueLayerClient trueLayerClient, ...
public EurController([FromKeyedServices("TrueLayerEur")]ITrueLayerClient trueLayerClient, ...
```

Or `GetRequiredKeyedService`

```c#
var GbpClient = ServiceProvider.GetRequiredKeyedService<ITrueLayerClient>("TrueLayerGbp");
var EurClient = ServiceProvider.GetRequiredKeyedService<ITrueLayerClient>("TrueLayerEur");
```

### Make a payment

Expand Down
10 changes: 5 additions & 5 deletions examples/MvcExample/Controllers/MerchantAccountsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ namespace MvcExample.Controllers
{
public class MerchantAccountsController : Controller
{
private readonly ITrueLayerClient _truelayer;
private readonly ITrueLayerClient _trueLayerClient;
private readonly ILogger<MerchantAccountsController> _logger;

public MerchantAccountsController(ITrueLayerClient truelayer, ILogger<MerchantAccountsController> logger)
public MerchantAccountsController(ITrueLayerClient trueLayerClient, ILogger<MerchantAccountsController> logger)
{
_truelayer = truelayer;
_trueLayerClient = trueLayerClient;
_logger = logger;
}

public async Task<IActionResult> Index()
{
var apiResponse = await _truelayer.MerchantAccounts.ListMerchantAccounts();
var apiResponse = await _trueLayerClient.MerchantAccounts.ListMerchantAccounts();

if (apiResponse.IsSuccessful)
{
Expand All @@ -42,7 +42,7 @@ public async Task<IActionResult> Index()

public async Task<IActionResult> Details(string id)
{
var apiResponse = await _truelayer.MerchantAccounts.GetMerchantAccount(id);
var apiResponse = await _trueLayerClient.MerchantAccounts.GetMerchantAccount(id);

if (apiResponse.IsSuccessful)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@

namespace MvcExample.Controllers
{
public class HomeController : Controller
public class PaymentsController : Controller
{
private readonly ITrueLayerClient _truelayer;
private readonly ILogger<HomeController> _logger;
private readonly ITrueLayerClient _trueLayerClient;
private readonly ILogger<PaymentsController> _logger;

public HomeController(ITrueLayerClient truelayer, ILogger<HomeController> logger)
public PaymentsController(ITrueLayerClient trueLayerClient, ILogger<PaymentsController> logger)
{
_truelayer = truelayer;
_trueLayerClient = trueLayerClient;
_logger = logger;
}

Expand All @@ -39,7 +39,9 @@ public async Task<IActionResult> Donate(DonateModel donateModel)
}

OneOf<Provider.UserSelected, Provider.Preselected> providerSelection = donateModel.UserPreSelectedFilter
? new Provider.Preselected(providerId: "mock-payments-gb-redirect", schemeSelection: new SchemeSelection.Preselected { SchemeId = "faster_payments_service"})
? new Provider.Preselected(
providerId: "mock-payments-gb-redirect",
schemeSelection: new SchemeSelection.Preselected { SchemeId = "faster_payments_service"})
: new Provider.UserSelected();

var paymentRequest = new CreatePaymentRequest(
Expand All @@ -56,7 +58,7 @@ public async Task<IActionResult> Donate(DonateModel donateModel)
null
);

var apiResponse = await _truelayer.Payments.CreatePayment(
var apiResponse = await _trueLayerClient.Payments.CreatePayment(
paymentRequest,
idempotencyKey: Guid.NewGuid().ToString()
);
Expand All @@ -78,26 +80,31 @@ public async Task<IActionResult> Donate(DonateModel donateModel)
}

return apiResponse.Data.Match<IActionResult>(
authorizing =>
{
ViewData["Status"] = authorizing.Status;
return View("Success");
},
authorizationRequired =>
{
var hppLink = _truelayer.Payments.CreateHostedPaymentPageLink(authorizationRequired.Id,
authorizationRequired.ResourceToken, new Uri(Url.ActionLink("Complete")));
// Return Uri must be whitelisted in TrueLayer console
var returnUri = new Uri(Url.ActionLink("Success"));

var hppLink = _trueLayerClient.Payments.CreateHostedPaymentPageLink(
authorizationRequired.Id,
authorizationRequired.ResourceToken,
returnUri);
return Redirect(hppLink);
},
authorized =>
{
ViewData["Status"] = authorized.Status;
return View("Success");
return View("Pending");
},
failed =>
{
ViewData["Status"] = failed.Status;
return View("Failed");
},
authorizing =>
{
ViewData["Status"] = authorizing.Status;
return View("Pending");
});
}

Expand All @@ -107,15 +114,20 @@ public async Task<IActionResult> Complete([FromQuery(Name = "payment_id")] strin
if (string.IsNullOrWhiteSpace(paymentId))
return StatusCode((int)HttpStatusCode.BadRequest);

var apiResponse = await _truelayer.Payments.GetPayment(paymentId);
var apiResponse = await _trueLayerClient.Payments.GetPayment(paymentId);

IActionResult Failed(string status, OneOf<PaymentMethod.BankTransfer, PaymentMethod.Mandate>? paymentMethod)
{
ViewData["Status"] = status;
if (!apiResponse.IsSuccessful)
return Failed(apiResponse.StatusCode.ToString(), null!);

SetProviderAndSchemeId(paymentMethod);
return View("Failed");
}
return apiResponse.Data.Match(
authRequired => Failed(authRequired.Status, authRequired.PaymentMethod),
SuccessOrPending,
SuccessOrPending,
SuccessOrPending,
SuccessOrPending,
failed => Failed(failed.Status, failed.PaymentMethod),
attemptFailed => Failed(attemptFailed.Status, attemptFailed.PaymentMethod)
);

IActionResult SuccessOrPending(PaymentDetails payment)
{
Expand All @@ -137,18 +149,13 @@ void SetProviderAndSchemeId(OneOf<PaymentMethod.BankTransfer, PaymentMethod.Mand
ViewData["SchemeId"] = schemeId;
}

if (!apiResponse.IsSuccessful)
return Failed(apiResponse.StatusCode.ToString(), null!);
IActionResult Failed(string status, OneOf<PaymentMethod.BankTransfer, PaymentMethod.Mandate>? paymentMethod)
{
ViewData["Status"] = status;

return apiResponse.Data.Match(
authRequired => Failed(authRequired.Status, authRequired.PaymentMethod),
SuccessOrPending,
SuccessOrPending,
SuccessOrPending,
SuccessOrPending,
failed => Failed(failed.Status, failed.PaymentMethod),
attemptFailed => Failed(attemptFailed.Status, attemptFailed.PaymentMethod)
);
SetProviderAndSchemeId(paymentMethod);
return View("Failed");
}
}

public IActionResult Privacy()
Expand Down
10 changes: 5 additions & 5 deletions examples/MvcExample/Controllers/PayoutController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ namespace MvcExample.Controllers
{
public class PayoutController : Controller
{
private readonly ITrueLayerClient _truelayer;
private readonly ITrueLayerClient _trueLayerClient;
private readonly ILogger<PayoutController> _logger;

public PayoutController(ITrueLayerClient truelayer, ILogger<PayoutController> logger)
public PayoutController(ITrueLayerClient trueLayerClient, ILogger<PayoutController> logger)
{
_truelayer = truelayer;
_trueLayerClient = trueLayerClient;
_logger = logger;
}

Expand Down Expand Up @@ -51,7 +51,7 @@ public async Task<IActionResult> CreatePayout(PayoutModel payoutModel)
externalAccount,
metadata: new() { { "a", "b" } });

var apiResponse = await _truelayer.Payouts.CreatePayout(
var apiResponse = await _trueLayerClient.Payouts.CreatePayout(
payoutRequest,
idempotencyKey: Guid.NewGuid().ToString()
);
Expand Down Expand Up @@ -88,7 +88,7 @@ public async Task<IActionResult> Complete(string payoutId)
return View();
}

var apiResponse = await _truelayer.Payouts.GetPayout(payoutId);
var apiResponse = await _trueLayerClient.Payouts.GetPayout(payoutId);

IActionResult Failed(string status)
{
Expand Down
28 changes: 14 additions & 14 deletions examples/MvcExample/Controllers/ProvidersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ namespace MvcExample.Controllers
{
public class ProvidersController : Controller
{
private readonly ITrueLayerClient _truelayer;
private readonly ITrueLayerClient _trueLayerClient;

public ProvidersController(ITrueLayerClient truelayer)
public ProvidersController(ITrueLayerClient trueLayerClient)
{
_truelayer = truelayer;
_trueLayerClient = trueLayerClient;
}

public IActionResult Index()
Expand All @@ -29,15 +29,11 @@ public async Task<IActionResult> GetProvider([FromQuery(Name = "id")] string pro
return StatusCode((int)HttpStatusCode.BadRequest);
}

var apiResponse = await _truelayer.PaymentsProviders.GetPaymentsProvider(providerId);
var apiResponse = await _trueLayerClient.PaymentsProviders.GetPaymentsProvider(providerId);

IActionResult Failed(string status)
{
ViewData["Status"] = status;
ViewData["ProviderId"] = providerId;

return View("Failed");
}
return apiResponse.IsSuccessful
? Success(apiResponse.Data)
: Failed(apiResponse.StatusCode.ToString());

IActionResult Success(PaymentsProvider provider)
{
Expand All @@ -46,9 +42,13 @@ IActionResult Success(PaymentsProvider provider)
return View("Success");
}

return apiResponse.IsSuccessful
? Success(apiResponse.Data)
: Failed(apiResponse.StatusCode.ToString());
IActionResult Failed(string status)
{
ViewData["Status"] = status;
ViewData["ProviderId"] = providerId;

return View("Failed");
}
}
}
}
2 changes: 1 addition & 1 deletion examples/MvcExample/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"applicationUrl": "https://localhost:3001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
Expand Down
Loading

0 comments on commit f836fcd

Please sign in to comment.