mirror of
https://github.com/bitwarden/server
synced 2025-12-14 15:23:42 +00:00
Create transaction for charges that were the result of a bank transfer
This commit is contained in:
@@ -36,7 +36,7 @@ public interface IStripeEventUtilityService
|
|||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
/// /// <param name="providerId"></param>
|
/// /// <param name="providerId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Transaction FromChargeToTransaction(Charge charge, Guid? organizationId, Guid? userId, Guid? providerId);
|
Task<Transaction> FromChargeToTransactionAsync(Charge charge, Guid? organizationId, Guid? userId, Guid? providerId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to pay the specified invoice. If a customer is eligible, the invoice is paid using Braintree or Stripe.
|
/// Attempts to pay the specified invoice. If a customer is eligible, the invoice is paid using Braintree or Stripe.
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ public interface IStripeFacade
|
|||||||
RequestOptions requestOptions = null,
|
RequestOptions requestOptions = null,
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
IAsyncEnumerable<CustomerCashBalanceTransaction> GetCustomerCashBalanceTransactions(
|
||||||
|
string customerId,
|
||||||
|
CustomerCashBalanceTransactionListOptions customerCashBalanceTransactionListOptions = null,
|
||||||
|
RequestOptions requestOptions = null,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task<Customer> UpdateCustomer(
|
Task<Customer> UpdateCustomer(
|
||||||
string customerId,
|
string customerId,
|
||||||
CustomerUpdateOptions customerUpdateOptions = null,
|
CustomerUpdateOptions customerUpdateOptions = null,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class ChargeRefundedHandler : IChargeRefundedHandler
|
|||||||
{
|
{
|
||||||
// Attempt to create a transaction for the charge if it doesn't exist
|
// Attempt to create a transaction for the charge if it doesn't exist
|
||||||
var (organizationId, userId, providerId) = await _stripeEventUtilityService.GetEntityIdsFromChargeAsync(charge);
|
var (organizationId, userId, providerId) = await _stripeEventUtilityService.GetEntityIdsFromChargeAsync(charge);
|
||||||
var tx = _stripeEventUtilityService.FromChargeToTransaction(charge, organizationId, userId, providerId);
|
var tx = await _stripeEventUtilityService.FromChargeToTransactionAsync(charge, organizationId, userId, providerId);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parentTransaction = await _transactionRepository.CreateAsync(tx);
|
parentTransaction = await _transactionRepository.CreateAsync(tx);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class ChargeSucceededHandler : IChargeSucceededHandler
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var transaction = _stripeEventUtilityService.FromChargeToTransaction(charge, organizationId, userId, providerId);
|
var transaction = await _stripeEventUtilityService.FromChargeToTransactionAsync(charge, organizationId, userId, providerId);
|
||||||
if (!transaction.PaymentMethodType.HasValue)
|
if (!transaction.PaymentMethodType.HasValue)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Charge success from unsupported source/method. {ChargeId}", charge.Id);
|
_logger.LogWarning("Charge success from unsupported source/method. {ChargeId}", charge.Id);
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ public class StripeEventUtilityService : IStripeEventUtilityService
|
|||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
/// /// <param name="providerId"></param>
|
/// /// <param name="providerId"></param>
|
||||||
/// <returns></returns>
|
/// <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
|
var transaction = new Transaction
|
||||||
{
|
{
|
||||||
@@ -209,6 +209,24 @@ public class StripeEventUtilityService : IStripeEventUtilityService
|
|||||||
transaction.PaymentMethodType = PaymentMethodType.BankAccount;
|
transaction.PaymentMethodType = PaymentMethodType.BankAccount;
|
||||||
transaction.Details = $"ACH => {achCreditTransfer.BankName}, {achCreditTransfer.AccountNumber}";
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@@ -413,4 +431,41 @@ public class StripeEventUtilityService : IStripeEventUtilityService
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 fundedCharge = 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 }:
|
||||||
|
{
|
||||||
|
fundedCharge = charge.PaymentIntentId == cashBalanceTransaction.AppliedToPayment.PaymentIntentId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !fundedCharge ? null : bankTransferType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public class StripeFacade : IStripeFacade
|
|||||||
{
|
{
|
||||||
private readonly ChargeService _chargeService = new();
|
private readonly ChargeService _chargeService = new();
|
||||||
private readonly CustomerService _customerService = new();
|
private readonly CustomerService _customerService = new();
|
||||||
|
private readonly CustomerCashBalanceTransactionService _customerCashBalanceTransactionService = new();
|
||||||
private readonly EventService _eventService = new();
|
private readonly EventService _eventService = new();
|
||||||
private readonly InvoiceService _invoiceService = new();
|
private readonly InvoiceService _invoiceService = new();
|
||||||
private readonly PaymentMethodService _paymentMethodService = new();
|
private readonly PaymentMethodService _paymentMethodService = new();
|
||||||
@@ -41,6 +42,13 @@ public class StripeFacade : IStripeFacade
|
|||||||
CancellationToken cancellationToken = default) =>
|
CancellationToken cancellationToken = default) =>
|
||||||
await _customerService.GetAsync(customerId, customerGetOptions, requestOptions, cancellationToken);
|
await _customerService.GetAsync(customerId, customerGetOptions, requestOptions, cancellationToken);
|
||||||
|
|
||||||
|
public IAsyncEnumerable<CustomerCashBalanceTransaction> GetCustomerCashBalanceTransactions(
|
||||||
|
string customerId,
|
||||||
|
CustomerCashBalanceTransactionListOptions customerCashBalanceTransactionListOptions,
|
||||||
|
RequestOptions requestOptions = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
=> _customerCashBalanceTransactionService.ListAutoPagingAsync(customerId, customerCashBalanceTransactionListOptions, requestOptions, cancellationToken);
|
||||||
|
|
||||||
public async Task<Customer> UpdateCustomer(
|
public async Task<Customer> UpdateCustomer(
|
||||||
string customerId,
|
string customerId,
|
||||||
CustomerUpdateOptions customerUpdateOptions = null,
|
CustomerUpdateOptions customerUpdateOptions = null,
|
||||||
|
|||||||
Reference in New Issue
Block a user