I'm using Braintree to collect payments for a SaaS subscription site built using Laravel 5.2 and testing it on my localhost using ngrok.io. The signup and initial charges work fine. I can cancel and resume subscriptions, as well. However, I can't get the custom webhooks to work. I'm following the Laravel docs. Here's my code:
routes.php
Route::post('/webhook/braintree', 'BraintreeWebhookController#handleWebhook');
BraintreeWebhookController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use Braintree\WebhookNotification;
use Laravel\Cashier\Http\Controllers\WebhookController;
use App\Transaction;
class BraintreeWebhookController extends WebhookController
{
//
public function handleSubscriptionChargedSuccessfully(WebhookNotification $notification)
{
$transaction = new Transaction();
$transaction->subscript_id = $notification->subscription->id;
$transaction->name = $notification->subscription->name;
$transaction->next_bill_date = $notification->subscription->next_bill_date;
$transaction->price = $notification->subscription->price;
$transaction->save();
return new Response('Webhook Handled', 200);
}
public function handleSubscriptionChargedUnsuccessfully(WebhookNotification $notification)
{
//
}
public function handleSubscriptionTrialEnded(WebhookNotification $notification)
{
//
}
}
The Braintree Webhooks Test URL link returns "Success! Server responded with 200." I tested the Transaction model using a test form and it creates a database record just fine. Is the Subscription_Charged_Successfully webhook not making it through to the controller or am I missing something in the handle function? Any help here would be greatly appreciated.
I'm pretty much doing the same as what you've got here. I'm using ngrok to test my local setup with braintree and I've figured out there's an exception with your code.
ErrorException: Undefined property on Braintree\Subscription: name in file /var/www/vendor/braintree/braintree_php/lib/Braintree/Base.php on line 49
Looks like name is not a property on subscription object
I figured out that you can get your local subscription like so
$localSubscription = Subscription::where('braintree_id', $notification->subscription->id)
->first();
Transaction::create([
'subscription_id' => $notification->subscription->id,
'name' => $localSubscription->name,
'next_bill_date' => $notification->subscription-> nextBillingDate,
'price' => $notification->subscription->price,
'user_id' => $localSubscription->user->id,
]);
Where Subscription is Laravel\Cashier\Subscription.
EDIT
Also next_bill_date should be nextBillingDate as seen above.
Related
I'm pretty new to laravel and Telegram API and I want to develop a bot that can reply your input messages.
For now I can send messages when I refresh a route, but I would like it to send a message and reply once your start a conversation with him.
I'm using this sdk: https://telegram-bot-sdk.readme.io/docs
and so far I managed to call the send message method:
class TelegramController extends Controller {
public function index(){
$chatID = 'xxxxxxx';
Telegram::sendMessage([
'chat_id' => $chatID,
'text' => '[▼皿▼]'
]);;
}
}
How can I add some sort of listener that can reply to user input?
Thanks in advance.
At first, you should add the route in routes/web.php, like this:
Route::post('/bot_webhook', 'TelegramController#index')->middleware('api');
And you should get update improve TelegramController like this:
class TelegramController extends Controller {
public function index(){
$update = json_decode(file_get_contents('php://input'));
# I'm not sure what library do you use, but it should work if it was like this
Telegram::sendMessage([
'chat_id' => $update->message->chat->id,
'text' => '[▼皿▼]'
]);;
}
}
I have created an Event called UserWalletNewTransaction.php and added this to it:
public $transaction;
public function __construct($transaction) {
$this->$transaction = $transaction;
}
Now in order to fire this event at the Controller, I coded this:
$newTransaction = UserWalletTransaction::create(['user_id' => $user_id, 'wallet_id' => $wallet_id, 'creator_id' => $creator_id, 'amount' => $amount_add_value, 'description' => $trans_desc]);
event(new UserWalletNewTransaction($newTransaction));
Then at the Listener, UserWalletNotification.php, I tried:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
}
So the scenario is, when Admins create a new Transaction for a custom user, a new alert message must be sent for him/her to let him/her know that new transaction was added for him/her.
But I don't really know how to do that.. So if you know, please let me know, I would really appreciate that...
Thanks in advance.
If by alert you mean showing a message on the web interface, use flash data.
https://laravel.com/docs/5.8/session#flash-data
$newTransaction = UserWalletTransaction::create(...);
event(new UserWalletNewTransaction($newTransaction));
$request->session()->flash('status', 'Transaction done.');
return view(...)
<span>{{ session('status') }}</span>
If you mean sending an email, just use the Mail facade in your listener to send a mailable.
https://laravel.com/docs/5.8/mail#sending-mail
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
Mail::to($user)->send(new TransactionDoneMail($event->transaction)); // TransactionDoneMail being your mailable class, made with "php artisan make:email TransactionDoneMail"
}
There are nice examples on how to build a mailable class in the documentation.
https://laravel.com/docs/5.8/mail#writing-mailables
There are many different things you can do in terms of "alerting" the customer.
One route would be to send an email or text message in your event listener. See https://laravel.com/docs/5.8/mail for help doing it via email.
Another way would be using browser push notifications. You could use OneSignal for this. You would setup the front end to display an alert to a customer user asking if they would like to subscribe to push notifications. When they subscribe, you will get back an ID for that specific user. Make an API call to your Laravel app, and store that ID in the users table (you will need a migration). Then from within your event listener, you can make a call to OneSignal's API and send the user a notification, which will popup on their computer.
Here is an example of using OneSignal to send an event to a user via the API:
Your OneSignal service:
<?php
namespace App\Services;
use App\User;
use GuzzleHttp\Client;
class OneSignalService
{
public function sendNotificationToUser(User $user, string $title, string $message, string $url, string $subtitle = null)
{
if (!$user->one_signal_id) {
return;
}
$fields = [
'app_id' => config('services.onesignal.app_id'),
'include_player_ids' => [$user->one_signal_id],
'headings' => ['en' => $title],
'contents' => ['en' => $message],
'url' => $url,
];
if ($subtitle) {
$fields['subtitle'] = ['en' => $subtitle];
}
$client = new Client([
'base_uri' => 'https://onesignal.com/api/v1/',
'headers' => [
'Content-Type' => 'application/json; charset=utf-8',
'Authorization' => 'Basic <<API_KEY>>',
]
]);
$client->request('POST', 'notifications', [
'json' => $fields
])
}
}
UserWalletNotification:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
$oneSignal = new OneSignalService();
$oneSignal->sendNotificationToUser($user, 'New Transaction', 'You have a new transaction', 'yourwebsite.com');
}
The way I would go about this would be via broadcasting, which would use websockets to instantly send the customer user an alert to their browser, in which you could then display a popup of some sort. You could install Laravel Echo Server, but to keep things simple you can use Pusher. Follow the guide to install on the front end of your website.
Then, create a private channel specific to a customer user "transaction.created.{{USER ID}}" and listen for it on your front end.
Within Laravel you will install the PHP Pusher SDK via composer.
Then within your .env file set:
BROADCAST_DRIVER=pusher
Next, open up channels.php within your routes directory in Laravel and add:
Broadcast::channel('transaction.created.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
This will verify authentication for your user to the private channel.
Create an Laravel Event:
<?php
namespace App\Events;
use App\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class TransactionCreated implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user = null;
public $transaction = null;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(User $user, UserWalletTransaction $transaction)
{
$this->user = $user;
$this->transaction = $transaction;
}
public function broadcastWith(): array
{
return $this->transaction->toArray(); //Or whatever information you want to send to the front end
}
public function broadcastAs(): string
{
return 'TransactionCreated';
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('transaction.created.' . $this->user->id);
}
}
Fire the event from UserWalletNotification:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
event(new TransactionCreated($user, $event->transaction));
}
Lastly, create some sort of popup and display it on the front end when your callback function for the private channel is hit.
If you need anymore help, feel free to comment.
What you want to do I believe, is asynchronous notifications.
Well, if you really mean flash messages - those who are stored in session - it will not be so easy.
Normal steps are create flash message for the user currently logged in on a website, stored in session that is unique for the current user. It can be shown only for this user.
What you want is to create flash message as the admin (from admin perspective) , then only to admin it can be shown.
I would do this, create new table, when these notification messages will be stored. Some table with columns like id, user_id, message, type, created_date, shown_date. Admins will put alert/notification messages for each user. Then create class (can be in controller for example) that will check this table for each user and if there is new not already shown message, show it normally in flash message for that current user. Dont forget to mark that message as shown. That is it.
So much for custom solution. I belive there must be some for example jQuery/other Jvascript plugins or Laravel plugins for asynchronous notifications, please check those.
I try to send notification to Twitter when new post created but I'm getting:
Couldn't post Notification. Response: Bad Authentication data.
Codes
Notification class
use NotificationChannels\Twitter\TwitterChannel;
use NotificationChannels\Twitter\TwitterStatusUpdate;
use App\Post;
class PostPublished extends Notification
{
use Queueable;
public function via($notifiable)
{
return [TwitterChannel::class, TelegramChannel::class];
}
public function toTwitter($post)
{
$title = $post->title;
$slug = $post->slug;
$image = $post->image;
return new TwitterStatusUpdate($title .' https://domain.co/blog/'. $slug, [$image]);
}
Post controller
use Illuminate\Notifications\Notifiable;
use App\Notifications\PostPublished;
$post->save();
$post->notify(new \App\Notifications\PostPublished($post));
Post model
use Illuminate\Notifications\Notifiable;
use Notifiable;
Question
Why I'm getting this error?
How to fix it?
It is definitely something wrong with your configuration or your tokens. It looks to me as if something is not set up properly. In your config/services.php file do you have the following?
'twitter' => [
'consumer_key' => env('TWITTER_CONSUMER_KEY'),
'consumer_secret' => env('TWITTER_CONSUMER_SECRET'),
'access_token' => env('TWITTER_ACCESS_TOKEN'),
'access_secret' => env('TWITTER_ACCESS_SECRET')
]
Please check to ensure all of these are set correctly using tinker. In the terminal type php artisan tinker and then check each of the following one line at a time:
env('TWITTER_CONSUMER_KEY'),
env('TWITTER_CONSUMER_SECRET'),
env('TWITTER_ACCESS_TOKEN'),
env('TWITTER_ACCESS_SECRET')
SOLVED
All I needed to do was :
php artisan config:cach
composer dump-autoload
now it's working like charm.
Note it can be type error in any part of .env file, not only twitter access tokens and key, for example:
APP_NAME=Name of your App
instead of:
APP_NAME="Name of your App"
I'm building a simple contact book from office 365, that lists all the shared contacts of my company.
I was trying with Graph and EWS too, but i can't figure out what is wrong.
Searching in the Microsoft Graph explorer seems to be no chance to see my "Other Contacts" -> "All Contacts" folder.
I've been trying with "/me/contactFolders" endpoint and with "/people" endpoint. Non of them gave me results.
I also used a php-ews library (the project is build on Laravel) to access to the folders via Exchange, with no luck.
using this example, i'm able to list just my contact, without any chance to see other folders or other kind of contacts.
Does anybody have any tip for a newbbie?!
thanks in advance.
EDIT
this is the Controller that works with PHP-EWS library
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use garethp\ews\ContactsAPI as ContactsAPI;
use garethp\ews\API;
use garethp\ews\API\Enumeration;
use garethp\ews\API\Type;
use garethp\ews\API\ExchangeWebServices;
//use garethp\ews\API\ExchangeAutodiscover;
//use garethp\ews\API\Exception\AutodiscoverFailed;
class SharedContatctsController extends Controller
{
//
public function index()
{
# code...
$mail='developeremail#example.com';
$pwd='password';
$version='Exchange2016';
//$apiS = ExchangeAutodiscover::getAPI($mail, $pwd);
//$server=$apiS->getClient()->getServer();
$server='mail.example.com;
$api = ContactsAPI::withUsernameAndPassword($server, $mail, $pwd);
$contacts = $api->getContacts();
//return print($api->getFolderId());
//If you want to get contacts within a folder
//$folder = $api->getFolderByDisplayName('Altri Contatti', 'contacts');
//$contacts = $api->getContacts($folder->getFolderId());
return view('shared',array('contacts'=>$contacts,'displayName'=>$contacts['displayName']));
}
}
This is the Controller that (quite works) displays the "BackupContacts" folder that is in the same directory as "Contact"
<?php
namespace App\Http\Controllers;
use Microsoft\Graph\Graph;
use Microsoft\Graph\Model;
use App\Http\Controllers\Controller;
class OutlookController extends Controller
{
public function contacts()
{
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
$tokenCache = new \App\TokenStore\TokenCache;
$graph = new Graph();
$graph->setAccessToken($tokenCache->getAccessToken());
$user = $graph->createRequest('GET', '/me')
->setReturnType(Model\User::class)
->execute();
$contactsQueryParams = array (
// // Only return givenName, surname, and emailAddresses fields
//"\$select" => "displayName,scoredEmailAddresses",
// Sort by given name
//"\$orderby" => "givenName ASC",
// Return at most 10 results
"\$orderby"=>"displayName",
"\$top" => "1000"
);
$getContactsUrl = '/me/contactFolders/{BackuPlderId-retrieved-with-Graph}/contacts/?'.http_build_query($contactsQueryParams);
$contacts = $graph->createRequest('GET', $getContactsUrl)
->addHeaders(array ('X-AnchorMailbox' => $user->getMail()))
->setReturnType(Model\Contact::class)
->execute();
return view('contacts', array(
'username' => $user->getdisplayName(),
'usermail' => $user->getMail(),
'contacts' => $contacts
));
}
}
The /me/contacts will return only the contacts in the current user's default contact folder. Similarly, the /me/contactFolders will only return the contact folders for the current user's mailbox (and an empty result if there isn't anything beyond their default folder.
The key here is the /me element. This is synonymous with currently authenticated user. So if the user who authenticated was person#company.com both /me/contacts and /users/person#company.com/contacts would return the exact same results.
The currently isn't a method for retrieving Organization Contacts (i.e. contacts stored in your GAL) from the /v1.0 API set. There is however an orgContact object in the /beta API so support is forthcoming.
I'm having a nightmare trying to set up a cron job in my Symfony2 project.
I understand the principle of setting it up and where to put the code but I just cannot get it to do what I need.
Basically, I need the cron job to run every day and check a database of clients in order to find out if an invoice needs sending. The actual client referencing is yet to be done but I have written a test which I want to generate and email and invoice based on hardcoded values I pass to the function.
// AppBundle/Command/CronRunCommand.php
protected function execute(InputInterface $input, OutputInterface $output)
{
$request = new Request();
$request->attributes->set('client','14');
$request->attributes->set('invoice_id','3');
$request->attributes->set('dl','0');
$output->writeln('<comment>Running Invoice Cron Task...</comment>');
return $this->getContainer()->get('invoices')->generateInvoiceAction($request);
}
I have set invoices up as a service in my config.yml:
services:
invoices:
class: AppBundle\Controller\InvoiceController
And in InvoiceController there is a function that will generate an invoice by using Invoice Painter Bundle and then send it to the specified email address (currently hard coded for development purposes).
When I run the cron command on my console, it throws the following error:
[Symfony\Component\Debug\Exception\FatalErrorException]
Error: Call to a member function has() on null
I have searched for this and I believe it is to do with the fact that it's referencing a controller method and my command file does not extend controller, but I'm so confused about how I can do this - surely there is a way of running a method in a controller as a cron job?
Any help appreciated.
Michael
I fear you may still not be understanding the big picture. Console apps don't have a request object and thus the whole request_stack is not going to work. I know you tried creating a request object but that is not going to impact the request stack.
Your console app should look something like:
protected function execute(InputInterface $input, OutputInterface $output)
{
$data = [
'client' => 14,
'invoice' => 3,
'dl' => 0,
];
$invoiceManager = $this->getContainer()->get('invoices');
$results = $invoiceManager->generateInvoice($data);
}
Your controller action would be something like:
public function generateInvoiceAction(Request $request)
{
$data = [
'client' => $request->attribute->get('client'),
'invoice' => $request->attribute->get('invoice'),
'dl' => $request->attribute->get('dl'),
];
$invoiceManager = $this->getContainer()->get('invoices');
$results = $invoiceManager->generateInvoice($data);
The invoice manager might look like:
class InvoiceManager {
public function __construct($em) {
$em = $this->em;
}
public function generateInvoice($data) {
$client = $this->em->find('Client',$data['client']);