コード例 #1
0
        public virtual async Task <IActionResult> PayInvoice(string cryptoCode, PayLightningInvoiceRequest lightningInvoice, CancellationToken cancellationToken = default)
        {
            var lightningClient = await GetLightningClient(cryptoCode, true);

            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(cryptoCode);

            if (lightningInvoice?.BOLT11 is null ||
                !BOLT11PaymentRequest.TryParse(lightningInvoice.BOLT11, out _, network.NBitcoinNetwork))
            {
                ModelState.AddModelError(nameof(lightningInvoice.BOLT11), "The BOLT11 invoice was invalid.");
            }

            if (!ModelState.IsValid)
            {
                return(this.CreateValidationError(ModelState));
            }

            var param = lightningInvoice?.MaxFeeFlat != null || lightningInvoice?.MaxFeePercent != null
                ? new PayInvoiceParams {
                MaxFeePercent = lightningInvoice.MaxFeePercent, MaxFeeFlat = lightningInvoice.MaxFeeFlat
            }
                : null;
            var result = await lightningClient.Pay(lightningInvoice.BOLT11, param, cancellationToken);

            return(result.Result switch
            {
                PayResult.CouldNotFindRoute => this.CreateAPIError("could-not-find-route", "Impossible to find a route to the peer"),
                PayResult.Error => this.CreateAPIError("generic-error", result.ErrorDetail),
                PayResult.Ok => Ok(new LightningPaymentData
                {
                    TotalAmount = result.Details?.TotalAmount,
                    FeeAmount = result.Details?.FeeAmount
                }),
                _ => throw new NotSupportedException("Unsupported Payresult")
            });
コード例 #2
0
        public virtual async Task <IActionResult> PayInvoice(string cryptoCode, PayLightningInvoiceRequest lightningInvoice)
        {
            var lightningClient = await GetLightningClient(cryptoCode, true);

            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(cryptoCode);

            if (lightningInvoice?.BOLT11 is null ||
                !BOLT11PaymentRequest.TryParse(lightningInvoice.BOLT11, out _, network.NBitcoinNetwork))
            {
                ModelState.AddModelError(nameof(lightningInvoice.BOLT11), "The BOLT11 invoice was invalid.");
            }

            if (!ModelState.IsValid)
            {
                return(this.CreateValidationError(ModelState));
            }

            var result = await lightningClient.Pay(lightningInvoice.BOLT11);

            switch (result.Result)
            {
            case PayResult.CouldNotFindRoute:
                return(this.CreateAPIError("could-not-find-route", "Impossible to find a route to the peer"));

            case PayResult.Error:
                return(this.CreateAPIError("generic-error", result.ErrorDetail));

            case PayResult.Ok:
                return(Ok());

            default:
                throw new NotSupportedException("Unsupported Payresult");
            }
        }
コード例 #3
0
 public BoltInvoiceClaimDestination(string bolt11, BOLT11PaymentRequest paymentRequest)
 {
     Bolt11         = bolt11 ?? throw new ArgumentNullException(nameof(bolt11));
     PaymentRequest = paymentRequest;
     PaymentHash    = paymentRequest.Hash;
     Amount         = paymentRequest.MinimumAmount.ToDecimal(LightMoneyUnit.BTC);
 }
コード例 #4
0
        public async Task <(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, bool validate)
        {
            destination = destination.Trim();
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(paymentMethodId.CryptoCode);

            try
            {
                string lnurlTag = null;
                var    lnurl    = EmailValidator.IsEmail(destination)
                    ? LNURL.LNURL.ExtractUriFromInternetIdentifier(destination)
                    : LNURL.LNURL.Parse(destination, out lnurlTag);

                if (lnurlTag is null)
                {
                    var info = (LNURLPayRequest)(await LNURL.LNURL.FetchInformation(lnurl, CreateClient(lnurl)));
                    lnurlTag = info.Tag;
                }

                if (lnurlTag.Equals("payRequest", StringComparison.InvariantCultureIgnoreCase))
                {
                    return(new LNURLPayClaimDestinaton(destination), null);
                }
            }
            catch (FormatException)
            {
            }
            catch
            {
                return(null, "The LNURL / Lightning Address provided was not online.");
            }

            var result =
                BOLT11PaymentRequest.TryParse(destination, out var invoice, network.NBitcoinNetwork)
                    ? new BoltInvoiceClaimDestination(destination, invoice)
                    : null;

            if (result == null)
            {
                return(null, "A valid BOLT11 invoice (with 30+ day expiry) or LNURL Pay or Lightning address was not provided.");
            }
            if (validate && (invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow).Days < 30)
            {
                return(null,
                       $"The BOLT11 invoice must have an expiry date of at least 30 days from submission (Provided was only {(invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow).Days}).");
            }
            if (invoice.ExpiryDate.UtcDateTime < DateTime.UtcNow)
            {
                return(null,
                       "The BOLT11 invoice submitted has expired.");
            }

            return(result, null);
        }
コード例 #5
0
    //we group per store and init the transfers by each
    async Task <bool> TrypayBolt(ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData,
                                 BOLT11PaymentRequest bolt11PaymentRequest)
    {
        var boltAmount = bolt11PaymentRequest.MinimumAmount.ToDecimal(LightMoneyUnit.BTC);

        if (boltAmount != payoutBlob.CryptoAmount)
        {
            return(false);
        }

        var result = await lightningClient.Pay(bolt11PaymentRequest.ToString());

        return(result.Result == PayResult.Ok);
    }
コード例 #6
0
 public async Task <IActionResult> PayInvoice(string invoice)
 {
     try
     {
         BOLT11PaymentRequest.TryParse(invoice, out var bolt11PaymentRequest, GetNetwork());
     }
     catch (Exception e)
     {
         ModelState.AddModelError(nameof(invoice), "The BOLT11 invoice was invalid.");
     }
     if (CheckValidation(out var errorActionResult))
     {
         return(errorActionResult);
     }
     return(Json(await GetLightningClient().Pay(invoice)));
 }
コード例 #7
0
        public async Task <LightningInvoice> GetInvoice(string invoiceId,
                                                        CancellationToken cancellation = default(CancellationToken))
        {
            InvoiceResponse result = null;

            try
            {
                result = await _eclairClient.GetInvoice(invoiceId, cancellation);
            }
            catch (EclairClient.EclairApiException ex) when(ex.Error.Error == "Not found" || ex.Error.Error.Contains("Invalid hexadecimal", StringComparison.OrdinalIgnoreCase))
            {
                return(null);
            }

            GetReceivedInfoResponse info = null;

            try
            {
                info = await _eclairClient.GetReceivedInfo(invoiceId, null, cancellation);
            }
            catch (EclairClient.EclairApiException)
            {
            }

            var parsed    = BOLT11PaymentRequest.Parse(result.Serialized, _network);
            var lnInvoice = new LightningInvoice()
            {
                Id        = result.PaymentHash,
                Amount    = parsed.MinimumAmount,
                ExpiresAt = parsed.ExpiryDate,
                BOLT11    = result.Serialized
            };

            if (DateTimeOffset.UtcNow >= parsed.ExpiryDate)
            {
                lnInvoice.Status = LightningInvoiceStatus.Expired;
            }
            if (info != null && info.Status.Type == "received")
            {
                lnInvoice.AmountReceived = info.Status.Amount;
                lnInvoice.Status         = info.Status.Amount >= parsed.MinimumAmount ? LightningInvoiceStatus.Paid : LightningInvoiceStatus.Unpaid;
                lnInvoice.PaidAt         = info.Status.ReceivedAt;
            }
            return(lnInvoice);
        }
コード例 #8
0
        public async Task CanCreateInvoiceWithDescriptionHash()
        {
            var hashToUse =
                new uint256(new SHA256Managed().ComputeHash(Encoding.UTF8.GetBytes("CanCreateInvoiceWithDescriptionHash")));

            async Task <LightningInvoice> CreateWithHash(ILightningClient lightningClient)
            {
                return(await lightningClient.CreateInvoice(new CreateInvoiceParams(10000, hashToUse,
                                                                                   TimeSpan.FromMinutes(5))));
            }

            await WaitServersAreUp();

            foreach (var client in Tester.GetLightningClients())
            {
                switch (client.Client)
                {
                case CLightningClient _:
                case LndClient _:
                    Logs.Tester.LogInformation($"{client.Name}: {nameof(CanCreateInvoiceWithDescriptionHash)}");
                    var createdInvoice = await CreateWithHash(client.Client);

                    var retrievedInvoice = await client.Client.GetInvoice(createdInvoice.Id);

                    Logs.Tester.LogInformation(JObject.FromObject(createdInvoice).ToString());
                    Logs.Tester.LogInformation(JObject.FromObject(retrievedInvoice).ToString());
                    AssertUnpaid(createdInvoice);
                    AssertUnpaid(retrievedInvoice);
                    var createdInvoiceBOLT    = BOLT11PaymentRequest.Parse(createdInvoice.BOLT11, Network.RegTest);
                    var retrievedInvoiceeBOLT = BOLT11PaymentRequest.Parse(retrievedInvoice.BOLT11, Network.RegTest);
                    Assert.Equal(createdInvoiceBOLT.PaymentHash, retrievedInvoiceeBOLT.PaymentHash);
                    Assert.Equal(hashToUse, createdInvoiceBOLT.DescriptionHash);
                    break;

                default:
                    await Assert.ThrowsAsync <NotSupportedException>(async() =>
                    {
                        await CreateWithHash(client.Client);
                    });

                    break;
                }
            }
        }
コード例 #9
0
        public virtual async Task <IActionResult> PayInvoice(string cryptoCode, PayLightningInvoiceRequest lightningInvoice)
        {
            var lightningClient = await GetLightningClient(cryptoCode, true);

            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(cryptoCode);

            if (lightningClient == null || network == null)
            {
                return(NotFound());
            }

            try
            {
                BOLT11PaymentRequest.TryParse(lightningInvoice.Invoice, out var bolt11PaymentRequest, network.NBitcoinNetwork);
            }
            catch (Exception)
            {
                ModelState.AddModelError(nameof(lightningInvoice), "The BOLT11 invoice was invalid.");
            }

            if (CheckValidation(out var errorActionResult))
            {
                return(errorActionResult);
            }

            var result = await lightningClient.Pay(lightningInvoice.Invoice);

            switch (result.Result)
            {
            case PayResult.Ok:
                return(Ok());

            case PayResult.CouldNotFindRoute:
                ModelState.AddModelError(nameof(lightningInvoice.Invoice), "Could not find route");
                break;

            case PayResult.Error:
                ModelState.AddModelError(nameof(lightningInvoice.Invoice), result.ErrorDetail);
                break;
            }

            return(BadRequest(new ValidationProblemDetails(ModelState)));
        }
コード例 #10
0
        public async Task <LightningInvoice> CreateInvoice(LightMoney amount, string description, TimeSpan expiry,
                                                           CancellationToken cancellation = default(CancellationToken))
        {
            var result = await _eclairClient.CreateInvoice(
                description,
                amount.MilliSatoshi,
                Convert.ToInt32(expiry.TotalSeconds), null, cancellation);

            var parsed  = BOLT11PaymentRequest.Parse(result.Serialized, _network);
            var invoice = new LightningInvoice()
            {
                BOLT11    = result.Serialized,
                Amount    = amount,
                Id        = result.PaymentHash,
                Status    = LightningInvoiceStatus.Unpaid,
                ExpiresAt = parsed.ExpiryDate
            };

            return(invoice);
        }
コード例 #11
0
        internal LightningInvoice GetLightningInvoiceObject(ListInvoiceResultResponse invoice, Network network)
        {
            var parsed = BOLT11PaymentRequest.Parse(invoice.Bolt11, network);

            var lightningInvoice = new LightningInvoice()
            {
                Id             = invoice.Hash,
                Amount         = invoice.AmountMsat,
                AmountReceived = invoice.AmountMsat,
                BOLT11         = invoice.Bolt11,
                Status         = ToStatus(invoice.State),
                PaidAt         = null,
                ExpiresAt      = parsed.ExpiryDate
            };

            if (invoice.State == "used")
            {
                lightningInvoice.PaidAt = parsed.ExpiryDate;
            }

            return(lightningInvoice);
        }
コード例 #12
0
        public async Task <LightningInvoice> GetInvoice(string invoiceId,
                                                        CancellationToken cancellation = default(CancellationToken))
        {
            var result = await _eclairClient.GetInvoice(invoiceId, cancellation);

            GetReceivedInfoResponse info;

            try
            {
                info = await _eclairClient.GetReceivedInfo(invoiceId, null, cancellation);
            }
            catch (EclairClient.EclairApiException)
            {
                info = new GetReceivedInfoResponse()
                {
                    AmountMsat  = 0,
                    ReceivedAt  = 0,
                    PaymentHash = invoiceId
                };
            }

            var parsed = BOLT11PaymentRequest.Parse(result.Serialized, _network);

            return(new LightningInvoice()
            {
                Id = result.PaymentHash,
                Amount = parsed.MinimumAmount,
                ExpiresAt = parsed.ExpiryDate,
                BOLT11 = result.Serialized,
                AmountReceived = info.AmountMsat,
                Status = info.AmountMsat >= parsed.MinimumAmount ? LightningInvoiceStatus.Paid :
                         DateTime.Now >= parsed.ExpiryDate ? LightningInvoiceStatus.Expired : LightningInvoiceStatus.Unpaid,
                PaidAt = info.ReceivedAt == 0
                    ? (DateTimeOffset?)null
                    : DateTimeOffset.FromUnixTimeMilliseconds(info.ReceivedAt)
            });
        }
コード例 #13
0
        public async Task <LightningInvoice> GetInvoice(string invoiceId,
                                                        CancellationToken cancellation = default(CancellationToken))
        {
            var result = await _eclairClient.GetInvoice(invoiceId, cancellation);

            GetReceivedInfoResponse info = null;

            try
            {
                info = await _eclairClient.GetReceivedInfo(invoiceId, null, cancellation);
            }
            catch (EclairClient.EclairApiException)
            {
            }

            var parsed    = BOLT11PaymentRequest.Parse(result.Serialized, _network);
            var lnInvoice = new LightningInvoice()
            {
                Id        = result.PaymentHash,
                Amount    = parsed.MinimumAmount,
                ExpiresAt = parsed.ExpiryDate,
                BOLT11    = result.Serialized
            };

            if (DateTimeOffset.UtcNow >= parsed.ExpiryDate)
            {
                lnInvoice.Status = LightningInvoiceStatus.Expired;
            }
            if (info != null && info.Status.Type == "received")
            {
                lnInvoice.AmountReceived = info.Status.Amount;
                lnInvoice.Status         = info.Status.Amount >= parsed.MinimumAmount ? LightningInvoiceStatus.Paid : LightningInvoiceStatus.Unpaid;
                lnInvoice.PaidAt         = info.Status.ReceivedAt;
            }
            return(lnInvoice);
        }
コード例 #14
0
 public BOLT11PaymentRequest ParsePaymentRequest(string payReq)
 {
     return(BOLT11PaymentRequest.Parse(payReq, _network));
 }
コード例 #15
0
        public static async Task <ResultVM> TrypayBolt(ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData, BOLT11PaymentRequest bolt11PaymentRequest, PaymentMethodId pmi)
        {
            var boltAmount = bolt11PaymentRequest.MinimumAmount.ToDecimal(LightMoneyUnit.BTC);

            if (boltAmount != payoutBlob.CryptoAmount)
            {
                return(new ResultVM
                {
                    PayoutId = payoutData.Id,
                    Result = PayResult.Error,
                    Message = $"The BOLT11 invoice amount ({boltAmount} {pmi.CryptoCode}) did not match the payout's amount ({payoutBlob.CryptoAmount.GetValueOrDefault()} {pmi.CryptoCode})",
                    Destination = payoutBlob.Destination
                });
            }
            var result = await lightningClient.Pay(bolt11PaymentRequest.ToString(), new PayInvoiceParams());

            if (result.Result == PayResult.Ok)
            {
                var message = result.Details?.TotalAmount != null
                    ? $"Paid out {result.Details.TotalAmount.ToDecimal(LightMoneyUnit.BTC)}"
                    : null;
                payoutData.State = PayoutState.Completed;
                return(new ResultVM
                {
                    PayoutId = payoutData.Id,
                    Result = result.Result,
                    Destination = payoutBlob.Destination,
                    Message = message
                });
            }

            return(new ResultVM
            {
                PayoutId = payoutData.Id,
                Result = result.Result,
                Destination = payoutBlob.Destination,
                Message = result.ErrorDetail
            });
        }
コード例 #16
0
        public async Task <IActionResult> GetLNURLForPullPayment(string cryptoCode, string pullPaymentId, string pr)
        {
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(cryptoCode);

            if (network is null || !network.SupportLightning)
            {
                return(NotFound());
            }

            var pmi = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
            var pp  = await _pullPaymentHostedService.GetPullPayment(pullPaymentId, true);

            if (!pp.IsRunning() || !pp.IsSupported(pmi))
            {
                return(NotFound());
            }

            var blob = pp.GetBlob();

            if (!blob.Currency.Equals(cryptoCode, StringComparison.InvariantCultureIgnoreCase))
            {
                return(NotFound());
            }

            var progress = _pullPaymentHostedService.CalculatePullPaymentProgress(pp, DateTimeOffset.UtcNow);

            var remaining = progress.Limit - progress.Completed - progress.Awaiting;
            var request   = new LNURLWithdrawRequest()
            {
                MaxWithdrawable = LightMoney.FromUnit(remaining, LightMoneyUnit.BTC),
                K1              = pullPaymentId,
                BalanceCheck    = new Uri(Request.GetCurrentUrl()),
                CurrentBalance  = LightMoney.FromUnit(remaining, LightMoneyUnit.BTC),
                MinWithdrawable =
                    LightMoney.FromUnit(
                        Math.Min(await _lightningLikePayoutHandler.GetMinimumPayoutAmount(pmi, null), remaining),
                        LightMoneyUnit.BTC),
                Tag      = "withdrawRequest",
                Callback = new Uri(Request.GetCurrentUrl()),
            };

            if (pr is null)
            {
                return(Ok(request));
            }

            if (!BOLT11PaymentRequest.TryParse(pr, out var result, network.NBitcoinNetwork) || result is null)
            {
                return(BadRequest(new LNUrlStatusResponse {
                    Status = "ERROR", Reason = "Pr was not a valid BOLT11"
                }));
            }

            if (result.MinimumAmount < request.MinWithdrawable || result.MinimumAmount > request.MaxWithdrawable)
            {
                return(BadRequest(new LNUrlStatusResponse {
                    Status = "ERROR", Reason = "Pr was not within bounds"
                }));
            }
            var store = await _storeRepository.FindStore(pp.StoreId);

            var pm = store !.GetSupportedPaymentMethods(_btcPayNetworkProvider)
                     .OfType <LightningSupportedPaymentMethod>()
                     .FirstOrDefault(method => method.PaymentId == pmi);

            if (pm is null)
            {
                return(NotFound());
            }

            var claimResponse = await _pullPaymentHostedService.Claim(new ClaimRequest()
            {
                Destination     = new BoltInvoiceClaimDestination(pr, result),
                PaymentMethodId = pmi,
                PullPaymentId   = pullPaymentId,
                StoreId         = pp.StoreId,
                Value           = result.MinimumAmount.ToDecimal(LightMoneyUnit.BTC)
            });

            if (claimResponse.Result != ClaimRequest.ClaimResult.Ok)
            {
                return(BadRequest(new LNUrlStatusResponse {
                    Status = "ERROR", Reason = "Pr could not be paid"
                }));
            }
            switch (claimResponse.PayoutData.State)
            {
            case PayoutState.AwaitingPayment:
            {
                var client =
                    _lightningLikePaymentHandler.CreateLightningClient(pm, network);
                PayResponse payResult;
                try
                {
                    payResult = await client.Pay(pr);
                }
                catch (Exception e)
                {
                    payResult = new PayResponse(PayResult.Error, e.Message);
                }

                switch (payResult.Result)
                {
                case PayResult.Ok:
                    await _pullPaymentHostedService.MarkPaid(new PayoutPaidRequest()
                        {
                            PayoutId = claimResponse.PayoutData.Id, Proof = new ManualPayoutProof {
                            }
                        });

                    return(Ok(new LNUrlStatusResponse {
                            Status = "OK"
                        }));

                default:
                    await _pullPaymentHostedService.Cancel(
                        new PullPaymentHostedService.CancelRequest(new string[]
                        {
                            claimResponse.PayoutData.Id
                        }));

                    return(Ok(new LNUrlStatusResponse
                        {
                            Status = "ERROR",
                            Reason = $"Pr could not be paid because {payResult.ErrorDetail}"
                        }));
                }
            }

            case PayoutState.AwaitingApproval:
                return(Ok(new LNUrlStatusResponse
                {
                    Status = "OK",
                    Reason =
                        "The payment request has been recorded, but still needs to be approved before execution."
                }));

            case PayoutState.InProgress:
            case PayoutState.Completed:
                return(Ok(new LNUrlStatusResponse {
                    Status = "OK"
                }));

            case PayoutState.Cancelled:
                return(BadRequest(new LNUrlStatusResponse {
                    Status = "ERROR", Reason = "Pr could not be paid"
                }));
            }

            return(Ok(request));
        }
コード例 #17
0
 public uint256 GetPaymentHash(Network network)
 {
     return(PaymentHash ?? BOLT11PaymentRequest.Parse(BOLT11, network).PaymentHash);
 }
コード例 #18
0
        public override async Task <IPaymentMethodDetails> CreatePaymentMethodDetails(
            InvoiceLogs logs,
            LightningSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, Data.StoreData store,
            BTCPayNetwork network, object preparePaymentObject)
        {
            if (paymentMethod.ParentEntity.Type == InvoiceType.TopUp)
            {
                throw new PaymentMethodUnavailableException("Lightning Network payment method is not available for top-up invoices");
            }

            if (preparePaymentObject is null)
            {
                return(new LightningLikePaymentMethodDetails()
                {
                    Activated = false
                });
            }
            //direct casting to (BTCPayNetwork) is fixed in other pull requests with better generic interfacing for handlers
            var storeBlob = store.GetStoreBlob();
            var test      = GetNodeInfo(supportedPaymentMethod, network, paymentMethod.PreferOnion);

            var     invoice = paymentMethod.ParentEntity;
            decimal due     = Extensions.RoundUp(invoice.Price / paymentMethod.Rate, network.Divisibility);

            try
            {
                due = paymentMethod.Calculate().Due.ToDecimal(MoneyUnit.BTC);
            }
            catch (Exception)
            {
                // ignored
            }
            var client = supportedPaymentMethod.CreateLightningClient(network, Options.Value, _lightningClientFactory);
            var expiry = invoice.ExpirationTime - DateTimeOffset.UtcNow;

            if (expiry < TimeSpan.Zero)
            {
                expiry = TimeSpan.FromSeconds(1);
            }

            LightningInvoice?lightningInvoice = null;

            string description = storeBlob.LightningDescriptionTemplate;

            description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
                          .Replace("{ItemDescription}", invoice.Metadata.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
                          .Replace("{OrderId}", invoice.Metadata.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
            using (var cts = new CancellationTokenSource(LIGHTNING_TIMEOUT))
            {
                try
                {
                    var request = new CreateInvoiceParams(new LightMoney(due, LightMoneyUnit.BTC), description, expiry);
                    request.PrivateRouteHints = storeBlob.LightningPrivateRouteHints;
                    lightningInvoice          = await client.CreateInvoice(request, cts.Token);
                }
                catch (OperationCanceledException) when(cts.IsCancellationRequested)
                {
                    throw new PaymentMethodUnavailableException("The lightning node did not reply in a timely manner");
                }
                catch (Exception ex)
                {
                    throw new PaymentMethodUnavailableException($"Impossible to create lightning invoice ({ex.Message})", ex);
                }
            }

            var nodeInfo = await test;

            return(new LightningLikePaymentMethodDetails
            {
                Activated = true,
                BOLT11 = lightningInvoice.BOLT11,
                PaymentHash = BOLT11PaymentRequest.Parse(lightningInvoice.BOLT11, network.NBitcoinNetwork).PaymentHash,
                InvoiceId = lightningInvoice.Id,
                NodeInfo = nodeInfo.First().ToString()
            });
        }
コード例 #19
0
 public BoltInvoiceClaimDestination(string bolt11, BOLT11PaymentRequest invoice)
 {
     _bolt11 = bolt11 ?? throw new ArgumentNullException(nameof(bolt11));
     _amount = invoice?.MinimumAmount.ToDecimal(LightMoneyUnit.BTC) ?? throw new ArgumentNullException(nameof(invoice));
 }
コード例 #20
0
 public BoltInvoiceClaimDestination(string bolt11, Network network)
 {
     _bolt11 = bolt11 ?? throw new ArgumentNullException(nameof(bolt11));
     _amount = BOLT11PaymentRequest.Parse(bolt11, network).MinimumAmount.ToDecimal(LightMoneyUnit.BTC);
 }
コード例 #21
0
 //we group per store and init the transfers by each
 async Task <bool> TrypayBolt(ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData,
                              BOLT11PaymentRequest bolt11PaymentRequest)
 {
     return((await UILightningLikePayoutController.TrypayBolt(lightningClient, payoutBlob, payoutData, bolt11PaymentRequest,
                                                              payoutData.GetPaymentMethodId())).Result == PayResult.Ok);
 }
コード例 #22
0
        public async Task <Transaction> Send(Wallet wallet, BOLT11PaymentRequest bolt11, string paymentRequest)
        {
            await using var dbContext = _dbContextFactory.CreateDbContext();
            var amount = bolt11.MinimumAmount;

            if (bolt11.ExpiryDate <= DateTimeOffset.UtcNow)
            {
                throw new Exception($"Payment request already expired at {bolt11.ExpiryDate}.");
            }

            if (wallet.Balance < amount)
            {
                var balanceSats = wallet.Balance.ToUnit(LightMoneyUnit.Satoshi);
                var amountSats  = amount.ToUnit(LightMoneyUnit.Satoshi);
                throw new Exception($"Insufficient balance: {balanceSats} sats, tried to send {amountSats} sats.");
            }

            // pay via the node and fall back to internal payment
            Transaction internalReceivingTransaction = null;

            try
            {
                await _btcpayService.PayLightningInvoice(new LightningInvoicePayRequest
                {
                    PaymentRequest = paymentRequest
                });
            }
            catch (GreenFieldAPIException ex) when(ex.APIError.Code == "could-not-find-route")
            {
                internalReceivingTransaction = await GetTransaction(new TransactionQuery
                {
                    PaymentRequest = paymentRequest,
                    HasInvoiceId   = true
                });

                if (internalReceivingTransaction == null)
                {
                    throw;
                }
            }

            if (internalReceivingTransaction != null)
            {
                if (internalReceivingTransaction.IsExpired)
                {
                    throw new Exception($"Payment request already expired at {internalReceivingTransaction.ExpiresAt}.");
                }
                if (internalReceivingTransaction.IsPaid)
                {
                    throw new Exception($"Payment request has already been paid.");
                }
            }

            // https://docs.microsoft.com/en-us/ef/core/saving/transactions#controlling-transactions
            await using var dbTransaction = await dbContext.Database.BeginTransactionAsync();

            var now   = DateTimeOffset.UtcNow;
            var entry = await dbContext.Transactions.AddAsync(new Transaction
            {
                WalletId       = wallet.WalletId,
                PaymentRequest = paymentRequest,
                Amount         = amount,
                AmountSettled  = new LightMoney(amount.MilliSatoshi * -1),
                ExpiresAt      = bolt11.ExpiryDate,
                Description    = bolt11.ShortDescription,
                PaidAt         = now
            });

            await dbContext.SaveChangesAsync();

            if (internalReceivingTransaction != null)
            {
                await MarkTransactionPaid(internalReceivingTransaction, amount, now);
            }
            await dbTransaction.CommitAsync();

            return(entry.Entity);
        }