Below is an example code for "Paytrail_Module_Rest.php", a set of classes for interacting with a rest api for a payment gateway. Some of the classes can be instantiated ahead of time such as (Paytrail_Module_rest which holds credentials), but some need to be instantiated with information only available in the controller, (such as Paytrail_Module_Rest_Payment_S1 which sets payment details such as price)
Can anyone suggest a clean way of injecting it into slim3? I can't see any good way of doing it with the standard container injection methods.
$urlset = new\App\Service\Paytrail\Paytrail_Module_Rest_Urlset(
"https://www.demoshop.com/sv/success", // return address for successful payment
"https://www.demoshop.com/sv/failure", // return address for failed payment
"https://www.demoshop.com/sv/notify", // address for payment confirmation from Paytrail server
"" // pending url not in use
);
$orderNumber = '1';
$price = 99.00;
$payment = new \App\Service\Paytrail\Paytrail_Module_Rest_Payment_S1($orderNumber, $urlset, $price);
$payment->setLocale('en_US');
$module = new \App\Service\Paytrail\Paytrail_Module_Rest(13466, '6pKF4jkv97zmqBJ3ZL8gUw5DfT2NMQ');
try {
$result = $module->processPayment($payment);
}
catch (\App\Service\Paytrail\Paytrail_Exception $e) {
die('Error in creating payment to Paytrail service:'. $e->getMessage());
}
echo $result->getUrl();
( credentials listed here are public test credentials )
Add the stuff that doesn't change to the container like the module and the urlset thingy
$container[\App\Service\Paytrail\Paytrail_Module_Rest_Urlset::class] = function($c) {
return new \App\Service\Paytrail\Paytrail_Module_Rest_Urlset(
"https://www.demoshop.com/sv/success", // return address for successful payment
"https://www.demoshop.com/sv/failure", // return address for failed payment
"https://www.demoshop.com/sv/notify", // address for payment confirmation from Paytrail server
"" // pending url not in use
);
};
$container[\App\Service\Paytrail\Paytrail_Module_Rest::class] = function($c) {
return new \App\Service\Paytrail\Paytrail_Module_Rest(13466, '6pKF4jkv97zmqBJ3ZL8gUw5DfT2NMQ');
};
And then you either can instantiate the payment every time you need or add a helper class like an adapter:
class PaymentAdapter {
public function __construct(
\App\Service\Paytrail\Paytrail_Module_Rest $module,
\App\Service\Paytrail\Paytrail_Module_Rest_Urlset $urlset)
{
$this->module = $module;
$this->urlset = $urlset;
}
function createAndProcessPayment($orderNumber, $price)
{
$payment = new \App\Service\Paytrail\Paytrail_Module_Rest_Payment_S1($orderNumber, $this->urlset, $price);
$payment->setLocale('en_US');
try {
$result = $module->processPayment($payment);
}
catch (\App\Service\Paytrail\Paytrail_Exception $e) {
die('Error in creating payment to Paytrail service:'. $e->getMessage());
}
return $result;
}
}
Then add the adapter also to the container:
$container[\yournamespace\PaymentAdapter::class] = function($c) {
return new \yournamespace\PaymentAdapter(
$c[\App\Service\Paytrail\Paytrail_Module_Rest::class],
$c[\App\Service\Paytrail\Paytrail_Module_Rest_Urlset::class]
);
};
Related
Good morning everyone.
I need to send a notification whenever a user swipes a tab on a particular sensor.
The problem is not the connection to the sensor, which at this moment already takes place and subject to user access.
Currently I have created a server socket inside my yii2 app to be able to send the notification event to the client and update them in real time.
This is my controller server
class ServerController extends Controller
{
public function actionStart()
{
// $server = new CommandsServer();
$server = new ChatServer();
$server->port = 80; //This port must be busy by WebServer and we handle an error
$server->on(WebSocketServer::EVENT_WEBSOCKET_OPEN_ERROR, function ($e) use ($server) {
echo "Error opening port " . $server->port . "\n";
$server->port += 1; //Try next port to open
$server->start();
});
$server->on(WebSocketServer::EVENT_WEBSOCKET_OPEN, function ($e) use ($server) {
echo "Server started at port " . $server->port;
});
$server->start();
}
}
This is my chat server which I created for testing
<?php
namespace frontend\daemons;
use consik\yii2websocket\events\WSClientEvent;
use consik\yii2websocket\WebSocketServer;
use Ratchet\ConnectionInterface;
class ChatServer extends WebSocketServer
{
public function init()
{
parent::init();
$this->on(self::EVENT_CLIENT_CONNECTED, function (WSClientEvent $e) {
$e->client->name = null;
});
}
protected function getCommand(ConnectionInterface $from, $msg)
{
$request = json_decode($msg, true);
return !empty($request['action']) ? $request['action'] : parent::getCommand($from, $msg);
}
public function commandPing(ConnectionInterface $client, $msg)
{
$arr = ["Neo", "Morpheus", "Trinity", "Cypher", "Tank"];
$res = ['type' => 'ping', 'message' => json_encode(array_rand($arr, 1))];
foreach ($this->clients as $chatClient) {
$chatClient->send(json_encode($res));
}
}
}
Now, what I wish I could do is be able to use that commandPing inside another controller, but I haven't found a way I can implement this.
In this sense the round would be:
user swipes card on sensor -> the sensor calls my method to see if the user is actually authorized to enter -> I call commandPing (as an example) to send a notification to the customer (OK / KO )
On the web interface side I will then intercept the message via new Websocket (but this is not a problem)
I'm implementing gocardless api in my website. I'm stuck in webhook. When i'm sending an webhook from sandbox test environment into my website, it showing 200 response, but after that no code is executing. and also i'm not seeing anything in response body, its showing null.
I'm using laravel 5.7 for that. Here is my code
route.php
Route::post('/webhook', 'HomeController#webhook');
HomeController.php
public function webhook()
{
$webhook_endpoint_secret = env("GOCARDLESS_WEBHOOK_ENDPOINT_SECRET");
$request_body = file_get_contents('php://input');
$headers = getallheaders();
$signature_header = $headers["Webhook-Signature"];
try {
$events = Webhook::parse($request_body, $signature_header, $webhook_endpoint_secret);
foreach ($events as $event) {
print("Processing event " . $event->id . "\n");
switch ($event->resource_type) {
case "mandates":
$this->process_mandate_event($event);
break;
default:
print("Don't know how to process an event with resource_type " . $event->resource_type . "\n");
break;
}
}
header("HTTP/1.1 204 OK");
} catch(InvalidSignatureException $e) {
header("HTTP/1.1 498 Invalid Token");
}
}
public function process_mandate_event($event)
{
switch ($event->action) {
case "cancelled":
print("Mandate " . $event->links["mandate"] . " has been cancelled!\n");
break;
default:
print("Don't know how to process a mandate " . $event->action . " event\n");
break;
}
}
I tried to execute some database query, nothing is working anyway. Can anyone point me out what and where i'm doing wrong?
$responseBody = file_get_contents('php://input');
if ($responseBody <> "") {
$response_new = json_decode($responseBody, true);
foreach ($response_new["events"] as $event) {
print_r($event); // you will see all the data which you want
//if($event['resource_type'] == 'subscriptions')
//payments,mandates or etc...
//
}
}
use email sending the code to debugging, when webhook called, email sends to your address with response body then you will data in the email body.
Hope you understand
The best way to handle the webhooks with Laravel and it's structure is to add a Middleware to verify the webhook signature:
public function handle($request, Closure $next)
{
$signature = $request->header('Webhook-Signature');
if (!$signature) {
throw WebhookFailed::missingSignature();
}
if (!$this->isValid($signature, $request->getContent(), $request->route('configKey'))) {
throw WebhookFailed::invalidSignature($signature);
}
return $next($request);
}
The isValid method will check the signature of the webhook and your saved secret.
Then at your controller, you can handle the events that come from the webhook (remember that Gocardless can send more than one event in a single webhook request).
public function __invoke(Request $request)
{
$payload = $request->input();
foreach ($payload['events'] as $event) {
// Do whatever do you need with the events.
}
}
return response()->json(['message' => 'ok']);
}
We have created a package for Laravel can help you with the handling and processing of the Gocardless webhooks.
Nestednet/Gocardless-laravel
Got the solution. I was having the problem while getting the headers values. In laravel you can't get header value using $headers = getallheaders(); You need to use use Request; and then Request::header("Webhook-Signature"); which then solved my problem.
Using the ci-php-unit-test library to do unit tests for PHP/codeigniter, and unit testing controller methods.
Having trouble working out how to mock an external library that is installed using composer.
my SUT method is:
function twitter()
{
$this->load->model('misc/twitter_model');
$request_token = [];
$request_token['oauth_token'] = $_SESSION['twitter_oauth_token'];
$request_token['oauth_token_secret'] = $_SESSION['twitter_oauth_token_secret'];
if ( (isset($_GET['oauth_token'])
&& ($request_token['oauth_token'] !== $_GET['oauth_token'])))
{
log_message('info','abort something is wrong!');
}
else
{
$connection = new Abraham\TwitterOAuth\TwitterOAuth(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, $request_token['oauth_token'], $request_token['oauth_token_secret']);
$access_token = $connection->oauth("oauth/access_token", array("oauth_verifier" => $_REQUEST['oauth_verifier']));
$this->session->set_userdata('twitter_access_token',$access_token);
redirect(get_session('twitter_callback1'));
}
}
my test method (so far) is:
public function test_twitter()
{
$_SESSION['twitter_oauth_token'] = 'twitter_oauth_token';
$_SESSION['twitter_oauth_token_secret'] = 'twitter_oauth_token_secret';
$this->request->setCallable(
function (& $CI) {
// Get mock object
$twitter_oa = $this->getMockBuilder('TwitterOAuth')
->disableOriginalConstructor()
->setMethods(['oauth'])
->getMock();
$twitter_oa->method('oauth')
->willReturn('access_token');
}
);
$output = $this->request('GET','callbacks/twitter',['oauth_token'=>'twitter_oauth_token']);
var_export($output);
}
But the original library is being executed because it isn't being mocked - the $twitter_oa isn't being attached to the CI instance.
This is because the external library has not been instantiated after the codeigniter controller has been instantiated. (this is was the setCallable method does)
My question is, how can I mock TwitterOAuth after the codeigniter controller is instantiated so it can return set testing text?
(and obviously not instantiate the twitter Oauth library)
ok, so when you see a new Class(); in unit testing, you have to re-work something. This may not be the best option, but this works for me.
in the SUT, the controller code is now:
function twitter()
{
$this->load->model('misc/twitter_model');
$request_token = [];
$request_token['oauth_token'] = $_SESSION['twitter_oauth_token'];
$request_token['oauth_token_secret'] = $_SESSION['twitter_oauth_token_secret'];
if ( (isset($_GET['oauth_token'])
&& ($request_token['oauth_token'] !== $_GET['oauth_token'])))
{
log_message('info','abort something is wrong!');
}
else
{
$connection = $this->twitter_connection($request_token['oauth_token'],$request_token['oauth_token_secret']);
$access_token = $connection->oauth("oauth/access_token", array("oauth_verifier" => $_REQUEST['oauth_verifier']));
$this->session->set_userdata('twitter_access_token',$access_token);
redirect(get_session('twitter_callback1'));
}
}
/**
* #param $token
* #param $secret
* #return \Abraham\TwitterOAuth\TwitterOAuth
*
* #codeCoverageIgnore
*
*/
public function twitter_connection($token, $secret)
{
return new Abraham\TwitterOAuth\TwitterOAuth(
TWITTER_CONSUMER_KEY,
TWITTER_CONSUMER_SECRET,
$token,
$secret);
}
There is a new file in APPPATH.tests/mocks/external_libaries with:
class MockTwitterOAuth
{
public function oauth()
{
return 'access_token';
}
}
and the test code is now:
public function test_twitter()
{
$_SESSION['twitter_oauth_token'] = 'twitter_oauth_token';
$_SESSION['twitter_oauth_token_secret'] = 'twitter_oauth_token_secret';
$_SESSION['twitter_callback1'] = 'cb1';
require_once(APPPATH.'tests/mocks/external_libraries/MockTwitterOauth.php');
$twitter_connection = new MockTwitterOAuth();
MonkeyPatch::patchMethod('Callbacks',[
'twitter_connection'=>$twitter_connection
]);
$output = $this->request('GET','callbacks/twitter',['oauth_token'=>'twitter_oauth_token']);
$this->assertNull($output);
$this->assertResponseCode(HTTP_FOUND);
$this->assertRedirect(base_url('cb1'));
$this->assertEquals('access_token',$_SESSION['twitter_access_token']);
}
The trick is to have twitter connection return a new TwitterOAuth class normally, but when the system is unit testing, return the MockTwitterOAuth class that only has one method instead. This can be accomplished by monkeypatching the controller code.
Hope this answer is useful for others, and if you haven't used it already the https://github.com/kenjis/ci-phpunit-test is pretty good, even though it is hard to get started. Purchasing the companion book is recommended!
I try to build a little realtime websocket use-case, where users can login and see all other users logged in, get notified when a new user signs in or an existing user logs out.
For this scenario i use the ZMQ PUSH Socket in my UserController when a user logs in or logs out.
UserConstroller
public function login() {
//... here is the auth code, model call etc...
$aUserData = array();// user data comes from the database with username, logintime, etc....
$context = new \ZMQContext();
$oSocket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'USER_LOGIN_PUSHER'); // use persistent_id
if($oSocket instanceof \ZMQSocket) {
$oSocket->connect("tcp://127.0.0.1:5555"); //
$oSocket->send(json_encode($aUserData));
}
}
public function logout() {
//... here is the logout code, model call etc ....
$aUserData = array();// user data comes from the SESSION with username, logintime, etc....
$context = new \ZMQContext();
$oSocket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'USER_LOGOUT_PUSHER'); // use persistent_id
if($oSocket instanceof \ZMQSocket) {
$oSocket->connect("tcp://127.0.0.1:5555"); //
$oSocket->send(json_encode($aUserData));
}
}
Then i've got a Pusher class like in the Ratchet docs: link
In this class there are two methods: onUserLogin and onUserLogout and of course all the other stuff like
onSubscribe, onOpen, onPublish
UserInformationPusher
public function onUserLogin($aUserData) {
//var_dump("onUserLogin");
$sUserData = json_decode($aUserData, true);
$oTopic = $this->subscribedTopics["user_login"];
if($oTopic instanceof Topic) {
$oTopic->broadcast($sUserData);
} else {
return;
}
}
public function onUserLogout($aUserData) {
//var_dump("onUserLogout");
$entryData = json_decode($aUserData, true);
$oTopic = $this->subscribedTopics["user_logout"];
if($oTopic instanceof Topic) {
$oTopic->broadcast($entryData);
} else {
return;
}
}
The last piece is the WampServer/WsServer/HttpServer with a Loop that listens to the incoming connections. There is also my ZMQ PULL socket
RatchetServerConsole
public function start_server() {
$oPusher = new UserInformationPusher();
$oLoop = \React\EventLoop\Factory::create();
$oZMQContext = new \React\ZMQ\Context($oLoop);
$oPullSocket = $oZMQContext->getSocket(\ZMQ::SOCKET_PULL);
$oPullSocket->bind('tcp://127.0.0.1:5555'); // Binding to 127.0.0.1 means the only client that can connect is itself
$oPullSocket->on('message', array($oPusher, 'onUserLogin'));
$oPullSocket->on('message', array($oPusher, 'onUserLogout'));
$oMemcache = new \Memcache();
$oMemcache->connect('127.0.0.1', 11211);
$oMemcacheHandler = new Handler\MemcacheSessionHandler($oMemcache);
$oSession = new SessionProvider(
new \Ratchet\Wamp\WampServer(
$oPusher
),
$oMemcacheHandler
);
//$this->Output->info("Server start initiation with memcache!...");
$webSock = new \React\Socket\Server($oLoop);
$webSock->listen(8080, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect
$oServer = new \Ratchet\Server\IoServer(
new \Ratchet\Http\HttpServer(
new \Ratchet\WebSocket\WsServer(
$oSession
)
),
$webSock
);
$this->Output->info("Server started ");
$oLoop->run();
}
In this example, the call from login() or logout() would always call both methods(onUserLogin and onUserLogout).
I was not able to find some docs, which describe what events i can use in the on($event, callable $listener) method, does anyone have a link/knowledge base?
What is the best approach to check which method from the UserController was fired?
I could add some information to the $sUserData in the Controller and check this in the Pusher
I could bind an other socket to a different port (e.g. 5554 for PULL and PUSH) and use the on() method on this one
I could... is there another best practice to solve this?
No Client code needed cause it works fine
In your RatchetServerConsole,
Remove,
$oPullSocket->on('message', array($oPusher, 'onUserLogin'));
$oPullSocket->on('message', array($oPusher, 'onUserLogout'));
Add,
$oPullSocket->on('message', array($oPusher, 'onUserActionBroadcast'));
.
In your UserInformationPusher,
Remove onUserLogin() and onUserLogout().
Add,
public function onUserActionBroadcast($aUserData)
{
$entryData = json_decode($aUserData, true);
// If the lookup topic object isn't set there is no one to publish to
if (!array_key_exists($entryData['topic'], $this->subscribedTopics)) {
return;
}
$topic = $this->subscribedTopics[$entryData['topic']];
unset($entryData['topic']);
// re-send the data to all the clients subscribed to that category
$topic->broadcast($entryData);
}
.
Your UserConstroller (add the topic in $aUserData),
public function login() {
//... here is the auth code, model call etc...
$aUserData = array();// user data comes from the database with username, logintime, etc....
$aUserData['topic'] = 'USER_LOGIN'; // add the topic name
$context = new \ZMQContext();
$oSocket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'my pusher'); // use persistent_id
if($oSocket instanceof \ZMQSocket) {
$oSocket->connect("tcp://127.0.0.1:5555"); //
$oSocket->send(json_encode($aUserData));
}
}
public function logout() {
//... here is the logout code, model call etc ....
$aUserData = array();// user data comes from the SESSION with username, logintime, etc....
$aUserData['topic'] = 'USER_LOGOUT'; // add the topic name
$context = new \ZMQContext();
$oSocket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'my pusher'); // use persistent_id
if($oSocket instanceof \ZMQSocket) {
$oSocket->connect("tcp://127.0.0.1:5555"); //
$oSocket->send(json_encode($aUserData));
}
}
.
Finally in your view file,
<script>
var conn = new ab.Session('ws://yourdomain.dev:9000', // Add the correct domain and port here
function() {
conn.subscribe('USER_LOGIN', function(topic, data) {
console.log(topic);
console.log(data);
});
conn.subscribe('USER_LOGOUT', function(topic, data) {
console.log(topic);
console.log(data);
});
},
function() {
console.warn('WebSocket connection closed');
},
{'skipSubprotocolCheck': true}
);
</script>
.
NOTE: The basic idea was to use a single broadcast function in the pusher class.
After one month of intensive handling with PHPs best practice in websockets i changed from my approach to the Crossbar.io, voryx/Thruway in the PHP Backend and Autobahn|JS in the Frontend.
All of these componentes support the WAMP V2 Websocket Standard and are able to handle my requirements.
If there are some requests i can post the solution to my problem above, with the usage of the mentioned components.
I am trying to setup an Oauth server using the pecl-php oauth library http://php.net/manual/en/book.oauth.php
This code assumes that the client has already received a user verified access token, for simplicity sake I've not included any database calls and have hardcoded matching values into my client and provider.
Class OauthVerify
{
private static $consumer_secret = 'f63ed7f7a8899e59d3848085c9668a0d';
private static $token_secret = '72814e6059441037152eecef2e8559a748b84259';
private $provider;
public function __construct()
{
$this->provider = new OAuthProvider();
$this->provider->consumerHandler(array($this,'consumerHandler'));
$this->provider->timestampNonceHandler(array($this,'timestampNonceHandler'));
$this->provider->tokenHandler(array($this,'checkAccessToken'));
}
//Check the client request
public function checkRequest()
{
try {
$this->provider->checkOAuthRequest();
} catch (Exception $Exception) {
return OAuthProvider::reportProblem($Exception);
}
return true;
}
public static function timestampNonceHandler($Provider)
{
//I'm leaving out this logic now, to keep it simple and for testing purposes
return OAUTH_OK;
}
public static function consumerHandler($Provider)
{
//I'm leaving out this logic now, to keep it simple and for testing purposes
$Provider->consumer_secret = self::$consumer_secret;
return OAUTH_OK;
}
public static function checkAccessToken($Provider)
{
$Provider->token_secret = self::$token_secret;
return OAUTH_OK;
}
}
The above code should give me the barebones I need to authenticate an Oauth request.
Before any particular route is executed I call the $OauthVerify->checkRequest() method which checks if the client request is valid, however the server keeps throwing a 'signatures do not match' error. I don't think that the problem is with the clients as I've tried both postman (for chrome) and a PHP implementation and they both generate the same signature. I have however for interest sake included my client call.
$consumer_key = '87d6d61e87f0e30d8747810ae40041d1';
$consumer_secret = 'f63ed7f7a8899e59d3848085c9668a0d';
$token= 'b9d55b3ec4b755d3fe25d7a781da1dfd044b5155';
$token_secret = '72814e6059441037152eecef2e8559a748b84259';
$timestamp = '1417515075';
$nonce = '9QV4rn';
$version = '1.0';
$method = 'GET';
$url = 'https://localhost/micro/v1/nappi';
try {
$oauth = new OAuth($consumer_key, $consumer_secret, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
$oauth->enableDebug();
$oauth->disableSSLChecks();
$oauth->setNonce($nonce);
$oauth->setTimestamp($timestamp);
$oauth->setToken($token, $token_secret);
$oauth->setVersion($version);
$oauth->fetch("$url");
$json = json_decode($oauth->getLastResponse());
print_r($json);
}
catch(OAuthException $E) {
print_r($E);
}
I've burned a good couple of hours trying to figure this out, someone please help!
I finally managed to solve it, my .htaccess file had a rewrite rule that was processing a _url parameter for my framework. This parameter was ofcourse being included in the signature that the server generated. I simply instructed OAuth Provider to ignore the the _url parameter in my constructor:
$this->provider->setParam('_url',NULL);,
that was all it took, everything runs perfectly now.