diff --git a/BTCPayServer.Lightning.sln b/BTCPayServer.Lightning.sln
index 6568ac47..1f7a4fc1 100644
--- a/BTCPayServer.Lightning.sln
+++ b/BTCPayServer.Lightning.sln
@@ -31,6 +31,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{216059DB-7
src\Build\Common.csproj = src\Build\Common.csproj
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Lightning.Blink", "src\BTCPayServer.Lightning.Blink\BTCPayServer.Lightning.Blink.csproj", "{DD000D73-3AD5-465E-9AA9-64802CC88BF3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlinkTest", "BlinkTest\BlinkTest.csproj", "{4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -149,6 +153,30 @@ Global
{B024DBD2-FCF4-4C48-9EBE-09AA7AAF36FB}.Release|x64.Build.0 = Release|Any CPU
{B024DBD2-FCF4-4C48-9EBE-09AA7AAF36FB}.Release|x86.ActiveCfg = Release|Any CPU
{B024DBD2-FCF4-4C48-9EBE-09AA7AAF36FB}.Release|x86.Build.0 = Release|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|x64.Build.0 = Debug|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|x86.Build.0 = Debug|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|x64.ActiveCfg = Release|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|x64.Build.0 = Release|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|x86.ActiveCfg = Release|Any CPU
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|x86.Build.0 = Release|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|x64.Build.0 = Debug|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|x86.Build.0 = Debug|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|x64.ActiveCfg = Release|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|x64.Build.0 = Release|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|x86.ActiveCfg = Release|Any CPU
+ {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -162,6 +190,7 @@ Global
{542D3F73-7067-4873-89EF-FA0345E32C04} = {5BA1A1B2-2713-4CF4-9B63-087531598797}
{4057015B-9D8A-411A-B7C2-3342D9F53BD0} = {5BA1A1B2-2713-4CF4-9B63-087531598797}
{B024DBD2-FCF4-4C48-9EBE-09AA7AAF36FB} = {5BA1A1B2-2713-4CF4-9B63-087531598797}
+ {DD000D73-3AD5-465E-9AA9-64802CC88BF3} = {5BA1A1B2-2713-4CF4-9B63-087531598797}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5E91C820-C650-4ACA-B064-05ECE0A74CE8}
diff --git a/BlinkTest/BlinkTest.csproj b/BlinkTest/BlinkTest.csproj
new file mode 100644
index 00000000..38bfbd64
--- /dev/null
+++ b/BlinkTest/BlinkTest.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
diff --git a/BlinkTest/Program.cs b/BlinkTest/Program.cs
new file mode 100644
index 00000000..89dd28e1
--- /dev/null
+++ b/BlinkTest/Program.cs
@@ -0,0 +1,38 @@
+using System.Text.Json;
+
+using BTCPayServer.Lightning.Blink;
+using BTCPayServer.Lightning.Blink.Utilities;
+
+Uri apiUri = new Uri("https://api.blink.sv/graphql");
+// put your blink API key here
+string apiKey = "";
+
+// To include your stablesats in your total balance as converted to sats
+// var blinkApiClient = new BlinkApiClient(apiUri, apiKey, true);
+var blinkApiClient = new BlinkApiClient(apiUri, apiKey, false);
+
+var defaultWallet = await blinkApiClient.GetDefaultWallet(CancellationToken.None);
+Console.WriteLine(defaultWallet.Id);
+
+var totalBalance = await blinkApiClient.GetBalance(CancellationToken.None);
+Console.WriteLine(totalBalance.OffchainBalance.Local);
+
+var createIncoiceResponse = await blinkApiClient.CreateLnInvoice(
+ CancellationToken.None,
+ 100L,
+ TimeSpan.FromMinutes(10),
+ "a memo to remember");
+
+string jsonResponse = JsonSerializer.Serialize(createIncoiceResponse, new JsonSerializerOptions { WriteIndented = true });
+Console.WriteLine(jsonResponse);
+
+var btcRate = await blinkApiClient.getBtcRate(CancellationToken.None);
+
+var convertedToSats = RateConversions.convertCentsToSats(createIncoiceResponse.Amount, btcRate);
+var deviation = (createIncoiceResponse.AmountReceived - convertedToSats) / createIncoiceResponse.AmountReceived * 100;
+
+Console.WriteLine($"Invoice created with {createIncoiceResponse.Amount.MilliSatoshi} {defaultWallet.WalletCurrency}");
+Console.WriteLine($"Conversion rate is {btcRate.Base} / 10^{btcRate.Offset}");
+Console.WriteLine($"My own conversion: {convertedToSats} sats");
+Console.WriteLine($"Received sats: {createIncoiceResponse.AmountReceived.MilliSatoshi}");
+Console.WriteLine($"Deviation: %{deviation}");
diff --git a/src/BTCPayServer.Lightning.Blink/BTCPayServer.Lightning.Blink.csproj b/src/BTCPayServer.Lightning.Blink/BTCPayServer.Lightning.Blink.csproj
new file mode 100644
index 00000000..97a54fb9
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/BTCPayServer.Lightning.Blink.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BTCPayServer.Lightning.Blink/BlinkApiClient.cs b/src/BTCPayServer.Lightning.Blink/BlinkApiClient.cs
new file mode 100644
index 00000000..63594546
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/BlinkApiClient.cs
@@ -0,0 +1,234 @@
+using GraphQL;
+using GraphQL.Client.Http;
+using GraphQL.Client.Serializer.Newtonsoft;
+
+using BTCPayServer.Lightning.Blink.Models.Responses;
+using BTCPayServer.Lightning.Blink.Models;
+using BTCPayServer.Lightning.Blink.Utilities;
+
+namespace BTCPayServer.Lightning.Blink;
+
+public class BlinkApiClient
+{
+ private Uri _baseUri;
+ private string _apiKey;
+ private bool _includeStablesats;
+
+ public BlinkApiClient(Uri baseUri, string apiKey, bool includeStablesats = false)
+ {
+ _baseUri = baseUri;
+ _apiKey = apiKey;
+ _includeStablesats = includeStablesats;
+ }
+
+ public async Task GetDefaultWallet(CancellationToken cancellationToken)
+ {
+ string query = @"
+ query GetDefaultWallet {
+ me {
+ defaultAccount {
+ defaultWalletId
+ wallets {
+ balance
+ id
+ walletCurrency
+ }
+ }
+ }
+ }";
+
+ var defaultWalletRequest = new GraphQLRequest
+ {
+ Query = query,
+ OperationName = "GetDefaultWallet"
+ };
+
+ var response = await getGraphQLHttpClient().SendQueryAsync(defaultWalletRequest, cancellationToken);
+ string defaultWalletId = response.Data.Me.DefaultAccount.DefaultWalletId;
+ var defaultWallet = response.Data.Me.DefaultAccount.Wallets.Find((w) => w.Id == defaultWalletId);
+
+ if (defaultWallet == null)
+ {
+ throw new Exception(string.Format("Could not find wallet information for wallet id {0}", defaultWalletId));
+ }
+
+ return new BlinkWallet
+ {
+ Id = defaultWallet.Id,
+ Balance = defaultWallet.Balance,
+ WalletCurrency = defaultWallet.WalletCurrency
+ };
+ }
+
+ public async Task getBtcRate(CancellationToken cancellationToken)
+ {
+ string query = @"
+ query RealtimePrice($currency: DisplayCurrency) {
+ realtimePrice(currency: $currency) {
+ btcSatPrice {
+ base,
+ offset
+ }
+ }
+ }";
+
+ var getBtcRateRequest = new GraphQLRequest
+ {
+ Query = query,
+ Variables = new
+ {
+ input = new
+ {
+ currency = "USD"
+ }
+ }
+ };
+
+ var response = await getGraphQLHttpClient().SendQueryAsync(getBtcRateRequest, cancellationToken);
+
+ return response.Data.RealtimePrice.BtcSatPrice;
+ }
+
+ public async Task GetBalance(CancellationToken cancellationToken)
+ {
+ string realtimePriceQuery = _includeStablesats
+ ? @"realtimePrice {
+ btcSatPrice {
+ base
+ offset
+ }
+ denominatorCurrency
+ }"
+ : "";
+
+ string query = $@"
+ query GetWalletBalances {{
+ me {{
+ defaultAccount {{
+ defaultWalletId,
+ wallets {{
+ balance
+ id
+ walletCurrency
+ }}
+ }}
+ }}
+ {realtimePriceQuery}
+ }}";
+
+ var balancesRequest = new GraphQLRequest
+ {
+ Query = query,
+ OperationName = "GetWalletBalances"
+ };
+
+ var response = await getGraphQLHttpClient().SendQueryAsync(balancesRequest, cancellationToken);
+ var responseData = response.Data;
+
+ long totalBalanceSats = responseData.Me.DefaultAccount.Wallets.Sum(wallet =>
+ {
+ if (wallet.WalletCurrency == "USD")
+ {
+ var realtimePrice = responseData.RealtimePrice;
+
+ if (_includeStablesats
+ && realtimePrice != null
+ && realtimePrice.BtcSatPrice != null
+ && realtimePrice.DenominatorCurrency == "USD")
+ {
+ return RateConversions.convertCentsToSats(wallet.Balance, realtimePrice.BtcSatPrice);
+ }
+
+ return 0;
+ } else
+ {
+ return wallet.Balance;
+ }
+ });
+
+ var offchain = new OffchainBalance
+ {
+ Local = new LightMoney(totalBalanceSats, LightMoneyUnit.MilliSatoshi)
+ };
+
+ return new LightningNodeBalance(null, offchain);
+ }
+
+ public async Task CreateLnInvoice(CancellationToken cancellationToken, long amount, TimeSpan expiresIn, string? memo = null)
+ {
+ var wallet = await this.GetDefaultWallet(cancellationToken);
+
+ string apiCall = wallet.WalletCurrency == "USD" ? "lnUsdInvoiceCreate" : "LnInvoiceCreate";
+
+ string query = $@"
+ mutation {apiCall}($input: {apiCall.Capitalize()}Input!) {{
+ {apiCall}(input: $input) {{
+ invoice {{
+ paymentRequest
+ paymentHash
+ paymentSecret
+ satoshis
+ }}
+ errors {{
+ message
+ }}
+ }}
+ }}";
+
+ var createLnInvoiceRequest = new GraphQLRequest
+ {
+ Query = query,
+ OperationName = apiCall,
+ Variables = new
+ {
+ input = new
+ {
+ amount,
+ walletId = wallet.Id,
+ expiresIn,
+ memo
+ }
+ }
+ };
+
+ var response = await getGraphQLHttpClient().SendMutationAsync(createLnInvoiceRequest, cancellationToken);
+ var graphQlErrors = response.Errors;
+
+ if (graphQlErrors != null && graphQlErrors.Length > 0)
+ {
+ var messages = string.Join(", ", graphQlErrors.Select(e => e.Message));
+ throw new Exception($"An error occured with invoice creation query: {messages}");
+ }
+
+ var invoiceData = response.Data.LnInvoiceCreate != null
+ ? response.Data.LnInvoiceCreate
+ : response.Data.LnUsdInvoiceCreate;
+
+ if (invoiceData.Errors.Count > 0)
+ {
+ var messages = string.Join(", ", invoiceData.Errors.Select(e => e.Message));
+ throw new Exception($"An error occured with invoice creation: {messages}");
+ }
+
+ return new LightningInvoice
+ {
+ Amount = amount,
+ PaymentHash = invoiceData.Invoice.PaymentHash,
+ ExpiresAt = DateTimeOffset.UtcNow.Add(expiresIn),
+ AmountReceived = invoiceData.Invoice.Satoshis,
+ Status = LightningInvoiceStatus.Unpaid,
+ BOLT11 = invoiceData.Invoice.PaymentRequest,
+ Preimage = invoiceData.Invoice.PaymentSecret
+ };
+ }
+
+ private GraphQLHttpClient getGraphQLHttpClient() {
+ var graphQLClient = new GraphQLHttpClient(_baseUri, new NewtonsoftJsonSerializer());
+
+ graphQLClient.HttpClient.DefaultRequestHeaders.Clear();
+ graphQLClient.HttpClient.DefaultRequestHeaders.Add("X-API-KEY", _apiKey);
+ graphQLClient.HttpClient.DefaultRequestHeaders.Add("User-Agent", "BTCPayServer.Lightning.BlinkApiClient");
+
+ return graphQLClient;
+ }
+}
diff --git a/src/BTCPayServer.Lightning.Blink/BlinkLightningClient.cs b/src/BTCPayServer.Lightning.Blink/BlinkLightningClient.cs
new file mode 100644
index 00000000..a02aa235
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/BlinkLightningClient.cs
@@ -0,0 +1,116 @@
+using System;
+using NBitcoin;
+
+namespace BTCPayServer.Lightning.Blink
+{
+ public class BlinkLightningClient : ILightningClient
+ {
+ private BlinkApiClient _client;
+
+ public BlinkLightningClient(Uri baseUri, string apiKey)
+ {
+ _client = new BlinkApiClient(baseUri, apiKey);
+ }
+
+ public Task CancelInvoice(string invoiceId, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ConnectTo(NodeInfo nodeInfo, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task CreateInvoice(LightMoney amount, string description, TimeSpan expiry, CancellationToken cancellation = default)
+ {
+ return _client.CreateLnInvoice(cancellation, amount.MilliSatoshi, expiry);
+ }
+
+ public Task CreateInvoice(CreateInvoiceParams createInvoiceRequest, CancellationToken cancellation = default)
+ {
+ return _client.CreateLnInvoice(cancellation, createInvoiceRequest.Amount, createInvoiceRequest.Expiry, createInvoiceRequest.Description);
+ }
+
+ public Task GetBalance(CancellationToken cancellation = default)
+ {
+ return _client.GetBalance(cancellation);
+ }
+
+ public Task GetDepositAddress(CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task GetInfo(CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task GetInvoice(string invoiceId, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task GetInvoice(uint256 paymentHash, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task GetPayment(string paymentHash, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ListChannels(CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task Listen(CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ListInvoices(CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ListInvoices(ListInvoicesParams request, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ListPayments(CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ListPayments(ListPaymentsParams request, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task OpenChannel(OpenChannelRequest openChannelRequest, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task Pay(PayInvoiceParams payParams, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task Pay(string bolt11, PayInvoiceParams payParams, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task Pay(string bolt11, CancellationToken cancellation = default)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/BlinkWallet.cs b/src/BTCPayServer.Lightning.Blink/Models/BlinkWallet.cs
new file mode 100644
index 00000000..a697ea09
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/BlinkWallet.cs
@@ -0,0 +1,10 @@
+namespace BTCPayServer.Lightning.Blink.Models
+{
+ public class BlinkWallet
+ {
+ public string Id { get; set; }
+ public string WalletCurrency { get; set; }
+ public long Balance { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/AccountResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/AccountResponse.cs
new file mode 100644
index 00000000..24a35fc0
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/AccountResponse.cs
@@ -0,0 +1,9 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class AccountResponse
+ {
+ public string DefaultWalletId { get; set; }
+ public List Wallets { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/ErrorResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/ErrorResponse.cs
new file mode 100644
index 00000000..da2b98c0
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/ErrorResponse.cs
@@ -0,0 +1,8 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class ErrorResponse
+ {
+ public string Message { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/GetBtcRateResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetBtcRateResponse.cs
new file mode 100644
index 00000000..dde271ca
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetBtcRateResponse.cs
@@ -0,0 +1,8 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class GetBtcRateResponse
+ {
+ public RealtimePriceResponse RealtimePrice { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/GetDefaultWalletIdResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetDefaultWalletIdResponse.cs
new file mode 100644
index 00000000..2f8d8cc5
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetDefaultWalletIdResponse.cs
@@ -0,0 +1,9 @@
+using System;
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class GetDefaultWalletIdResponse
+ {
+ public MeResponse Me { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/GetWalletBalancesResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetWalletBalancesResponse.cs
new file mode 100644
index 00000000..c78a7fe2
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetWalletBalancesResponse.cs
@@ -0,0 +1,9 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class GetWalletBalancesResponse
+ {
+ public MeResponse Me { get; set; }
+ public RealtimePriceResponse RealtimePrice { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/InvoiceResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/InvoiceResponse.cs
new file mode 100644
index 00000000..2eb9d529
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/InvoiceResponse.cs
@@ -0,0 +1,11 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class InvoiceResponse
+ {
+ public string PaymentRequest { get; set; }
+ public string PaymentHash { get; set; }
+ public string PaymentSecret { get; set; }
+ public long Satoshis { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreate.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreate.cs
new file mode 100644
index 00000000..14a96039
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreate.cs
@@ -0,0 +1,9 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class LnInvoiceCreate
+ {
+ public InvoiceResponse Invoice { get; set; }
+ public List Errors { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreateResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreateResponse.cs
new file mode 100644
index 00000000..75b265c7
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreateResponse.cs
@@ -0,0 +1,9 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class LnInvoiceCreateResponse
+ {
+ public LnInvoiceCreate LnInvoiceCreate { get; set; }
+ public LnInvoiceCreate LnUsdInvoiceCreate { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/MeResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/MeResponse.cs
new file mode 100644
index 00000000..eb356eb2
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/MeResponse.cs
@@ -0,0 +1,7 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class MeResponse
+ {
+ public AccountResponse DefaultAccount { get; set; }
+ }
+}
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/PriceInfo.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/PriceInfo.cs
new file mode 100644
index 00000000..3cd77610
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/PriceInfo.cs
@@ -0,0 +1,9 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class PriceInfo
+ {
+ public long Base { get; set; }
+ public uint Offset { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/RealtimePriceResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/RealtimePriceResponse.cs
new file mode 100644
index 00000000..c8411ca7
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/RealtimePriceResponse.cs
@@ -0,0 +1,10 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class RealtimePriceResponse
+ {
+ public PriceInfo BtcSatPrice { get; set; }
+
+ public string DenominatorCurrency { get; set; }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/WalletResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/WalletResponse.cs
new file mode 100644
index 00000000..9cff5698
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/WalletResponse.cs
@@ -0,0 +1,9 @@
+namespace BTCPayServer.Lightning.Blink.Models.Responses
+{
+ public class WalletResponse
+ {
+ public long Balance { get; set; }
+ public string Id { get; set; }
+ public string WalletCurrency { get; set; }
+ }
+}
diff --git a/src/BTCPayServer.Lightning.Blink/Utilities/RateConversions.cs b/src/BTCPayServer.Lightning.Blink/Utilities/RateConversions.cs
new file mode 100644
index 00000000..d95db9bb
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Utilities/RateConversions.cs
@@ -0,0 +1,16 @@
+using System;
+using BTCPayServer.Lightning.Blink.Models.Responses;
+
+namespace BTCPayServer.Lightning.Blink.Utilities
+{
+ public class RateConversions
+ {
+ public static long convertCentsToSats(long cents, PriceInfo btcRate)
+ {
+ double divisionResult = (double)cents / btcRate.Base;
+
+ return (long)(divisionResult * (long)Math.Pow(10, btcRate.Offset));
+ }
+ }
+}
+
diff --git a/src/BTCPayServer.Lightning.Blink/Utilities/StringExtensions.cs b/src/BTCPayServer.Lightning.Blink/Utilities/StringExtensions.cs
new file mode 100644
index 00000000..f9179df2
--- /dev/null
+++ b/src/BTCPayServer.Lightning.Blink/Utilities/StringExtensions.cs
@@ -0,0 +1,16 @@
+namespace BTCPayServer.Lightning.Blink.Utilities
+{
+ public static class StringExtensions
+ {
+ public static string Capitalize(this string input)
+ {
+ if (string.IsNullOrEmpty(input))
+ {
+ return input;
+ }
+
+ return char.ToUpper(input[0]) + input.Substring(1);
+ }
+ }
+}
+