Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Billing/Services/IStripeEventUtilityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public interface IStripeEventUtilityService
/// <param name="userId"></param>
/// /// <param name="providerId"></param>
/// <returns></returns>
Transaction FromChargeToTransaction(Charge charge, Guid? organizationId, Guid? userId, Guid? providerId);
Task<Transaction> FromChargeToTransactionAsync(Charge charge, Guid? organizationId, Guid? userId, Guid? providerId);

/// <summary>
/// Attempts to pay the specified invoice. If a customer is eligible, the invoice is paid using Braintree or Stripe.
Expand Down
6 changes: 6 additions & 0 deletions src/Billing/Services/IStripeFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ Task<Customer> GetCustomer(
RequestOptions requestOptions = null,
CancellationToken cancellationToken = default);

IAsyncEnumerable<CustomerCashBalanceTransaction> GetCustomerCashBalanceTransactions(
string customerId,
CustomerCashBalanceTransactionListOptions customerCashBalanceTransactionListOptions = null,
RequestOptions requestOptions = null,
CancellationToken cancellationToken = default);

Task<Customer> UpdateCustomer(
string customerId,
CustomerUpdateOptions customerUpdateOptions = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public async Task HandleAsync(Event parsedEvent)
{
// Attempt to create a transaction for the charge if it doesn't exist
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
{
parentTransaction = await _transactionRepository.CreateAsync(tx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public async Task HandleAsync(Event parsedEvent)
return;
}

var transaction = _stripeEventUtilityService.FromChargeToTransaction(charge, organizationId, userId, providerId);
var transaction = await _stripeEventUtilityService.FromChargeToTransactionAsync(charge, organizationId, userId, providerId);
if (!transaction.PaymentMethodType.HasValue)
{
_logger.LogWarning("Charge success from unsupported source/method. {ChargeId}", charge.Id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public bool IsSponsoredSubscription(Subscription subscription) =>
/// <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
{
Expand Down Expand Up @@ -209,6 +209,24 @@ public Transaction FromChargeToTransaction(Charge charge, Guid? organizationId,
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;
}
Expand Down Expand Up @@ -413,4 +431,41 @@ private async Task<bool> AttemptToPayInvoiceWithStripeAsync(Invoice invoice)
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;
}
}
8 changes: 8 additions & 0 deletions src/Billing/Services/Implementations/StripeFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class StripeFacade : IStripeFacade
{
private readonly ChargeService _chargeService = new();
private readonly CustomerService _customerService = new();
private readonly CustomerCashBalanceTransactionService _customerCashBalanceTransactionService = new();
private readonly EventService _eventService = new();
private readonly InvoiceService _invoiceService = new();
private readonly PaymentMethodService _paymentMethodService = new();
Expand Down Expand Up @@ -41,6 +42,13 @@ public async Task<Customer> GetCustomer(
CancellationToken cancellationToken = default) =>
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(
string customerId,
CustomerUpdateOptions customerUpdateOptions = null,
Expand Down
Loading