How do you preview a proration using Stripe.NET? - c#

I'm following the example provided in the Stripe documentation on Previewing Proration using the Stripe.NET library to try to find the amount that will be charged when a customer upgrades from Plan A to Plan B.
However, when I use the code sample in the documentation, I get an error:
UpcomingInvoiceOptions options = new UpcomingInvoiceOptions()
{
CustomerId = "cus_XXXXXXXXXXXXX",
SubscriptionProrationDate = DateTime.UtcNow,
SubscriptionItems = new List<InvoiceSubscriptionItemOptions>()
{
new InvoiceSubscriptionItemOptions()
{
Id = "si_XXXXXXXXXXXXXX", // Current Sub Item
PlanId = "plan_XXXXXXXXXXXX" // New plan
}
}
};
InvoiceService service = new InvoiceService();
var result = service.Upcoming(options);
The last line throws a Stripe.StripeException: You cannot update a subscription item without a subscription.

Turns out options.SubscriptionId is a required field for this action if you don't call service.Get first.
The following code produces the correct results:
UpcomingInvoiceOptions options = new UpcomingInvoiceOptions()
{
CustomerId = "cus_XXXXXXXXXXXXX",
SubscriptionId = "sub_XXXXXXXXXXXX",
SubscriptionProrationDate = DateTime.UtcNow,
SubscriptionItems = new List<InvoiceSubscriptionItemOptions>()
{
new InvoiceSubscriptionItemOptions()
{
Id = "si_XXXXXXXXXXXXXX", // Current Sub Item
PlanId = "plan_XXXXXXXXXXXX" // New plan
}
}
};
InvoiceService service = new InvoiceService();
var result = service.Upcoming(options);

Related

Amazon EventBridge rule cannot trigger a Lambda Function

Using the .Net AWSSDK.EventBridge I created a rule:
var client = new AmazonEventBridgeClient();
string ruleName = "SomeRule";
var putRuleRequest = new Amazon.EventBridge.Model.PutRuleRequest()
{
Name = ruleName,
ScheduleExpression = "rate(10 minutes)",
State = RuleState.ENABLED
}
await client.PutRuleAsync(putRuleRequest);
var target = new Amazon.EventBridge.Model.Target();
target.Arn = "ARN_LAMBDA_FUNCTION";
target.Id = "LAMBDA_FUNCTION_NAME";
target.Input = JsonSerializer.Serialize(new { someId, someDate});
var targetList = new List<Amazon.EventBridge.Model.Target>();
targetList.Add(target);
var putTargetRequest = new Amazon.EventBridge.Model.PutTargetsRequest()
{
Rule = ruleName,
Targets = targetList
};
await client.PutTargetsAsync(putTargetRequest);
The Lambda function is already created so I put the ARN and name on the Target. The idea is that there is one function but multiple rules will call it.
The rule, schedule, and target to the function are created when I run the code but the problem is that the function can't be triggered by the rule. When I edit the rule, update it in the AWS Console without changing anything the trigger works.
What am I missing?
After searching through I found out I was missing the permission for Lambda.
//This line replaces the code above
var putRuleResponse = client.PutRuleAsync(putRuleRequest).Result;
var lp = new Amazon.Lambda.Model.AddPermissionRequest();
lp.FunctionName = FUNCTION_ARN;
lp.Action = "lambda:InvokeFunction";
lp.SourceArn = putRuleResponse.RuleArn;
lp.Principal = "events.amazonaws.com";
lp.StatementId = "SomeStatement";
var lambdaClient = new AmazonLambdaClient();
await lambdaClient.AddPermissionAsync(lp);

Is Universal Analytics C# SDK compatible with GA4?

I am using Google.Apis.AnalyticsReporting.v4 library for old google analytics views. How do I convert this code to GA4? I can't find a line about switching View Id to something else in code.
I have checked this post "How do I get view id in GA4", but my properties already exist and I don't see option to modify them after creation.
using (var svc = new AnalyticsReportingService(authInitializer.CreateInitializer()))
{
var dateRange = new DateRange
{
StartDate = analyticsParams.From.ToString("yyyy-MM-dd"),
EndDate = analyticsParams.To.ToString("yyyy-MM-dd")
};
var sessions = new Metric
{
Expression = "ga:sessions",
Alias = "Sessions"
};
var date = new Dimension { Name = "ga:date" };
var reportRequest = new ReportRequest
{
DateRanges = new List<DateRange> { dateRange },
Dimensions = new List<Dimension> { date },
Metrics = new List<Metric> { sessions },
ViewId = analyticsParams.ViewId, // <------------------------- My view id
};
var getReportsRequest = new GetReportsRequest
{
ReportRequests = new List<ReportRequest> { reportRequest }
};
var batchRequest = svc.Reports.BatchGet(getReportsRequest);
var response = batchRequest.Execute();
var reports = response.Reports.First();
return reports.Data.Rows.Select(x => new DataEntry()
{
Date = DateTime.ParseExact(x.Dimensions[0], "yyyyMMdd", CultureInfo.InvariantCulture),
Value = int.Parse(x.Metrics[0].Values[0]),
}).ToList();
}
You need to use the Google Analytics Data API V1 (currently in alpha) in order to access your GA4 properties. Here is a quick start sample for .NET which seems similar to what you are trying to do.
using Google.Analytics.Data.V1Alpha;
using System;
namespace AnalyticsSamples
{
class QuickStart
{
static void SampleRunReport(string propertyId)
{
// Using a default constructor instructs the client to use the credentials
// specified in GOOGLE_APPLICATION_CREDENTIALS environment variable.
AlphaAnalyticsDataClient client = AlphaAnalyticsDataClient.Create();
// Initialize request argument(s)
RunReportRequest request = new RunReportRequest
{
Entity = new Entity{ PropertyId = propertyId },
Dimensions = { new Dimension{ Name="city"}, },
Metrics = { new Metric{ Name="activeUsers"}, },
DateRanges = { new DateRange{ StartDate="2020-03-31", EndDate="today"}, },
};
// Make the request
RunReportResponse response = client.RunReport(request);
Console.WriteLine("Report result:");
foreach( Row row in response.Rows )
{
Console.WriteLine("{0}, {1}", row.DimensionValues[0].Value, row.MetricValues[0].Value);
}
}
static int Main(string[] args)
{
if (args.Length == 0 || args.Length > 2)
{
Console.WriteLine("Arguments: <GA4 property ID>");
Console.WriteLine("A GA4 property id parameter is required to make a query to the Google Analytics Data API.");
return 1;
}
string propertyId = args[0];
SampleRunReport(propertyId);
return 0;
}
}
}
Currently there aren't API available for GA4 Property. Furthermore GA4 does not provide Views, you have to use BigQuery to get data programmatically.

Stripe exception thrown when creating new connect account with bank account

After upgrading to the latest version of Stripe.Net.
I'm trying to create a new custom connect account, which includes a bank account, with the .Net API and Stripe is throwing this exception.
This account can only be updated with an account token, because it was originally created with an account token. (Attempted to update param 'account_token' directly.)
I'm assigning the AccountToken I'm generating from Stripe.js and that seems to be generating ok. Additionally I have no issue adding an external bank to a already created connect account. I just can't seem to create a new custom account
Here is my c# code
AccountDobOptions dobOptions = new AccountDobOptions()
{
Day = yogaProfile.Birthdate.Day,
Month = yogaProfile.Birthdate.Month,
Year = yogaProfile.Birthdate.Year
};
AddressOptions addressOptions = new AddressOptions()
{
City = bankDetails.City,
Country = bankDetails.CountryCode,
State = bankDetails.CountryCode == "US" ? bankDetails.USStateCode : bankDetails.NonUSStateCode,
PostalCode = bankDetails.PostalCode,
Line1 = bankDetails.AddressLine1,
Line2 = bankDetails.AddressLine2
};
AccountLegalEntityOptions legal = new AccountLegalEntityOptions();
legal.Dob = dobOptions;
legal.Type = "individual";
legal.Address = addressOptions;
legal.FirstName = accountFullName.Split(' ')[0];
legal.LastName = accountFullName.Split(' ')[1];
//legal.SSNLast4 = bankDetails.LastFourSSN;
AccountTosAcceptanceOptions tosOptions = new AccountTosAcceptanceOptions()
{
Date = DateTime.UtcNow,
Ip = clientIpAddress != null ? clientIpAddress : GetUserIpAddress()
};
var accountOptions = new AccountCreateOptions()
{
Email = yogaProfile.ApplicationUser.Email,
Type = AccountType.Custom,
Country = bankDetails.CountryCode,
LegalEntity = legal,
TosAcceptance = tosOptions,
AccountToken = stripeToken,
//TransferScheduleInterval = "weekly",
ExternalBankAccount = new AccountBankAccountOptions()
};
var accountService = new AccountService();
Account account = accountService.Create(accountOptions);

Problem adding credit card source to an existing customer

I try to add a new credit card to an existing customer but I get this error:
Received unknown parameter: card
I figure out how to verify if a card already exists using StripeTokenService to retrieve the card fingerprint, but I'm stuck trying to add that card to the customer.
My code look like this:
var sourceService = new Stripe.StripeSourceService();
// Get customer with current payment source.
var stripeCustomer = customerService.Get(stripeCustomerWithAccount.Id, new Stripe.StripeRequestOptions { ApiKey = ConfigurationManager.AppSettings["StripeSecretKey"] });
// Set Stripe Customer Id and Stripe Token options.
var tokenService = new Stripe.StripeTokenService();
var stripeToken = tokenService.Get(tokenId, new Stripe.StripeRequestOptions { ApiKey = ConfigurationManager.AppSettings["StripeSecretKey"] });
// Check if credit card already exists.
if (!CreditCardExists(stripeCustomer, stripeToken))
{
// Create new credit card.
var sourceOptions = new StripeNet.StripeSourceCreateOptions()
{
Customer = stripeCustomer.Id,
Card = new StripeNet.StripeCreditCardOptions
{
TokenId = stripeToken.StripeCard.Id
}
};
var source = sourceService.Create(sourceOptions, new Stripe.StripeRequestOptions { ApiKey = ConfigurationManager.AppSettings["StripeSecretKey"] });
}
Ok, I figured out how and I will share my answer:
if (!CreditCardExists(stripeCustomer, stripeToken))
{
var creditCardService = new StripeNet.StripeCardService();
var creditCardOptions = new StripeNet.StripeCardCreateOptions { SourceToken = tokenId };
var creditCard = creditCardService.Create(stripeCustomer.Id, creditCardOptions, new StripeNet.StripeRequestOptions { ApiKey = ConfigurationManager.AppSettings["StripeSecretKey"] });
// The only way I found to get customer with all sources...
stripeCustomer = customerService.Get(stripeCustomerWithAccount.Id, new StripeNet.StripeRequestOptions { ApiKey = ConfigurationManager.AppSettings["StripeSecretKey"] });
}

How can I specify Product/Service for an Invoice Line Item for QBO IPP .NET SDK V3?

I'm trying to specify a Product/Service list item for invoice line items of invoices that I am importing to QuickBooks Online (QBO) company file and I am getting an error.
Error I receive is:
Intuit.Ipp.Exception.IdsException: InternalServerError ---> Intuit.Ipp.Exception.EndpointNotFoundException: Ids service endpoint was not found.
The exception doesn't give any further breakdown as to if what I'm doing is valid or not.
My unit test method:
[TestMethod()]
public void CreateTest()
{
Entities.Invoice invoice = new Entities.Invoice();
invoice.ReferenceId = Guid.NewGuid().ToString("N").Substring(0, 10);
invoice.CreatedDate = DateTime.Now;
invoice.CustomerId = 1;
invoice.LineItems.Add(new InvoiceLine() { ItemName = "Initial Funding", Description = "Initial Funding", Amount = 5500 });
invoice.LineItems.Add(new InvoiceLine() { ItemName = "Lien Fee", Description = "Lien Fee", Amount = 100 });
IPPRestProfile restProfile = new IPPRestProfile(realmId, accessToken, accessTokenSecret, Intuit.Ipp.Core.IntuitServicesType.QBO, consumerKey, consumerSecret);
IPP.Invoices target = new IPP.Invoices(restProfile);
Intuit.Ipp.Data.Invoice actual = target.Create(invoice);
if (actual != null)
{
Console.WriteLine("QB Invoice ID: {0}", actual.Id);
Console.WriteLine("QB Sync Token: {0}", actual.SyncToken);
Console.WriteLine("================================================");
ObjectDumper.Write(actual, 4);
}
}
The method that the unit test calls:
public Intuit.Ipp.Data.Invoice Create(Entities.Invoice invoice)
{
// Check pre-conditions
if (invoice == null) { throw new ArgumentException("Invoice object is required.", "invoice"); }
var qboInvoice = new Intuit.Ipp.Data.Invoice();
BuildInvoiceEntity(qboInvoice, invoice);
return _Service.Add(qboInvoice) as Intuit.Ipp.Data.Invoice;
}
And finally the build invoice method:
private void BuildInvoiceEntity(Intuit.Ipp.Data.Invoice qboInvoice, Entities.Invoice invoice)
{
if (qboInvoice != null && invoice != null)
{
IQuickBooksHeader header = invoice as IQuickBooksHeader;
if (String.IsNullOrEmpty(header.Id))
{
qboInvoice.DocNumber = invoice.ReferenceId;
qboInvoice.TxnDate = invoice.CreatedDate;
qboInvoice.TxnDateSpecified = true;
// Customer
qboInvoice.CustomerRef = new ReferenceType()
{
type = objectNameEnumType.Customer.ToString(),
Value = invoice.CustomerId.ToString()
};
// AR Account
qboInvoice.ARAccountRef = new ReferenceType()
{
type = objectNameEnumType.Account.ToString(),
name = "Accounts Receivable"
};
}
if (invoice.LineItems.Count > 0)
{
Intuit.Ipp.Data.Line[] invoiceLineCollection = new Intuit.Ipp.Data.Line[invoice.LineItems.Count];
for (int i = 0; i < invoice.LineItems.Count; i++)
{
var line = invoice.LineItems[i];
var qboInvoiceLine = new Intuit.Ipp.Data.Line()
{
Amount = line.Amount,
AmountSpecified = true,
Description = line.Description,
DetailType = LineDetailTypeEnum.SalesItemLineDetail,
DetailTypeSpecified = true,
AnyIntuitObject = new SalesItemLineDetail()
{
ItemRef = new ReferenceType()
{
name = line.ItemName,
},
ItemElementName = ItemChoiceType.UnitPrice,
AnyIntuitObject = line.Amount
}
};
invoiceLineCollection[i] = qboInvoiceLine;
}
qboInvoice.Line = invoiceLineCollection;
}
}
}
If I remove this piece of code from my build method:
ItemRef = new ReferenceType()
{
name = line.ItemName,
},
the invoice is successfully added with the default "Services" list item for the Product/Service of the invoice line items.
The online documentation for the IPP .NET SDK V3 is vague on what to specify for ReferenceType. What is wrong about just specifying the name of the list item? If I'm wrong about how I'm trying to specify a Product/Service list item for invoice line items, what is the correct way?
After days of researching, I never did find an answer as to why I can't just use the name like I wanted to, even though it works that way when specifying an AccountRef. But I digress, here is my solution:
// Hold a collection of QBO items
private ReadOnlyCollection<Item> _Items;
// I set the collection in the constructor only once
public Invoices(Entities.IPPRestProfile restProfile)
{
if (restProfile == null)
throw new ArgumentException("IPPRestProfile object is required.", "restProfile");
OAuthRequestValidator oAuthValidator = new OAuthRequestValidator(restProfile.OAuthAccessToken, restProfile.OAuthAccessTokenSecret,
restProfile.ConsumerKey, restProfile.ConsumerSecret);
ServiceContext context = new ServiceContext(restProfile.RealmId, restProfile.DataSource, oAuthValidator);
_Service = new DataService(context);
_Items = (new QueryService<Item>(context)).ExecuteIdsQuery("SELECT * FROM Item", QueryOperationType.query);
}
Whenever I build my invoice I query the collection for the Id of the item by name:
private void BuildInvoiceEntity(Intuit.Ipp.Data.Invoice qboInvoice, Entities.Invoice invoice)
{
...
// Get the Id value of the item by name
string itemTypeId = _Items.Where(o => o.Name == line.ItemName).FirstOrDefault().Id;
// Specify the Id value in the item reference of the SalesItemLineDetail
var qboInvoiceLine = new Intuit.Ipp.Data.Line()
{
Amount = (decimal)amount,
AmountSpecified = true,
Description = line.Description,
DetailType = LineDetailTypeEnum.SalesItemLineDetail,
DetailTypeSpecified = true,
AnyIntuitObject = new SalesItemLineDetail()
{
ItemRef = new ReferenceType() { Value = itemTypeId },
AnyIntuitObject = (decimal)line.Rate,
ItemElementName = ItemChoiceType.UnitPrice
}
};
...
}
Hopefully this helps point someone in the right direction to a possible better solution.

Categories