How do I retrieve Stripe Session ID for a connected account? - stripe-payments

I'm using Stripe checkout. I create the session and process the charge. On success, I have the session id. I want to retrieve the session object for the connected account. (This works fine for a charge to my standard account, but fails when for retrieving the session for the connected account).
For reference, here's the PHP code for creating the session before the charge:
\Stripe\Stripe::setApiKey($Skey);
$session = \Stripe\Checkout\Session::create([
'customer_email' => $Email,
'payment_method_types' => ['card'],
'line_items' => $itms,
'payment_intent_data' => [
'description' => $Web_Short_Name . '*' .$Transaction_ID,
'application_fee_amount' => $Fee,
'metadata' => ['Transaction_ID' => $Transaction_ID],
],
'success_url' => 'https://[myweb]/success.php?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://[myweb]/cart.php',
],
['stripe_account' => $Stripe_Account] );
}
FIRST ATTEMPT:
$s = $_GET['session_id'];
$stripe = new \Stripe\StripeClient(
['api_key' => '[my secret key'],
['stripe_account' => '[connected account]']
);
$s2=$stripe->checkout->sessions->retrieve($s,[]);
SECOND ATTEMPT:
$s = $_GET['session_id'];
\Stripe\Stripe::setApiKey('[my secret key]');
$stripe = new \Stripe\StripeClient(
'[my secret key]'
);
$s2=$stripe->checkout->sessions->retrieve($s,[]);
Thanks in advance!
Bob
(I've used StackOverflow as a resource for years...but this is my first post).

For connected accounts, you can fill the second parameter of the retrieve function, just like you did when creating the session:
\Stripe\Stripe::setApiKey('<your API key>');
$session = \Stripe\Checkout\Session::retrieve('<the session id>', [
'stripe_account' => '<the id of the connected Stripe account>'
]);

Got what I needed to work. Essentially I was looking for the PaymentIntent object when I got the call to my webhook from Stripe.
Snippet from my checkout webhook:
<?
require __DIR__ . '/vendor/autoload.php';
$payload = #file_get_contents('php://input');
$event = null;
try {
$event = \Stripe\Event::constructFrom(
json_decode($payload, true)
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
}
// Handle the event
\Stripe\Stripe::setApiKey({YOUR_SECRET_KEY});
$session_id = $event->data->object->id;
switch ($event->type) {
case 'checkout.session.completed':
$checkout = $event->data->object; // contains a \Stripe\PaymentIntent
// Then define and call a method to handle the successful payment intent.
//handleCheckoutSucceeded($checkout);
handleCheckout($db,$Transaction_ID,$session_id,'SUCCEEDED');
?>

\Stripe\Stripe::setApiKey('<your API key>');
$session = \Stripe\Checkout\Session::retrieve('<the session id>',[], [
'stripe_account' => '<the id of the connected Stripe account>'
]);
The second param array should be empty if no other parameters to pass and stripe_account is to be passed in the third array which is for the options array.

Related

PHPMailer with Gmail API auth works with unlimited scope, does not work with limited scope

I am trying to set up a application that will use PHPMailer with Gmail API functionality.
I have written my program and tested it and it worked great. The last step for me was to see if I could make the system a bit more secure and follow best practices by only requesting the scope that is needed.
This version of the code works.
$params = [
'clientId' => $clientKeys['clientID'],
'clientSecret' => $clientKeys['secretKey'],
'redirectUri' => $oauth2redirectURI,
'accessType' => 'offline'
];
$provider = new Google($params);
$options = [
'scope' => [
'https://mail.google.com/'
]
];
However, when I reduce the scope to this:
$params = [
'clientId' => $clientKeys['clientID'],
'clientSecret' => $clientKeys['secretKey'],
'redirectUri' => $oauth2redirectURI,
'accessType' => 'offline'
];
$provider = new Google($params);
$options = [
'scope' => [
'https://www.googleapis.com/auth/gmail.send'
]
];
The initial authorization works to get the refresh token and such.
However, PHPMailer no longer is able to send any messages as that user. Instead when attempting to send email messages, PHPMailer throws the following error:
SMTP connect() failed. https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting1

How to check if Google Client_ID and Client_Secret Valid or not

I am making a module in Node.js where the user can make their own Livestream Bot for youtube. The module requires the user to provide their client id and client secret of their application. But how do I check if the client id and client secret they entered is valid and if it isn't throw an appropriate error. Is there an endpoint to do that?
There is no way of validating the client id or the client secret. The only way is to try them and if they dont work then display an error to the user.
You could maybe come up with a regex test for the client_id but i wouldn't recommend it as the format has changed several times over the years which would cause problems in your application.
I would just say try to use it if it fails give them an error message.
Note: I hope you are not prompting people to give you their client id and client secret as its against googles TOS for people to shire their client credentials if you are enc urging people to give you their credentials you are telling them to break the TOS.
Valid Code:
function ProjectValid($PostArray){
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://oauth2.googleapis.com/token',
CURLOPT_POST => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
],
CURLOPT_POSTFIELDS => http_build_query([
'code' => md5(time()),
'client_id' => $PostArray['ClientID'],
'client_secret' => $PostArray['ClientSecret'],
'redirect_uri' => $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER["HTTP_HOST"].'/'.$URL,
'grant_type' => 'authorization_code'
]),
));
$Response = curl_exec($curl);
curl_close($curl);
if($Array=json_decode($Response,true) and isset($Array['error']) and $Array['error'] == 'invalid_grant'){
return true;
}else{
return false;
}
}

Firebase auth email - prevent fake/invalid accounts

I am using Firebase auth email accounts to sign up users to a site.
What I have noticed lately is the below cases.
Users sign up using a valid email address and then never verify the
email address
Users attempt to sign up using a fake email address
For the first case we can search all accounts that have not been verified within a time span and delete them.
admin.auth().getUser(uid).then(user => {
const creationTime = user.metadata.creationTime
const isVerified = user.emailVerified
const lastSignInTime = user.lastSignInTime
if(!isVerified){
// Process and delete unverified accounts after x days
...
}
})
How can we handle accounts where the email address is fake or misspelled? I am not seeing any property on the firebase.User object that indicates an invalid email address. We do however receive a mail delivery failure message for each user that has signed up using a invalid email address - this is not enough to automatically purge fake / invalid accounts.
What are best practices on preventing fake signups?
Kind regards /K
You can't stop someone from using any string that looks like an email address, and the system doesn't have a way of telling you that the verification email was successfully sent.
The usual way to deal with this is to create some database record for each user account that tracks their validation status. You can query the database to find out which users have not validated after some amount of time. Your app should be sending your backend ID tokens from the user that can be used to check if they are validated, and if so, update the database to show that it happened.
So this is the code I came up with to purge unverified accounts.
May not be the most elegant solution, but works.
exports.scheduledUserCleanup = functions
.region('europe-west1')
.pubsub
.schedule('0 3 * * *')
.timeZone('Europe/Stockholm')
.onRun(async (event) => {
const today = moment()
const inactivityThresholdDays = 7 //Get purge threshold days
let myPromises = [] //Collect all promises to carry out
//Search for users that have NOT validated email
database.ref('user-signups').once('value', (usersSnapshots) => {
usersSnapshots.forEach((snapshot) => {
const uid = snapshot.key
// Get user from firebase auth
admin.auth().getUser(uid)
.then((firebaseAuthUser) => {
const creationTimeStr = firebaseAuthUser.metadata.creationTime
const isVerified = firebaseAuthUser.emailVerified
const lastSignInTimeStr = firebaseAuthUser.metadata.lastSignInTime
const neverSignedIn = (creationTimeStr === lastSignInTimeStr) ? true : false
if(!isVerified && neverSignedIn){
// Process and delete unverified accounts after 7 days
const creationTime = moment(creationTimeStr)
const daysSinceCreation = today.diff(creationTime, 'days')
if(daysSinceCreation > inactivityThresholdDays){
console.log('Remove user from db and Firebase auth', uid)
myPromises.push( admin.auth().deleteUser(firebaseAuthUser.uid) )
myPromises.push( database.ref(`user-signups/${uid}`).remove() )
}else{
console.log(`Keep for ${inactivityThresholdDays} days before deleting`, uid)
}
}
return true
})
.catch((error) => {
// Remove if not found in Firebase Auth
const notFoundInFirebaseAuth = (error.code) ? error.code === 'auth/user-not-found' : false
if(notFoundInFirebaseAuth){
console.log('Remove user from db', uid)
myPromises.push( database.ref(`user-signups/${uid}`).remove() )
}
return false
})
})
})
// Execute promises
return Promise.all(myPromises)
.then(() => Promise.resolve(true))
.catch((err) => {
console.error('Error', err)
return Promise.reject(err)
})
})

Stripe error: You cannot use a PaymentMethod as a source for Customers

I am new to NodeJS, and I am trying to integrate Stripe payments, using Firebase Cloud functions. I Followed these steps:
I got the token from client-side, stored it in Firestore, the token looks like this: pm_1FFhvNDcXKDMgaqV...
I've created a Customer on Stripe
exports.createNewStripeCustomer =
functions.auth.user().onCreate(
async (user) => {
const customer = await stripe.customers.create({email:
user.email}) ;
return admin.firestore()
.collection('customers')
.doc(user.uid)
.set({customer_id: customer.id});
}
);
The above code works.
Now I've tried to add a payment source using the token as specified in tutorials and docs and I keep getting the following error:
Error: You cannot use a payment method as a source for Customers. Instead, use the payment methods API to Attach a Customer to a payment method. See https://stripe.com/docs/api/payment_methods/attach
Here's the code that causes the error:
exports.newPaymentSource = functions.firestore.document('customers/{userId}/tokens/{tokenId}').onWrite(async (change, context) => {
//Get token that strike sdk gave to the client...
const data = change.after.data();
if (data ===null) { return null }
const token = data.tokenId;
const snapshot = await firestore.collection('customers').doc(context.params.userId).get();
const customer = snapshot.data().customer_id;
//calling stripe api...
console.log(customer + ":" + token + ":" + context.params.userId);
const respFromStripe = await stripe.customers.createSource(customer, { source: token });
// console.log(respFromStripe);
return firestore.collection('users').doc(context.params.userId)
.collection('sources').doc(respFromStripe.card.fingerprint).set(respFromStripe, { merge: true });
});
PaymentMethod objects (which represent your user's cards) need to be attached to a Customer with /v1/payment_methods/pm_123/attach endpoint or in Stripe-Node:
pm = await stripe.paymentMethods.attach('pm_678', {customer: 'cus_123'});
https://stripe.com/docs/api/payment_methods/attach?lang=node
The way you're using it (customer.createSource()) works for older Tokens (tok_123) and Source (src_123) objects, not PaymentMethods.

Creating a ACH charge with stripe + plaid - no such token; a similar object exists in live mode

I've had this problem before when trying to make a charge on a live card/customer when in dev mode. I've never made an ACH charge with stripe before and I'm in dev mode for sure.
https://stripe.com/docs/ach#integrating-plaid
js
var linkHandler = Plaid.create({
env: 'tartan',
clientName: 'Stripe / Plaid Test',
key: '[Plaid key]',
product: 'auth',
selectAccount: true,
onSuccess: function(public_token, metadata) {
// Token & Account ID - I use this for subsequent cURL requuest
console.log('public_token: ' + public_token);
console.log('account ID: ' + metadata.account_id);
},
});
// Trigger the Link UI
document.getElementById('linkButton').onclick = function() {
linkHandler.open();
};
Response is valid. I use the public_token and account ID from above:
$data = array(
'client_id' => 'MY_CLIENT_ID',
'secret' => 'MY_SECRET',
'public_token' => 'MY_PUBLIC_TOKEN_FROM_ABOVE',
'account_id' => 'MY_ACCOUNT_ID_FROM_ABOVE'
);
$string = http_build_query($data);
//initialize session
$ch=curl_init("https://tartan.plaid.com/exchange_token");
//set options
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//execute session
$keys = curl_exec($ch);
$keys = json_decode($keys);
//close session
curl_close($ch);
This also results in a valid response object:
{
access_token: 'MY_ACCESS_TOKEN',
account_id: 'MY_ACCOUNT_ID',
stripe_bank_account_token: 'MY_STRIPE_BANK_ACCOUNT'
}
This is where I'm mixed up I suppose. The docs say:
The response will contain a verified Stripe bank account token ID. You can attach this token to a Stripe Customer object, or create a charge directly on it.
However when I create a charge on the bank account token like this:
\Stripe\Stripe::setApiKey("sk_test_MY_TEST_KEY");
$charge = \Stripe\Charge::create(array(
"amount" => 2100,
"currency" => "usd",
"source" => $keys->stripe_bank_account_token, //(btok_MY_TOKEN_FROM_ABOVE)
"description" => "my description"
));
var_dump( $charge );
Error I get is: Fatal error: Uncaught exception 'Stripe\Error\InvalidRequest' with message 'No such token: btoken_MY_BANK_TOKEN; a similar object exists in live mode, but a test mode key was used to make this request.'
That means you created a live bank account token.
If you want to test your integration, you need to generate the Plaid token with the following credentials:
Username: test_plaid
Password: test_good
Code: 1234
This will return a test bank account token that you can use in an API request sent with your Stripe test secret API key (sk_test_...).
If you want to process a live charge, then you need to use real credentials in Plaid link to get a real bank account token back, then use the bank account token in an API request sent with your Stripe live secret API key (sk_live_...).

Resources