Stripe checkout session seems to be adding a new customer even if they already exist - asp.net-mvc

I have used the following code in .Net Core to create a Stripe checkout session, it seems to work fine for one customer buying one subscription.
I have a scenario though where a customer would be paying for subscriptions for other people, so may well use the checkout portal multiple times. When this happens Stripe will add the customer email address as a new customer each time, so rather than have one customer who can log in to the customer portal and see all subscriptions they have paid for, they can only log in to the 1st occurance of the customer with that email address.
Is there a way to use the checkout session so that if a customer email already exists it is not added again as a new customer?
var options = new SessionCreateOptions
{
SuccessUrl = successUrl+"&email=" + email,
CancelUrl = cancelUrl,
Mode = "subscription",
AllowPromotionCodes = true,
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions
{
Price = priceId,
Quantity = 1,
},
}
};
var service = new SessionService(this.client);
try
{
var session = await service.CreateAsync(options);
Response.Headers.Add("Location", session.Url);
return new StatusCodeResult(303);
}

Checkout does not automatically de-duplicate customers for you. How your customer records are managed is up to you.
If you want to have a Checkout session associated with an existing customer, you need to provide that customer id when you create the session.
https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-customer

Related

How to update a customer belonging to a connected account in Stripe?

I am building a Rails 5.2 app. In this app I have integrated Stripe Connect.
I am currently building a feature where my customers can enroll their customers in subscriptions. The front end is Angular 11.
After the user have entered their payment data I need to attach this payment method to the created customer but I am not able too.
When I use this code it says that the customer is not found (even though I am using the correct ID). I guess it's because I created the Customer to a connected account.
The problem is I have no way of setting the stripe_account ID to the code.
customer = Stripe::Customer.update(
params[:customer],
invoice_settings: {
default_payment_method: params[:payment_method_id]
}
)
If I try this it says the stripe_account parameter is not valid.
"(Status 400) (Request req_sByQlJONJi0oHt) Received unknown parameter: stripe_account"
customer = Stripe::Customer.update(
params[:customer],
invoice_settings: {
default_payment_method: params[:payment_method_id]
}, stripe_account: params[:account]
)
So basically, how can I update a customer belonging to a Connected Account?
The update definition looks like this:
def update(id, params = {}, opts = {})
So in your case, the stripe_account is being passed in the params argument instead of the opts argument. Wrap the params in brackets ({}).
customer = Stripe::Customer.update(params[:customer],
{invoice_settings: {default_payment_method: params[:payment_method_id]}},
{stripe_account: params[:account]})

Using the Browse option

I'm writing an application for Twinfield. I login to an account with 4 administrations in it. I would like to retrieve all information belonging to not payed invoices.
With the search opttion I get al open invoices for a certain office.
string[][] finderOptions = new string[2][];
switch (office)
{
case 0:
finderOptions[0] = new string[] { "office", "xxxx01-01" };
break;
case 1:
finderOptions[0] = new string[] { "office", "xxxx03-01" };
break;
}
finderOptions[1] = new string[] { "dim1", "1300" };
TwinfieldFinder.MessageOfErrorCodes[] errorCodes = xmlFinder.Search(hdrXml, "IVT", "*", 0, 1, 0, finderOptions, out findResult);
This works. But it retuns the invoicenumber and I also need the transaction number. Therefore I perform a Browse to find the traansaction number.
Maybe there is another way to find the complete transaction using the invoicenumber iso the transactionnumber?
The Browse call looks like this:
TwinfieldProcessXml.ProcessXmlSoapClient xmlClient = new
TwinfieldProcessXml.ProcessXmlSoapClient("ProcessXmlSoap", cluster + "/webservices/processxml.asmx?wsdl");
TwinfieldProcessXml.Header hdrXml2 = new TwinfieldProcessXml.Header();
hdrXml2.CompanyCode = finderOptions[0][1];
hdrXml2.AnyAttr = hdr.AnyAttr;
hdrXml2.SessionID = hdr.SessionID;
It doens't matter if I user the CompanyCode in the headers It alwasy return the informatie belonging to the first office: xxxx01-01.
When using the browse codes in Twinfield, make sure to do the SoapCall to select the right company, as documented there:
https://c3.twinfield.com/webservices/documentation/#/FAQ
Otherwise you will get the data back for the default company:
Q. When using the Browse Data functionality, in the response I get data from a different company. What is wrong?
A. In the browse data request there is no option to set the current company. Before sending the request, make sure the correct company is set by using the SelectCompany function. See also Web Services Authentication.
To get the open invoices the best way is to use the browse codes. Select code 100 and add a filter on the column matchstatus, here is an example:
https://gist.github.com/alexjeen/d4ef3295820dc98c7f0171e47294dbfe

UCWA MyOnlineMeeting attendees requirements? External attendees

I'm currently trying to develop an application that creates Skype meetings.
I'm leveraging the C# UCWA SDK and developing against Skype for Business online.
Meeting creation works fine if I only include people from the tenant in attendees, as soon as I include people not from the tenant in the meeting I get this error message:
{"code":"BadRequest","subcode":"ParameterValidationFailure","message":"Please check what you entered and try again.","debugInfo":{"diagnosticsCode":"2"}}
Here is my code sample
var meeting = new MyOnlineMeeting()
{
AccessLevel = AccessLevel.Everyone,
Attendees = new string[] { $"sip:{Settings.SkypeUserEmail}" }, //Adding anybody else than the service account makes it fail for now
Subject = series.Subject,
ExpirationTime = DateTime.Now.AddDays(3),
AutomaticLeaderAssignment = AutomaticLeaderAssignment.SameEnterprise,
Leaders = series.Organizers.Select(x => $"sip:{x.EmailAddress}").ToArray(),
LobbyBypassForPhoneUsers = LobbyBypassForPhoneUsers.Enabled,
PhoneUserAdmission = PhoneUserAdmission.Disabled
};
var dialIn = await client.OnlineMeetings.GetPhoneDialInInformation();
var meetings = await client.OnlineMeetings.GetMyOnlineMeetings();
var result = await meetings.Create(meeting);
Adding external users to the organizers properties works fine though.
My question is: how can I add external attendees to the meeting I'm creating? Is there anything specific around attendees?
After a few exchanges on the Microsoft Skype for Business MVP's private distribution list, it appears that attendees have to be part of the organization or otherwise the call will fail.
Submitted a Pull Request to update the latest version of the documentation

Add event to Google Calendar via API v3 using OAuth 2.0 service account?

I am getting a forbidden (403) when trying to add an event to my calendar (API v3 & OAuth2), as follows:
var service = CalendarService();
CalendarListResource.ListRequest request = service.CalendarList.List();
IList<CalendarListEntry> calendarList = request.Execute().Items;
foreach (CalendarListEntry entry in calendarList)
{
Console.WriteLine(
"Summary:{0}\nLocation:{1}\nTimeZone:{2}",
entry.Summary, entry.Location, entry.TimeZone
);
}
var startDate = DateTime.Now.AddDays(1);
var endDate = startDate.AddDays(1);
var eventBody = new Google.Apis.Calendar.v3.Data.Event
{
Description = "Test 4 description",
Summary = "Test 4 summary",
Start = new EventDateTime
{
DateTime = startDate
},
End = new EventDateTime
{
DateTime = endDate
}
};
var insertRequest = service.Events.Insert(calendarId: calendarId_emailAddress, body: eventBody); //InsertRequest
insertRequest.Execute();
I have set up oauth2 service account, granted scopes, and shared the calendar. However, something I noticed on the calendar share part is that I am unable (no option available) to share for read/write. Only option is "freeBusyRead".
EDIT:
Let me add that calendarId_emailAddress = "my_email_address". I was able to add an event. No errors. And then iterate and find it. But it does not show up in my calendar. I seem to not be making the connection between the calendarId and the "actual calendar" in my user panel.
So how to do this. Any help appreciated. Thanks.
Your Google Apps domain probably limits the level of sharing to external accounts to free/busy only. Service accounts, even when created by a domain user, are always considered external to the domain.
The proper way to do this would be to authorize the service account to act on behalf of your domain users and then, authenticating as the service account and acting on behalf of the user, add the event to the user calendar. Domain-wide delegation of authority is discussed in the Drive API docs but can easily be applied to Calendar API also.
The trouble was in the authentication of the service. Scenarios...
1) ServiceAccountCredential with no user set, and calendarId = "primary" yields an entry into calendar. Which calendar? Who knows. I can't see the entry in my interface of that user, but I do find the entry in the Events.List("primary")
2) ServiceAccountCredential with user set to email of target calendar, and calendarId = "primary" yields an entry into calendar, which I do see in my interface of that user, and also find the entry in the Events.List("primary")
Scenario #1 really baffles me. No idea how to see that event in the calendar.
Any insight into this would be helpful. Thanks!

MVC Paypal Adaptive Payments

Here's what I'm designing a site where multiple shops my be able to offer their producs to sale. Every seller will have a virtual store on my site. I'm using paypal for purchase operations. I've considered to allow customers to use credit card without having a paypal account, and I'm trying to use Adaptive payments flow to allow "buy as guest" flow. I'm trying to use paypal default flow (not the rest api) since I don't want to be worried about handling credit card data and having to design my site as PCI compliant.
So with this escenario here's what I'm using:
From this site https://developer.paypal.com/webapps/developer/docs/classic/adaptive-payments/integration-guide/APIntro/ I'm trying to implement the payment flow specified on the section
Setting Up Web Pages to Invoke the Embedded Payment Flow Using a Lightbox
Since this payment flow requires a pay key to be generated, I'm using the code found on this link:
https://github.com/paypal/rest-api-sdk-dotnet/tree/master/Samples/RestApiSample
-So on my MVC I have a page that generates the order, and it calls a Helper methods to get the paykey. Here's the most relevant one:
public static string GetPayKey(DummyPurchase purchase)
{
ReceiverList receiverList = new ReceiverList();
receiverList.receiver = new List<Receiver>();
//(Required) Amount to be paid to the receiver
string[] amt = new string[1] { purchase.TotalPrice.ToString() };
// Receiver's email address. This address can be unregistered with paypal.com.
// If so, a receiver cannot claim the payment until a PayPal account is linked
// to the email address. The PayRequest must pass either an email address or a phone number.
// Maximum length: 127 characters
string[] receiverEmail = new string[1] { purchase.StoreId.ToString() };
string cancelUrl = ConfigurationHelper<string>.GetKeyValue(Constants.PAYPAL_CANCEL_URL);
string returnUrl = ConfigurationHelper<string>.GetKeyValue(Constants.PAYPAL_RETURN_URL);
string currency = ConfigurationHelper<string>.GetKeyValue(Constants.PAYPAL_CURRENCY_CODE);
//Generate Receivers list
for (int i = 0; i < amt.Length; i++)
{
Receiver rec = new Receiver(Convert.ToDecimal(amt[i]));
if (receiverEmail[i] != string.Empty)
{
rec.email = receiverEmail[i];
}
receiverList.receiver.Add(rec);
}
PayRequest request = new PayRequest(new RequestEnvelope("en_US"), "PAY",
cancelUrl, currency,
receiverList, returnUrl);
//call the service
AdaptivePaymentsService service = null;
PayResponse response = null;
try
{
// (https://github.com/paypal/sdk-core-dotnet/wiki/SDK-Configuration-Parameters)
Dictionary<string, string> configurationMap = GetAcctAndConfig();
// Creating service wrapper object to make an API call and loading
// configuration map for your credentials and endpoint
service = new AdaptivePaymentsService(configurationMap);
response = service.Pay(request);
}
catch (Exception ex)
{
Elmah.ErrorLog.GetDefault(null).Log(new Elmah.Error(ex));
return "";
}
Dictionary<string, string> responseValues = new Dictionary<string, string>();
string redirectUrl = null;
if (!(response.responseEnvelope.ack == AckCode.FAILURE) &&
!(response.responseEnvelope.ack == AckCode.FAILUREWITHWARNING))
{
return response.payKey;
}
return "";
}
-After I get this key, I get the html from another view that has the form as the API guide specifies, having the paykey string as the model for this view.
#model string
<h2>ConfirmCheckout</h2>
<script src="https://www.paypalobjects.com/js/external/dg.js">
</script>
<form action="https://www.paypal.com/webapps/adaptivepayment/flow/pay"
target="PPDGFrame">
<input id="type" type="hidden" name="expType" value="light">
<input id="paykey" type="hidden" name="paykey" value="#Model">
<input type="submit" id="submitBtn" value="Pay with PayPal">
</form>
-After the view is rendered, I call the javascript code to start the flow:
var dgFlow = new PAYPAL.apps.DGFlow({ trigger: 'submitBtn' });
-The flow works perfectly and I get a valid pay key rendered on this form. But when I click this button (submit button on form with paykey) I get 2 different errors. This is the most frequent one:
This transaction has already been approved. Please visit your PayPal Account Overview to see the details.
-But sometimes I get a "Your payment session has expired" error.
I have 2 questions:
Does someone know how to fix those errors?
I'm using clasic API since guest payment flow for adaptive payments require a PayKey to start the flow (in order to avoid security and PCI complience matters). I did not found a method on the Paypal REST API that could get the same PayKey. Is there any method to get those keys?
Thanks a lot
Well this is really embarrasing... but the real issue was the url on the post action of the form. I had
<form action="https://www.paypal.com/webapps/adaptivepayment/flow/pay" target="PPDGFrame">
Which is production link. And I'm not going live yet, I'm using paypal api credentials for a sanbox account, so the real form action should be:
<form action="https://www.sandbox.paypal.com/webapps/adaptivepayment/flow/pay" target="PPDGFrame">
Duh!. Well hope that this could help another person with same kind of errors.
Thanks a lot #Andrew Angell
It sounds like you're sending a Preapproval key with your Pay request. When you do that, there's no redirect to PayPal required. That's the whole point...you've already got approval via the Preapproval Key, so when you submit a Pay request with a preapproval key included the payment will be made immediatly (as long as the preapproval profile is still valid.)
As such, when you're doing the redirect, it's telling you exactly what happened...the transaction was already processed. Again, no need for a redirect.
The whole point is to be able to trigger payments using the preapproval profile within your application at any time you need to with any redirect or further approval. Once you have that preapproval key you can trigger payments any way you want to. For example, you might want to charge somebody per use and trigger a payment when they log in to your website, but you don't want them to have to approve the payment every time. The payment could happen in the background via the Pay API with the preapproval key included but the user would not be interrupted at all.
Hope that helps.

Resources