private async Task UpdatePayoutsAwaitingForPayment(NewOnChainTransactionEvent newTransaction,
                                                       AddressTrackedSource addressTrackedSource)
    {
        try
        {
            var network        = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(newTransaction.CryptoCode);
            var destinationSum =
                newTransaction.NewTransactionEvent.Outputs.Sum(output => output.Value.GetValue(network));
            var destination     = addressTrackedSource.Address.ToString();
            var paymentMethodId = new PaymentMethodId(newTransaction.CryptoCode, BitcoinPaymentType.Instance);

            await using var ctx = _dbContextFactory.CreateContext();
            var payouts = await ctx.Payouts
                          .Include(o => o.StoreData)
                          .Include(o => o.PullPaymentData)
                          .Where(p => p.State == PayoutState.AwaitingPayment)
                          .Where(p => p.PaymentMethodId == paymentMethodId.ToString())
#pragma warning disable CA1307 // Specify StringComparison
                          .Where(p => destination.Equals(p.Destination))
#pragma warning restore CA1307 // Specify StringComparison
                          .ToListAsync();

            var payoutByDestination = payouts.ToDictionary(p => p.Destination);

            if (!payoutByDestination.TryGetValue(destination, out var payout))
            {
                return;
            }
            var payoutBlob = payout.GetBlob(_jsonSerializerSettings);
            if (payoutBlob.CryptoAmount is null ||
                // The round up here is not strictly necessary, this is temporary to fix existing payout before we
                // were properly roundup the crypto amount
                destinationSum !=
                BTCPayServer.Extensions.RoundUp(payoutBlob.CryptoAmount.Value, network.Divisibility))
            {
                return;
            }

            var derivationSchemeSettings = payout.StoreData
                                           .GetDerivationSchemeSettings(_btcPayNetworkProvider, newTransaction.CryptoCode).AccountDerivation;

            var storeWalletMatched = (await _explorerClientProvider.GetExplorerClient(newTransaction.CryptoCode)
                                      .GetTransactionAsync(derivationSchemeSettings,
                                                           newTransaction.NewTransactionEvent.TransactionData.TransactionHash));
            //if the wallet related to the store related to the payout does not have the tx: it is external
            var isInternal = storeWalletMatched is { };

            var proof = ParseProof(payout) as PayoutTransactionOnChainBlob ??
                        new PayoutTransactionOnChainBlob()
            {
                Accounted = isInternal
            };
            var txId = newTransaction.NewTransactionEvent.TransactionData.TransactionHash;
            if (!proof.Candidates.Add(txId))
            {
                return;
            }
            if (isInternal)
            {
                payout.State = PayoutState.InProgress;
                var walletId = new WalletId(payout.StoreDataId, newTransaction.CryptoCode);
                _eventAggregator.Publish(new UpdateTransactionLabel(walletId,
                                                                    newTransaction.NewTransactionEvent.TransactionData.TransactionHash,
                                                                    UpdateTransactionLabel.PayoutTemplate(new ()
                {
                    { payout.PullPaymentDataId ?? "", new List <string> {
                          payout.Id
                      } }
                }, walletId.ToString())));
            }
            else
            {
                await _notificationSender.SendNotification(new StoreScope(payout.StoreDataId),
                                                           new ExternalPayoutTransactionNotification()
                {
                    PaymentMethod = payout.PaymentMethodId,
                    PayoutId      = payout.Id,
                    StoreId       = payout.StoreDataId
                });
            }

            proof.TransactionId ??= txId;
            SetProofBlob(payout, proof);


            await ctx.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            Logs.PayServer.LogWarning(ex, "Error while processing a transaction in the pull payment hosted service");
        }
    }
Exemplo n.º 2
0
    private async Task UpdatePayoutsAwaitingForPayment(NewOnChainTransactionEvent newTransaction)
    {
        try
        {
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(newTransaction.CryptoCode);
            Dictionary <string, decimal> destinations;
            if (newTransaction.NewTransactionEvent.TrackedSource is AddressTrackedSource addressTrackedSource)
            {
                destinations = new Dictionary <string, decimal>()
                {
                    {
                        addressTrackedSource.Address.ToString(),
                            newTransaction.NewTransactionEvent.Outputs.Sum(output => output.Value.GetValue(network))
                    }
                };
            }
            else
            {
                destinations = newTransaction.NewTransactionEvent.TransactionData.Transaction.Outputs
                               .GroupBy(txout => txout.ScriptPubKey)
                               .ToDictionary(
                    txoutSet => txoutSet.Key.GetDestinationAddress(network.NBitcoinNetwork).ToString(),
                    txoutSet => txoutSet.Sum(txout => txout.Value.ToDecimal(MoneyUnit.BTC)));
            }

            var paymentMethodId = new PaymentMethodId(newTransaction.CryptoCode, BitcoinPaymentType.Instance);

            using var ctx = _dbContextFactory.CreateContext();
            var payouts = await ctx.Payouts
                          .Include(o => o.PullPaymentData)
                          .Where(p => p.State == PayoutState.AwaitingPayment)
                          .Where(p => p.PaymentMethodId == paymentMethodId.ToString())
                          .Where(p => destinations.Keys.Contains(p.Destination))
                          .ToListAsync();

            var payoutByDestination = payouts.ToDictionary(p => p.Destination);
            foreach (var destination in destinations)
            {
                if (!payoutByDestination.TryGetValue(destination.Key, out var payout))
                {
                    continue;
                }
                var payoutBlob = payout.GetBlob(_jsonSerializerSettings);
                if (payoutBlob.CryptoAmount is null ||
                    // The round up here is not strictly necessary, this is temporary to fix existing payout before we
                    // were properly roundup the crypto amount
                    destination.Value != BTCPayServer.Extensions.RoundUp(payoutBlob.CryptoAmount.Value, network.Divisibility))
                {
                    continue;
                }
                var proof = ParseProof(payout) as PayoutTransactionOnChainBlob;
                if (proof is null)
                {
                    proof = new PayoutTransactionOnChainBlob()
                    {
                        Accounted = !(newTransaction.NewTransactionEvent.TrackedSource is AddressTrackedSource),
                    };
                }
                var txId = newTransaction.NewTransactionEvent.TransactionData.TransactionHash;
                if (proof.Candidates.Add(txId))
                {
                    if (proof.Accounted is true)
                    {
                        payout.State = PayoutState.InProgress;
                        var walletId = new WalletId(payout.PullPaymentData.StoreId, newTransaction.CryptoCode);
                        _eventAggregator.Publish(new UpdateTransactionLabel(walletId,
                                                                            newTransaction.NewTransactionEvent.TransactionData.TransactionHash,
                                                                            UpdateTransactionLabel.PayoutTemplate(payout.Id, payout.PullPaymentDataId, walletId.ToString())));
                    }
                    if (proof.TransactionId is null)
                    {
                        proof.TransactionId = txId;
                    }
                    SetProofBlob(payout, proof);
                }
            }

            await ctx.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            Logs.PayServer.LogWarning(ex, "Error while processing a transaction in the pull payment hosted service");
        }
    }