diff --git a/TinkerSrc/CreateOauthClient.cs b/TinkerSrc/CreateOauthClient.cs
new file mode 100644
index 0000000..f9db12f
--- /dev/null
+++ b/TinkerSrc/CreateOauthClient.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Bunq.Sdk.Context;
+using Bunq.Sdk.Exception;
+using Bunq.Sdk.Json;
+using Bunq.Sdk.Model.Core;
+using Bunq.Sdk.Model.Generated.Endpoint;
+using Mono.Options;
+using Tinker.Utils;
+
+namespace TinkerSrc
+{
+ public class CreateOauthClient : ITinker
+ {
+ ///
+ /// API constants.
+ ///
+ protected const string OPTION_CONTEXT = "context=";
+ protected const string OPTION_REDIRECT_URI = "redirect=";
+
+ /**
+ * File constants.
+ */
+ protected const string FILE_OAUTH_CONFIGURATION = "oauth.conf";
+
+ /**
+ * Error constants.
+ */
+ protected const string ERROR_MISSING_MANDATORY_OPTION = "Missing mandatory option.";
+
+ public void Run(string[] args)
+ {
+ Dictionary allOption = AssertMandatoryOptions(args);
+
+ BunqContext.LoadApiContext(
+ ApiContext.Restore(allOption[OPTION_CONTEXT])
+ );
+
+ OauthClient oauthClient;
+ if (File.Exists(FILE_OAUTH_CONFIGURATION))
+ {
+ oauthClient = OauthClient.CreateFromJsonString(File.ReadAllText(FILE_OAUTH_CONFIGURATION));
+ }
+ else
+ {
+ int oauthClientId = OauthClient.Create().Value;
+
+ OauthCallbackUrl.Create(oauthClientId, allOption[OPTION_REDIRECT_URI]);
+ oauthClient = OauthClient.Get(oauthClientId).Value;
+
+ String serializedClient = BunqJsonConvert.SerializeObject(oauthClient);
+ File.WriteAllText(FILE_OAUTH_CONFIGURATION, serializedClient);
+ }
+
+ OauthAuthorizationUri authorizationUri = OauthAuthorizationUri.Create(
+ OauthResponseType.CODE,
+ allOption[OPTION_REDIRECT_URI],
+ oauthClient
+ );
+
+ Console.WriteLine(" | Created oauth client. Stored in {0}.", FILE_OAUTH_CONFIGURATION);
+ Console.WriteLine(" | Point your user to {0} to obtain an Authorization code.", authorizationUri.AuthorizationUri);
+ }
+
+ private Dictionary AssertMandatoryOptions(string[] allArgument)
+ {
+ Dictionary allOption = new Dictionary();
+
+ new OptionSet
+ {
+ {OPTION_CONTEXT, value => allOption.Add(OPTION_CONTEXT, value)},
+ {OPTION_REDIRECT_URI, value => allOption.Add(OPTION_REDIRECT_URI, value)}
+ }.Parse(allArgument);
+
+ if (allOption[OPTION_CONTEXT] != null &&
+ allOption[OPTION_REDIRECT_URI] != null) {
+ return allOption;
+ }
+
+ throw new BunqException(ERROR_MISSING_MANDATORY_OPTION);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TinkerSrc/CreatePsd2Configuration.cs b/TinkerSrc/CreatePsd2Configuration.cs
new file mode 100644
index 0000000..a46022d
--- /dev/null
+++ b/TinkerSrc/CreatePsd2Configuration.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Security.Cryptography.X509Certificates;
+using Bunq.Sdk.Context;
+using Bunq.Sdk.Exception;
+using Bunq.Sdk.Model.Generated.Object;
+using Bunq.Sdk.Security;
+using Mono.Options;
+using Tinker.Utils;
+using TinkerSrc.Lib;
+
+namespace TinkerSrc
+{
+ public class CreatePsd2Configuration : ITinker
+ {
+ ///
+ /// API constants.
+ ///
+ protected const string API_DEVICE_DESCRIPTION = "##### YOUR DEVICE DESCRIPTION #####";
+
+ ///
+ /// Option constants.
+ ///
+ protected const string OPTION_CREDENTIALS = "credentials=";
+ protected const string OPTION_CERTIFICATE_CHAIN = "chain=";
+ protected const string OPTION_PASSPHRASE = "pass=";
+
+ ///
+ /// Error constants.
+ ///
+ protected const string ERROR_MISSING_MANDATORY_OPTION = "Missing mandatory option.";
+
+ ///
+ /// File constants.
+ ///
+ protected const string FILE_CONTEXT = "bunq-psd2.conf";
+
+ public void Run(string[] args)
+ {
+ Dictionary allOption = AssertMandatoryOptions(args);
+
+ ApiContext apiContext = ApiContext.CreateForPsd2(
+ ShareLib.DetermineEnvironmentType(args),
+ SecurityUtils.GetCertificateFromFile(allOption[OPTION_CREDENTIALS], allOption[OPTION_PASSPHRASE]),
+ new X509CertificateCollection () {
+ SecurityUtils.GetCertificateFromFile(allOption[OPTION_CERTIFICATE_CHAIN])
+ },
+ API_DEVICE_DESCRIPTION,
+ new List()
+ );
+
+ apiContext.Save(FILE_CONTEXT);
+ }
+
+ private Dictionary AssertMandatoryOptions(string[] allArgument)
+ {
+ Dictionary allOption = new Dictionary();
+
+ new OptionSet
+ {
+ {OPTION_CREDENTIALS, value => allOption.Add(OPTION_CREDENTIALS, value)},
+ {OPTION_CERTIFICATE_CHAIN, value => allOption.Add(OPTION_CERTIFICATE_CHAIN, value)},
+ {OPTION_PASSPHRASE, value => allOption.Add(OPTION_PASSPHRASE, value)}
+ }.Parse(allArgument);
+
+ if (allOption[OPTION_CERTIFICATE_CHAIN] != null &&
+ allOption[OPTION_CREDENTIALS] != null &&
+ allOption[OPTION_PASSPHRASE] != null) {
+ return allOption;
+ }
+
+ throw new BunqException(ERROR_MISSING_MANDATORY_OPTION);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TinkerSrc/TestOauth.cs b/TinkerSrc/TestOauth.cs
new file mode 100644
index 0000000..414cd66
--- /dev/null
+++ b/TinkerSrc/TestOauth.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using Bunq.Sdk.Context;
+using Bunq.Sdk.Exception;
+using Bunq.Sdk.Model.Core;
+using Bunq.Sdk.Model.Generated.Endpoint;
+using Mono.Options;
+using Tinker.Utils;
+using TinkerSrc.Lib;
+
+namespace TinkerSrc
+{
+ public class TestOauth : ITinker
+ {
+ ///
+ /// Option constants.
+ ///
+ protected const string OPTION_AUTH_CODE = "code=";
+ protected const string OPTION_CLIENT_CONFIGURATION = "configuration=";
+ protected const string OPTION_REDIRECT = "redirect=";
+ protected const string OPTION_CONTEXT = "context=";
+
+ ///
+ /// API constants.
+ ///
+ protected const string API_DEVICE_DESCRIPTION = "##### YOUR DEVICE DESCRIPTION #####";
+
+ ///
+ /// Error constants.
+ ///
+ protected const string ERROR_MISSING_MANDATORY_OPTION = "Missing mandatory option.";
+
+ public void Run(string[] args)
+ {
+ Dictionary allOption = AssertMandatoryOptions(args);
+
+ ApiContext apiContext = ApiContext.Restore(allOption[OPTION_CONTEXT]);
+ BunqContext.LoadApiContext(apiContext);
+
+ OauthAccessToken accessToken = OauthAccessToken.Create(
+ OauthGrantType.AUTHORIZATION_CODE,
+ allOption[OPTION_AUTH_CODE],
+ allOption[OPTION_REDIRECT],
+ CreateOauthClientFromFile(allOption[OPTION_CLIENT_CONFIGURATION])
+ );
+
+ apiContext = CreateApiContextByOauthToken(
+ accessToken,
+ ShareLib.DetermineEnvironmentType(args)
+ );
+ BunqContext.LoadApiContext(apiContext);
+
+ (new UserOverview()).Run(new String[]{});
+ }
+
+
+ private Dictionary AssertMandatoryOptions(string[] allArgument)
+ {
+ Dictionary allOption = new Dictionary();
+
+ new OptionSet
+ {
+ {OPTION_AUTH_CODE, value => allOption.Add(OPTION_AUTH_CODE, value)},
+ {OPTION_REDIRECT, value => allOption.Add(OPTION_REDIRECT, value)},
+ {OPTION_CONTEXT, value => allOption.Add(OPTION_CONTEXT, value)},
+ {OPTION_CLIENT_CONFIGURATION, value => allOption.Add(OPTION_CLIENT_CONFIGURATION, value)}
+ }.Parse(allArgument);
+
+ if (allOption[OPTION_AUTH_CODE] != null &&
+ allOption[OPTION_REDIRECT] != null &&
+ allOption[OPTION_CONTEXT] != null &&
+ allOption[OPTION_CLIENT_CONFIGURATION] != null) {
+ return allOption;
+ }
+
+ throw new BunqException(ERROR_MISSING_MANDATORY_OPTION);
+ }
+
+ private OauthClient CreateOauthClientFromFile(String path)
+ {
+ return OauthClient.CreateFromJsonString(
+ File.ReadAllText(path)
+ );
+ }
+
+ private static ApiContext CreateApiContextByOauthToken(OauthAccessToken token, ApiEnvironmentType environmentType)
+ {
+ return ApiContext.Create(
+ environmentType,
+ token.Token,
+ API_DEVICE_DESCRIPTION
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/TinkerSrc/TinkerSrc.csproj b/TinkerSrc/TinkerSrc.csproj
index 449b4ad..4717a59 100755
--- a/TinkerSrc/TinkerSrc.csproj
+++ b/TinkerSrc/TinkerSrc.csproj
@@ -1,10 +1,10 @@
Exe
- netcoreapp1.1
+ netcoreapp2.2
-
+
\ No newline at end of file
diff --git a/go-pro b/go-pro
index 215811a..0fc4d9e 100755
--- a/go-pro
+++ b/go-pro
@@ -22,6 +22,28 @@ USAGE="\
┌───────────────────────┬────────────────────────────────────────────────────────────┐
│ │ Run │
├───────────────────────┼────────────────────────────────────────────────────────────┤
+ │ 🔐 Create PSD2 Provider! │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll CreatePsd2Configuration${ANSI_FORMAT_CLEAR} │
+ │ │ │
+ │ │ ${ANSI_FORMAT_DIM}Required parameters:${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--env [SANDBOX/PRODUCTION]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--certificate [path]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--chain [path]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--key [path]${ANSI_FORMAT_CLEAR} │
+ ├───────────────────────┼────────────────────────────────────────────────────────────┤
+ │ 🔒 Create PSD2 OAuth Client │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll CreateOauthClient${ANSI_FORMAT_CLEAR} │
+ │ │ │
+ │ │ ${ANSI_FORMAT_DIM}Required parameters:${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--context [path]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--redirect [uri]${ANSI_FORMAT_CLEAR} │
+ ├───────────────────────┼────────────────────────────────────────────────────────────┤
+ │ 🔓 Test PSD2 OAuth │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll TestOauth${ANSI_FORMAT_CLEAR} │
+ │ │ │
+ │ │ ${ANSI_FORMAT_DIM}Required parameters:${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--env [SANDBOX/PRODUCTION]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--code [authcode]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--configuration [path]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--redirect [uri]${ANSI_FORMAT_CLEAR} │
+ |───────────────────────┼────────────────────────────────────────────────────────────┤
│ ✅ Show Overview │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll UserOverview --production${ANSI_FORMAT_CLEAR} │
├───────────────────────┼────────────────────────────────────────────────────────────┤
│ 🔼 Make a payment │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll MakePayment --production${ANSI_FORMAT_CLEAR} │
diff --git a/go-tinker b/go-tinker
index 4c28189..6804b87 100755
--- a/go-tinker
+++ b/go-tinker
@@ -19,6 +19,28 @@ USAGE="\
┌───────────────────────┬──────────────────────────────────────────────┐
│ │ Run │
├───────────────────────┼──────────────────────────────────────────────┤
+ │ 🔐 Create PSD2 Provider! │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll CreatePsd2Configuration${ANSI_FORMAT_CLEAR} │
+ │ │ │
+ │ │ ${ANSI_FORMAT_DIM}Required parameters:${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--env [SANDBOX/PRODUCTION]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--certificate [path]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--chain [path]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--key [path]${ANSI_FORMAT_CLEAR} │
+ ├───────────────────────┼──────────────────────────────────────────────┤
+ │ 🔒 Create PSD2 OAuth Client │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll CreateOauthClient${ANSI_FORMAT_CLEAR} │
+ │ │ │
+ │ │ ${ANSI_FORMAT_DIM}Required parameters:${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--context [path]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--redirect [uri]${ANSI_FORMAT_CLEAR} │
+ ├───────────────────────┼──────────────────────────────────────────────┤
+ │ 🔓 Test PSD2 OAuth │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll TestOauth${ANSI_FORMAT_CLEAR} │
+ │ │ │
+ │ │ ${ANSI_FORMAT_DIM}Required parameters:${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--env [SANDBOX/PRODUCTION]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--code [authcode]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--configuration [path]${ANSI_FORMAT_CLEAR} │
+ │ │ ${ANSI_FORMAT_DIM}--redirect [uri]${ANSI_FORMAT_CLEAR} │
+ ├───────────────────────┼──────────────────────────────────────────────┤
│ ✅ Show Overview │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll UserOverview${ANSI_FORMAT_CLEAR} │
├───────────────────────┼──────────────────────────────────────────────┤
│ 🔼 Make a payment │ ${ANSI_FORMAT_VERBOSE}dotnet Tinker/TinkerSrc.dll MakePayment${ANSI_FORMAT_CLEAR} │