I am using Laravel 10 and I have implemented the following controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\MarketplaceItem;
use Stripe\Stripe;
use Stripe\Checkout\Session;
use Illuminate\Support\Facades\Log;
class PaymentController extends Controller
{
/**
* Create a new Stripe Checkout Session for the specified item.
*
* @param \Illuminate\Http\Request $request
* @param int $itemId The ID of the item being purchased.
* @return \Illuminate\Http\JsonResponse
*/
public function createCheckoutSession(Request $request, $itemId)
{
try {
// Fetch the specified item from the database.
$item = MarketplaceItem::findOrFail($itemId);
// Fetch the seller's Stripe account ID.
$sellerStripeAccountId = $item->user->stripe_account_id;
// Calculate the total amount for the item in cents.
$amount = (int)($item->price * 100);
// Calculate the application's commission (20% of the total amount).
$applicationFee = (int)($amount * 0.20);
// Set the secret API key for Stripe.
Stripe::setApiKey(config('stripe.secret'));
// Set the Stripe-Account header
$options = ['stripe_account' => $sellerStripeAccountId];
// Create a new Stripe Checkout Session.
$session = Session::create([
'payment_method_types' => ['card'], // Allow card payments only.
'line_items' => [[
'price_data' => [
'currency' => 'usd', // Set the currency.
'product_data' => [
'name' => $item->prompt_name, // Set the item name.
],
'unit_amount' => $amount, // Set the unit amount.
],
'quantity' => 1, // Set the quantity.
]],
'mode' => 'payment', // Set the payment mode.
'success_url' => route('payment.success'), // Set the success URL.
'cancel_url' => route('payment.cancel'), // Set the cancel URL.
'payment_intent_data' => [
'application_fee_amount' => $applicationFee, // Set the application fee amount.
'transfer_data' => [
'destination' => $sellerStripeAccountId, // Set the destination Stripe account.
],
],
], $options); // Pass the options array here
// Return the session ID as a JSON response.
return response()->json(['session_id' => $session->id]);
} catch (\Exception $e) {
// Log the exception
Log::error('PaymentController@createCheckoutSession: ' . $e->getMessage());
// Optionally, you could return a generic error message to the frontend
return response()->json(['error' => 'An error occurred while processing your payment.'], 500);
}
}
/**
* Display the payment success page.
*
* @return \Illuminate\View\View
*/
public function success()
{
return view('payment.success');
}
/**
* Display the payment cancellation page.
*
* @return \Illuminate\View\View
*/
public function cancel()
{
return view('payment.cancel');
}
}
My user has a $sellerStripeAccountId
however I get the error:
"The 'payment_intent_data[transfer_data][destination]' param cannot be set to your own account."
I do not understand the error, because when a user registers I give the user a stripe_account_id like the following in my RegisterController
:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use App\Models\Country;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Stripe\Stripe;
use Stripe\Account;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
public function showRegistrationForm()
{
$countries = Country::orderBy('name')->get();
return view('auth.register', compact('countries'));
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'country_of_origin' => ['required', 'string', 'max:255'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\Models\User
*/
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'country_of_origin' => $data['country_of_origin'],
]);
$this->createStripeAccount($user);
return $user;
}
/**
* Create a new Stripe Account for the specified user.
*
* @param \App\Models\User $user
* @return void
*/
protected function createStripeAccount(User $user)
{
// Set your secret key. Remember to switch to your live secret key in production!
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe::setApiKey(config('stripe.secret'));
$account = Account::create([
'type' => 'express',
'email' => $user->email,
'country' => $user->country_of_origin,
'capabilities' => [
'card_payments' => ['requested' => true],
'transfers' => ['requested' => true],
],
'settings' => [
'payouts' => [
'schedule' => [
'interval' => 'manual',
],
],
],
]);
$user->stripe_account_id = $account->id;
$user->save();
}
}
What I am doing wrong?
I believe there's some confusion here. In your code, you're setting connected account ID as Stripe-Account
header as well as transfer_data.destination
.
What sort of connect charge flow are you trying to integrate? Direct charges or Destination charges? You only need one or the other based on the charges. Not both.
The way your code is right now, it sets the connected account ID as Stripe-Account
header and the same connected account ID as destination which means you're making a request on that account to create a destination charge to itself which isn't possible.
You'd likely want to refer to Stripe docs as they explain this in detail:
Direct charges: https://stripe.com/docs/connect/direct-charges
Destination charges: https://stripe.com/docs/connect/destination-charges