Laravel scheduler to charge users recurring payments via peach payment - laravel

I have a project that uses Ionic and Laravel as a backend. The user uses our app based on monthly or annual subscriptions. So far I have been successful in implementing the initial charge of the subscriptions via peach payment.
So now I want every month end to charge the recurring monthly or annual subscriptions.
The idea I have is as follow:
Run Scheduler to check subscriptions table for all monthly active users where the expiry date is month-end (31/12/2020) or is the current month.
Generate transactions for all those users and save them to the billing table ( this is to get transaction_id to send to a payment gateway).
Run the scheduler using the billing info where created_at is today, send each transaction to payment gateway api for processing.
On Api response get status of each payment request and save/update the db billing table using transaction_id as primaryKey.
Update the subscriptions table (status, new expiry date) after the billing table is update
Any help or direction with this will be appreciated. The code below is the code to use for sending a single charge request.
/**
* Monthly Subscriptions
* #param Request $request
*/
public function chargeMonthlySubscription(Request $request)
{
$curl = curl_init();
$url = "$this->url/registrations/$request->registrationId/payments";
$data = "entityId=$this->entityId" .
"&amount=$request->subscription_amount" .
"&currency=ZAR" .
"&customer.merchantCustomerId=$request->subscription_no" .
"&customer.givenName=$request->first_name" .
"&customer.surname=$request->last_name" .
"&recurringType=$request->recurring_type" .
"&paymentType=DB";
curl_setopt_array($curl, array(
CURLOPT_URL => "$url",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTPHEADER => array(
$this->token,
"Content-Type: application/x-www-form-urlencoded"
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
}

You can make a cron job that will run at the end of the month, first create a command:
php artisan make:command YourCommand
Your command would look like something else:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class YourCommand extends Command
{
//Name that will be used to call command
protected $signature = 'your:command';
protected $description = 'Command description';
public function __construct()
{
parent::__construct();
}
public function handle()
{
//Here you will handle all the logic. For ex:
$record = App/Example::first();
//Implement condition that will determine if the record will be updated
$record->update();
}
}
After that, inside App/Console/Kernel.php create the schedule for that command:
protected function schedule(Schedule $schedule)
{
//Name that you gave it in the protected $signature = 'your:command';
$schedule->command('your:command')->monthly('23:30');
}
This way the command will be run monthly and you can do anything according to your business logic. You can read more about task scheduling & commands on the official docs.

Related

How to update invoice using XERO API?

I am using xero api to integrate it with my web app to manage invoices, currently i want to update invoice through invoice id, i have an helper xero.php file to handle crud operations. I have a function get invoice by invoice id, i want to update the InvoiceNumber. What is the best way to update invoice?
update_invoice_function
public function update_invoice(){
$invoice_id = '******-***-****-****-************';
$updated_invoice = Xero::find_invoice_by_id($invoice_id);
$updated_invoice['response']->TotalDiscount = "1";
$updated_invoice['response']->Date = "2020-01-20";
$updated_invoice['response']->Status = "DRAFT";
$get_invoice_response = Xero::update_invoice_by_id($invoice_id,$updated_invoice['response']);
dd($get_invoice_response);
}
update_invoice_by_id function
public static function update_invoice_by_id($invoice_id,$updated_invoice){
self::instanciate();
try{
$update = self::$xero->loadByGUID('Accounting\\Invoice',$invoice_id);
dd($update);
$update->jsonSerialize($updated_invoice);
$invoice_response = self::$xero->save($update);
$response = [
'error' => false,
'status' => 200,
'message' => 'Invoice updated successfully',
'response' => $invoice_response->getElements()
];
}
catch (Exception $e){
$response = [
'error' => true,
'status' => $e->getCode(),
'message' => $e->getMessage()
];
}
return $response;
}
we have an example app that shows some sample calls to things like createInvoice.. However worth noting that there was recently a breaking change for the newer version of the API to support batch calls for invoice Create & Updates:
Older Way
$result = $apiInstance->updateInvoice($xeroTenantId, $guid, $invoice);
New Way
-> updateOrCreateInvoices is the newest way.. I recommend looking at your version of the package you are running as the function has changed.
https://github.com/XeroAPI/xero-php-oauth2-app/blob/4bf74e915df1b0fee66f954ffcbdc331e762a06a/example.php#L1222
However - in general, doing a POST on an existing invoice with the invoice ID and the New Number will enable you to update it.
{
"InvoiceID": "292532ba-xxxx-xxxx-xxxx-60e7c39c4360",
"InvoiceNumber": "INV-im-a-new-number"
}
Hope this un-blocks you!

How to send report in in scheduled time

In laravel 5.8 I have a report with a button “Send Email” by clicking on this button ajax request is run, with content of a report
in “report_html” var to control like:
public function sentReportEmailContent()
{
$request= request();
$requestData= $request->all();
$report_html= $requestData['report_html'];
$loggedUser= Auth::user();
$reportAvailableSpacesByZonesAcceptorsArray = config('app.reportAvailableSpacesByZonesAcceptorsArray', []);
$site_name = config('app.name', '');
if ( count($reportAvailableSpacesByZonesAcceptorsArray) == 0 ) {
return response()->json(['error_code' => 1, 'message' => 'There are no receiver emails specified !'], HTTP_RESPONSE_INTERNAL_SERVER_ERROR);
}
$to= $reportAvailableSpacesByZonesAcceptorsArray[0];
$subject= 'Available Spaces By Zones report was sent at ' . $site_name;
$additiveVars= [ 'html'=> $report_html ];
unset($reportAvailableSpacesByZonesAcceptorsArray[0]);
$cc= $reportAvailableSpacesByZonesAcceptorsArray;
\Mail::to($to)->send( new SendgridMail( 'emailContainer', $to, $cc, $subject , $additiveVars ) );
return response()->json(['error_code' => 0, 'message' => '', 'user'=> $loggedUser->id], HTTP_RESPONSE_OK);
}
and with Sendgrid service report is sent to users defined in config ok.
Now I need to run this report and send email to recievers in scheduler.
I created a new command :
php artisan make:command reportAvailableSpacesByZones --command=report:available-spaces-by-zones
which has handle method:
public function handle()
{
\Log::info( 'Report run # ' . time() );
}
which is triggered in scheduled time.
But how can I run my report and sent it's content like it is done manually ?
Modified block :
My report is run by (local )url :
http://local-boxbooking2.com/admin/report/available-spaces-by-zones
I remade so that if to run url
http://local-boxbooking2.com/admin/report/available-spaces-by-zones/send-email-on-open
in browser report is opened and checking “send-email-on-open” javascript function is triggered to sent by
email (with Sendgrid service ) content of the page(report actually)
I tried to trigger command by cron tasks :
In app/Console/Commands/reportAvailableSpacesByZones.php :
class reportAvailableSpacesByZones extends Command
{
public function handle()
{
\Log::info( 'Report From inside app/Console/Commands/reportAvailableSpacesByZones.php run # ' . time() );
return redirect()->to('/admin/report/available-spaces-by-zones/send-email-on-open');
}
I see log info , but no reports by email.
Which way is correct ?
Thanks!
In app/Console/Kernal.php add the command to the protected commands array
'App\Console\Commands\reportAvailableSpacesByZones',
in the scheudle method add
$schedule->command('cron:reportAvailableSpacesByZones')->weeklyOn(2, '20:30');
other commands available
https://laravel.com/docs/5.8/scheduling
on the server crontab
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

Laravel 5.5 cashier test subscriptions with short trial

I tested web site subscriptions users through stripe. Website uses php laravel 5.5 with cashier on linux server.
How to make a short grace period for tests?
I use this code:
$user->newSubscription('main', $request->get('subs_type'))
->trialUntil(Carbon::now()->addSeconds(330))
->create($request->get('stripeToken'), [
'email' => $user->email,
]);
instead of the usual
$user->newSubscription('main', $request->get('subs_type'))
->trialDays(1)
->create($request->get('stripeToken'), [
'email' => $user->email,
]);
But after the end of the short trial laravel cashier method onTrial() returns true. Since the method compares dates, not hours or minutes.
/**
* Determine if the subscription is within its trial period.
*
* #return bool
*/
public function onTrial()
{
if (! is_null($this->trial_ends_at)) {
return Carbon::today()->lt($this->trial_ends_at);
} else {
return false;
}
}
Are there any practices that will correct this.
Of course, I do not have to edit the source of laravel

Laravel 5.3 - Omnipay Paypal Express not returning success message

I'm new to Laravel. I've been struggling to implement Paypal Express Checkout in my website for a couple days in order to enable donations to a non-profit organization. Thanks to these explanations I've been able to install Omnipay, let the user input the amount (s)he wants to donate and go to Paypal.
But, when I try to end the transaction (Pay), I'm not redirected to my succes message. My sandbox account does not show any transactions either, so it seems the payment is not completed correctly. I'm guessing there's something wrong with my "getSuccessPayment" function, but I can't figure out what it is...
Here's my Controller so far :
<?php namespace App\Http\Controllers;
use Omnipay\Omnipay;
use Session;
use App\Http\Requests\PaymentRequest;
class PaymentController extends Controller {
public function postPayment(PaymentRequest $request)
{
$price = $request->get('price');
$items[] = array('name' => 'Don', 'quantity' => 1, 'price' => $price);
$params = array(
'cancelUrl'=>url('/donner'),
'returnUrl'=>url('/payment_success'),
'amount' => $price,
'currency' => 'EUR'
);
Session::put('params', $params);
Session::save();
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername('my sandbox email');
$gateway->setPassword('my sandbox password');
$gateway->setSignature('my sandbox signature');
$gateway->setTestMode(true);
$response = $gateway->purchase($params)->setItems($items)->send();
if ($response->isSuccessful()) {
print_r($response);
} elseif ($response->isRedirect()) {
$response->redirect();
} else {
echo $response->getMessage();
}
}
public function getSuccessPayment()
{
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername('my sandbox email');
$gateway->setPassword('my sandbox password');
$gateway->setSignature('my sandbox signature');
$gateway->setTestMode(true);
$params = Session::get('params');
$response = $gateway->completePurchase($params)->send();
$paypalResponse = $response->getData();
if(isset($paypalResponse['PAYMENTINFO_0_ACK']) && $paypalResponse['PAYMENTINFO_0_ACK'] === 'Success') {
return redirect('/payment_success');
} else {
//payment fails
return redirect('/payment_failure');
}
}
}
?>
And my Routes :
Route::post('donner',
['as' => 'payment', 'uses' => 'PaymentController#postPayment']);
Route::get('payment_success', 'PaymentController#getSuccessPayment');
Route::get('payment_failure', 'PaymentController#getSuccessPayment');
When creating your gateway parameters you are passing in /donner as the returnUrl, this is where your users are returned to after completing the PayPal express login and payment confirmation so Laravel would look Route::get('donner'... route which you don't have, changing this to 'returnUrl'=>url('/payment_success'), will bring your users back to your success route and allow you to file the completePurchase call.
Edit for further details based on edited question and comments:
Customers are returned to your returnUrl if the successfully complete the PayPal login and checkout screens, they go to the cancelUrl if for whatever reason they quit the process.
In your PaymentController#getSuccessPayment method paypal will send back a token and payerID in the query string (www.example.com/payment_success?token=EC-12345&PayerID=ABC123, omnipay-paypal will automatically pick up on this in the completePurchase call which is where you are effective confirming with PayPal that the customer completed the checkout correctly and that the transaction was successful.
To avoid confusion I would rename your current Route::get('payment_success', 'PaymentController#getSuccessPayment'); route to something like Route::get('complete_payment', 'PaymentController#getCompletePayment'); and create a new payment_success route that a user is sent to after you have confirmed the status of the payment with PayPal.

omnipay paypal express not returning address

I am using the omnipay setup here: https://github.com/adrianmacneil/omnipay to process a paypal express checkout.
The process works fine in that the user is redirected to paypal -> they login and choose to pay -> they get returned to my site at which point I capture the payment.
The problem I've got is that I need to capture the address they have entered into paypal as their billing / shipping address.
To send the user across to paypal I have the following:
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('XX-USERNAME_XX');
$gateway->setPassword('XX_PASSWORDXX');
$gateway->setSignature('XX_SIG_XX');
$gateway->setTestMode(true);
$response = $gateway->purchase(
array(
'cancelUrl'=>'http://www.XXX.co.uk/',
'returnUrl'=>'http://www.XXX.co.uk/paypalexpress_confirm',
'amount' => $totalamount,
'currency' => 'GBP'
)
)->send();
$response->redirect();
When the user is returned I have the following:
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('XX-USERNAME_XX');
$gateway->setPassword('XX_PASSWORDXX');
$gateway->setSignature('XX_SIG_XX');
$gateway->setTestMode(true);
$response = $gateway->completePurchase(
array(
'cancelUrl'=>'http://www.XXX.co.uk/',
'returnUrl'=>'http://www.XXX.co.uk/paypalexpress_confirm',
'amount' => $totalamount,
'currency' => 'GBP'
)
)->send();
echo $responsemsg=$response->getMessage();
echo '<br><br><br>';
$data = $response->getData();
print_r($data);
Nothing in the response message or the raw data contains the customer address.
Has anyone got this working as i'm struggling and it's the final step to complete the transaction.
For those who are trying to get this work it's as Adrian said.
You first do the normal omnipay paypal payment and then afterwards:
get the token you were given
preform a second call to paypal using the call getexpresscheckoutdetails method
this returns all the info you need
API info here: https://cms.paypal.com/uk/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetExpressCheckoutDetails
The php script paypal provide to do it all for you:
https://cms.paypal.com/cms_content/ES/es_ES/files/developer/nvp_ECGetExpressCheckout_php.txt
omnipay\paypal\ProGateway.php add new function
public function fetchExpressCheckoutDetail(array $parameters = array())
{
return $this->createRequest('\Omnipay\PayPal\Message\FetchExpressCheckoutRequest', $parameters);
}
omnipay\paypal\src\Message add new file FetchExpressCheckoutRequest.php
namespace Omnipay\PayPal\Message;
class FetchExpressCheckoutRequest extends AbstractRequest
{
public function getData()
{
$data = $this->getBaseData('GetExpressCheckoutDetails');
$this->validate('transactionReference');
$data['TOKEN'] = $this->getTransactionReference();
$url = $this->getEndpoint()."?USER={$data['USER']}&PWD={$data['PWD']}&SIGNATURE={$data['SIGNATURE']}&METHOD=GetExpressCheckoutDetails&VERSION={$data['VERSION']}&TOKEN={$data['TOKEN']}";
parse_str (file_get_contents( $url ),$output);
$data = array_merge($data,$output);
return $data;
}
}
Usage:
$response = $gateway->completePurchase($params)->send();
$data = $response->getData();
$gateway->fetchExpressCheckoutDetail(array('transactionReference'=>$data['TOKEN']))->getData();
It will be not the best. But it works. :)
If it's not returned by the $response->getData() method, you might need to call PayPal's GetExpressCheckoutDetails API method to get the extra details about the transaction.
Omnipay doesn't support this out of the box, so you will probably need to copy and customize one of the existing requests to make a separate API call after you have confirmed payment.

Resources