I try to create a website using Stripe for monthly subscription.
I got a pricing page, when i choose a product, i create a checkout session using JS, and when i pay, i got successfully the payement and everything, but my subscription database is empty. The default cashier webhook do nothing but he are called successfully on my stripe cli, I also tryied to create a webhook but i got nothing from it i got every times error 500 on stripe cli.
Do you have an idea ?
Here is my JS
<script src="https://js.stripe.com/v3/"></script>
<script defer>
$("#form").submit(function(e){
e.preventDefault();
});
var handleResult = function(result) {
if (result.error) {
showErrorMessage(result.error.message);
}
};
function subscribe(offer)
{
fetch("{{ route('subscription_setup') }}", {
method: "GET",
headers: {
"Content-Type": "application/json",
}
})
.then((result) => result.json())
.then(function(result) {
var publishableKey = result.publishableKey;
var stripe = Stripe(publishableKey);
var createCheckoutSession = function(offer) {
return fetch("{{ route('subscription_create_checkout_session') }}", {
method: "POST",
headers: {
"Content-Type": "application/json",
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
body: JSON.stringify({
offer: offer
})
}).then(function(result) {
return result.json();
});
}
createCheckoutSession(offer).then(function(data) {
stripe.redirectToCheckout({
sessionId: data.sessionId
}).then(handleResult);
});
})
}
</script>
Here is my PHP :
<?php
namespace App\Http\Controllers;
use Exception;
use Illuminate\Http\Request;
use \Stripe\Stripe;
use Illuminate\Support\Facades\Auth;
use Laravel\Cashier\Cashier;
use \Stripe\Checkout\Session;
class SubscriptionController extends Controller
{
public function __construct() {
Stripe::setApiKey(env('STRIPE_SECRET'));
}
public function index()
{
return view('subscription/index');
}
public function create_checkout_session(Request $request)
{
$user = Auth::user();
// User is not in stripe database
if(empty($user->stripe_id))
{
$stripe_user = $user->createAsStripeCustomer();
}
// User already in stripe database
else
{
$stripe_user = Cashier::findBillable($user->stripe_id);
}
// After retrieve the stripe user
$offer = $request->post('offer');
if($offer == 'football')
{
$price = 'price_1I4z8kDdfEiamf5bMiTElVAM';
}
else
{
return response()->json(['error' => 'Vous n\'avez pas sélectionné une offre valide']);
}
$checkout_session = Session::create([
'payment_method_types' => ['card'],
'line_items' => [
[
'price' => $price,
'quantity' => 1,
],
],
'mode' => 'subscription',
'success_url' => url('/'),
'cancel_url' => url('/subscription'),
'customer' => $stripe_user['stripe_id']
]);
return response()->json(['sessionId' => $checkout_session['id']]);
}
public function setup()
{
return response()->json([
'publishableKey' => env('STRIPE_KEY')
]);
}
}
I'm open to every suggestions, thank's a lot.
Edit :
My custom webhook
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;
use \Stripe\Subscription;
use Illuminate\Support\Facades\DB;
class WebhookController extends CashierController
{
/**
* Handle invoice payment succeeded.
*
* #param array $payload
* #return \Symfony\Component\HttpFoundation\Response
*/
public function handleInvoicePaymentSucceeded($payload)
{
$session = $payload['data']['object'];
$user = Auth::user();
// $plan = Subscription::retrieve($session['subscription']['items']['data']['price']['id']);
DB::transaction(function () use ($session, $user) {
$user->update(['stripe_id' => $session['customer']]);
$user->subscriptions()->create([
'name' => 'default',
'stripe_id' => $session['subscription'],
'stripe_status' => 'active',
'stripe_plan' => 'price_1I4z8kDdfEiamf5bMiTElVAM',
'quantity' => 1,
'trial_ends_at' => null,
'ends_at' => now()->addDays(30),
]);
});
return $this->successMethod();
}
}
My verifycsrftoken.php
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'stripe/*',
];
}
my routes :
Route::post('/stripe/webhook', [WebhookController::class, 'handleWebhook']);
I got a error 500 into my stripe cli while i sub to a product.
Edit : I finally used this tutorial https://weeklyhow.com/create-website-subscription-with-laravel-cashier/#:~:text=Laravel%20cashier%20is%20a%20package,and%20even%20generate%20invoice%20PDFs. and don't use stripe checkout anymore.
Related
I'm developing Laravel 9 and Vue.js 3 app (a simple social network) at the moment. I have a problem described in the title. The error itself:
Here are the pieces of code that might help you. And this is the link to the repository of my project on GitHub.
Login.vue, if you will need it (the code is too long to paste here).
loginUser() action:
loginUser(ctx, payload) {
return new Promise((resolve, reject) => {
ctx.commit('setInProcess', true, { root: true });
axios.post('api/login', payload).then(response => {
if (response.data.access_token) {
ctx.commit('setLoggedIn', true, { root: true })
localStorage.setItem('token', response.data.access_token);
ctx.commit('setToken', response.data.access_token, { root: true })
resolve(response);
router.push('/');
} else {
ctx.dispatch(
'setAndDisplayErrors',
[ctx.rootState.helper.undefinedErrorMessage],
{ root: true }
);
reject(response)
}
}).catch(error => {
console.log(error)
if (error.response.data.errors) {
ctx.dispatch(
'setAndDisplayErrors',
error.response.data.errors,
{ root: true }
);
} else {
ctx.dispatch(
'setAndDisplayErrors',
[ctx.rootState.helper.undefinedErrorMessage],
{ root: true }
);
}
reject(error);
}).finally(() => {
ctx.commit('setInProcess', false, { root: true });
})
})
},
/login route:
Route::post("login", [AuthController::class, "login"]);
AuthController login() method:
public function login(LoginRequest $request) {
LoginAction::run($request);
}
LoginRequest:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array<string, mixed>
*/
public function rules()
{
return [
'email' => 'required|string|email|max:255',
'password' => 'required|between:8,255'
];
}
}
LoginAction:
<?php
namespace App\Actions\Auth;
use Illuminate\Http\Request;
use Laravel\Passport\Client;
use Lorisleiva\Actions\Concerns\AsAction;
class LoginAction
{
use AsAction;
public function handle($formData)
{
$passwordGrantClient = Client::where('password_client', 1)->first();
$data = [
'grant_type' => 'password',
'client_id' => $passwordGrantClient->id,
'client_secret' => $passwordGrantClient->secret,
'username' => $formData->email,
'password' => $formData->password,
'scope' => '*'
];
$tokenRequest = Request::create('oauth/token', 'post', $data);
$tokenResponse = app()->handle($tokenRequest);
$contentString = $tokenResponse->getContent();
$tokenContent = json_decode($contentString, true);
if (!empty($tokenContent['access_token'])) {
return $tokenResponse;
} else {
return response()->json([
'errors' => ['Incorrect e-mail or password.']
], 401);
}
}
}
Thanks in advance! If you need something else to understand my question, feel free to ask!
There's nothing wrong with your request. It returns the result, as expected. The error happens in Vuex. And because it's all wrapped in a promise, it's an uncaught error.
To catch the error, I suggest the following syntax:
loginUser(ctx, payload) {
ctx.commit("setInProcess", true, { root: true });
return axios
.post("api/login", payload)
.then((response) => {
console.log(response)
// ...
})
.catch((error) => {
console.log(error)
// ...
})
.finally(() => {
// ...
});
}
You'll still get the error, but this time it won't be uncaught, so you'll have more details about it.
My only mistake was that I forgot return before LoginAction::run($request) at AuthController login() method, so it didn't return anything. Because of this, the response was empty. Thank you for paying attention to this question, #tao!
AuthController login() method (new code):
public function login(LoginRequest $request) {
return LoginAction::run($request);
}
I want to create a payment intention with the Stripe API from Laravel.
I don't get a successful payment intent, but I get this error:
DELL#DELL-PC MINGW64 /c/xampp/htdocs/LaravelPayment
$ php artisan tinker
Psy Shell v0.11.2 (PHP 8.0.8 — cli) by Justin Hileman
$stripe = new App\Services\StripeService;
=> APP\Services\StripeService {#3555}
$stripe->createIntent(5.01, 'USD', 'pm_1KlyY1JJef7TvJrq8R8fD5B6');
GuzzleHttp\Exception\ClientException with message 'Client error: POST https://api.stripe.com/v1/payment_intents resulted in a 400 Bad Request response:
{
"error": {
"code": "parameter_missing",
"doc_url": "https://stripe.com/docs/error-codes/parameter-missing",
(truncated...)
'
Below, you can find my code:
<?php
namespace APP\Services;
use Illuminate\Http\Request;
use App\Traits\ConsumesExternalServices;
class StripeService
{
use ConsumesExternalServices;
protected $baseUri;
protected $key;
protected $secret;
public function __construct()
{
$this->baseUri = config('services.stripe.base_uri');
$this->key = config('services.stripe.key');
$this->secret = config('services.stripe.secret');
}
public function resolveAuthorization(&$queryParams, &$formParams, &$headers)
{
$headers['Authorization'] = $this->resolveAccessToken();
}
public function decodeResponse($response)
{
return json_decode($response);
}
public function resolveAccessToken()
{
return "Bearer {$this->secret}";
}
public function handlePayment(Request $request)
{
//
}
public function handleApproval()
{
//
}
public function createIntent($value, $currency, $paymentMethod)
{
return $this->makeRequest(
'POST',
'/v1/payment_intents',
[],
[
'amount' => round($value * $this->resolveFactor($currency)),
'currency' => strtolower($currency),
'payment_method' => $paymentMethod,
'confirmation_method' => 'manual',
],
);
}
public function resolveFactor($currency)
{
$zeroDecimalCurrencies = ['JPY'];
if (in_array(strtoupper($currency), $zeroDecimalCurrencies)) {
return 1;
}
return 100;
}
}
Could you please check with me what is the problem?
I've tried to debug and it seems that those values are sent correctly:
What should I do now?
I am using axios in vue in laravel to post a method. And i am receiving the below error:
CommentController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\Comment;
use Auth;
class CommentController extends Controller {
public function index(Post $post){
return response()->json($post->comments()->with('user')->latest()->get()); //latest is used to sort them(the latest one in the tops)
}
public function store(Request $request, Post $post){
$comment = $post->comments()->create([
'body' => $request->body,
'user_id' => Auth::id() //Auth::id();
]);
$comment = Comment::where('id', $comment->id)->with('user')->first();
return $comment->toJson();
}
}
Vue.js:
<script>
const app = new Vue({
el: '#app',
data: {
comments: {},
commentBox: '',
post: {!! $post->toJson() !!},
user: {!! Auth::check() ? Auth::user()->toJson() : 'null' !!}
},
created: function() {
this.getComments();
},
methods: {
getComments: function(){
axios.get(`/api/posts/${this.post.id}/comments`)
.then(res => {
this.comments = res.data;
});
},
postComment: function() {
axios.post(`/api/posts/${this.post.id}/comment`,{
body: this.commentBox,
user_id: this.user.id
})
.then(response => {
console.log(response);
this.comments.unshift(response.data);
this.commentBox = '';
})
.catch(error => {
console.log(this.user);
console.log(error.response);
});
}
}
});
</script>
When i pass user_id in CommentController.php hard-coded like below, the error disappear and everything run successfully:
$comment = $post->comments()->create([
'body' => $request->body,
'user_id' => '4' //Auth::id();
]);
Comment Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
protected $fillable = [
'body', 'user_id'
];
public function post(){
return $this->belongsTo('App\Post');
}
public function user(){
return $this->belongsTo('App\User');
}
}
api.php routes:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('posts/{post}/comments','CommentController#index');
Route::post('posts/{post}/comment','CommentController#store');
Route::middleware('auth:api')->group(function () {
});
app/Middlware/Authentication.php
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}
Anyone have any idea why Auth::id() in controller is not w?
sry i wrote wrong answer, because i could not write comment
you using wrong class.
try import this
use Illuminate\Support\Facades\Auth;
Try this:
$user = Auth::user();
$comment = $post->comments()->create([
'body' => $request->body,
'user_id' => $user->id
]);
I am using Laravel 6 with Vue axios, I want to populate a form-select with what I have in my "fjl_groups" table. But everytime I check the console for the result it is returning me an empty string, any idea why is this? My Laravel logs aren't returning any error either, so I have no idea what's going on.
Vue's part
<b-col cols="4">
<label for="editorial">Group</label>
<b-form-select v-model="group" :options="groups" id="groups" name="groups"></b-form-select>
</b-col>
<script>
export default {
data() {
return {
group: null,
groups: [{
value: null,
text: 'Select'
}]
}
},
created(){
axios.get('/clubs/create')
.then(res => {
this.groups = res.data;
console.log(this.groups);
}).catch(e => {
console.log(e);
})
},
}
}
</script>
I have a club and I want to assign a group for it from the ones I have added in my database, this is why I have it like that.
My controller (ClubsController)
use Illuminate\Support\Facades\DB;
use App\Models\Club;
use App\Models\Group;
public function create(Request $request)
{
if($request->ajax()){
DB::table('fjl_groups')->select('id as value', 'nom as text')->get();
}
else{
return view('clubs.create');
}
}
Group Model
class Group extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'fjl_groups';
public $timestamps = false;
}
You are not returning a value. You're just performing the select.
Try returning it:
return response()->json([
'data' => DB::table('fjl_groups')->select('id as value', 'nom as text')->get()
]);
In my Laravel 5.7 app I have 2 tables Tag, TagDetail(One to One relation) and the second table has image uploaded to storage and image field.
I want using boot method for automatic deletion of related rows and image. As result deleting Tag row related TagDetail is deleted, but image of TagDetail
is not deleted.
I have 2 models and new Tag())->d( is just debugging function
app/Tag.php :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use DB;
use App\MyAppModel;
use App\TagDetail;
use App\Http\Traits\funcsTrait;
use Illuminate\Validation\Rule;
use App\Rules\TagUniqueness;
class Tag extends MyAppModel
{
use funcsTrait;
protected $table = 'tags';
protected $primaryKey = 'id';
public $timestamps = false;
private $votes_tag_type= 'votesTagType';
public function getTableName() : string
{
return $this->table;
}
public function getPrimaryKey() : string
{
return $this->primaryKey;
}
public function tagDetail()
{
return $this->hasOne('App\TagDetail', 'tag_id', 'id');
}
protected static function boot() {
parent::boot();
static::deleting(function($tag) {
with (new Tag())->d( '<pre>Tag BOOT $tag::' . $tag->id);
$relatedTagDetail= $tag->tagDetail();
if ( !empty($relatedTagDetail) ) {
$relatedTagDetail->delete(); // I see this is triggered and relatedTagDetail is deleted
}
});
}
and app/TagDetail.php :
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use DB;
use App\MyAppModel;
use App\library\ImagePreviewSize;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use App\Http\Traits\funcsTrait;
class TagDetail extends MyAppModel
{
use Notifiable;
use funcsTrait;
protected $table = 'tag_details';
protected $primaryKey = 'id';
public $timestamps = false;
protected $fillable = [
'tag_id',
'image',
'description',
];
public function getTableName() : string
{
return $this->table;
}
public function getPrimaryKey() : string
{
return $this->primaryKey;
}
public function Tag()
{
return $this->belongsTo('App\Tag', 'tag_id');
}
protected static function boot() {
parent::boot();
static::deleting(function($tagDetail) { // THIS METHOD IS NOT TRIGGERED AT ALL!
with (new TagDetail())->d( '<pre>TagDetail BOOT $tagDetail::' . $tagDetail->id);
$tag_detail_image_path= TagDetail::getTagDetailImagePath($tagDetail->id, $tagDetail->image, true);
with (new TagDetail())->d( '<pre>TagDetail BOOT $tag_detail_image_path::' . $tag_detail_image_path);
TagDetail::deleteFileByPath($tag_detail_image_path, true);
});
}
Is something wrong in my models declarations ?
MODIFIED BLOCK # 2:
In my included file public/js/defaultBS41Backend/admin/tag.js I have method:
backendTag.prototype.deleteTag = function (id, name) {
confirmMsg('Do you want to delete "' + name + '" tag with all related data ?', function () {
var href = this_backend_home_url + "/admin/tag/destroy";
$.ajax({
type: "DELETE",
dataType: "json",
url: href,
data: {"id": id, "_token": this_csrf_token},
success: function (response) {
$("#btn_run_search").click()
},
error: function (error) {
alertMsg(error.responseJSON.message, 'Tag deleting error!', 'OK', 'fa fa-exclamation-triangle')
}
});
}
);
} // backendTag.prototype.deleteTag = function ( id, name ) {
and in control :
public function destroy(Request $request)
{
$id = $request->get('id');
$tag = MyTag::find($id);
if ($tag == null) {
return response()->json(['error_code' => 11, 'message' => 'Tag # "' . $id . '" not found!', 'tag' => null],
HTTP_RESPONSE_INTERNAL_SERVER_ERROR); //500
}
DB::beginTransaction();
try {
$tag->delete();
DB::commit();
} catch (Exception $e) {
DB::rollBack();
return response()->json(['error_code' => 1, 'message' => $e->getMessage(), 'tag' => null], HTTP_RESPONSE_INTERNAL_SERVER_ERROR);
}
return response()->json(['error_code' => 0, 'message' => ''], HTTP_RESPONSE_OK_RESOURCE_DELETED); // 204
} // public function delete(Request $request)
and in routes/web.php :
Route::group(['middleware' => ['auth', 'isVerified'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
Route::delete('/tag/destroy', 'Admin\TagsController#destroy');
...
The issue is $tag->tagDetail(): You are using the query builder and deleting the model directly in the database. But the deleting event can only be triggered when you retrieve the model first.
Replace $relatedTagDetail = $tag->tagDetail(); with $relatedTagDetail= $tag->tagDetail;.