I have an application where users will enter accommodation details including the cost per night and then select the appropriate currency from a drop down. I am then using fixer.io to show the cost in both the currency it was entered in as well as converted to the user's default currency.
The cost is stored in the database as an integer. I have methods convertToInteger and convertFromInteger that take the currency, look up how many subunits there are and do the appropriate math.
There is also a formatCurrency method to format according to currency. And a convertBetweenCurrency method for the actual conversion.
The convertFromInteger method is called inside the formatCurrency method.
The $cost_per_night_entered works fine and displays the correct amount and formatting. However, the $cost_per_night_user appears to be converted from Integer twice - it is always out by that unit. So, $148 is displayed as $1.48.
I've tried using Laravel's fresh() and refresh() to no avail. Is this related to 'nesting' the calls together? I don't think I've done something dumb like call it twice - but fresh eyes may prove otherwise :)
Controller show method:
$cost_per_night_entered = formatCurrency($accommodation->cost_per_night, $accommodation->currency, getLocale($accommodation->country));
$cost_per_night_user = formatCurrency(convertBetweenCurrency($accommodation->cost_per_night, $accommodation->currency, Auth::user()->currency), Auth::user()->currency, request()->server('HTTP_ACCEPT_LANGUAGE'));
Model methods:
function formatCurrency($amount, $currency_code, $locale)
{
$value = intval(convertFromInteger($amount, $currency_code));
$money = new Money\Money($value, new Money\Currency($currency_code));
$currencies = new Money\Currencies\ISOCurrencies();
$numberFormatter = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$moneyFormatter = new Money\Formatter\IntlMoneyFormatter($numberFormatter, $currencies);
return $moneyFormatter->format($money);
}
function convertBetweenCurrency($amount, $from_currency, $to_currency)
{
$endpoint = 'convert';
$access_key = 'xxx';
// initialize CURL:
$ch = curl_init('http://data.fixer.io/api/'.$endpoint.'? access_key='.$access_key.'&from='.$from_currency.'&to='.$to_currency.'&amount='.$amount.'');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// get the JSON data:
$json = curl_exec($ch);
curl_close($ch);
// Decode JSON response:
$conversionResult = json_decode($json, true);
// access the conversion result
return intval($conversionResult['result']);
}
function convertFromInteger($amount, $currency_code)
{
$currencies = PragmaRX\Countries\Package\Countries::currencies();
$currency = $currencies->where('iso.code', $currency_code)->toArray();
return $amount / (1 / $currency[$currency_code]['units']['minor']['majorValue']);
}
Related
I have been working around Bit-Wasp/bitcoin-php library for a while now and I encountered problems with it that I cannot resolve.
I have this as my code:
public function bitcoinWalletFromPublicKey($key, $index) {
$adapter = Bitcoin::getEcAdapter();
if (config('market.btc_network') == "mainnet") {
$btc = NetworkFactory::bitcoin();
$bitcoinPrefixes = new BitcoinRegistry();
} else {
$btc = NetworkFactory::bitcoinTestnet();
$bitcoinPrefixes = new BitcoinTestnetRegistry();
}
$slip132 = new Slip132(new KeyToScriptHelper($adapter));
$pubkeytype=substr($key, 0, 4);
if ($pubkeytype=='xpub' || $pubkeytype =='tpub') $pubPrefix = $slip132->p2pkh($bitcoinPrefixes);
if ($pubkeytype=='ypub') $pubPrefix = $slip132->p2shP2wpkh($bitcoinPrefixes);
if ($pubkeytype=='zpub' || $pubkeytype =='vpub') $pubPrefix = $slip132->p2wpkh($bitcoinPrefixes);
$config = new GlobalPrefixConfig([
new NetworkConfig($btc, [$pubPrefix])
]);
$serializer = new Base58ExtendedKeySerializer(
new ExtendedKeySerializer($adapter, $config)
);
$path = '0/' . $index;
$fkey = $serializer->parse($btc, $key);
$child_key = $fkey->derivePath($path);
#$account0Key = $child_key->derivePath("84'/0'/0'");
#$child_key = $fkey->derivePath("0/1");
//dd($child_key->getAddress(new AddressCreator())->getAddress());
return $child_key->getAddress(new AddressCreator())->getAddress();
}
I have two problems with this code:
Problem #1
On the first few lines of the code you will see that I used an If statement to check what network should it use. On my test im using testnet network and I'm sure as well that the code on my If / else { # code } works and it uses NetworkFactory::bitcoinTestnet() and new BitcoinTestnetRegistry() properly;
$key variable represents the Master Public Key of my user from Electrum wallet or whatever with a format of (xpub#########################/vpub#########################) or in my case since its on testnet it uses tpub######################### format. However, it returns an address with a format of bc#########, this means that its passing on mainnet network wherein it should be on testnet network.
Problem #2
On lower part of my code, I'm using $fkey = $serializer->parse($btc, $key); and $child_key = $fkey->derivePath($path) wherein $path = '0/' $index. $index here are just random numbers. It can be 0/1 or 0/99 or whatever 0/random.
Problem here is that somehow related to Problem #1, after it generates the wrong address, when I try to use this address for transaction my rpc returns an invalid address Error. As you can see as well I have a commented code $account0Key = $child_key->derivePath("84'/0'/0'"); wherein i got an error that it needs a private key instead of a public one. Now, my concern is that I do not want the users of the system i'm making to put their private keys whatsoever as it will might just compromise their wallets.
Basically, What I want to achieve using with this library from BitWasp is when a user put in their master public key from their wallet, my system would be able to then generate an address to be used for a btc transaction. Please help.
Passing the network inside the getAddress() method works
return $child_key->getAddress(new AddressCreator())->getAddress($btc);
I have 2 apps communicating with each other. The 1st app is the one who do the transactions and the other app is a settings app to control the 1st app's system settings.
Upon on reaching the login page of the first app, I'm calling a WebService through curl inside the config.php and it will communicate to the 2nd app and it will return corresponding values. Now, my problem is, the value was encrypted using the Security::cipher() function and it was encrypted using a module inside of the settings app. When I try to decrypt it, there is no error prompted and even no error is logged in the error logs file. I suspect that when I decrypt from WebServicesController it doesn't read the Security component. I tried to put App::uses('Security','Utility') on the top of the codes. Here's how I code it:
1st app
curl_setopt($ch, CURLOPT_URL, Configure::read('TMSWebServices.Url').'getSystemSetting.json');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
$result_json = curl_exec($ch);
curl_close($ch);
$result = json_decode($result_json, true);
debug($result); exit;
2nd app
App::uses('AuthComponent', 'Controller/Component');
App::uses('UsersController', 'Controller');
App::uses('Security','Utility');
class WebServicesController extends AppController {
public $name = 'WebServices';
public $uses = array('Tenant', 'TbNextId', 'ReportMaster' );
public $components = array('RequestHandler');
public function getSystemSetting(){
$this->loadModel('SystemSetting');
$results = $this->SystemSetting->find($type, $params);
$return_value = array();
$return_value = $results['SystemSetting'];
foreach($return_value['Config'] as $key=>$value){
if ($value['ENCRYPT_IND'] == 'Y'){
$encryptedValue = $return_value['Config'][$key]['SYSTEM_SETTING_VALUE'];
//decrypt the value
$decryptedValue = Security::cipher($encryptedValue,
Configure::read('Security.salt')); // the problem starts here
$return_value['Config'][$key]['SYSTEM_SETTING_VALUE'] = $decryptedValue;
}
}
}
}
$this->set(array(
'return_value' => $return_value,
'_serialize' => array('return_value')
));
When I try to just put simple value on the $return_value, the communication works. But if I use the Security::cipher to decrypt, it doesn't work and give me a null value.
I would look at two problems:
The Security.salt value is usually not the same across two CakePHP apps. From your error message I think that it's either empty or not set (i.e. null). Unless you set Security.salt to the same value in both apps, cipher will not work giving you either null or some incorrect value.
As #ndm mentioned in the comment, you are creating a flat array (not an associative array) so I wonder how it is that your code even enters the foreach loop.
Look at the following code and my comments:
// ...
$return_value = array(); // we have ourselves an empty array
$return_value[] = $results['SystemSetting']; // we assign a value to $return_value[0]
foreach($return_value['Config'] as $key=>$value){ // no such key 'Config' in the array
// loop code follows, but execution flow shouldn't enter here...
While we are at it, I strongly encourage you to not use Security.salt because it is just a weak XOR cipher, try to use Security.encrypt() and Security.decrypt()
I am using the SageOne API PHP Library. It works fine, but I get an error if I try to use get or post.
The error is,
Only variables should be passed by reference sage.api.php on line 130
My get request code is
$client = new SageOne(SAGE_CLIENT_ID, SAGE_CLIENT_SECRET);
$client->setAccessToken("c7c7547xxxxxxxxxxxx8efa4f5df08f750df");
$data = array( );
$result = "";
$client = $client->get('/products', $data);
I don’t know what’s wrong.
Full Code
require 'sage.api.php';
define('SAGE_CLIENT_ID', "fa1e8c1b114347a356d2");
define('SAGE_CLIENT_SECRET', "faaa7b353521f823ba13e3a20e72dd057c3a5fd1");
$client = new SageOne(SAGE_CLIENT_ID, SAGE_CLIENT_SECRET);
$callbackURL = 'xxxxx/addonmodules.php?module=sageone';
// We need to build the authorise url and redirect user to authorise our app
if(!$_GET['code']){
$authoriseURL = $client->getAuthoriseURL($callbackURL);
// redirect user
header("Location: ".$authoriseURL);
exit;
// We now have the authorisation code to retrieve the access token
} else {
$accessToken = $client->getAccessToken($_GET['code'], $callbackURL);
$token= $accessToken['access_token'];
$end = 'public';
$data ='';
$result = $client->get($end, $data);
echo '<pre>';
print_r($result);
Code Snippets from sage.api.php
class SageOne { ...
...
public function get($endpoint, $data=false){
return $this->call($endpoint, 'get', $data);
}
...
// error line 130 from this code
private function buildSignature($method, $url, $params, $nonce){
// uc method and append &
$signature = strtoupper($method).'&';
// percent encode bit of url before ? and append &
$signature .= rawurlencode(array_shift(explode('?', $url))).'&';
// percent encode any params and append &
if (is_array($params)){
// sort params alphabetically
$this->ksortRecursive($params);
// build query string from params, encode it and append &
$signature .= str_replace(
array('%2B'),
array('%2520'),
rawurlencode(http_build_query($params, '', '&'))
).'&';
// params can be string
} else {
// build query string from params, encode it and append &
$signature .= rawurlencode($params).'&';
}
// add 'nonce' - just use an md5
$signature .= $nonce;
// now generate signing key
$signingKey = rawurlencode($this->signingSecret).'&'.rawurlencode($this->accessToken);
// encode using sha 1, then base64 encode
$finalSignature = base64_encode(hash_hmac('sha1', $signature, $signingKey, true));
return $finalSignature;
}
This is the shortest i can make to see all important code
This is due to trying to return the result of a function or method directly to another function or method... the result doesn't have a reference.
So, for example:
$obj->method(doSomething(), 'asdf', 'qwerty');
The error means you should assign the value of doSomething() before passing it.
$result = doSomething();
$obj->method($result, 'asdf', 'qwerty');
Also see: Only variables should be passed by reference
A function (in this case, $client->get()) can be defined to receive its parameters by reference. This means that it can modify those parameters directly. So if you call $client->get($a, $b), the function may alter the values of $a and $b.
Clearly, it can only alter the values of variables, so when a function receives a parameter by reference, you must pass it a variable, not a string, an integer, or a direct call to another function.
So if the function $client->get() receives its first parameter by reference, none of the following can work:
$client->get('string', $data);
$client->get(15, $data); // int
$client->get(other_function_call(), $data);
$client->get(12.5, $data); // float
$client->get(array(), $data);
You have to do this:
$a = 'string';
$client->get($a, $data);
Or $a = whatever, be it a string, an int, a function call. The point is (and this is stated quite clearly in the error message) that you must pass a variable. So save whatever you want to pass as a variable, then pass that.
The title may not make sense, not sure how to word it. Anyways, i'm practicing curl and OOP at the same time here with the riot games API. the API is kind of set up dumb where some info you want to request requires input that you wouldn't know off hand, so it requires another separate call to get the required info first.
class league
{
const URL = 'http://prod.api.pvp.net/api/lol/na/v1.1/';
const URL_2 = 'http://prod.api.pvp.net/api/lol/na/v2.1/';
const KEY = 'key';
public function summonerByName($summoner_name)
{
$request = 'summoner/by-name/' . $summoner_name . '?api_key =' . self::KEY;
return $this->fetch($request);
}
public function recentGamesByName($summoner_name)
{
//need to make two calls for this since you cant get recent games by name in the api
$id = summonerByName($summoner_name);
//now get recent games
$request = 'game/by-summoner/' . $id->id . '/recent';
return $this->fetch($request);
}
private function fetch($request)
{
$url = self::URL . $request . '?api_key=' . self::KEY;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($curl);
curl_close($curl);
return json_decode($data);
}
}
this is returning Fatal error: Call to undefined function summonerbyname()
if i use all this extra code below in the public function recentGamesByName() instead of $id = summonerByName() it works fine, but it seems unnecessary, and i want to replace that with just the function.
$grg = self::URL . 'summoner/by-name/' . $summoner_name . '?api_key=' . self::KEY;
$placeholder = curl_init($grg);
curl_setopt($placeholder, CURLOPT_RETURNTRANSFER, 1);
$ph_result = curl_exec($placeholder);
curl_close($placeholder);
$ph_result = json_decode($ph_result);
$id = $this->summonerByName($summoner_name);
You may want to read up on OOP.
A couple of things to remember about OOP. When you're INSIDE the class and need to call another function, you use the the special $this variable.
So you would use:
$someVariable = $this->summonerByName($summoner_name);
to get the results from that function.
If you're OUTSIDE the class and need to access that function, then you need to assign the entire class to a variable like so:
$league = new league();
and then you can access any function within the class using that variable.
So you could do...
$someVariable = $league->summonerByName($summoner_name);
if you had already assigned the class to a variable name $league. By the way, that $league variable? It's called an object. Thus Object Oriented Programming. Objects are kinda like arrays, but use a different syntax. You can print_r an object just like you can print_r an array. When accessing an object's variable you use the $objectName->variableName syntax instead of $arrayName['variablename'] syntax that you use in arrays.
I'm currently developing a payment method and things are working quite well.
Just one thing: The customer enters some information along the payment method and through debugging I can see that it gets written into the InfoInstance via Mage_Payment_Model_Method_Abstract::assignData()
Unfortunately, I can't read that data when I'm in the capture()-Method. I retrieve the InfoInstance and try to read the information, but it's not set.
assignData() method:
public function assignData($data) {
if (!($data instanceof Varien_Object)) {
$data = new Varien_Object($data);
}
$info = $this->getInfoInstance();
$info->setEtixType($data->getEtixType());
return $this;
}
capture() method:
public function capture(Varien_Object $payment, $amount) {
// ...
$info = $this->getInfoInstance();
Mage::log('etix_type: '.$info->getEtixType()); //I expect something like "etix_type: cc"
// ...
}
Any help is appreciated. I'm sure I missed something.
Found it,
Assigning veriables directly to the InfoInstance works, but it does not persist through the whole checkout process. Instead, you have to set it on the additional_data:
$info = $this->getInfoInstance();
$info->setAdditionalInformation('etix_type', $data->getEtixType());
And later you can read it via:
$info = $this->getInfoInstance();
$etix_type = $info->getAdditionalInformation('etix_type');