public InvoiceItemServiceTest(MockHttpClientFixture mockHttpClientFixture) : base(mockHttpClientFixture) { this.service = new InvoiceItemService(); this.createOptions = new InvoiceItemCreateOptions { Amount = 123, Currency = "usd", CustomerId = "cus_123", }; this.updateOptions = new InvoiceItemUpdateOptions { Metadata = new Dictionary <string, string> { { "key", "value" }, }, }; this.listOptions = new InvoiceItemListOptions { Limit = 1, }; }
public virtual async Task <InvoiceLineItem> Create(InvoiceItemCreateOptions options) { var url = this.ApplyAllParameters(options, Urls.InvoiceItems, false); var response = await Requestor.Post(url); return(Mapper <InvoiceLineItem> .MapFromJson(response)); }
public async Task CreateInvoiceItemAsync( string transactionId, string description, string customer, long amount ) { var options = new InvoiceItemCreateOptions { Customer = customer, Currency = Options.Invoice.Currency, Amount = amount, Description = description, Metadata = new Dictionary <string, string> { [nameof(transactionId)] = transactionId } }; await this.CreateAsync(options); }
public InvoiceItemServiceTest() { this.service = new InvoiceItemService(); this.createOptions = new InvoiceItemCreateOptions() { Amount = 123, Currency = "usd", CustomerId = "cus_123", }; this.updateOptions = new InvoiceItemUpdateOptions() { Metadata = new Dictionary <string, string>() { { "key", "value" }, }, }; this.listOptions = new InvoiceItemListOptions() { Limit = 1, }; }
public async Task <bool> PreviewUpcomingInvoiceAndPayAsync(ISubscriber subscriber, string planId, List <InvoiceSubscriptionItemOptions> subItemOptions, int prorateThreshold = 500) { var invoiceService = new InvoiceService(); var invoiceItemService = new InvoiceItemService(); var pendingInvoiceItems = invoiceItemService.ListAutoPaging(new InvoiceItemListOptions { CustomerId = subscriber.GatewayCustomerId }).ToList().Where(i => i.InvoiceId == null); var pendingInvoiceItemsDict = pendingInvoiceItems.ToDictionary(pii => pii.Id); var upcomingPreview = await invoiceService.UpcomingAsync(new UpcomingInvoiceOptions { CustomerId = subscriber.GatewayCustomerId, SubscriptionId = subscriber.GatewaySubscriptionId, SubscriptionItems = subItemOptions }); var itemsForInvoice = upcomingPreview.Lines?.Data? .Where(i => pendingInvoiceItemsDict.ContainsKey(i.Id) || (i.Plan.Id == planId && i.Proration)); var invoiceAmount = itemsForInvoice?.Sum(i => i.Amount) ?? 0; var invoiceNow = invoiceAmount >= prorateThreshold; if (invoiceNow) { // Owes more than prorateThreshold on next invoice. // Invoice them and pay now instead of waiting until next billing cycle. Invoice invoice = null; var createdInvoiceItems = new List <InvoiceItem>(); Braintree.Transaction braintreeTransaction = null; try { foreach (var ii in itemsForInvoice) { if (pendingInvoiceItemsDict.ContainsKey(ii.Id)) { continue; } var invoiceItem = await invoiceItemService.CreateAsync(new InvoiceItemCreateOptions { Currency = ii.Currency, Description = ii.Description, CustomerId = subscriber.GatewayCustomerId, SubscriptionId = ii.SubscriptionId, Discountable = ii.Discountable, Amount = ii.Amount }); createdInvoiceItems.Add(invoiceItem); } invoice = await invoiceService.CreateAsync(new InvoiceCreateOptions { Billing = Billing.SendInvoice, DaysUntilDue = 1, CustomerId = subscriber.GatewayCustomerId, SubscriptionId = subscriber.GatewaySubscriptionId }); var invoicePayOptions = new InvoicePayOptions(); var customerService = new CustomerService(); var customer = await customerService.GetAsync(subscriber.GatewayCustomerId); if (customer != null) { if (customer.Metadata.ContainsKey("btCustomerId")) { invoicePayOptions.PaidOutOfBand = true; var btInvoiceAmount = (invoiceAmount / 100M); var transactionResult = await _btGateway.Transaction.SaleAsync( new Braintree.TransactionRequest { Amount = btInvoiceAmount, CustomerId = customer.Metadata["btCustomerId"], Options = new Braintree.TransactionOptionsRequest { SubmitForSettlement = true, PayPal = new Braintree.TransactionOptionsPayPalRequest { CustomField = $"{subscriber.BraintreeIdField()}:{subscriber.Id}" } }, CustomFields = new Dictionary <string, string> { [subscriber.BraintreeIdField()] = subscriber.Id.ToString() } }); if (!transactionResult.IsSuccess()) { throw new GatewayException("Failed to charge PayPal customer."); } braintreeTransaction = transactionResult.Target; await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions { Metadata = new Dictionary <string, string> { ["btTransactionId"] = braintreeTransaction.Id, ["btPayPalTransactionId"] = braintreeTransaction.PayPalDetails.AuthorizationId } }); } } await invoiceService.PayAsync(invoice.Id, invoicePayOptions); } catch (Exception e) { if (braintreeTransaction != null) { await _btGateway.Transaction.RefundAsync(braintreeTransaction.Id); } if (invoice != null) { await invoiceService.DeleteAsync(invoice.Id); // Restore invoice items that were brought in foreach (var item in pendingInvoiceItems) { var i = new InvoiceItemCreateOptions { Currency = item.Currency, Description = item.Description, CustomerId = item.CustomerId, SubscriptionId = item.SubscriptionId, Discountable = item.Discountable, Metadata = item.Metadata, Quantity = item.Quantity, UnitAmount = item.UnitAmount }; await invoiceItemService.CreateAsync(i); } } else { foreach (var ii in createdInvoiceItems) { await invoiceItemService.DeleteAsync(ii.Id); } } throw e; } } return(invoiceNow); }
private async Task <InvoiceResult> Create(InvoiceInput input, Guid organizationId) { _logger.LogInformation(GetLogMessage("{organizationId} with options {@input}"), organizationId, input); var retVal = new InvoiceResult() { ProjectId = input.ProjectId }; var project = await _projectService .Repository.Queryable() .Include(x => x.CustomerAccount) .ThenInclude(x => x.PaymentTerm) .Include(x => x.Contracts) .ThenInclude(x => x.InvoiceItems) .Include(x => x.BuyerOrganization) .ThenInclude(x => x.OrganizationBuyerAccount) .Include(x => x.ProviderOrganization) .Include(x => x.Contracts) .ThenInclude(x => x.TimeEntries) .Include(x => x.Contracts) .ThenInclude(x => x.Contractor) .ThenInclude(x => x.Person) .Include(x => x.ProviderOrganization) .ThenInclude(x => x.Organization) .Include(x => x.Contracts) .ThenInclude(x => x.ProviderOrganization) .ThenInclude(x => x.Organization) .Where(x => x.Id == input.ProjectId && x.ProjectManagerOrganizationId == organizationId) .FirstAsync(); if (project == null) { throw new ApplicationException("Project Not Found. Id : " + input.ProjectId + " Organization Id : " + organizationId); } if (project.BuyerOrganization.OrganizationBuyerAccount == null) { _logger.LogInformation(GetLogMessage("No buyer account found, creating...")); var result = await _buyerAccountService.PushCustomer(project.CustomerOrganizationId, project.CustomerId); if (result > 0) { _logger.LogDebug(GetLogMessage("{0} records updated in database"), result); return(await Create(input, organizationId)); } retVal.ErrorMessage = "Unable to establish buyer account for customer"; return(retVal); } List <Contract> contracts; // this could be filtered by active, etc if (input.IncludeAllContracts) { contracts = project.Contracts.ToList(); } else { contracts = project .Contracts .Where(x => input.ContractIds.Contains(x.Id)) .ToList(); } _logger.LogDebug(GetLogMessage("Contracts Found: {contracts}"), contracts.Count); _logger.LogDebug(GetLogMessage("Buyer Account: {buyerAcct}"), project .BuyerOrganization.OrganizationBuyerAccount.BuyerAccountId); var options = new InvoiceCreateOptions() { Customer = project.BuyerOrganization.OrganizationBuyerAccount.BuyerAccountId, AutoAdvance = false, CollectionMethod = "send_invoice", //DaysUntilDue = project.CustomerAccount.PaymentTerm.NetValue > 0 ? project.CustomerAccount.PaymentTerm.NetValue : 1, //Footer = project.Proposal.AgreementText, DueDate = DateTime.Today.AddDays(project.CustomerAccount.PaymentTerm.NetValue > 0 ? project.CustomerAccount.PaymentTerm.NetValue : 1), CustomFields = new List <InvoiceCustomFieldOptions>() { new InvoiceCustomFieldOptions() { Name = "Project Name", Value = project.Name }, new InvoiceCustomFieldOptions() { Name = "Provider Company", Value = project.ProviderOrganization.Organization.Name } }, Metadata = new Dictionary <string, string>() { { "proj_id", project.Id.ToString() } } }; var itemsCreated = 0; var itemsUpdated = 0; foreach (var c in contracts) { _logger.LogInformation(GetLogMessage("Contract Id: {0}"), c.Id); var timeEntries = c.TimeEntries .Where(x => x.Status == TimeStatus.Approved && x.InvoiceItemId == null) .ToList(); _logger.LogDebug(GetLogMessage("{entries} Approved Entries Found"), timeEntries.Count); var totalHours = timeEntries.Sum(x => x.TotalHours); if (totalHours > 0) { var totalCustomerAmount = timeEntries.Sum(x => x.TotalCustomerAmount); var ancientEntry = c.TimeEntries.Min(x => x.StartDate); var latterDayEntry = c.TimeEntries.Max(x => x.EndDate); var hours = totalHours.ToString("F"); _logger.LogDebug(GetLogMessage("Hours: {0}"), hours); _logger.LogDebug(GetLogMessage("Amount {amount:C}"), totalCustomerAmount); var description = $@"{hours} Hours Worked by {c.Contractor.Person.DisplayName} [{c.ProviderOrganization.Organization.Name}]"; var hasInvoiceItems = c.InvoiceItems.Any(x => x.InvoiceId == null); if (hasInvoiceItems) { var invoiceItems = c.InvoiceItems.Where(x => x.InvoiceId == null); _logger.LogDebug(GetLogMessage("Contract has invoice items : {0}"), invoiceItems.Count()); foreach (var item in invoiceItems) { _logger.LogDebug(GetLogMessage("Invoice Item Id : {0}"), item.Id); var stripeItem = _invoiceItemService.Update(item.Id, new InvoiceItemUpdateOptions() { Description = description, Amount = Convert.ToInt64(totalCustomerAmount * 100m) }); if (stripeItem != null) { _logger.LogDebug(GetLogMessage("Item Updated in Stripe. Stripe Item Id : {0}"), stripeItem.Id); itemsUpdated++; } else { _logger.LogDebug(GetLogMessage("Item Update Failed in Stripe")); } } } else { _logger.LogDebug(GetLogMessage("Contract doesn't have invoice items. Creating New Invoice Item")); var invoiceItemOptions = new InvoiceItemCreateOptions() { Period = new InvoiceItemPeriodOptions() { Start = ancientEntry.DateTime, End = latterDayEntry.DateTime }, Customer = project.BuyerOrganization.OrganizationBuyerAccount.BuyerAccountId, Amount = Convert.ToInt64(totalCustomerAmount * 100), Currency = "usd", Description = description, Metadata = new Dictionary <string, string>() { { "contract-id", c.Id.ToString() } } }; _logger.LogInformation(GetLogMessage("options: {0}"), invoiceItemOptions); var invoiceItem = _invoiceItemService.Create(invoiceItemOptions); _logger.LogDebug(GetLogMessage("Invoice Item: {0}"), invoiceItem); var invoiceItemResult = await InvoiceItemCreated(invoiceItem); _logger.LogDebug(GetLogMessage("Invoice Item Result: {@result}"), invoiceItemResult); if (invoiceItemResult.Succeeded) { itemsCreated++; } } if (itemsUpdated + itemsCreated > 0) { c.ObjectState = ObjectState.Modified; } } else { _logger.LogDebug(GetLogMessage("no billable time for {contract}"), c.Id); } } var entriesUpdated = _timeEntries.Commit(); _logger.LogDebug(GetLogMessage("Entries Updated: {entriesUpdated}"), entriesUpdated); if (entriesUpdated == 0) { _logger.LogWarning(GetLogMessage("No Entities were updated")); } _logger.LogInformation(GetLogMessage("options: {@Options}"), options); var invoice = _invoiceService.Create(options); if (invoice != null) { var stripeResult = await InvoiceCreated(invoice, input.RefNo); _logger.LogDebug(GetLogMessage("Stripe Result: {@result}"), stripeResult); if (stripeResult.Succeeded) { retVal.Succeeded = true; retVal.InvoiceId = invoice.Id; } } else { _logger.LogDebug(GetLogMessage("Unable to create invoice")); } if (retVal.Succeeded) { await Task.Run(() => new InvoiceCreatedEvent() { InvoiceId = invoice.Id }); } return(retVal); }
public async Task <Invoice> CreateInvoice( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "invoice/create")] CreatePaymentRequest request, ILogger log) { // このサンプルでは input validation をしていません。 // 既存の Customer に紐づけることもできますが、今回は毎回 Customer を作成します。 var customerCreateOptions = new CustomerCreateOptions { Email = request.Email, PaymentMethod = request.PaymentMethodId, InvoiceSettings = new CustomerInvoiceSettingsOptions { DefaultPaymentMethod = request.PaymentMethodId } }; var customer = await _customerService.CreateAsync(customerCreateOptions); // Attach payment method: // 新規作成した Customer に PaymentMethod を紐づけます。 // paymentMethodMethod は既に別の customer に紐づけられている場合エラーになります。 var options = new PaymentMethodAttachOptions { Customer = customer.Id }; var paymentMethod = await _paymentMethodService.AttachAsync(request.PaymentMethodId, options); // Update customer's default payment method. var customerOptions = new CustomerUpdateOptions { InvoiceSettings = new CustomerInvoiceSettingsOptions { DefaultPaymentMethod = paymentMethod.Id, } }; await _customerService.UpdateAsync(customer.Id, customerOptions); // Create InvoiceItem. var createOptions = new InvoiceItemCreateOptions { Customer = customer.Id, Price = request.PriceId }; await _invoiceItemService.CreateAsync(createOptions); // Create Invoice var invoiceOptions = new InvoiceCreateOptions { Customer = customer.Id, AutoAdvance = true, }; var invoiceResult = await _invoiceService.CreateAsync(invoiceOptions); //ここで、Stripe の Dashboard > Billing > インボイスにインボイスが追加されます。 return(invoiceResult); }
public string SendInvoice( string customerEmail, decimal amountToPay, string currency, string description = "", bool sendInvoice = true) { try { CustomerCreateOptions customerInfo = new CustomerCreateOptions { Email = customerEmail, //PaymentMethod = "card", }; var customerService = new CustomerService(); var customer = customerService.Create(customerInfo); var invoiceItemOption = new InvoiceItemCreateOptions { Customer = customer.Id, Amount = Convert.ToInt32(amountToPay * 100), Currency = currency, }; var invoiceItemService = new InvoiceItemService(); var invoiceItem = invoiceItemService.Create(invoiceItemOption); var invoiceOptions = new InvoiceCreateOptions { Customer = customer.Id, CollectionMethod = "send_invoice", DaysUntilDue = 30, Description = description, AutoAdvance = true }; var service = new InvoiceService(); var invoice = service.Create(invoiceOptions); invoice = service.FinalizeInvoice(invoice.Id); try { var paymentIntentService = new PaymentIntentService(); var paymentIntent = paymentIntentService.Get(invoice.PaymentIntentId); var paymentIntentUpdateOptions = new PaymentIntentUpdateOptions { Description = description }; paymentIntentService.Update(paymentIntent.Id, paymentIntentUpdateOptions); } catch (Exception) { //continue } if (sendInvoice) { invoice = service.SendInvoice(invoice.Id); } return(invoice.Id); } catch (Exception e) { Console.WriteLine(e); return(null); } }
public InvoiceInfo GeneratePayNowLink( string customerEmail, decimal amountToPay, string currency, string description = "") { try { CustomerCreateOptions customerInfo = new CustomerCreateOptions { Email = customerEmail, //PaymentMethod = "card", }; var customerService = new CustomerService(); var customer = customerService.Create(customerInfo); var invoiceItemOption = new InvoiceItemCreateOptions { Customer = customer.Id, Amount = Convert.ToInt32(amountToPay * 100), Currency = currency, }; var invoiceItemService = new InvoiceItemService(); var invoiceItem = invoiceItemService.Create(invoiceItemOption); var invoiceOptions = new InvoiceCreateOptions { Customer = customer.Id, CollectionMethod = "send_invoice", DaysUntilDue = 30, Description = description }; var service = new InvoiceService(); var invoice = service.Create(invoiceOptions); invoice = service.FinalizeInvoice(invoice.Id); try { var paymentIntentService = new PaymentIntentService(); var paymentIntent = paymentIntentService.Get(invoice.PaymentIntentId); var paymentIntentUpdateOptions = new PaymentIntentUpdateOptions { Description = description }; paymentIntentService.Update(paymentIntent.Id, paymentIntentUpdateOptions); } catch (Exception) { //continue } var result = new InvoiceInfo { Url = invoice.HostedInvoiceUrl, Id = invoice.Id }; return(result); } catch (Exception e) { Console.WriteLine(e); return(null); } }