Laravel 7.0 Cashier - Stripe Payment Exception - php

I'm having problem on this Laravel Cashier Stripe payment. I need to combine the charge and new subscription as one so that when there is an IncompletePayment exceptions I can still get the stripe webhooks.
try{
$user->charge(1000, $creditCard->id, [
'description' => 'Premium Registration',
])
$user->newSubscription('premium_member', $recurring)
->create($creditCard->id);
}
} catch (IncompletePayment $e) {
$intent = \Stripe\PaymentIntent::retrieve($e->payment->id);
$intent->confirm([
'return_url' => url('api/payments-3d-success'),
]);
return response()->json([
'e' => $intent,
]);
}
Another way is to catch the exception and build like laravel way of handling Incomplete exceptions.
try{
$subscription = \Stripe\Subscription::create([
'customer' => $customer->id,
'items' => [[
'price' => $recurring,
]],
'add_invoice_items' => [[
'price' => $oneTime,
]],
]);
}
//I need to catch the exception here from stripe and build like a laravel way like IncompletePayment exceptions
catch(Exception $e){
$intent = \Stripe\PaymentIntent::retrieve($e->payment->id);
$intent->confirm([
'return_url' => url('api/payments-3d-success'),
]);
return response()->json([
'e' => $intent,
]);
}
Please let me know how you handle this problem. Thanks

There is a way you can do this using Invoice Items: https://stripe.com/docs/billing/invoices/subscription#adding-upcoming-invoice-items

Related

GuzzleHttp\Exception\ConnectException: cURL error 7: Failed to connect - Laravel

I'm trying to post a Third party Api with raw body with my controller , It works fine when I test it from localhost , but when I publish my project on the Server (Cpanel) , I get this Error :
GuzzleHttp\Exception\ConnectException: cURL error 7: Failed to connect.
Here is an example of my code inside the controller :
use Illuminate\Support\Facades\Http;
public function testApi(){
$array = [
'FullName' => 'Full Name',
'PhoneNumber' => '9999999999',
'Date' => '2022-06-26 17:20',
'Note' => '',
];
try {
$response = Http::withBody(json_encode($array) , 'application/json')
->post('https://example');
return $response->status();
} catch (Exception $exception){
return $exception;
}
}
and I also tried using GuzzleHttp and the same thing it works on localhost , and not working when I publish the project on the server.
use GuzzleHttp\Client;
public function testApi(){
$array = [
'FullName' => 'Full Name',
'PhoneNumber' => '9999999999',
'Date' => '2022-06-26 17:20',
'Note' => '',
];
try {
$client = new Client();
$response = $client->request('POST', 'https://example', [
'body' => json_encode($array),
'headers' => [
'Content-Type' => 'application/json',
]
]);
return $response->getStatusCode();
} catch (Exception $exception){
return $exception;
}
}
before disable firewall and test again.
there can be a firewall that blocks your requests

how to work with laravel transaction rollback

I am trying to execute some queries which are dependant on each other, so what I want is if any error occurs then rollback all the queries inside transaction. what I've tried so far is
DB::transaction(function () use($user,$token,$request) {
$billing = User_billings::create([
'users_id' => $user->id,
'agent_id' => null,
'purpose' => 'Some Purpose',
'agent_token_id'=>$token->id,
'amount' => $request->amount,
'payment_method' => 'Agent Token',
'status' => 1
]);
if($billing){
$user_jorunal = User_journal::create([
'bill2_id' => $billing->id, //Intentionally made this error to test transaction, this should be 'bill_id'
'u_id' => $user->id,
'purpose' => 'Topup via Agent Token',
'debit' => $billing->amount,
'invoice_number' => time()
]);
if($user_jorunal){
if($this->agentTokenBalance($request->token_id) == 0){
$token->status=1;
$token->update();
}
return response()->json(['status'=>true,'message'=>'TopUp was Successful!']);
}
}
});
so when I execute this query It generates an error as SQLSTATE[HY000]: General error: 1364 Field 'bill_id' doesn't have a default value, but it also creates a row on user_billings table.
Can you please specify where I am wrong?
all of the above code is running fine, be sure that there is no logical error in query except the intentional one .
I am using laravel 5.7 in this project
PHP version is 7.2.19
following laravel documentation
Manually Using Transactions
use DB::beginTransaction(); to start transaction.
use DB::rollBack(); after each error.
use DB::commit(); when transaction confirmed. ;
laravel reference
Create a $status variable that will make sure that everything has been creeted in db. If any error occur, all db action will be rolled back.
Below, i have adapted your code with that logic.
$status = true;
try
{
DB::beginTransaction();
$billing = User_billings::create([
'users_id' => $user->id,
'agent_id' => null,
'purpose' => 'Some Purpose',
'agent_token_id' => $token->id,
'amount' => $request->amount,
'payment_method' => 'Agent Token',
'status' => 1,
]);
$user_jorunal = User_journal::create([
'bill2_id' => $billing->id, //Intentionally made this error to test transaction, this should be 'bill_id'
'u_id' => $user->id,
'purpose' => 'Topup via Agent Token',
'debit' => $billing->amount,
'invoice_number' => time(),
]);
$status = $status && $billing && $user_jorunal;
} catch (\Exception $e)
{
DB::rollBack();
//throw $e; //sometime you want to rollback AND throw the exception
}
//if previous DB action are OK
if ($status)
{
DB::commit();
if ($this->agentTokenBalance($request->token_id) == 0)
{
$token->status = 1;
$token->update();
}
return response()->json(['status' => true, 'message' => 'TopUp was Successful!']);
} else
{
DB::rollBack();
//return somme errors
}
Please note that MyIsam engine doesn't support transaction as explained here MyIsam engine transaction support.
Haven't used the DB::transaction with a callback method... but you can do the following
DB::beginTransaction();
$billing = new User_billings;
$billing->users_id = $user->id;
// rest of the assignments
$billing->save();
// Rest of your code... Inserts, Deletes, Updates...
DB::commit();
You don't need to implement the DB::rollBack() in this case, if anything fails between those two lines, the transaction won't commit.

Laravel API Validation errors to be displayed

I am valdating the fields sent through api and need to display the errors.
I tried using try and catch no errors thrown. I have already have a code validating the login
try {
$request->validate([
'email' => 'required|string|email',
'password' => 'required|string',
'remember_me' => 'boolean',
]);
} catch (Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
I found no errors return has json instead it is redirecting to the login page
How to handle rerros in API and sent the message as json?None of the example show the way to handle errors. I tried with everything
And also how to handle errors while creating the model
try {
$company = Company::create([
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'email' => $data['email'],
'country_code' => $data['country_code']]);
} catch (Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
$request->validate() should automatically return a response to the browser with errors if it fails, it doesn't throw an exception.
If your request is json it should detect that and return the error in a json error response, you can catch this in your front-end javascript and interrogate the response to interpret the error message.
e.g. using axios:
this.$axios.post('your api url',{data})
.then(response=>{
// ALL Good
})
.error(error=>{
// Catch returned error and process as required
});
If as you say I found no errors return has json instead it is redirecting to the login page this probably means that Laravel thinks that the request is a standard request, in which case it will issue a response()->back()->withErrors() which is probably what's sending it back to your login.
Try checking the original request type and ensure it's json, it should have a header of Accept: application/json.
Alternatively you can define your own validator
https://laravel.com/docs/7.x/validation#manually-creating-validators, and process the validation on the server as you like.
If there is error into validation then it will automatilly handle by laravel. You don't need to catch exception for that. It doesn't through exception.
Look it sample function which I have used for store Region
public function createRegion(Request $request)
{
$data = $request->all();
// Create a new validator instance.
$request->validate([
'name' => 'required',
'details' => 'required'
]);
try {
$region = new Region();
$region->name = $data['name'];
$region->details = $data['details'];
$region->save();
$content = array(
'success' => true,
'data' => $region,
'message' => trans('messages.region_added')
);
return response($content)->setStatusCode(200);
} catch (\Exception $e) {
$content = array(
'success' => false,
'data' => 'something went wrong.',
'message' => 'There was an error while processing your request: ' .
$e->getMessage()
);
return response($content)->setStatusCode(500);
}
}

DB Transaction Laravel not rolling back

I'm trying to use Laravel DB facade to rollback my database transactions if a certain condition is met. But my rollback is not working.
First I started my DB transaction with DB::beginTransaction();
Secondly, I wrote my queries to create the following :
Create a new payment batch
Create payment batch record
Create payment approval
Create payment approvers
Thirdly I then did some condition to know when to rollback or commit my DB transactions. But the rollback is not working. Example this block of condition was met in my code but never rollback
if(!$mail_status){
DB::rollBack();
return back()->with('error', 'Mail not send to approvers. Try again or contact system administrator.');
}
Below is the full block of code.
//Start transaction
DB::beginTransaction();
try{
//Run Queries
//Create a new payment batch
$batch = Batch::create([
'uuid' => uniqid(),
'batch_name' => $batch_config->batch_name,
'service_code' => Auth::user()->service_code,
'created_by' => Auth::user()->name,
]);
//Create parameters for new payment batch record
foreach($batch_record_config as $data){
$record[] = [
'batch_id' => $batch->batch_id,
'payee_name' => $data['payee_name'],
'bank_id' => $data['bank_id'],
'bank_name' => $data['bank_name'],
'account_type' => $data['account_type'],
'account_number' => $data['account_number'],
'amount' => $data['amount'],
'description' => $data['description'],
'year' => date("Y"),
'month' => date("F", mktime(0, 0, 0, date("n"), 10)),
'uuid' => uniqid(),
'service_code' => Auth::user()->service_code,
'created_at' => now(),
'created_by' => Auth::user()->name,
];
}
$batch_record_status = DB::table('batch_record')->insert($record);
//Generate new payment approval
$payment_approval = PaymentApproval::create([
'uuid' => uniqid(),
'batch_id' => $batch->batch_id,
'service_code' => Auth::user()->service_code,
'created_by' => Auth::user()->name,
'deleted' => 0,
]);
//Generate payment approvers for the payment approval
$payment_approvers = [];
foreach($approval_users as $data){
$payment_approvers[] = [
'uuid' => uniqid(),
'payment_approval_id' => $payment_approval->id,
'approval_user_id' => $data->id,
'approval_level_id' => $data->approval_level_id,
'batch_id' => $batch->batch_id,
'approval_position_value' => $data->approval_level->approval_position,
'approval_level_name' => $data->approval_level->approval_level,
'user_id' => $data->user_id,
'batch_name' => $batch->batch_name,
'service_code' => Auth::user()->service_code,
'approved' => 0,
'deleted' => 0,
'created_at' => now(),
'created_by' => Auth::user()->name,
];
}
$payment_approvers_status = DB::table('payment_approvers')->insert($payment_approvers);
// If DB transcation is successful.
if($batch && $batch_record_status && $payment_approval && $payment_approvers_status){
//get the next approvers
$next_approvers = $this->next_approvals($payment_approval->id);
if($next_approvers){
//send mail to the next approvers
$mail_status = $this->send_approvers_mail($next_approvers,$batch);
if(!$mail_status){
DB::rollBack();
return back()->with('error', 'Mail not send to approvers. Try again or contact system administrator.');
}else{
DB::commit();
return back()->with('success', 'Batch has being initialized');
}
}else{
DB::rollBack();
return back()->with('error', 'Approvers not found. Try again or contact system administrator.');
}
}else{
DB::rollBack();
return back()->with('error', 'Something went wrong. Try again.');
}
}catch(\Exception $e){
DB::rollBack();
dd($e);
return back()->with('error', 'Something went wrong... Contact system administrator. Thanks.');
}
I had a look at your and as others have rightly pointed you need to commit the transactions. Which i can't see in your code. There are basically two ways of using transactions which i am aware of.
1.
\DB::beginTransaction();
try {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
} catch (\Exception $e) {
$count = 0;
\DB::rollBack();
\Log::info(self::LOG_PREFIX . 'Some Msg ', [$e->__toString()]);
}
\DB::commit();
2.
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
});
If you're using second method you won't have to do any commit, this will be rolled back automatically on failure.
I persume the error is in this line of yours
$next_approvers = $this->next_approvals($payment_approval->id);
I couldn't find *next_approvals8 property defined in your code, Plus you're using both Laravel ORM and Raw Statements (PaymentApproval::create & DB::table('batch_record')->insert($record);)
Try sticking to 1 and see if this works.
You didn't mention that either your DB get commit or throw some error?.Check what are you getting in mail function response. I thin you got success in mail because your else condition is not execute any case.

Stripe payment SCA

My current Stripe code is as below: as I pass to stripe.
Had been using following Stripe Library:
https://github.com/stripe/stripe-php
Its working fine as of now. Now I need to change code according to SCA. But I am total lost with code update.
My controller
$this->stripe_->add_charge_array($stripe_user_id, $order_info, $price * 100, $currency, $metadata);
$this->stripe_->pay($currency_id, , $stripeToken, $card_id);
$data = $this->stripe_->get_charge_response($stripe_user_id, , $order_id);
$order->setStripeChargeId($data['charge_id']);//Here I save to db
And in stripe library:
public function pay($currency_id, $stripe_token = null, $card_id = null, $account_id = null){
$charge = $this->charge_card($cart['amount'], $cart['currency'], $stripe_token, $description, $cart['metadata'], $currency_id, $account_id);
}
And my charge_card method connecting to Stripe
public function charge_card(){
try {
\Stripe\Stripe::setApiKey($STRIPE_KEY);
$charge = \Stripe\Charge::create(array(
"amount" => intval($amount),
"currency" => $currency,
"source" => $stripe_token, // obtained with Stripe.js
"description" => $description,
"metadata" => $metadata
));
} catch (\Stripe\Error\Base $e) {
// Display a very generic error to the user, and maybe send
// yourself an email
$result = self::put_stripe_error($e);
} catch (Exception $e) {
// Something else happened, completely unrelated to Stripe
self::put_stripe_error($e);
}
return $charge;
}
Stripe library:
https://js.stripe.com/v3/
From what I did understand from documentation is that I need to change my charge_card method like below.
$intent = \Stripe\PaymentIntent::create([
'payment_method' => '{{PAYMENT_METHOD_ID}}',
'customer' => '{{CUSTOMER_ID}}',
'amount' => 1099,
'currency' => 'gbp',
'confirmation_method' => 'manual',
'confirm' => true,
'setup_future_usage' => 'off_session',
]);
But then I get payment method Id in $intent . How to proceed from here.
Documentation misses this.

Categories