I've multiple stripe account on my site, and each stripe account is associated with a webhook.
My webhook is returning 403 Error "No signatures found matching the expected signature for payload"
i've checked the Cashier middleware and its getting the webhook secret key from the env file.
Since this project attached to multiple stripe account, we can't store the webhook secret in env file. so, we're placing the webhook secret key of each stripe account in a table.
I would like to get the secret key from database instead of this config file.
Is it possible to listen to multiple stripe account's webhook?
Any help will be appreciated.
I am not sure if this is a good approach but you can:
Send meta data in the checkout which gets posted to the web hook
Get the raw json posted by the web hook before you use the web hook key to validate that the post was from Stripe. Stripe.Net contains a parse method and a construct method. Parse does not require the key. Construct uses the key to validate the post was from Stripe.
So with Stripe.net:
string endpointSecret;
// get the json posted
var json = await new
StreamReader(HttpContext.Request.Body).ReadToEndAsync();
// convert the json into a stripe event object
var objStripeEvent = EventUtility.ParseEvent(json);
if (objStripeEvent.Type == Events.CheckoutSessionCompleted)
{
// get the session object and see if it contains the Meta data we passed
// in at checkout
var session = objStripeEvent.Data.Object as Session;
var met = session.Metadata;
if (met.ContainsKey("FranchiseGuid"))
{
// if the meta data contains the franchise guid get the correct
// wh secret from the DB
var FranchiseGuid= new Guid(met["FranchiseGuid"]);
endpointSecret = _repo.GetWebHookSecret(FranchiseGuid);
}
}
// Then you can go on to use the Construct method to validate the post with the correct key for the Stripe account where the web hook is based.
try
{
// check if was from Stripe
var stripeEvent = EventUtility.ConstructEvent(
json,
Request.Headers["Stripe-Signature"],
endpointSecret);
---- etc
I've requested help on this from Stripe support but they have promised to get back to me. I'll test out the above to see if it works. I don't think it's ideal though because if a hacker were able to get a valid franchise guid they could possibly fake posts and spam the endpoint. It would not be easy to guess a guid and these id's are not available in any way publicly. Plus https is used. But it still makes me nervous because the franchise guid would be one of a dozen or more. Not like a booking guid which is generated and sent once for the booking that is marked as paid. The franchise guid would be sent every time a payment was made for that franchise.
I think what I may do is use the booking guid since this is randomly generated for every booking. I can join to the franchise table from the booking and get the web hook secret.
We'll see if Stripe come back with something useful.
Related
I am using the MERN stack for an app im building. In this app im using twilio. I have decided to use twilio sub-accounts. The way this works is I create a MASTER twilio account that give me an accountSid and authToken.
I can store these as ENV variables in Heroku when I want to deploy, and anytime I need to access these ENV vars I can just use the process.env.AUTH_TOKEN in my Node.js server.
Every customer that signs up for my app is going have their own subaccount that is a child of my MASTER account. When this sub account is created, It will give that user their own accountSid and authToken.
This is where my issue stands, Do I need to store each users authToken on Heroku as an ENV variable?
ex..
process.env.USER_1_AUTH_TOKEN
process.env.USER_2_AUTH_TOKEN
process.env.USER_3_AUTH_TOKEN
process.env.USER_4_AUTH_TOKEN
process.env.USER_5_AUTH_TOKEN
I dont think this will work because how will I know which users auth token belongs to them?
Currently in Development I am storing the sub-account authToken directly on the user object, this user object is visible to the client side of the app and im worried that exposing the auth token directly to the client could result in some sort of hack?
Is it safe to store the auth token on the user object directly in mongodb and whenever my react app needs the user, just send the user object without the auth token?
Should I create a auth-token Model, and store a document in the auth-token model containing the auth-token and user_id and everytime I need the auth token just query mongodb for the auth-token with user_id as a parameter?
How does one go about storing say 100,000 of these auth-tokens?
I'm worried about security and twilio docs dont say much about this...
According to the Subaccounts API documentation you can use the Twilio rest API to instantiate a subaccount and assign that subaccount a friendly name that is easy to retrieve.
client.api.v2010.accounts
.create({friendlyName: 'Submarine'})
.then(account => console.log(account.sid));
This returns an object that contains a lot of information but it has a unique SID for that is associated to that new number/subaccount. That object is then also linked back to your main account via the owner_account_sid which is attached to that object.
Twilio provides functionality in the subaccount API to allow you to retrieve a subaccounts data based on the friendly name like so...
client.api.v2010.accounts
.list({friendlyName: 'MySubaccount', limit: 20})
.then(accounts => accounts.forEach(a => console.log(a.sid)));
So what you should be doing is as follows...
Create a naming convention within your system that can be used to form friendly names to assign to subaccounts.
Use the API to create a new subaccount with the Twilio API under the friendly naming convention you've developed.
Anytime you want to make a call, text, or other supported action from the Twilio API first perform an API action to look up the SID of that account by the friendly name.
Grab the sid the friendly name returns and attach it to your client object like so require('twilio')(accountSid, authToken, { accountSid: subaccountSid });
Perform your action through that client using the subaccount Sid that is now attached.
I would like to add some additional info for anyone using twilio subaccounts.
So what I did was create a master account with twilio. This gives you an accountSid and authToken.
These you can store in Heroku under config vars.
When you create your login function for a user via passport login, google-passport or some custom login you create, make your api call to create a subaccount. ( Like when you buy numbers, you buy them under your main account and drill them to your sub accounts) When this is created ONLY add the sub account accountSid to your user object. the sub accountSid is worthless without the auth-token and since you need to make an api call with your env vars your sub account auth tokens are safely stored in twilio.
Now whenever you need to make twilio api calls, say for sending a message or making a phone call etc... first make a call to this endpoint
client.api.v2010.accounts(subActServiceSid)
.fetch()
.then(account => account.authToken);
const accountSid = keys.accountSid // master accountSid
const authToken = keys.authToken // master authToken
// these will be stored in heroku
const listChatMessages = async (req, res) => {
const { subActServiceSid } = req.user // getting sub accountSid from user object
const subActAuthToken = client.api.v2010.accounts(subActServiceSid)
.fetch()
.then(account => account.authToken);
const subClient = require('twilio')(subActServiceSid, subActAuthToken)
await subClient.messages.list({ // make all the api calls your heart desires
from: chat.phone
})
.then( messages => messages.forEach((m) => { console.log("message", m})
this will contain a JSON object with the sub accounts authToken. You can then use this authToken for the API call. No need to worry about storing 100,000 users authTokens somewhere.... If this is still confusing message me.
I am using the envelopes list status changes api and getting the error below. I am the only user on the account and a DS Admin. Also, we have used this code successfully with other DocuSign accounts.
Can you please help me resolve the issue?
Error:
ApiException: Error while requesting server, received a non successful HTTP code 400 with response
Body:
'{"errorCode":"USER_LACKS_MEMBERSHIP","message":"The UserID does not have a valid membership in this Account."}'
ApiClient apiClient = new ApiClient(basePath);
apiClient.setAccessToken(token, tokenExpirationSeconds);
EnvelopesApi envelopesApi = new EnvelopesApi(apiClient);
// prepare the request body
EnvelopesApi.ListStatusChangesOptions options = envelopesApi.new ListStatusChangesOptions();
LocalDate date = LocalDate.now().minusDays(30);
options.setFromDate(date.toString("yyyy/MM/dd"));
// call the API - ERROR HERE
EnvelopesInformation results = envelopesApi.listStatusChanges(accountId, options);
the userId you used for your JWT token must be an admin of the same exact account in DocuSign.
Any chance you use demo vs. production? two different accounts?
This error simply means your API calls is using a user that is not authorized to view this envelope because it lacks a membership to the account that this envelope is part of.
The most common cause of that error (after you've confirmed the account ID is correct) is hitting the wrong Base URL for the account. The first time you generate an authentication token for a particular user, you should make a UserInfo call and cache the BaseURL value.
Thanks! Ended up rebuilding credentials from scratch. I appreciate the insights.
I m using guesty.com api.
Adding a a payment about reservation
see this link
https://docs.guesty.com/#update-a-reservation
anyone can tell me what is the
"stripePaymentMethodToken" here
In order to create a Stripe token you'll need to send a call to Stripe using Guesty’s public Stripe key: pk_live_P0FSIEtbwU1GSvgvEM3DYuUZ, than send the received object to POST https://api.guesty.com/api/v2/guests//payment-methods
I want to create a new customer in Stripe upon form submit and add that customer's credit card to their account. As of now, I'm using the following code upon submit in my React App. The create customer call is then made separately from my server:
async submit(ev) {
let {token} = await this.props.stripe.createToken({name: "Name"});
let response = await fetch("https://api.stripe.com/v1/customers", {
method: "POST",
headers: {"Content-Type": "text/plain"},
body: token.id
});
When sending that data, I get a 401 error on the let response = ... line. I know that a 401 is an auth error, but my test API keys are definitely correct and don't have limits on how they can access my stripe account. Can anyone advise?
The issue here is that you are trying to create a Customer object client-side in raw Javascript. This API request requires your Secret API key. This means you can never do this client-side, otherwise anyone could find your API key and use it to make refunds or transfer for example.
Instead, you need to send the token to your own server. There, you will be able to create a Customer or a Charge using one of Stripe's official libraries instead of making the raw HTTP request yourself.
In my case, it's throwing the error due to a missing of stripe public key
var stripe = Stripe('{{ env("STRIPE_KEY") }}');
then I pass the public key as above, and it worked like a charm.
We are building a solution that will need to access our customers Gmail accounts to read/send mail. On account signup, we'd have a pop-up for our customer to do Gmail auth page and then a backend process to periodically read their emails.
The documentation doesn't seem to cover this use case. For example https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth says that client tokens should be stored in client_secrets.json - what if we have 1000s of clients, what then?
Service accounts are for non-user info, but rather application data. Also, if I use the GoogleWebAuthorizationBroker and the user has deleted access or the tokens have expired, I don't want my backend server app to pop open a web brower, as this seems to do.
I would imagine I could use IMAP/SMTP accomplish this, but I don't think it's a good idea to store those credentials in my db, nor do I think Google wants this either.
Is there a reference on how this can be accomplished?
I have this same situation. We are planning a feature where the user is approving access to send email on their behalf, but the actual sending of the messages is executed by a non-interactive process (scheduled task running on an application server).
I think the ultimate answer is a customized IAuthorizationCodeFlow that only supports access with an existing token, and will not execute the authorization process. I would probably have the flow simulate the response that occurs when a user clicks the Deny button on an interactive flow. That is, any need to get an authorization token will simply return a "denied" AuthorizationResult.
My project is still in the R&D phase, and I am not even doing a proof of concept yet. I am offering this answer in the hope that it helps somebody else develop a concrete solution.
While #hurcane's answer more than likely is correct (haven't tried it out), this is what I got working over the past few days. I really didn't want to have to de/serialize data from the file to get this working, so I kinda mashed up this solution
Web app to get customer approval
Using AuthorizationCodeMvcApp from Google.Apis.Auth.OAuth2.Mvc and documentation
Store resulting access & refresh tokens in DB
Use AE.Net.Mail to do initial IMAP access with access token
Backend also uses AE.Net.Mail to access
If token has expired, then use refresh token to get new access token.
I've not done the sending part, but I presume SMTP will work similarly.
The code is based on SO & blog posts:
t = EF object containing token info
ic = new ImapClient("imap.gmail.com", t.EmailAddress, t.AccessToken, AuthMethods.SaslOAuth, 993, true);
To get an updated Access token (needs error handling) (uses the same API as step #1 above)
using (var wb = new WebClient())
{
var data = new NameValueCollection();
data["refresh_token"] = refresh;
data["client_id"] = "(Web app OAuth id)";
data["client_secret"] = "(Web app OAuth secret)";
data["grant_type"] = "refresh_token";
var response = wb.UploadValues(#"https://accounts.google.com/o/oauth2/token", "POST", data);
string Tokens = System.Text.Encoding.UTF8.GetString(response);
var token = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(Tokens);
at = token.access_token;
return at;
}