1
0
mirror of https://github.com/bitwarden/server synced 2026-01-07 11:03:37 +00:00

[PM-28128] Create transaction for bank transfer charges (#6691)

* Create transaction for charges that were the result of a bank transfer

* Claude feedback

* Run dotnet format
This commit is contained in:
Alex Morask
2025-12-16 10:12:56 -06:00
committed by GitHub
parent 794240f108
commit 04efe402be
6 changed files with 87 additions and 4 deletions

View File

@@ -124,7 +124,7 @@ public class StripeEventUtilityService : IStripeEventUtilityService
/// <param name="userId"></param>
/// /// <param name="providerId"></param>
/// <returns></returns>
public Transaction FromChargeToTransaction(Charge charge, Guid? organizationId, Guid? userId, Guid? providerId)
public async Task<Transaction> FromChargeToTransactionAsync(Charge charge, Guid? organizationId, Guid? userId, Guid? providerId)
{
var transaction = new Transaction
{
@@ -209,6 +209,24 @@ public class StripeEventUtilityService : IStripeEventUtilityService
transaction.PaymentMethodType = PaymentMethodType.BankAccount;
transaction.Details = $"ACH => {achCreditTransfer.BankName}, {achCreditTransfer.AccountNumber}";
}
else if (charge.PaymentMethodDetails.CustomerBalance != null)
{
var bankTransferType = await GetFundingBankTransferTypeAsync(charge);
if (!string.IsNullOrEmpty(bankTransferType))
{
transaction.PaymentMethodType = PaymentMethodType.BankAccount;
transaction.Details = bankTransferType switch
{
"eu_bank_transfer" => "EU Bank Transfer",
"gb_bank_transfer" => "GB Bank Transfer",
"jp_bank_transfer" => "JP Bank Transfer",
"mx_bank_transfer" => "MX Bank Transfer",
"us_bank_transfer" => "US Bank Transfer",
_ => "Bank Transfer"
};
}
}
break;
}
@@ -406,4 +424,55 @@ public class StripeEventUtilityService : IStripeEventUtilityService
throw;
}
}
/// <summary>
/// Retrieves the bank transfer type that funded a charge paid via customer balance.
/// </summary>
/// <param name="charge">The charge to analyze.</param>
/// <returns>
/// The bank transfer type (e.g., "us_bank_transfer", "eu_bank_transfer") if the charge was funded
/// by a bank transfer via customer balance, otherwise null.
/// </returns>
private async Task<string> GetFundingBankTransferTypeAsync(Charge charge)
{
if (charge is not
{
CustomerId: not null,
PaymentIntentId: not null,
PaymentMethodDetails: { Type: "customer_balance" }
})
{
return null;
}
var cashBalanceTransactions = _stripeFacade.GetCustomerCashBalanceTransactions(charge.CustomerId);
string bankTransferType = null;
var matchingPaymentIntentFound = false;
await foreach (var cashBalanceTransaction in cashBalanceTransactions)
{
switch (cashBalanceTransaction)
{
case { Type: "funded", Funded: not null }:
{
bankTransferType = cashBalanceTransaction.Funded.BankTransfer.Type;
break;
}
case { Type: "applied_to_payment", AppliedToPayment: not null }
when cashBalanceTransaction.AppliedToPayment.PaymentIntentId == charge.PaymentIntentId:
{
matchingPaymentIntentFound = true;
break;
}
}
if (matchingPaymentIntentFound && !string.IsNullOrEmpty(bankTransferType))
{
return bankTransferType;
}
}
return null;
}
}