MVC Paypal Adaptive Payments - asp.net-mvc

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.

Related

persist bot chat data in bot framework wrt to Form Flow

I want to know whether is there any feature in bot framework to get the user and bot chat completely. I have gone through the official documentation, but the way I understood it is that, only to that context we can save the chat data. If at all we have to store the whole data, we have to take care of it.
I tried using this,
StateClient sc = activity.GetStateClient();
BotData userData1 =
sc.BotState.GetConversationData(activity.ChannelId, activity.Conversation.Id);
userData1.Data = userData1.Data + activity.Text;
sc.BotState.SetConversationData(activity.ChannelId, activity.Conversation.Id, userData1);
This does persist the user data, but I am stuck with how to persist it in the form flow.
I am not sure how to persist data of bot and user wrt to Form Flow using SetConversationData. I even need the bot to persist the prompt message of the form flow. So that I ll have the complete conversation b/w user and the bot.
This tutorial may help:
Introduction To FormFlow With The Microsoft Bot Framework
The data from the user is automatically persisted during the FormFlow.
When the FormFlow is completed you can persist it like this:
public static IForm<ProfileForm> BuildForm()
{
return new FormBuilder<ProfileForm>()
.Message("Welcome to the profile bot!")
.OnCompletion(async (context, profileForm) =>
{
// Set BotUserData
context.PrivateConversationData.SetValue<bool>(
"ProfileComplete", true);
context.PrivateConversationData.SetValue<string>(
"FirstName", profileForm.FirstName);
context.PrivateConversationData.SetValue<string>(
"LastName", profileForm.LastName);
context.PrivateConversationData.SetValue<string>(
"Gender", profileForm.Gender.ToString());
// Tell the user that the form is complete
await context.PostAsync("Your profile is complete.");
})
.Build();
}

Get a user's profile using AspNet.Security.OpenIdConnect.Server

recently I've spent some time digging into AspNet.Security.OpenIdConnect.Server. I use MVC sample as a code base for my client/server apps.
The thing I need to implement now is obtaining a user's profile info from an external provider (Google) and saving the info into the server's database.
What is the right place for getting and saving a profile's info and a proper way to implement it?
Note: since ASOS (AspNet.Security.OpenIdConnect.Server) only handles the OAuth2/OpenID Connect server part, it's actually not involved in the authentication part, and thus doesn't directly deal with the external providers you configure.
To achieve what you want, the best approach is to configure the Google middleware to extract more information from the user object returned in the user profile response and to persist them in the authentication cookie so you can later retrieve them in your application code.
app.UseGoogleAuthentication(options => {
options.ClientId = "client_id";
options.ClientSecret = "client_secret";
options.Events = new OAuthEvents {
OnCreatingTicket = context => {
// Extract the "language" property from the JSON payload returned by
// the user profile endpoint and add a new "urn:language" claim.
var language = context.User.Value<string>("language");
context.Identity.AddClaim(new Claim("urn:language", language));
return Task.FromResult(0);
}
};
});
If the response doesn't include the data you need, nothing prevents you from using context.Backchannel to make another HTTP call to retrieve more data from a different Google endpoint.

whar is the oauth service name for the google apps reseller api

I tried to use the google apps reseller api with google apps script. To use oauth I need the AuthServiceName. what is the right name? "apps" does not work.
AuthServiceName is defined in your application, its not dependent on the API that you are connecting to, i would suspect that you may not have completed all the steps necessary or that your oauth call is not properly structured.
Here is an example of a call that retrieves the details of domains.
function getCustomer() {
//set up oauth for Google Reseller API
var oAuthConfig1 = UrlFetchApp.addOAuthService("doesNotMatter");
oAuthConfig1.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=https://www.googleapis.com/auth/apps.order.readonly");
oAuthConfig1.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig1.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken?oauth_callback=https://script.google.com/a/macros");
oAuthConfig1.setConsumerKey(CONSUMER_KEY);
oAuthConfig1.setConsumerSecret(CONSUMER_SECRET);
var options1 = {oAuthServiceName:"doesNotMatter", oAuthUseToken:"always",
method:"GET", headers:{"GData-Version":"3.0"}, contentType:"application/x-www-form-urlencoded"};
//set up user profiles url
var theUrl = "https://www.googleapis.com/apps/reseller/v1/customers/somedomain.com";
//urlFetch for customer list
var customerInfo = "";
try {
var response = UrlFetchApp.fetch(theUrl,options1);
customerInfo = response.getContentText();
} catch(problem) {
Logger.log(problem.message);
}
Logger.log(customerInfo);
}
This will work if
You have a reseller account (I guess i.e. I did not test on my non reseller account)
You have create a project in the API console, and enabled the Reseller API
You know your SECRET and KEY lifted form the console
I have use a read.only scope which is safe, if not you need to set up your tests in the sand box
Let me know if you need any more clarifications

How to import Google contacts in ASP.NET MVC (no third party please)

I have Used a API for importing the contacts of GMail. The code is something like this:
public static DataTable GetGmailContacts(string App_Name, string Uname, string UPassword)
{
Log.LogDebug(string.Format("SettingsController.cs-Importing Contacts for email={0}, password={1} from gmail server", Uname, UPassword));
DataTable dt = new DataTable();
DataColumn C2 = new DataColumn();
C2.DataType = Type.GetType("System.String");
C2.ColumnName = "EmailID";
try
{
dt.Columns.Add(C2);
RequestSettings rs = new RequestSettings(App_Name, Uname, UPassword);
rs.AutoPaging = true;
ContactsRequest cr = new ContactsRequest(rs);
Feed<Contact> f = cr.GetContacts();
foreach (Contact t in f.Entries)
{
foreach (EMail email in t.Emails)
{
DataRow dr1 = dt.NewRow();
dr1["EmailID"] = email.Address.ToString();
dt.Rows.Add(dr1);
}
}
Log.LogDebug(string.Format("SettingsController.cs-Imported Contacts for email={0}, password={1} from gmail server", Uname, UPassword));
return dt;
}
catch (Exception e)
{
dt = null;
Log.LogDebug(string.Format("SettingsController.cs-Imported Contacts for email={0}, password={1} from gmail server", Uname, UPassword));
return dt;
}
}
This code importing the GMail contacts but it is third party DLL. So some time Google warned me to not use it. So I want to use Direct API.
Versions 1 and 2 of the Google Contacts API have been officially deprecated as of April 20, 2012. They will continue to work as per our deprecation policy, but we encourage you to move to version 3
From: https://developers.google.com/google-apps/contacts/v2/developers_guide?hl=en
Authorizing requests with OAuth 2.0
Requests to the Google Contacts API for non-public user data must be authorized by an authenticated user.
The details of the authorization process, or "flow," for OAuth 2.0 vary somewhat depending on what kind of application you're writing. The following general process applies to all application types:
When you create your application, you register it with Google.
Google then provides information you'll need later, such as a client
ID and a client secret.
Activate the Google Contacts API in the Services pane of the Google
APIs Console. (If it isn't listed in the Console, then skip this
step.)
When your application needs access to user data, it asks Google for
a particular scope of access.
Google displays an OAuth dialog to the user, asking them to
authorize your application to request some of their data.
If the user approves, then Google gives your application a
short-lived access token.
Your application requests user data, attaching the access token to
the request.
If Google determines that your request and the token are valid, it
returns the requested data.
From: https://developers.google.com/google-apps/contacts/v3/#authorizing_requests_with_oauth_20
Check Google Contacts API v2 Developer's Guide - .Net and Google Contacts API version 3.0 to write your own code :)

Getting user profile with google oauth2

I'm trying to get user profile information upon logging in with google-oauth2. User successfully logs in and i can get the access_token and can refresh the token when needed.
Though i could not manage to get any information about the user despite reading the docs and trying for hours.
From "Retrieving profiles" section of developers guide :
https://www.google.com/m8/feeds/profiles/domain/domainName/full
should be enough. i've tried with "gmail.com", "google.com", "gmail", "google", "orkut", "orkut.com" , myregisteredappsdomainname (and .com) as domainName. i've also tried it with
https://www.google.com/m8/feeds/profiles/domain/domainName/full?access_token=access_token_for_user
all i managed to get was 401 error, where it says "That’s an error.". Regarding 401 error, I've refreshed the token and tried again with new token, but kept getting 401s.
How can i get profile information and image address for user upon logging in?
The scope you're looking for is:
https://www.googleapis.com/oauth2/v1/userinfo
This has been already answered here
I was getting similar errors requesting profiles even after correctly defining the scope and getting access tokens etc.. The trick for me was to include the API version on my requests. See here for more info http://code.google.com/googleapps/domain/profiles/developers_guide.html#Versioning
Maybe little late yet could this be helpful to someone. Below is the working code I wrote to get gplus user profile
In HTML below markup will display goolge signIn button
<span id="signinButton">
<span
class="g-signin"
data-callback="signinCallback"
data-clientid="YOUR GPLUS CLIENT ID"
data-cookiepolicy="single_host_origin"
data-scope="email">
</span>
</span>
Below is the java script
var access_token;
/**
* Called when the Google+ client library reports authorization status.
*/
function signinCallback(authResult) {
access_token = authResult.access_token;
gapi.client.load('plus', 'v1', function () {
gapi.client.plus.people.get({ userId: 'me' }).execute(printProfile);
});
}
/**
* Response callback for when the API client receives a response.
*
* #param resp The API response object with the user email and profile information.
*/
function printProfile(resp) {
if (resp.code != 403) {
console.log('name:' + access_token.givenname);
console.log('last name:' + access_token.lastname);
console.log('email:' + access_token.emails[0]);
console.log('gender:' + access_token.gender);
console.log('profile image url:' + access_token.image.url);
}
}
Please make sure that you load google api javascript asynchronously within the body tag as below
<script type="text/javascript">
(function () {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/platform.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
</script>
To handle logout refer to the answer I provide in below link, you will need to store access_token in backend so that during logout call this to be used, in my case I have stored in session and getting through ajax call
gapi.auth.signOut(); not working I'm lost
Hey why don't you look at the code given at:
http://www.codeproject.com/KB/aspnet/OAuth4Client.aspx
It definitely helps you. The project is actually an oauth playground to send correct oauth header to correct endpoints.

Resources