Task <bool> IPaymentMethodHandler.IsAvailable(ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network) { if (supportedPaymentMethod is T method) { return(IsAvailable(method, network)); } return(Task.FromResult(false)); }
object IPaymentMethodHandler.PreparePayment(ISupportedPaymentMethod supportedPaymentMethod, StoreData store, BTCPayNetwork network) { if (supportedPaymentMethod is T method) { return(PreparePayment(method, store, network)); } throw new NotSupportedException("Invalid supportedPaymentMethod"); }
public Task <IPaymentMethodDetails> CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetworkBase network, object preparePaymentObject) { if (supportedPaymentMethod is TSupportedPaymentMethod method && network is TBTCPayNetwork correctNetwork) { return(CreatePaymentMethodDetails(method, paymentMethod, store, correctNetwork, preparePaymentObject)); } throw new NotSupportedException("Invalid supportedPaymentMethod"); }
public override object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod) { if (supportedPaymentMethod is MoneroSupportedPaymentMethod moneroSupportedPaymentMethod) { return(new { moneroSupportedPaymentMethod.AccountIndex, }); } return(null); }
public override object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod, bool canModifyStore) { if (supportedPaymentMethod is ZcashSupportedPaymentMethod ZcashSupportedPaymentMethod) { return(new { ZcashSupportedPaymentMethod.AccountIndex, }); } return(null); }
public override object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod) { if (supportedPaymentMethod is LightningSupportedPaymentMethod lightningSupportedPaymentMethod) { return new LightningNetworkPaymentMethodBaseData() { ConnectionString = lightningSupportedPaymentMethod.GetDisplayableConnectionString() } } ; return(null); }
public Task <IPaymentMethodDetails> CreatePaymentMethodDetails(InvoiceLogs logs, ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetworkBase network, object preparePaymentObject, IEnumerable <PaymentMethodId> invoicePaymentMethods) { if (supportedPaymentMethod is TSupportedPaymentMethod method && network is TBTCPayNetwork correctNetwork) { return(CreatePaymentMethodDetails(logs, method, paymentMethod, store, correctNetwork, preparePaymentObject, invoicePaymentMethods)); } throw new NotSupportedException("Invalid supportedPaymentMethod"); }
/// <summary> /// Set or remove a new supported payment method for the store /// </summary> /// <param name="paymentMethodId">The paymentMethodId</param> /// <param name="supportedPaymentMethod">The payment method, or null to remove</param> public void SetSupportedPaymentMethod(PaymentMethodId paymentMethodId, ISupportedPaymentMethod supportedPaymentMethod) { if (supportedPaymentMethod != null && paymentMethodId != null && paymentMethodId != supportedPaymentMethod.PaymentId) { throw new InvalidOperationException("Incoherent arguments, this should never happen"); } if (supportedPaymentMethod == null && paymentMethodId == null) { throw new ArgumentException($"{nameof(supportedPaymentMethod)} or {nameof(paymentMethodId)} should be specified"); } if (supportedPaymentMethod != null && paymentMethodId == null) { paymentMethodId = supportedPaymentMethod.PaymentId; } #pragma warning disable CS0618 JObject strategies = string.IsNullOrEmpty(DerivationStrategies) ? new JObject() : JObject.Parse(DerivationStrategies); bool existing = false; foreach (var strat in strategies.Properties().ToList()) { var stratId = PaymentMethodId.Parse(strat.Name); if (stratId.IsBTCOnChain) { // Legacy stuff which should go away DerivationStrategy = null; } if (stratId == paymentMethodId) { if (supportedPaymentMethod == null) { strat.Remove(); } else { strat.Value = PaymentMethodExtensions.Serialize(supportedPaymentMethod); } existing = true; break; } } if (!existing && supportedPaymentMethod == null && supportedPaymentMethod.PaymentId.IsBTCOnChain) { DerivationStrategy = null; } else if (!existing && supportedPaymentMethod != null) { strategies.Add(new JProperty(supportedPaymentMethod.PaymentId.ToString(), PaymentMethodExtensions.Serialize(supportedPaymentMethod))); } DerivationStrategies = strategies.ToString(); #pragma warning restore CS0618 }
public override object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod) { if (supportedPaymentMethod is EthereumSupportedPaymentMethod ethereumSupportedPaymentMethod) { return(new { ethereumSupportedPaymentMethod.XPub //no clue what all those properties saved are and don't care. }); } return(null); }
public override object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod) { if (supportedPaymentMethod is DerivationSchemeSettings derivationSchemeSettings) { return new OnChainPaymentMethodBaseData() { DerivationScheme = derivationSchemeSettings.AccountDerivation.ToString(), AccountKeyPath = derivationSchemeSettings.GetSigningAccountKeySettings().GetRootedKeyPath(), Label = derivationSchemeSettings.Label } } ; return(null); }
public override object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod, bool canModifyStore) { if (supportedPaymentMethod is LNURLPaySupportedPaymentMethod lightningSupportedPaymentMethod) { return new LNURLPayPaymentMethodBaseData() { UseBech32Scheme = lightningSupportedPaymentMethod.UseBech32Scheme, EnableForStandardInvoices = lightningSupportedPaymentMethod.EnableForStandardInvoices, LUD12Enabled = lightningSupportedPaymentMethod.LUD12Enabled } } ; return(null); }
public static JToken Serialize(ISupportedPaymentMethod factory) { // Legacy if (factory.PaymentId.PaymentType == PaymentTypes.BTCLike) { return(new JValue(((DerivationStrategy)factory).DerivationStrategyBase.ToString())); } ////////////// else { var str = JsonConvert.SerializeObject(factory); return(JObject.Parse(str)); } throw new NotSupportedException(); }
public static JToken Serialize(ISupportedPaymentMethod factory) { // Legacy if (factory.PaymentId.PaymentType == PaymentTypes.BTCLike) { var derivation = (DerivationSchemeSettings)factory; var str = derivation.Network.NBXplorerNetwork.Serializer.ToString(derivation); return(JObject.Parse(str)); } ////////////// else { var str = JsonConvert.SerializeObject(factory); return(JObject.Parse(str)); } }
public override object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod, bool canModifyStore) { if (supportedPaymentMethod is LightningSupportedPaymentMethod lightningSupportedPaymentMethod) { return new LightningNetworkPaymentMethodBaseData() { ConnectionString = lightningSupportedPaymentMethod.IsInternalNode ? lightningSupportedPaymentMethod.GetDisplayableConnectionString() : canModifyStore ? lightningSupportedPaymentMethod.GetDisplayableConnectionString() : "*NEED CanModifyStoreSettings PERMISSION TO VIEW*" } } ; return(null); }
/// <summary> /// Set or remove a new supported payment method for the store /// </summary> /// <param name="paymentMethodId">The paymentMethodId</param> /// <param name="supportedPaymentMethod">The payment method, or null to remove</param> public void SetSupportedPaymentMethod(ISupportedPaymentMethod supportedPaymentMethod) { #pragma warning disable CS0618 JObject strategies = string.IsNullOrEmpty(DerivationStrategies) ? new JObject() : JObject.Parse(DerivationStrategies); bool existing = false; foreach (var strat in strategies.Properties().ToList()) { var stratId = PaymentMethodId.Parse(strat.Name); if (stratId.IsBTCOnChain) { // Legacy stuff which should go away DerivationStrategy = null; } if (stratId == supportedPaymentMethod.PaymentId) { if (supportedPaymentMethod == null) { strat.Remove(); } else { strat.Value = PaymentMethodExtensions.Serialize(supportedPaymentMethod); } existing = true; break; } } if (!existing && supportedPaymentMethod == null && supportedPaymentMethod.PaymentId.IsBTCOnChain) { DerivationStrategy = null; } else if (!existing && supportedPaymentMethod != null) { strategies.Add(new JProperty(supportedPaymentMethod.PaymentId.ToString(), PaymentMethodExtensions.Serialize(supportedPaymentMethod))); } DerivationStrategies = strategies.ToString(); #pragma warning restore CS0618 }
private async Task <PaymentMethod> CreatePaymentMethodAsync(Dictionary <CurrencyPair, Task <RateResult> > fetchingByCurrencyPair, IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network, InvoiceEntity entity, StoreData store, InvoiceLogs logs) { try { var logPrefix = $"{handler.ToPrettyString(supportedPaymentMethod.PaymentId)}:"; var storeBlob = store.GetStoreBlob(); var preparePayment = handler.PreparePayment(supportedPaymentMethod, store, network); var rate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, entity.ProductInformation.Currency)]; if (rate.BidAsk == null) { return(null); } PaymentMethod paymentMethod = new PaymentMethod(); paymentMethod.ParentEntity = entity; paymentMethod.Network = network; paymentMethod.SetId(supportedPaymentMethod.PaymentId); paymentMethod.Rate = rate.BidAsk.Bid; paymentMethod.PreferOnion = this.Request.IsOnion(); using (logs.Measure($"{logPrefix} Payment method details creation")) { var paymentDetails = await handler.CreatePaymentMethodDetails(supportedPaymentMethod, paymentMethod, store, network, preparePayment); paymentMethod.SetPaymentMethodDetails(paymentDetails); } Func <Money, Money, bool> compare = null; CurrencyValue limitValue = null; string errorMessage = null; if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.LightningLike && storeBlob.LightningMaxValue != null) { compare = (a, b) => a > b; limitValue = storeBlob.LightningMaxValue; errorMessage = "The amount of the invoice is too high to be paid with lightning"; } else if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.BTCLike && storeBlob.OnChainMinValue != null) { compare = (a, b) => a < b; limitValue = storeBlob.OnChainMinValue; errorMessage = "The amount of the invoice is too low to be paid on chain"; } if (compare != null) { var limitValueRate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, limitValue.Currency)]; if (limitValueRate.BidAsk != null) { var limitValueCrypto = Money.Coins(limitValue.Value / limitValueRate.BidAsk.Bid); if (compare(paymentMethod.Calculate().Due, limitValueCrypto)) { logs.Write($"{logPrefix} {errorMessage}"); return(null); } } } /////////////// #pragma warning disable CS0618 if (paymentMethod.GetId().IsBTCOnChain) { entity.TxFee = paymentMethod.NextNetworkFee; entity.Rate = paymentMethod.Rate; entity.DepositAddress = paymentMethod.DepositAddress; } #pragma warning restore CS0618 return(paymentMethod); } catch (PaymentMethodUnavailableException ex) { logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Payment method unavailable ({ex.Message})"); } catch (Exception ex) { logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Unexpected exception ({ex.ToString()})"); } return(null); }
protected override async Task Process(ISupportedPaymentMethod paymentMethod, PayoutData[] payouts) { var storePaymentMethod = paymentMethod as DerivationSchemeSettings; if (storePaymentMethod?.IsHotWallet is not true) { Logs.PayServer.LogInformation($"Wallet is not a hot wallet."); return; } if (!_explorerClientProvider.IsAvailable(PaymentMethodId.CryptoCode)) { Logs.PayServer.LogInformation($"{paymentMethod.PaymentId.CryptoCode} node is not available"); return; } var explorerClient = _explorerClientProvider.GetExplorerClient(PaymentMethodId.CryptoCode); var paymentMethodId = PaymentMethodId.Parse(PaymentMethodId.CryptoCode); var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(paymentMethodId.CryptoCode); var extKeyStr = await explorerClient.GetMetadataAsync <string>( storePaymentMethod.AccountDerivation, WellknownMetadataKeys.AccountHDKey); if (extKeyStr == null) { Logs.PayServer.LogInformation($"Wallet keys not found."); return; } var wallet = _btcPayWalletProvider.GetWallet(PaymentMethodId.CryptoCode); var reccoins = (await wallet.GetUnspentCoins(storePaymentMethod.AccountDerivation)).ToArray(); var coins = reccoins.Select(coin => coin.Coin).ToArray(); var accountKey = ExtKey.Parse(extKeyStr, network.NBitcoinNetwork); var keys = reccoins.Select(coin => accountKey.Derive(coin.KeyPath).PrivateKey).ToArray(); Transaction workingTx = null; decimal? failedAmount = null; var changeAddress = await explorerClient.GetUnusedAsync( storePaymentMethod.AccountDerivation, DerivationFeature.Change, 0, true); var feeRate = await explorerClient.GetFeeRateAsync(1, new FeeRate(1m)); var transfersProcessing = new List <PayoutData>(); foreach (var transferRequest in payouts) { var blob = transferRequest.GetBlob(_btcPayNetworkJsonSerializerSettings); if (failedAmount.HasValue && blob.CryptoAmount >= failedAmount) { continue; } var claimDestination = await _bitcoinLikePayoutHandler.ParseClaimDestination(paymentMethodId, blob.Destination); if (!string.IsNullOrEmpty(claimDestination.error)) { Logs.PayServer.LogInformation($"Could not process payout {transferRequest.Id} because {claimDestination.error}."); continue; } var bitcoinClaimDestination = (IBitcoinLikeClaimDestination)claimDestination.destination; var txBuilder = network.NBitcoinNetwork.CreateTransactionBuilder() .AddCoins(coins) .AddKeys(keys); if (workingTx is not null) { foreach (var txout in workingTx.Outputs.Where(txout => !txout.IsTo(changeAddress.Address))) { txBuilder.Send(txout.ScriptPubKey, txout.Value); } } txBuilder.Send(bitcoinClaimDestination.Address, new Money(blob.CryptoAmount.Value, MoneyUnit.BTC)); try { txBuilder.SetChange(changeAddress.Address); txBuilder.SendEstimatedFees(feeRate.FeeRate); workingTx = txBuilder.BuildTransaction(true); transfersProcessing.Add(transferRequest); } catch (NotEnoughFundsException e) { Logs.PayServer.LogInformation($"Could not process payout {transferRequest.Id} because of not enough funds. ({e.Missing.GetValue(network)})"); failedAmount = blob.CryptoAmount; //keep going, we prioritize withdraws by time but if there is some other we can fit, we should } } if (workingTx is not null) { try { await using var context = _applicationDbContextFactory.CreateContext(); var txHash = workingTx.GetHash(); Logs.PayServer.LogInformation($"Processing {transfersProcessing.Count} payouts in tx {txHash}"); foreach (PayoutData payoutData in transfersProcessing) { context.Attach(payoutData); payoutData.State = PayoutState.InProgress; _bitcoinLikePayoutHandler.SetProofBlob(payoutData, new PayoutTransactionOnChainBlob() { Accounted = true, TransactionId = txHash, Candidates = new HashSet <uint256>() { txHash } }); await context.SaveChangesAsync(); } TaskCompletionSource <bool> tcs = new(); var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(20)); var task = _eventAggregator.WaitNext <NewOnChainTransactionEvent>( e => e.NewTransactionEvent.TransactionData.TransactionHash == txHash, cts.Token); var broadcastResult = await explorerClient.BroadcastAsync(workingTx, cts.Token); if (!broadcastResult.Success) { tcs.SetResult(false); } var walletId = new WalletId(_PayoutProcesserSettings.StoreId, PaymentMethodId.CryptoCode); foreach (PayoutData payoutData in transfersProcessing) { _eventAggregator.Publish(new UpdateTransactionLabel(walletId, txHash, UpdateTransactionLabel.PayoutTemplate(payoutData.Id, payoutData.PullPaymentDataId, walletId.ToString()))); } await Task.WhenAny(tcs.Task, task); } catch (OperationCanceledException) { } catch (Exception e) { Logs.PayServer.LogError(e, "Could not finalize and broadcast"); } } }
private async Task <PaymentMethod?> CreatePaymentMethodAsync(Dictionary <CurrencyPair, Task <RateResult> > fetchingByCurrencyPair, IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetworkBase network, InvoiceEntity entity, StoreData store, InvoiceLogs logs) { try { var logPrefix = $"{supportedPaymentMethod.PaymentId.ToPrettyString()}:"; var storeBlob = store.GetStoreBlob(); object?preparePayment; if (storeBlob.LazyPaymentMethods) { preparePayment = null; } else { preparePayment = handler.PreparePayment(supportedPaymentMethod, store, network); } var rate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, entity.Currency)]; if (rate.BidAsk == null) { return(null); } var paymentMethod = new PaymentMethod { ParentEntity = entity, Network = network, Rate = rate.BidAsk.Bid, PreferOnion = Uri.TryCreate(entity.ServerUrl, UriKind.Absolute, out var u) && u.DnsSafeHost.EndsWith(".onion", StringComparison.OrdinalIgnoreCase) }; paymentMethod.SetId(supportedPaymentMethod.PaymentId); using (logs.Measure($"{logPrefix} Payment method details creation")) { var paymentDetails = await handler.CreatePaymentMethodDetails(logs, supportedPaymentMethod, paymentMethod, store, network, preparePayment); paymentMethod.SetPaymentMethodDetails(paymentDetails); } var criteria = storeBlob.PaymentMethodCriteria?.Find(methodCriteria => methodCriteria.PaymentMethod == supportedPaymentMethod.PaymentId); if (criteria?.Value != null && entity.Type != InvoiceType.TopUp) { var currentRateToCrypto = await fetchingByCurrencyPair[new CurrencyPair(supportedPaymentMethod.PaymentId.CryptoCode, criteria.Value.Currency)]; if (currentRateToCrypto?.BidAsk != null) { var amount = paymentMethod.Calculate().Due.GetValue(network as BTCPayNetwork); var limitValueCrypto = criteria.Value.Value / currentRateToCrypto.BidAsk.Bid; if (amount < limitValueCrypto && criteria.Above) { logs.Write($"{logPrefix} invoice amount below accepted value for payment method", InvoiceEventData.EventSeverity.Error); return(null); } if (amount > limitValueCrypto && !criteria.Above) { logs.Write($"{logPrefix} invoice amount above accepted value for payment method", InvoiceEventData.EventSeverity.Error); return(null); } } else { var suffix = currentRateToCrypto?.EvaluatedRule is string s ? $" ({s})" : string.Empty; logs.Write($"{logPrefix} This payment method should be created only if the amount of this invoice is in proper range. However, we are unable to fetch the rate of those limits. {suffix}", InvoiceEventData.EventSeverity.Warning); } } #pragma warning disable CS0618 if (paymentMethod.GetId().IsBTCOnChain) { entity.TxFee = paymentMethod.NextNetworkFee; entity.Rate = paymentMethod.Rate; entity.DepositAddress = paymentMethod.DepositAddress; } #pragma warning restore CS0618 return(paymentMethod); } catch (PaymentMethodUnavailableException ex) { logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Payment method unavailable ({ex.Message})", InvoiceEventData.EventSeverity.Error); } catch (Exception ex) { logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Unexpected exception ({ex})", InvoiceEventData.EventSeverity.Error); } return(null); }
private async Task <PaymentMethod> CreatePaymentMethodAsync(Dictionary <CurrencyPair, Task <RateResult> > fetchingByCurrencyPair, IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetworkBase network, InvoiceEntity entity, StoreData store, InvoiceLogs logs) { try { var logPrefix = $"{supportedPaymentMethod.PaymentId.ToPrettyString()}:"; var storeBlob = store.GetStoreBlob(); var preparePayment = handler.PreparePayment(supportedPaymentMethod, store, network); var rate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, entity.ProductInformation.Currency)]; if (rate.BidAsk == null) { return(null); } PaymentMethod paymentMethod = new PaymentMethod(); paymentMethod.ParentEntity = entity; paymentMethod.Network = network; paymentMethod.SetId(supportedPaymentMethod.PaymentId); paymentMethod.Rate = rate.BidAsk.Bid; paymentMethod.PreferOnion = Uri.TryCreate(entity.ServerUrl, UriKind.Absolute, out var u) && u.DnsSafeHost.EndsWith(".onion", StringComparison.OrdinalIgnoreCase); using (logs.Measure($"{logPrefix} Payment method details creation")) { var paymentDetails = await handler.CreatePaymentMethodDetails(logs, supportedPaymentMethod, paymentMethod, store, network, preparePayment); paymentMethod.SetPaymentMethodDetails(paymentDetails); } var errorMessage = await handler .IsPaymentMethodAllowedBasedOnInvoiceAmount(storeBlob, fetchingByCurrencyPair, paymentMethod.Calculate().Due, supportedPaymentMethod.PaymentId); if (!string.IsNullOrEmpty(errorMessage)) { logs.Write($"{logPrefix} {errorMessage}"); return(null); } #pragma warning disable CS0618 if (paymentMethod.GetId().IsBTCOnChain) { entity.TxFee = paymentMethod.NextNetworkFee; entity.Rate = paymentMethod.Rate; entity.DepositAddress = paymentMethod.DepositAddress; } #pragma warning restore CS0618 return(paymentMethod); } catch (PaymentMethodUnavailableException ex) { logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Payment method unavailable ({ex.Message})"); } catch (Exception ex) { logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Unexpected exception ({ex.ToString()})"); } return(null); }
private async Task <PaymentMethod> CreatePaymentMethodAsync(IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network, InvoiceEntity entity, StoreBlob storeBlob) { var rate = await storeBlob.ApplyRateRules(network, _RateProviders.GetRateProvider(network, false)).GetRateAsync(entity.ProductInformation.Currency); PaymentMethod paymentMethod = new PaymentMethod(); paymentMethod.ParentEntity = entity; paymentMethod.Network = network; paymentMethod.SetId(supportedPaymentMethod.PaymentId); paymentMethod.Rate = rate; var paymentDetails = await handler.CreatePaymentMethodDetails(supportedPaymentMethod, paymentMethod, network); if (storeBlob.NetworkFeeDisabled) { paymentDetails.SetNoTxFee(); } paymentMethod.SetPaymentMethodDetails(paymentDetails); // Check if Lightning Max value is exceeded if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.LightningLike && storeBlob.LightningMaxValue != null) { var lightningMaxValue = storeBlob.LightningMaxValue; var lightningMaxValueRate = 0.0m; if (lightningMaxValue.Currency == entity.ProductInformation.Currency) { lightningMaxValueRate = paymentMethod.Rate; } else { lightningMaxValueRate = await storeBlob.ApplyRateRules(network, _RateProviders.GetRateProvider(network, false)).GetRateAsync(lightningMaxValue.Currency); } var lightningMaxValueCrypto = Money.Coins(lightningMaxValue.Value / lightningMaxValueRate); if (paymentMethod.Calculate().Due > lightningMaxValueCrypto) { throw new PaymentMethodUnavailableException("Lightning max value exceeded"); } } /////////////// #pragma warning disable CS0618 if (paymentMethod.GetId().IsBTCOnChain) { entity.TxFee = paymentMethod.TxFee; entity.Rate = paymentMethod.Rate; entity.DepositAddress = paymentMethod.DepositAddress; } #pragma warning restore CS0618 return(paymentMethod); }
public abstract object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod);
protected abstract Task Process(ISupportedPaymentMethod paymentMethod, PayoutData[] payouts);
protected override async Task Process(ISupportedPaymentMethod paymentMethod, PayoutData[] payouts) { await using var ctx = _applicationDbContextFactory.CreateContext(); var lightningSupportedPaymentMethod = (LightningSupportedPaymentMethod)paymentMethod; if (lightningSupportedPaymentMethod.IsInternalNode && !(await Task.WhenAll((await _storeRepository.GetStoreUsers(_PayoutProcesserSettings.StoreId)) .Where(user => user.Role == StoreRoles.Owner).Select(user => user.Id) .Select(s => _userService.IsAdminUser(s)))).Any(b => b)) { return; } var client = lightningSupportedPaymentMethod.CreateLightningClient(_network, _options.Value, _lightningClientFactoryService); foreach (var payoutData in payouts) { var blob = payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings); var claim = await _payoutHandler.ParseClaimDestination(PaymentMethodId, blob.Destination); try { switch (claim.destination) { case LNURLPayClaimDestinaton lnurlPayClaimDestinaton: var endpoint = LNURL.LNURL.Parse(lnurlPayClaimDestinaton.LNURL, out var tag); var httpClient = _payoutHandler.CreateClient(endpoint); var lnurlInfo = (LNURLPayRequest)await LNURL.LNURL.FetchInformation(endpoint, "payRequest", httpClient); var lm = new LightMoney(blob.CryptoAmount.Value, LightMoneyUnit.BTC); if (lm > lnurlInfo.MaxSendable || lm < lnurlInfo.MinSendable) { continue; } else { try { var lnurlPayRequestCallbackResponse = await lnurlInfo.SendRequest(lm, _network.NBitcoinNetwork, httpClient); if (await TrypayBolt(client, blob, payoutData, lnurlPayRequestCallbackResponse .GetPaymentRequest(_network.NBitcoinNetwork))) { ctx.Attach(payoutData); payoutData.State = PayoutState.Completed; } } catch (LNUrlException) { continue; } } break; case BoltInvoiceClaimDestination item1: if (await TrypayBolt(client, blob, payoutData, item1.PaymentRequest)) { ctx.Attach(payoutData); payoutData.State = PayoutState.Completed; } break; } } catch (Exception e) { Logs.PayServer.LogError(e, $"Could not process payout {payoutData.Id}"); } } await ctx.SaveChangesAsync(); }
public abstract object GetGreenfieldData(ISupportedPaymentMethod supportedPaymentMethod, bool canModifyStore);
Task <IPaymentMethodDetails> IPaymentMethodHandler.CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetwork network, object preparePaymentObject) { if (supportedPaymentMethod is T method) { return(CreatePaymentMethodDetails(method, paymentMethod, store, network, preparePaymentObject)); } throw new NotSupportedException("Invalid supportedPaymentMethod"); }
private async Task <PaymentMethod> CreatePaymentMethodAsync(Dictionary <CurrencyPair, Task <RateResult> > fetchingByCurrencyPair, IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network, InvoiceEntity entity, StoreData store) { var storeBlob = store.GetStoreBlob(); var rate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, entity.ProductInformation.Currency)]; if (rate.Value == null) { return(null); } PaymentMethod paymentMethod = new PaymentMethod(); paymentMethod.ParentEntity = entity; paymentMethod.Network = network; paymentMethod.SetId(supportedPaymentMethod.PaymentId); paymentMethod.Rate = rate.Value.Value; var paymentDetails = await handler.CreatePaymentMethodDetails(supportedPaymentMethod, paymentMethod, store, network); if (storeBlob.NetworkFeeDisabled) { paymentDetails.SetNoTxFee(); } paymentMethod.SetPaymentMethodDetails(paymentDetails); Func <Money, Money, bool> compare = null; CurrencyValue limitValue = null; string errorMessage = null; if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.LightningLike && storeBlob.LightningMaxValue != null) { compare = (a, b) => a > b; limitValue = storeBlob.LightningMaxValue; errorMessage = "The amount of the invoice is too high to be paid with lightning"; } else if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.BTCLike && storeBlob.OnChainMinValue != null) { compare = (a, b) => a < b; limitValue = storeBlob.OnChainMinValue; errorMessage = "The amount of the invoice is too low to be paid on chain"; } if (compare != null) { var limitValueRate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, limitValue.Currency)]; if (limitValueRate.Value.HasValue) { var limitValueCrypto = Money.Coins(limitValue.Value / limitValueRate.Value.Value); if (compare(paymentMethod.Calculate().Due, limitValueCrypto)) { throw new PaymentMethodUnavailableException(errorMessage); } } } /////////////// #pragma warning disable CS0618 if (paymentMethod.GetId().IsBTCOnChain) { entity.TxFee = paymentMethod.TxFee; entity.Rate = paymentMethod.Rate; entity.DepositAddress = paymentMethod.DepositAddress; } #pragma warning restore CS0618 return(paymentMethod); }
public static void SetSupportedPaymentMethod(this StoreData storeData, ISupportedPaymentMethod supportedPaymentMethod) { storeData.SetSupportedPaymentMethod(null, supportedPaymentMethod); }
public void SetSupportedPaymentMethod(ISupportedPaymentMethod supportedPaymentMethod) { SetSupportedPaymentMethod(null, supportedPaymentMethod); }
Task <IPaymentMethodDetails> IPaymentMethodHandler.CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network) { if (supportedPaymentMethod is T method) { return(CreatePaymentMethodDetails(method, paymentMethod, network)); } throw new NotSupportedException("Invalid supportedPaymentMethod"); }
protected override async Task Process(ISupportedPaymentMethod paymentMethod, PayoutData[] payouts) { await using var ctx = _applicationDbContextFactory.CreateContext(); var lightningSupportedPaymentMethod = (LightningSupportedPaymentMethod)paymentMethod; if (lightningSupportedPaymentMethod.IsInternalNode && !(await Task.WhenAll((await _storeRepository.GetStoreUsers(_PayoutProcesserSettings.StoreId)) .Where(user => user.Role == StoreRoles.Owner).Select(user => user.Id) .Select(s => _userService.IsAdminUser(s)))).Any(b => b)) { return; } var client = lightningSupportedPaymentMethod.CreateLightningClient(_network, _options.Value, _lightningClientFactoryService); foreach (var payoutData in payouts) { var blob = payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings); var claim = await _payoutHandler.ParseClaimDestination(PaymentMethodId, blob.Destination); try { switch (claim.destination) { case LNURLPayClaimDestinaton lnurlPayClaimDestinaton: var lnurlResult = await UILightningLikePayoutController.GetInvoiceFromLNURL(payoutData, _payoutHandler, blob, lnurlPayClaimDestinaton, _network.NBitcoinNetwork); if (lnurlResult.Item2 is not null) { continue; } if (await TrypayBolt(client, blob, payoutData, lnurlResult.Item1)) { ctx.Attach(payoutData); payoutData.State = PayoutState.Completed; } break; case BoltInvoiceClaimDestination item1: if (await TrypayBolt(client, blob, payoutData, item1.PaymentRequest)) { ctx.Attach(payoutData); payoutData.State = PayoutState.Completed; } break; } } catch (Exception e) { Logs.PayServer.LogError(e, $"Could not process payout {payoutData.Id}"); } } await ctx.SaveChangesAsync(); }