public void TestInvoicePaymentSucceeded() { var invoice = new StripeInvoice { Subscription = "sub_123", Amount = 123, Charge = "ch_123", }; _donationService.Setup(mocked => mocked.CreateDonationForInvoice(invoice)).Returns(987); _fixture.InvoicePaymentSucceeded(DateTime.Now, invoice); _paymentProcessorService.VerifyAll(); _donationService.VerifyAll(); }
public static async Task <string> RequiredInvoice(string subscriptionId) { StripeConfiguration.SetApiKey("sk_test_5nCTR2UZmnOPWgZASvirbYDy"); StripeInvoiceService invoiceService = new StripeInvoiceService(); StripeList <StripeInvoice> invoiceItems = await invoiceService.ListAsync( new StripeInvoiceListOptions() { SubscriptionId = subscriptionId } ); StripeInvoice value = invoiceItems.First <StripeInvoice>(); return(value.Id); }
public static async Task <string> UpdateInvoice(string invoiceId) { StripeConfiguration.SetApiKey("sk_test_5nCTR2UZmnOPWgZASvirbYDy"); StripeInvoiceUpdateOptions invoiceOptions = new StripeInvoiceUpdateOptions() { Closed = false }; StripeInvoiceService invoiceService = new StripeInvoiceService(); //"in_1D18zxHWpLOEdy24Tgw0TzEq" // | // V StripeInvoice invoice = await invoiceService.UpdateAsync(invoiceId, invoiceOptions); return("OK"); }
public void SendSubscriptionPaymentReceiptEmail(StripeInvoice invoice, StripeCustomer customer) { var message = new MailMessage() { IsBodyHtml = true }; message.To.Add(new MailAddress(customer.Email)); message.Subject = string.Format("Software Payment Receipt"); message.Body = string.Format("<p>{0}</p>", string.Format("Thank you for using our software. Your credit card has been charged ${0}", Math.Ceiling((decimal)invoice.AmountDue / 100))); using (var smtp = new SmtpClient()) { smtp.Send(message); } }
private void InvoicePaymentFailed(DateTime?created, StripeInvoice invoice) { var charge = _paymentProcessorService.GetCharge(invoice.Charge); var notes = "No Stripe Failure Code"; if (charge != null) { notes = $"{charge.FailureCode ?? "No Stripe Failure Code"}: {charge.FailureMessage ?? "No Stripe Failure Message"}"; } _mpDonorRepository.ProcessRecurringGiftDecline(invoice.Subscription, notes); var gift = _mpDonorRepository.GetRecurringGiftForSubscription(invoice.Subscription); if (gift.ConsecutiveFailureCount > 2) { var subscription = _paymentProcessorService.CancelSubscription(gift.StripeCustomerId, gift.SubscriptionId); _paymentProcessorService.CancelPlan(subscription.Plan.Id); _mpDonorRepository.CancelRecurringGift(gift.RecurringGiftId.Value); } }
private async Task InvoicePaymentSucceededAsync(StripeInvoice inv) { var org = await _organizationRepository.GetByStripeCustomerIdAsync(inv.CustomerId).AnyContext(); if (org == null) { _logger.Error("Unknown customer id in payment succeeded notification: {0}", inv.CustomerId); return; } var user = await _userRepository.GetByIdAsync(org.BillingChangedByUserId).AnyContext(); if (user == null) { _logger.Error("Unable to find billing user: {0}", org.BillingChangedByUserId); return; } _logger.Info("Stripe payment succeeded. Customer: {0} Org: {1} Org Name: {2}", inv.CustomerId, org.Id, org.Name); }
private void InvoicePaymentSucceeded(StripeInvoice inv) { var org = _organizationRepository.GetByStripeCustomerId(inv.CustomerId); if (org == null) { Log.Error().Message("Unknown customer id in payment failed notification: {0}", inv.CustomerId).Write(); return; } var user = _userRepository.GetById(org.BillingChangedByUserId); if (user == null) { Log.Error().Message("Unable to find billing user: {0}", org.BillingChangedByUserId).Write(); return; } Log.Info().Message("Stripe payment succeeded. Customer: {0} Org: {1} Org Name: {2}", inv.CustomerId, org.Id, org.Name).Write(); }
public static PaymentInvoice ToPaymentInvoice(this StripeInvoice stripeInvoice) { var invoice = new PaymentInvoice { Id = stripeInvoice.Id, InvoiceNumber = stripeInvoice.Number, ReceiptNumber = stripeInvoice.ReceiptNumber, AmountDue = stripeInvoice.AmountDue.ToDecimalAmount(), AmountPaid = stripeInvoice.AmountPaid.ToDecimalAmount(), AmountRemaining = stripeInvoice.AmountRemaining.ToDecimalAmount(), AttemptCount = stripeInvoice.AttemptCount, Attempted = stripeInvoice.Attempted, CustomerId = stripeInvoice.CustomerId, ChargeId = stripeInvoice.ChargeId, Description = stripeInvoice.Description, //Todo: Discount DueDate = stripeInvoice.DueDate, HostedInvoiceUrl = stripeInvoice.HostedInvoiceUrl, HostedPdf = stripeInvoice.InvoicePdf, Subtotal = stripeInvoice.Subtotal.ToDecimalAmount(), Total = stripeInvoice.Total.ToDecimalAmount(), periodStartDate = stripeInvoice.PeriodStart, periodEndDate = stripeInvoice.PeriodEnd, Tax = stripeInvoice.Tax == null ? null : (decimal?)((int)stripeInvoice.Tax).ToDecimalAmount(), TaxPercent = stripeInvoice.TaxPercent }; //Items if (stripeInvoice.StripeInvoiceLineItems != null && stripeInvoice.StripeInvoiceLineItems.TotalCount > 0) { invoice.Items = new List <InvoiceItem>(); foreach (var lineItem in stripeInvoice.StripeInvoiceLineItems) { if (lineItem != null) { invoice.Items.Add(lineItem.ToInvoiceItem()); } } } return(invoice); }
private async Task InvoicePaymentFailedAsync(StripeInvoice inv) { var org = await _organizationRepository.GetByStripeCustomerIdAsync(inv.CustomerId).AnyContext(); if (org == null) { _logger.Error().Message("Unknown customer id in payment failed notification: {0}", inv.CustomerId).Write(); return; } var user = await _userRepository.GetByIdAsync(org.BillingChangedByUserId).AnyContext(); if (user == null) { _logger.Error().Message("Unable to find billing user: {0}", org.BillingChangedByUserId).Write(); return; } _logger.Info().Message("Stripe payment failed. Customer: {0} Org: {1} Org Name: {2} Email: {3}", inv.CustomerId, org.Id, org.Name, user.EmailAddress).Write(); await _mailer.SendPaymentFailedAsync(user, org).AnyContext(); }
public void HandleEvent(StripeEvent stripeEvent) { switch (stripeEvent.Type) { case "customer.subscription.updated": { StripeSubscription stripeSubscription = Mapper <StripeSubscription> .MapFromJson(stripeEvent.Data.Object.ToString()); SubscriptionUpdated(stripeSubscription); break; } case "customer.subscription.deleted": { StripeSubscription stripeSubscription = Mapper <StripeSubscription> .MapFromJson(stripeEvent.Data.Object.ToString()); SubscriptionDeleted(stripeSubscription); break; } case "invoice.payment_succeeded": { StripeInvoice stripeInvoice = Mapper <StripeInvoice> .MapFromJson(stripeEvent.Data.Object.ToString()); InvoicePaymentSucceeded(stripeInvoice); break; } case "invoice.payment_failed": { StripeInvoice stripeInvoice = Mapper <StripeInvoice> .MapFromJson(stripeEvent.Data.Object.ToString()); InvoicePaymentFailed(stripeInvoice); break; } default: { Log.Trace().Message("Unhandled stripe webhook called. Type: {0} Id: {1} UserId: {2}", stripeEvent.Type, stripeEvent.Id, stripeEvent.UserId).Write(); break; } } }
public async Task HandleEventAsync(StripeEvent stripeEvent) { switch (stripeEvent.Type) { case "customer.subscription.updated": { StripeSubscription stripeSubscription = Mapper <StripeSubscription> .MapFromJson(stripeEvent.Data.Object.ToString()); await SubscriptionUpdatedAsync(stripeSubscription).AnyContext(); break; } case "customer.subscription.deleted": { StripeSubscription stripeSubscription = Mapper <StripeSubscription> .MapFromJson(stripeEvent.Data.Object.ToString()); await SubscriptionDeletedAsync(stripeSubscription).AnyContext(); break; } case "invoice.payment_succeeded": { StripeInvoice stripeInvoice = Mapper <StripeInvoice> .MapFromJson(stripeEvent.Data.Object.ToString()); await InvoicePaymentSucceededAsync(stripeInvoice).AnyContext(); break; } case "invoice.payment_failed": { StripeInvoice stripeInvoice = Mapper <StripeInvoice> .MapFromJson(stripeEvent.Data.Object.ToString()); await InvoicePaymentFailedAsync(stripeInvoice).AnyContext(); break; } default: { _logger.Trace("Unhandled stripe webhook called. Type: {0} Id: {1} Account: {2}", stripeEvent.Type, stripeEvent.Id, stripeEvent.Account); break; } } }
public InvoiceModel(Organization organization, StripeInvoice invoice, ApiSettings apiSettings) { OurAddress1 = apiSettings.OurAddress1; OurAddress2 = apiSettings.OurAddress2; OurAddress3 = apiSettings.OurAddress3; CustomerName = organization.BusinessName ?? "--"; CustomerAddress1 = organization.BusinessAddress1; CustomerAddress2 = organization.BusinessAddress2; CustomerAddress3 = organization.BusinessAddress3; CustomerCountry = organization.BusinessCountry; CustomerVatNumber = organization.BusinessTaxNumber; InvoiceDate = invoice.Date?.ToLongDateString(); InvoiceDueDate = invoice.DueDate?.ToLongDateString(); InvoiceNumber = invoice.Id; Items = invoice.StripeInvoiceLineItems.Select(i => new Item(i)); SubtotalAmount = (invoice.Total / 100).ToString("C"); VatTotalAmount = 0.ToString("C"); TotalAmount = SubtotalAmount; Paid = invoice.Paid; }
public ActionResult Payment(string id) { if (String.IsNullOrEmpty(id)) { return(HttpNotFound()); } if (!id.StartsWith("in_")) { id = "in_" + id; } var invoiceService = new StripeInvoiceService(); StripeInvoice invoice = invoiceService.Get(id); if (invoice == null) { return(HttpNotFound()); } Organization org = _repository.GetByStripeCustomerId(invoice.CustomerId); if (org == null) { return(HttpNotFound()); } if (!CanAccessOrganization(org.Id)) { return(HttpNotFound()); } return(View(new InvoiceModel { Invoice = invoice, Organization = org })); }
static void TestInvoices2(StripePayment payment) { StripeCustomer cust = payment.GetCustomer("cus_ulcOcy5Seu2dpq"); StripePlanInfo planInfo = new StripePlanInfo { Amount = 1999, ID = "testplan", Interval = StripePlanInterval.Month, Name = "The Test Plan", //TrialPeriod = 7 }; //payment.DeletePlan (planInfo.ID); StripePlan plan = payment.CreatePlan(planInfo); StripeSubscriptionInfo subInfo = new StripeSubscriptionInfo { Card = GetCC(), Plan = planInfo.ID, Prorate = true }; StripeSubscription sub = payment.Subscribe(cust.ID, subInfo); payment.CreateInvoiceItem(new StripeInvoiceItemInfo { CustomerID = cust.ID, Amount = 1337, Description = "Test single charge" }); int total; List <StripeInvoice> invoices = payment.GetInvoices(0, 10, cust.ID, out total); StripeInvoice upcoming = payment.GetUpcomingInvoice(cust.ID); payment.Unsubscribe(cust.ID, true); payment.DeletePlan(planInfo.ID); foreach (StripeInvoiceLineItem line in upcoming) { Console.WriteLine("{0} for type {1}", line.Amount, line.GetType()); } }
// GET: /StripeWebhooks/ public async Task <HttpStatusCodeResult> Index() { var json = new StreamReader(Request.InputStream).ReadToEnd(); var stripeEvent = StripeEventUtility.ParseEvent(json); #region All Event types // All the event types explained here: https://stripe.com/docs/api#event_types switch (stripeEvent.Type) { case "account.updated": //Occurs whenever an account status or property has changed. break; case "account.application.deauthorized": // Occurs whenever a user deauthorizes an application. Sent to the related application only. break; case "application_fee.created": // Occurs whenever an application fee is created on a charge. break; case "application_fee.refunded": // Occurs whenever an application fee is refunded, whether from refunding a charge or from refunding the application fee directly, including partial refunds. break; case "balance.available": // Occurs whenever your Stripe balance has been updated (e.g. when a charge collected is available to be paid out). By default, Stripe will automatically transfer any funds in your balance to your bank account on a daily basis. break; case "charge.succeeded": // Occurs whenever a new charge is created and is successful. break; case "charge.failed": // Occurs whenever a failed charge attempt occurs. break; case "charge.refunded": // Occurs whenever a charge is refunded, including partial refunds. break; case "charge.captured": // Occurs whenever a previously uncaptured charge is captured. break; case "charge.updated": // Occurs whenever a charge description or metadata is updated. break; case "charge.dispute.created": // Occurs whenever a customer disputes a charge with their bank (chargeback). break; case "charge.dispute.updated": // Occurs when the dispute is updated (usually with evidence). break; case "charge.dispute.closed": // Occurs when the dispute is resolved and the dispute status changes to won or lost. break; case "customer.created": // Occurs whenever a new customer is created. break; case "customer.updated": // Occurs whenever any property of a customer changes. break; case "customer.deleted": // Occurs whenever a customer is deleted. break; case "customer.card.created": // Occurs whenever a new card is created for the customer. break; case "customer.card.updated": // Occurs whenever a card's details are changed. // TODO: Save card updated, might happen when the card is close to expire break; case "customer.card.deleted": // Occurs whenever a card is removed from a customer. break; case "customer.subscription.created": // Occurs whenever a customer with no subscription is signed up for a plan. break; case "customer.subscription.updated": // Occurs whenever a subscription changes. Examples would include switching from one plan to another, or switching status from trial to active. break; case "customer.subscription.deleted": // Occurs whenever a customer ends their subscription. break; case "customer.subscription.trial_will_end": // Occurs three days before the trial period of a subscription is scheduled to end. // TODO: If the user hasn't added credit card details -> Send email reminder. break; case "customer.discount.created": // Occurs whenever a coupon is attached to a customer. break; case "customer.discount.updated": // Occurs whenever a customer is switched from one coupon to another. break; case "customer.discount.deleted": break; case "invoice.created": // Occurs whenever a new invoice is created. If you are using webhooks, Stripe will wait one hour after they have all succeeded to attempt to pay the invoice; the only exception here is on the first invoice, which gets created and paid immediately when you subscribe a customer to a plan. If your webhooks do not all respond successfully, Stripe will continue retrying the webhooks every hour and will not attempt to pay the invoice. After 3 days, Stripe will attempt to pay the invoice regardless of whether or not your webhooks have succeeded. See how to respond to a webhook. break; case "invoice.payment_failed": // Occurs whenever an invoice attempts to be paid, and the payment fails. This can occur either due to a declined payment, or because the customer has no active card. A particular case of note is that if a customer with no active card reaches the end of its free trial, an invoice.payment_failed notification will occur. // TODO: Notify customer break; case "invoice.payment_succeeded": // Occurs whenever an invoice attempts to be paid, and the payment succeeds. StripeInvoice stripeInvoice = Stripe.Mapper <StripeInvoice> .MapFromJson(stripeEvent.Data.Object.ToString()); Invoice invoice = SaasEcom.Core.Infrastructure.Mappers.Map(stripeInvoice); if (invoice != null && invoice.Total > 0) { // TODO get the customer billing address, we still have to instantiate the address on the invoice invoice.BillingAddress = new BillingAddress(); await InvoiceDataService.CreateOrUpdateAsync(invoice); // TODO: Send invoice by email } break; case "invoice.updated": // Occurs whenever an invoice changes (for example, the amount could change). break; case "invoiceitem.created": // Occurs whenever an invoice item is created. break; case "invoiceitem.updated": // Occurs whenever an invoice item is updated. break; case "invoiceitem.deleted": // Occurs whenever an invoice item is deleted. break; case "plan.created": // Occurs whenever a plan is created. break; case "plan.updated": // Occurs whenever a plan is updated. break; case "plan.deleted": // Occurs whenever a plan is deleted. break; case "coupon.created": // Occurs whenever a coupon is created. break; case "coupon.deleted": // Occurs whenever a coupon is deleted. break; case "transfer.created": // Occurs whenever a new transfer is created. break; case "transfer.updated": // Occurs whenever the description or metadata of a transfer is updated. break; case "transfer.paid": // Occurs whenever a sent transfer is expected to be available in the destination bank account. If the transfer failed, a transfer.failed webhook will additionally be sent at a later time. break; case "transfer.failed": // Occurs whenever Stripe attempts to send a transfer and that transfer fails. break; } #endregion return(new HttpStatusCodeResult(HttpStatusCode.Accepted)); }
public async Task <IActionResult> PostWebhook([FromQuery] string key) { if (key != _billingSettings.StripeWebhookKey) { return(new BadRequestResult()); } StripeEvent parsedEvent; using (var sr = new StreamReader(HttpContext.Request.Body)) { var json = await sr.ReadToEndAsync(); parsedEvent = StripeEventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], _billingSettings.StripeWebhookSecret); } if (string.IsNullOrWhiteSpace(parsedEvent?.Id)) { return(new BadRequestResult()); } if (_hostingEnvironment.IsProduction() && !parsedEvent.LiveMode) { return(new BadRequestResult()); } var invUpcoming = parsedEvent.Type.Equals("invoice.upcoming"); var subDeleted = parsedEvent.Type.Equals("customer.subscription.deleted"); var subUpdated = parsedEvent.Type.Equals("customer.subscription.updated"); if (subDeleted || subUpdated) { StripeSubscription subscription = Mapper <StripeSubscription> .MapFromJson( parsedEvent.Data.Object.ToString()); if (subscription == null) { throw new Exception("Subscription is null."); } var ids = GetIdsFromMetaData(subscription.Metadata); var subCanceled = subDeleted && subscription.Status == "canceled"; var subUnpaid = subUpdated && subscription.Status == "unpaid"; if (subCanceled || subUnpaid) { // org if (ids.Item1.HasValue) { await _organizationService.DisableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd); } // user else if (ids.Item2.HasValue) { await _userService.DisablePremiumAsync(ids.Item2.Value, subscription.CurrentPeriodEnd); } } if (subUpdated) { // org if (ids.Item1.HasValue) { await _organizationService.UpdateExpirationDateAsync(ids.Item1.Value, subscription.CurrentPeriodEnd); } // user else if (ids.Item2.HasValue) { await _userService.UpdatePremiumExpirationAsync(ids.Item2.Value, subscription.CurrentPeriodEnd); } } } else if (false /* Disabled for now */ && invUpcoming) { StripeInvoice invoice = Mapper <StripeInvoice> .MapFromJson( parsedEvent.Data.Object.ToString()); if (invoice == null) { throw new Exception("Invoice is null."); } // TODO: maybe invoice subscription expandable is already here any we don't need to call API? var subscriptionService = new StripeSubscriptionService(); var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId); if (subscription == null) { throw new Exception("Invoice subscription is null."); } var ids = GetIdsFromMetaData(subscription.Metadata); // To include in email: // invoice.AmountDue; // invoice.DueDate; // org if (ids.Item1.HasValue) { // TODO: email billing contact } // user else if (ids.Item2.HasValue) { // TODO: email user } } return(new OkResult()); }
public async Task <InvoiceResult> InvoiceCreated(Invoice invoice, string refNo) { _logger.LogInformation(GetLogMessage("Invoice: {invoice}"), invoice.Id); var retVal = new InvoiceResult { InvoiceId = invoice.Id }; var entity = new StripeInvoice { Id = invoice.Id, ObjectState = ObjectState.Added, Updated = DateTimeOffset.UtcNow, AmountDue = Convert.ToDecimal(invoice.AmountDue / 100m), AmountRemaining = Convert.ToDecimal(invoice.AmountRemaining / 100m), Attempted = invoice.Attempted, CustomerId = invoice.CustomerId, InvoicePdf = invoice.InvoicePdf, Status = invoice.Status, SubscriptionId = invoice.SubscriptionId, AmountPaid = Convert.ToDecimal(invoice.AmountPaid / 100m), Total = Convert.ToDecimal(invoice.Total / 100m), Subtotal = Convert.ToDecimal(invoice.Subtotal / 100m), HostedInvoiceUrl = invoice.HostedInvoiceUrl, BillingReason = invoice.BillingReason, AttemptCount = invoice.AttemptCount, Number = invoice.Number, DueDate = invoice.DueDate }; if (invoice.Metadata.ContainsKey("proj_id")) { Guid projectId = Guid.Parse(invoice.Metadata["proj_id"]); _logger.LogInformation(GetLogMessage("Project Id : {0}"), projectId.ToString()); var project = await _projectService.Repository.Queryable() .Where(x => x.Id == projectId).FirstAsync(); retVal.ProjectId = projectId; entity.ProjectInvoice = new ProjectInvoice() { AccountManagerId = project.AccountManagerId, ProviderOrganizationId = project.ProjectManagerOrganizationId, CustomerId = project.CustomerId, BuyerOrganizationId = project.CustomerOrganizationId, ProjectManagerId = project.ProjectManagerId, ProjectId = projectId, RefNo = refNo, ObjectState = ObjectState.Added }; } else { _logger.LogDebug(GetLogMessage("Not a project invoice")); } var records = _invoices.InsertOrUpdateGraph(entity, true); _logger.LogDebug(GetLogMessage("{0} records updated"), records); if (records > 0) { UpdateInvoiceItems(invoice, true); retVal.Succeeded = true; } return(await Task.FromResult(retVal)); }
public ActionResult Index() { Stream request = Request.InputStream; request.Seek(0, SeekOrigin.Begin); string json = new StreamReader(request).ReadToEnd(); StripeEvent stripeEvent = null; try { stripeEvent = StripeEventUtility.ParseEvent(json); stripeEvent = VerifyEventSentFromStripe(stripeEvent); if (HasEventBeenProcessedPreviously(stripeEvent)) { return(new HttpStatusCodeResult(HttpStatusCode.OK)); } ; } catch (Exception ex) { return(new HttpStatusCodeResult(HttpStatusCode.BadRequest, string.Format("Unable to parse incoming event. The following error occurred: {0}", ex.Message))); } if (stripeEvent == null) { return(new HttpStatusCodeResult(HttpStatusCode.BadRequest, "Incoming event empty")); } var emailService = new StripeFundamentals.Web.Services.EmailService(); switch (stripeEvent.Type) { case StripeEvents.ChargeRefunded: var charge = Mapper <StripeCharge> .MapFromJson(stripeEvent.Data.Object.ToString()); emailService.SendRefundEmail(charge); break; case StripeEvents.CustomerSubscriptionTrialWillEnd: var subscription = Mapper <StripeSubscription> .MapFromJson(stripeEvent.Data.Object.ToString()); emailService.SendTrialEndEmail(subscription); break; case StripeEvents.InvoicePaymentSucceeded: StripeInvoice invoice = Mapper <StripeInvoice> .MapFromJson(stripeEvent.Data.Object.ToString()); var customer = StripeCustomerService.Get(invoice.CustomerId); var user = UserManager.FindByEmail(customer.Email); user.ActiveUntil = user.ActiveUntil.AddMonths(1); UserManager.Update(user); emailService.SendSubscriptionPaymentReceiptEmail(invoice, customer); break; case StripeEvents.InvoicePaymentFailed: var failedInvoice = Mapper <StripeInvoice> .MapFromJson(stripeEvent.Data.Object.ToString()); //TODO: implement sending email to customer and customer service break; default: break; } //TODO: log Stripe eventid to StripeEvent table in application database return(new HttpStatusCodeResult(HttpStatusCode.OK)); }
public async Task <IActionResult> PostWebhook([FromQuery] string key) { if (key != _billingSettings.StripeWebhookKey) { return(new BadRequestResult()); } StripeEvent parsedEvent; using (var sr = new StreamReader(HttpContext.Request.Body)) { var json = await sr.ReadToEndAsync(); parsedEvent = StripeEventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], _billingSettings.StripeWebhookSecret); } if (string.IsNullOrWhiteSpace(parsedEvent?.Id)) { return(new BadRequestResult()); } if (_hostingEnvironment.IsProduction() && !parsedEvent.LiveMode) { return(new BadRequestResult()); } var invUpcoming = parsedEvent.Type.Equals("invoice.upcoming"); var subDeleted = parsedEvent.Type.Equals("customer.subscription.deleted"); var subUpdated = parsedEvent.Type.Equals("customer.subscription.updated"); if (subDeleted || subUpdated) { StripeSubscription subscription = Mapper <StripeSubscription> .MapFromJson( parsedEvent.Data.Object.ToString()); if (subscription == null) { throw new Exception("Subscription is null."); } var ids = GetIdsFromMetaData(subscription.Metadata); var subCanceled = subDeleted && subscription.Status == "canceled"; var subUnpaid = subUpdated && subscription.Status == "unpaid"; if (subCanceled || subUnpaid) { // org if (ids.Item1.HasValue) { await _organizationService.DisableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd); } // user else if (ids.Item2.HasValue) { await _userService.DisablePremiumAsync(ids.Item2.Value, subscription.CurrentPeriodEnd); } } if (subUpdated) { // org if (ids.Item1.HasValue) { await _organizationService.UpdateExpirationDateAsync(ids.Item1.Value, subscription.CurrentPeriodEnd); } // user else if (ids.Item2.HasValue) { await _userService.UpdatePremiumExpirationAsync(ids.Item2.Value, subscription.CurrentPeriodEnd); } } } else if (invUpcoming) { StripeInvoice invoice = Mapper <StripeInvoice> .MapFromJson( parsedEvent.Data.Object.ToString()); if (invoice == null) { throw new Exception("Invoice is null."); } var subscriptionService = new StripeSubscriptionService(); var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId); if (subscription == null) { throw new Exception("Invoice subscription is null."); } string email = null; var ids = GetIdsFromMetaData(subscription.Metadata); // org if (ids.Item1.HasValue) { var org = await _organizationRepository.GetByIdAsync(ids.Item1.Value); if (org != null && OrgPlanForInvoiceNotifications(org)) { email = org.BillingEmail; } } // user else if (ids.Item2.HasValue) { var user = await _userService.GetUserByIdAsync(ids.Item2.Value); if (user.Premium) { email = user.Email; } } if (!string.IsNullOrWhiteSpace(email) && invoice.NextPaymentAttempt.HasValue) { var items = invoice.StripeInvoiceLineItems.Select(i => i.Description).ToList(); await _mailService.SendInvoiceUpcomingAsync(email, invoice.AmountDue / 100M, invoice.NextPaymentAttempt.Value, items, ids.Item1.HasValue); } } return(new OkResult()); }
public BillingInvoice(StripeInvoice inv) { Amount = inv.AmountDue / 100M; Date = inv.Date.Value; }
/// <summary> /// Create order in fortnox /// </summary> /// <param name="stripeEvent"></param> /// <returns></returns> public async Task <HttpResponseMessage> HandleInvoiceCreatedAsync(StripeInvoice invoice) { _log.LogInformation($"Invoice created with ID {invoice.Id}, type: {invoice.Billing}"); if (await OrderExists(invoice.Id)) { _log.LogInformation($"Duplicate request for invoice id {invoice.Id}"); return(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Duplicate request ignored") }); } var customerId = await GetFortnoxCustomerId(invoice.CustomerId); var order = new { CustomerNumber = customerId, Language = "EN", ExternalInvoiceReference1 = invoice.Id, Remarks = invoice.Billing == StripeBilling.ChargeAutomatically ? "Don't pay this invoice!\n\nYou have prepaid by credit/debit card." : "", CopyRemarks = true, Currency = invoice.Currency.ToUpperInvariant(), EmailInformation = new { EmailAddressFrom = "*****@*****.**", EmailAddressBCC = "*****@*****.**", EmailSubject = "Flexinets Invoice/Order Receipt {no}", EmailBody = invoice.Billing == StripeBilling.ChargeAutomatically ? "Dear Flexinets user,<br />This email contains the credit card receipt for your prepaid subscription. No action required.<br /><br />Best regards<br />Flexinets<br />www.flexinets.eu" : "hitta på text för fakturan" }, OrderRows = invoice.StripeInvoiceLineItems.Data.Select(line => new { Description = line.Description.Replace("×", "x"), // thats not an x, this is an x AccountNumber = "", ArticleNumber = ArticleNumber, Price = line.Amount / 100m, OrderedQuantity = line.Quantity.GetValueOrDefault(0), DeliveredQuantity = line.Quantity.GetValueOrDefault(0), VAT = invoice.TaxPercent.HasValue ? Convert.ToInt32(invoice.TaxPercent.Value) : 0, Discount = invoice.StripeDiscount?.StripeCoupon?.PercentOff != null ? invoice.StripeDiscount.StripeCoupon.PercentOff.Value : 0, DiscountType = "PERCENT" }).ToList() }; if (invoice.StripeDiscount?.StripeCoupon?.PercentOff != null) { order.OrderRows.Add(new { Description = $"Promo code {invoice.StripeDiscount.StripeCoupon.Id} applied: {invoice.StripeDiscount.StripeCoupon.Name}", AccountNumber = "0", ArticleNumber = "", Price = 0m, OrderedQuantity = 0, DeliveredQuantity = 0, VAT = 0, Discount = 0m, DiscountType = "" }); } order.OrderRows.Add(new { Description = $"Order date {invoice.Date.Value.ToUniversalTime():yyyy-MM-dd HH:mm:ss} UTC", AccountNumber = "0", ArticleNumber = "", Price = 0m, OrderedQuantity = 0, DeliveredQuantity = 0, VAT = 0, Discount = 0m, DiscountType = "" });
public static string GenerateStripeHTMLInvoice(StripeInvoice stripeInvoice) { var invoiceHtml = new StringBuilder(); #region Build out Invoice HTML //Generate dollar amounts var invoiceSubtotal = Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDollars(stripeInvoice.Subtotal.ToString()); var invoiceTotal = Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDollars(stripeInvoice.Total.ToString()); var invoiceAmountDue = Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDollars(stripeInvoice.AmountDue.ToString()); invoiceHtml.Append("<table style='width:90%; border-spacing:0px; font-size:13px;'>"); invoiceHtml.Append("<tr>"); invoiceHtml.Append("<td style='padding:2px; width:80%; border-bottom: solid 2px black; border-top: solid 3px black;'>"); invoiceHtml.Append("<strong>Description</strong>"); invoiceHtml.Append("</td>"); ///invoiceHtml.Append("<td style='border-bottom: solid 1px black; border-top: solid 3px black;'>"); ///invoiceHtml.Append("Quantity"); //invoiceHtml.Append("</td>"); invoiceHtml.Append("<td style='padding:2px; width:20%; border-bottom: solid 2px black; border-top: solid 3px black; text-align:center'>"); invoiceHtml.Append("<strong>Price</strong>"); invoiceHtml.Append("</td>"); invoiceHtml.Append("</tr>"); var last = stripeInvoice.StripeInvoiceLineItems.Data.Last(); foreach (var lineItem in stripeInvoice.StripeInvoiceLineItems.Data) { //Generate dollar amount var lineItemAmount = Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDollars(lineItem.Amount.ToString()); //Generate description string descriptionCopy = lineItem.Description; if (String.IsNullOrEmpty(descriptionCopy)) { if (lineItem.Plan != null) { descriptionCopy = lineItem.Plan.Name + " Plan"; } else { descriptionCopy = "No description available"; } } invoiceHtml.Append("<tr>"); if (!lineItem.Equals(last)) { invoiceHtml.Append("<td style='padding:10px; color:grey; border-bottom: dashed 1px #D1D1D1;'>"); } else { invoiceHtml.Append("<td style='padding:10px; color:grey; border-bottom: solid 2px black;'>"); } invoiceHtml.Append(descriptionCopy); invoiceHtml.Append("</td>"); //invoiceHtml.Append("<td style='color:grey'>"); ; //invoiceHtml.Append(lineItem.Quantity); //invoiceHtml.Append("</td>"); if (!lineItem.Equals(last)) { invoiceHtml.Append("<td style='padding:10px; color:grey; border-bottom: dashed 1px #D1D1D1; text-align:center'>"); } else { invoiceHtml.Append("<td style='padding:10px; color:grey; border-bottom: solid 2px black; text-align:center'>"); } invoiceHtml.Append(lineItemAmount); invoiceHtml.Append("</td>"); invoiceHtml.Append("</tr>"); } //-------------------- invoiceHtml.Append("<tr>"); //invoiceHtml.Append("<td>"); //invoiceHtml.Append(""); //invoiceHtml.Append("</td>"); invoiceHtml.Append("<td style='text-align:right'>"); invoiceHtml.Append("Subtotal:"); invoiceHtml.Append("</td>"); invoiceHtml.Append("<td style='padding:6px; text-align:center'>"); invoiceHtml.Append(invoiceSubtotal); invoiceHtml.Append("</td>"); invoiceHtml.Append("</tr>"); //-------------------- invoiceHtml.Append("<tr>"); //invoiceHtml.Append("<td>"); //invoiceHtml.Append(""); //invoiceHtml.Append("</td>"); invoiceHtml.Append("<td style='text-align:right'>"); invoiceHtml.Append("Total:"); invoiceHtml.Append("</td>"); invoiceHtml.Append("<td style='padding:6px; text-align:center'>"); invoiceHtml.Append(invoiceTotal); invoiceHtml.Append("</td>"); invoiceHtml.Append("</tr>"); //-------------------- invoiceHtml.Append("<tr>"); //invoiceHtml.Append("<td>"); //invoiceHtml.Append(""); //invoiceHtml.Append("</td>"); invoiceHtml.Append("<td style='text-align:right'>"); invoiceHtml.Append("<strong>Amount Due:</strong>"); invoiceHtml.Append("</td>"); invoiceHtml.Append("<td style='padding:8px; text-align:center; font-size:15px'><strong>"); invoiceHtml.Append(invoiceAmountDue); invoiceHtml.Append("</strong></td>"); invoiceHtml.Append("</tr>"); //------------------ invoiceHtml.Append("<table>"); #endregion return(invoiceHtml.ToString()); }
public void TestTransferPaid() { var transfer = new StripeTransfer { Id = "tx9876", Amount = 1443 }; var e = new StripeEvent { LiveMode = true, Type = "transfer.paid", Created = DateTime.Now.AddDays(-1), Data = new StripeEventData { Object = JObject.FromObject(transfer) } }; var paymentmetadata = new Dictionary <string, object> { { "crossroads_transaction_type", "payment" } }; var donationmetadata = new Dictionary <string, object> { { "crossroads_transaction_type", "donation" } }; var dateCreated = DateTime.Now; var charges = new List <StripeCharge> { new StripeCharge { Id = "ch111", Amount = 111, Fee = 1, Type = "charge", Created = dateCreated.AddDays(1), Metadata = paymentmetadata }, new StripeCharge { Id = "ch222", Amount = 222, Fee = 2, Type = "charge", Created = dateCreated.AddDays(2), Metadata = paymentmetadata }, new StripeCharge { Id = "ch333", Amount = 333, Fee = 3, Type = "charge", Created = dateCreated.AddDays(3), Metadata = donationmetadata }, new StripeCharge { Id = "ch777", Amount = 777, Fee = 7, Type = "charge", Created = dateCreated.AddDays(4), Metadata = donationmetadata }, new StripeCharge { Id = "ch888", Amount = 888, Fee = 8, Type = "charge", Created = dateCreated.AddDays(5), Metadata = donationmetadata }, new StripeCharge { Id = "re999", Amount = 999, Fee = 9, Type = "payment_refund", Created = dateCreated.AddDays(8), Metadata = donationmetadata }, new StripeCharge { Id = "ch444", Amount = 444, Fee = 4, Type = "charge", Created = dateCreated.AddDays(6), Metadata = donationmetadata }, new StripeCharge { Id = "ch555", Amount = 555, Fee = 5, Type = "refund", Created = dateCreated.AddDays(7), Metadata = donationmetadata } }; _paymentService.Setup(mocked => mocked.GetPaymentByTransactionCode("ch111")).Returns(new PaymentDTO { PaymentId = 1111, BatchId = null, Status = DonationStatus.Pending }); _paymentService.Setup(mocked => mocked.GetPaymentByTransactionCode("ch222")).Returns(new PaymentDTO { PaymentId = 2222, BatchId = null, Status = DonationStatus.Pending }); _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch333")).Returns(new DonationDTO { Id = "3333", BatchId = null, Status = DonationStatus.Pending }); _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch444")).Throws(new Exception("Not gonna do it, wouldn't be prudent.")); _paymentProcessorService.Setup(mocked => mocked.GetChargeRefund("ch555")).Returns(new StripeRefund { Data = new List <StripeRefundData> { new StripeRefundData() { Id = "ch555", Amount = "987", Charge = new StripeCharge { Id = "re_123456" } } } }); _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch555")).Returns(new DonationDTO { Id = "5555", BatchId = 1984 }); _donationService.Setup(mocked => mocked.GetDonationBatch(1984)).Returns(new DonationBatchDTO { Id = 5150, ProcessorTransferId = "OU812" }); var stripeRefundData = new StripeRefundData { Id = "re999", Amount = "999", ChargeId = "ch999", Charge = new StripeCharge { Id = "ch999" }, BalanceTransaction = new StripeBalanceTransaction { Created = DateTime.Now } }; var refund = new StripeRefund { Data = new List <StripeRefundData> { stripeRefundData } }; _paymentProcessorService.Setup(mocked => mocked.GetRefund("re999")).Returns(stripeRefundData); _donationService.Setup( mocked => mocked.CreateDonationForBankAccountErrorRefund(It.Is <StripeRefund>(r => r.Data != null && r.Data.Count == 1 && r.Data[0].Equals(stripeRefundData)))) .Returns(876); var firstRefund = true; _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("re999")).Returns(() => { if (firstRefund) { firstRefund = false; throw (new DonationNotFoundException("re999")); } return(new DonationDTO() { Id = "9999", BatchId = null, Amount = 999 }); }); _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch777")).Returns(new DonationDTO { Id = "7777", BatchId = 2112 }); _donationService.Setup(mocked => mocked.GetDonationBatch(2112)).Returns(new DonationBatchDTO { Id = 2112, ProcessorTransferId = null }); var first = true; _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch888")).Returns(() => { if (first) { first = false; throw (new DonationNotFoundException("ch888")); } return(new DonationDTO() { Id = "8888", BatchId = null, Amount = 888, Status = DonationStatus.Declined }); }); _donationService.Setup(mocked => mocked.UpdateDonationStatus(refund.Data[0].ChargeId, 777, refund.Data[0].BalanceTransaction.Created, null)).Returns(9999); var invoice = new StripeInvoice { Amount = 100, Id = "in_888" }; _paymentProcessorService.Setup(mocked => mocked.GetCharge("ch888")).Returns(new StripeCharge { Source = new StripeSource { Object = "bank_account" }, Invoice = invoice }); _donationService.Setup(mocked => mocked.CreateDonationForInvoice(invoice)).Returns(88); _paymentProcessorService.Setup(mocked => mocked.GetChargesForTransfer("tx9876")).Returns(charges); _donationService.Setup(mocked => mocked.GetDepositByProcessorTransferId("tx9876")).Returns((DepositDTO)null); _donationService.Setup( mocked => mocked.CreatePaymentProcessorEventError(e, It.IsAny <StripeEventResponseDTO>())); _paymentService.Setup(mocked => mocked.UpdatePaymentStatus(1111, 999, e.Created, null)).Returns(1111); _paymentService.Setup(mocked => mocked.UpdatePaymentStatus(2222, 999, e.Created, null)).Returns(2222); _donationService.Setup(mocked => mocked.UpdateDonationStatus(3333, 999, e.Created, null)).Returns(3333); _donationService.Setup(mocked => mocked.UpdateDonationStatus(7777, 999, e.Created, null)).Returns(7777); _donationService.Setup(mocked => mocked.UpdateDonationStatus(9999, 999, e.Created, null)).Returns(9999); _donationService.Setup(mocked => mocked.CreateDeposit(It.IsAny <DepositDTO>())).Returns( (DepositDTO o) => { o.Id = 98765; return(o); }); _donationService.Setup(mocked => mocked.CreateDonationBatch(It.IsAny <DonationBatchDTO>())).Returns((DonationBatchDTO o) => o); _paymentService.Setup(mocked => mocked.CreatePaymentBatch(It.IsAny <DonationBatchDTO>())).Returns((DonationBatchDTO o) => o); var result = _fixture.ProcessStripeEvent(e); Assert.IsNotNull(result); Assert.IsInstanceOf <TransferPaidResponseDTO>(result); var tp = (TransferPaidResponseDTO)result; Assert.AreEqual(8, tp.TotalTransactionCount); Assert.AreEqual(6, tp.SuccessfulUpdates.Count); Assert.AreEqual(charges.Take(6).Select(charge => charge.Id), tp.SuccessfulUpdates); Assert.AreEqual(2, tp.FailedUpdates.Count); Assert.AreEqual("ch555", tp.FailedUpdates[1].Key); Assert.AreEqual("ch444", tp.FailedUpdates[0].Key); Assert.AreEqual("Not gonna do it, wouldn't be prudent.", tp.FailedUpdates[0].Value); Assert.IsNotNull(tp.Batch); Assert.IsNotNull(tp.Deposit); Assert.IsNotNull(tp.Exception); _donationService.Verify(mocked => mocked.CreateDonationBatch(It.Is <DonationBatchDTO>(o => o.BatchName.Matches(@"MP\d{12}D") && o.SetupDateTime == o.FinalizedDateTime && o.BatchEntryType == 555 && o.ItemCount == 4 && o.BatchTotalAmount == ((333 + 777 + 888 + 999) / Constants.StripeDecimalConversionValue) && o.Donations != null && o.Donations.Count == 4 && o.DepositId == 98765 && o.ProcessorTransferId.Equals("tx9876") ))); _donationService.Verify(mocked => mocked.CreateDeposit(It.Is <DepositDTO>(o => o.DepositName.Matches(@"MP\d{12}") && !o.Exported && o.AccountNumber.Equals(" ") && o.BatchCount == 2 && o.DepositDateTime != null && o.DepositTotalAmount == ((transfer.Amount + 30) / Constants.StripeDecimalConversionValue) && o.ProcessorFeeTotal == (30 / Constants.StripeDecimalConversionValue) && o.DepositAmount == (transfer.Amount / Constants.StripeDecimalConversionValue) && o.Notes == null && o.ProcessorTransferId.Equals("tx9876") ))); _paymentProcessorService.VerifyAll(); _donationService.VerifyAll(); }
public IHttpActionResult GetInvoice(string id) { if (!Settings.Current.EnableBilling) { return(NotFound()); } if (!id.StartsWith("in_")) { id = "in_" + id; } StripeInvoice stripeInvoice = null; try { var invoiceService = new StripeInvoiceService(); stripeInvoice = invoiceService.Get(id); } catch (Exception ex) { Log.Error().Exception(ex).Message("An error occurred while getting the invoice: " + id).Write(); } if (stripeInvoice == null || String.IsNullOrEmpty(stripeInvoice.CustomerId)) { return(NotFound()); } var organization = _repository.GetByStripeCustomerId(stripeInvoice.CustomerId); if (organization == null || !IsInOrganization(organization.Id)) { return(NotFound()); } var invoice = new Invoice { Id = stripeInvoice.Id.Substring(3), OrganizationId = organization.Id, OrganizationName = organization.Name, Date = stripeInvoice.Date.GetValueOrDefault(), Paid = stripeInvoice.Paid, Total = stripeInvoice.Total / 100.0 }; foreach (var line in stripeInvoice.StripeInvoiceLineItems.Data) { var item = new InvoiceLineItem { Amount = line.Amount / 100.0 }; if (line.Plan != null) { item.Description = String.Format("Exceptionless - {0} Plan ({1}/{2})", line.Plan.Name, (line.Plan.Amount / 100.0).ToString("c"), line.Plan.Interval); } else { item.Description = line.Description; } if (stripeInvoice.PeriodStart == stripeInvoice.PeriodEnd) { item.Date = stripeInvoice.PeriodStart.ToShortDateString(); } else { item.Date = String.Format("{0} - {1}", stripeInvoice.PeriodStart.ToShortDateString(), stripeInvoice.PeriodEnd.ToShortDateString()); } invoice.Items.Add(item); } var coupon = stripeInvoice.StripeDiscount != null ? stripeInvoice.StripeDiscount.StripeCoupon : null; if (coupon != null) { double discountAmount = coupon.AmountOff ?? stripeInvoice.Subtotal * (coupon.PercentOff.GetValueOrDefault() / 100.0); string description = String.Format("{0} {1}", coupon.Id, coupon.PercentOff.HasValue ? String.Format("({0}% off)", coupon.PercentOff.Value) : String.Format("({0} off)", (coupon.AmountOff.GetValueOrDefault() / 100.0).ToString("C"))); invoice.Items.Add(new InvoiceLineItem { Description = description, Amount = discountAmount }); } return(Ok(invoice)); }
public void InvoicePaymentSucceeded(DateTime?eventTimestamp, StripeInvoice invoice) { _logger.Debug($"Processing invoice.payment_succeeded event for subscription id {invoice.Subscription}"); _donationService.CreateDonationForInvoice(invoice); }
public async Task <IHttpActionResult> GetInvoiceAsync(string id) { if (!Settings.Current.EnableBilling) { return(NotFound()); } if (!id.StartsWith("in_")) { id = "in_" + id; } StripeInvoice stripeInvoice = null; try { var invoiceService = new StripeInvoiceService(Settings.Current.StripeApiKey); stripeInvoice = await invoiceService.GetAsync(id); } catch (Exception ex) { _logger.Error().Exception(ex).Message("An error occurred while getting the invoice: " + id).Identity(CurrentUser.EmailAddress).Property("User", CurrentUser).SetActionContext(ActionContext).Write(); } if (String.IsNullOrEmpty(stripeInvoice?.CustomerId)) { return(NotFound()); } var organization = await _repository.GetByStripeCustomerIdAsync(stripeInvoice.CustomerId); if (organization == null || !CanAccessOrganization(organization.Id)) { return(NotFound()); } var invoice = new Invoice { Id = stripeInvoice.Id.Substring(3), OrganizationId = organization.Id, OrganizationName = organization.Name, Date = stripeInvoice.Date.GetValueOrDefault(), Paid = stripeInvoice.Paid, Total = stripeInvoice.Total / 100.0 }; foreach (var line in stripeInvoice.StripeInvoiceLineItems.Data) { var item = new InvoiceLineItem { Amount = line.Amount / 100.0 }; if (line.Plan != null) { item.Description = $"Exceptionless - {line.Plan.Name} Plan ({(line.Plan.Amount / 100.0):c}/{line.Plan.Interval})"; } else { item.Description = line.Description; } item.Date = $"{(line.StripePeriod.Start ?? stripeInvoice.PeriodStart).ToShortDateString()} - {(line.StripePeriod.End ?? stripeInvoice.PeriodEnd).ToShortDateString()}"; invoice.Items.Add(item); } var coupon = stripeInvoice.StripeDiscount?.StripeCoupon; if (coupon != null) { if (coupon.AmountOff.HasValue) { double discountAmount = coupon.AmountOff.GetValueOrDefault() / 100.0; string description = $"{coupon.Id} ({discountAmount.ToString("C")} off)"; invoice.Items.Add(new InvoiceLineItem { Description = description, Amount = discountAmount }); } else { double discountAmount = (stripeInvoice.Subtotal / 100.0) * (coupon.PercentOff.GetValueOrDefault() / 100.0); string description = $"{coupon.Id} ({coupon.PercentOff.GetValueOrDefault()}% off)"; invoice.Items.Add(new InvoiceLineItem { Description = description, Amount = discountAmount }); } } return(Ok(invoice)); }
public async Task <ActionResult <Invoice> > GetInvoiceAsync(string id) { if (!_stripeOptions.Value.EnableBilling) { return(NotFound()); } if (!id.StartsWith("in_")) { id = "in_" + id; } StripeInvoice stripeInvoice = null; try { var invoiceService = new StripeInvoiceService(_stripeOptions.Value.StripeApiKey); stripeInvoice = await invoiceService.GetAsync(id); } catch (Exception ex) { using (_logger.BeginScope(new ExceptionlessState().Tag("Invoice").Identity(CurrentUser.EmailAddress).Property("User", CurrentUser).SetHttpContext(HttpContext))) _logger.LogError(ex, "An error occurred while getting the invoice: {InvoiceId}", id); } if (String.IsNullOrEmpty(stripeInvoice?.CustomerId)) { return(NotFound()); } var organization = await _repository.GetByStripeCustomerIdAsync(stripeInvoice.CustomerId); if (organization == null || !CanAccessOrganization(organization.Id)) { return(NotFound()); } var invoice = new Invoice { Id = stripeInvoice.Id.Substring(3), OrganizationId = organization.Id, OrganizationName = organization.Name, Date = stripeInvoice.Date.GetValueOrDefault(), Paid = stripeInvoice.Paid, Total = stripeInvoice.Total / 100.0m }; foreach (var line in stripeInvoice.StripeInvoiceLineItems.Data) { var item = new InvoiceLineItem { Amount = line.Amount / 100.0m }; if (line.Plan != null) { string planName = line.Plan.Nickname ?? _billingManager.GetBillingPlan(line.Plan.Id)?.Name; item.Description = $"Exceptionless - {planName} Plan ({(line.Plan.Amount / 100.0):c}/{line.Plan.Interval})"; } else { item.Description = line.Description; } item.Date = $"{(line.StripePeriod.Start ?? stripeInvoice.PeriodStart).ToShortDateString()} - {(line.StripePeriod.End ?? stripeInvoice.PeriodEnd).ToShortDateString()}"; invoice.Items.Add(item); } var coupon = stripeInvoice.StripeDiscount?.StripeCoupon; if (coupon != null) { if (coupon.AmountOff.HasValue) { decimal discountAmount = coupon.AmountOff.GetValueOrDefault() / 100.0m; string description = $"{coupon.Id} ({discountAmount.ToString("C")} off)"; invoice.Items.Add(new InvoiceLineItem { Description = description, Amount = discountAmount }); } else { decimal discountAmount = (stripeInvoice.Subtotal / 100.0m) * (coupon.PercentOff.GetValueOrDefault() / 100.0m); string description = $"{coupon.Id} ({coupon.PercentOff.GetValueOrDefault()}% off)"; invoice.Items.Add(new InvoiceLineItem { Description = description, Amount = discountAmount }); } } return(Ok(invoice)); }