Omnipay with SagePay Server not accepting billingCountry - opayo

I'm getting the following error when trying to use SagePay Server (from printing out the response):
["data":protected]=>array(3) {
["VPSProtocol"]=>
string(4) "2.23"
["Status"]=>
string(7) "INVALID"
["StatusDetail"]=>
string(107) "The data in the BillingCountry field you supplied is an invalid length. Must be an ISO 3166-1 country code."
}
but I am passing the value "GB" as the billingCountry which is correct (SagePay expects billingCountry to be alphabetical and a max of 2 characters)
["card"]=>
object(Omnipay\Common\CreditCard)#46 (1) {
["parameters":protected]=>
object(Symfony\Component\HttpFoundation\ParameterBag)#48 (1) {
["parameters":protected]=>
array(11) {
["email"]=>
string(17) "test#test.com"
["billingFirstName"]=>
string(3) "Joe"
["shippingFirstName"]=>
string(3) "Joe"
["billingLastName"]=>
string(6) "Bloggs"
["shippingLastName"]=>
string(6) "Bloggs"
["billingAddress1"]=>
string(9) "Address 1"
["billingAddress2"]=>
string(9) "Address 2"
["billingCity"]=>
string(4) "City"
["billingPostcode"]=>
string(7) "AB1 1BA"
["billingCountry"]=>
string(2) "GB"
["billingPhone"]=>
string(13) "01234 567 890"
}
}
And this is my code:
$response = $this->gateway->Purchase(array(
'description'=> 'Online order',
'currency'=> 'GBP',
'transactionId'=> mt_rand(99, 9999),
'transactionReference' => 'test order',
'amount'=> '1.00',
'returnUrl' => 'http://www.test.com/returnURL/',
'cancelUrl' => 'http://www.test.com/cancelURL/',
'card' => array(
'email' => 'test#test.com',
'clientIp' => '123.123.123.123',
'firstName' => 'Joe',
'LastName' => 'Bloggs',
'billingAddress1' => 'Address 1',
'billingAddress2' => 'Address 2',
'billingCity' => 'City',
'billingPostcode' => 'AB1 1BA',
'billingCountry' => 'GB',
'billingPhone' => '01234 567 890'
)))->send();
I can't work it out as it all looks correct. I think I'm going slightly mad! Am I missing the totally obvious?
Updated 28.07.2014:
I have been trying various things including sending the data in different formats:
$formInputData = array(
'firstName' => 'Joe',
'lastName' => 'Bloggs',
'billingAddress1' => '88',
'billingAddress2' => 'Address 2',
'billingCity' => 'City',
'billingPostcode' => '412',
'billingCountry' => 'GB',
'billingPhone' => '01234 567 890',
'email' => 'test#test.com',
'clientIp' => '123.123.123.123'
);
$card = new CreditCard($formInputData);
$transactionID = mt_rand(99, 9999);
$response = $this->gateway->purchase(['amount' => '1.00', 'returnUrl' => 'http://www.example.com/return/', 'transactionId'=> $transactionID, 'description'=> 'Online order', 'transactionReference' => 'test order', 'currency'=> 'GBP', 'card' => $card])->send();
and it doesn't make any difference.
I have also tried sending the same data (but with the addition of test credit card details in the card parameter) to both SagePay Direct and CardSave.
With SagePay Direct I get "The BillingCountry field is missing from the POST".
With CardSave the transaction goes through successfully but I notice that the country field has "N/A" when I look at the transaction history in my CardSave.

The current release of omnipay/sagepay is incorrectly setting the US state for non-US billing addresses.
If the address is outside the US it passes NULL for the state which SagePay rejects. Unfortunately SagePay's error messages often refer to a neighbouring field rather than the actual error. So it is complaining about the BillingCountry not being set when it actually means the state's not been set.
There is a fix in the Git repository for this bug, but it isn't in the main releases so Composer misses it.
In the meantime you can just manually set the state to be an empty string $data['card']['state'] = ''.

Could this be a case-sensitivity issue? Does changing it to "BillingCountry" make any difference?

The firstName and lastName fields - they should be:
BillingFirstNames (yes, plural)
BillingSurname
The properties for all the fields are summarised in a gateway package I write last year:
https://github.com/academe/SagePay/blob/master/src/Academe/SagePay/Metadata/Transaction.php
This includes the field names, lengths, mandatory status, allowed characters, and which transaction types the fields are used in. These details are difficult to extract from the documentation, and many are not documented at all, and have been found through trial-and-error.
Edit: this is probably a silly answer if Omnipay translates the field names, so feel free to ignore it. My head was in SagePay and not Omnipay.

To respond to Jason's comments/answers, Omnipay handles multiple payment gateways and so converts fields passed to the correct field names as expected by the particular gateway. In the case of SagePay this is:
protected function getBaseAuthorizeData()
{
$this->validate('amount', 'card', 'transactionId');
$card = $this->getCard();
$data = $this->getBaseData();
$data['Description'] = $this->getDescription();
$data['Amount'] = $this->getAmount();
$data['Currency'] = $this->getCurrency();
$data['VendorTxCode'] = $this->getTransactionId();
$data['ClientIPAddress'] = $this->getClientIp();
$data['ApplyAVSCV2'] = $this->getApplyAVSCV2() ?: 0;
$data['Apply3DSecure'] = $this->getApply3DSecure() ?: 0;
// billing details
$data['BillingFirstnames'] = $card->getFirstName();
$data['BillingSurname'] = $card->getLastName();
$data['BillingAddress1'] = $card->getBillingAddress1();
$data['BillingAddress2'] = $card->getBillingAddress2();
$data['BillingCity'] = $card->getBillingCity();
$data['BillingPostCode'] = $card->getBillingPostcode();
$data['BillingState'] = $card->getBillingCountry() === 'US' ? $card->getBillingState() : null;
$data['BillingCountry'] = $card->getBillingCountry();
$data['BillingPhone'] = $card->getBillingPhone();
// shipping details
$data['DeliveryFirstnames'] = $card->getFirstName();
$data['DeliverySurname'] = $card->getLastName();
$data['DeliveryAddress1'] = $card->getShippingAddress1();
$data['DeliveryAddress2'] = $card->getShippingAddress2();
$data['DeliveryCity'] = $card->getShippingCity();
$data['DeliveryPostCode'] = $card->getShippingPostcode();
$data['DeliveryState'] = $card->getShippingCountry() === 'US' ? $card->getShippingState() : null;
$data['DeliveryCountry'] = $card->getShippingCountry();
$data['DeliveryPhone'] = $card->getShippingPhone();
$data['CustomerEMail'] = $card->getEmail();
return $data;
}

Since this bug has been fixed, I have also encountered the error. However the bug spoken about above has now been fixed.
In my case the problem stemmed from using "DeliveryXXXX" instead of "ShippingXXXX". Once I changed all instances of Delivery to Shipping I stopped receiving the error.

Related

Handling ContentType Relations with Doctrine/Bolt EM

I have an extension serving a route which creates an entity (a question), which has a related entry (each entry has 0, 1, or many associated questions).
I can't seem to get bolt's save/doctrine's persist methods to actually persist relationships. I've tried the following:
$question = $repo->create([
'question' => $request->get('question', 'What is love?'),
'status' => 'draft',
'entries' => [$entry]
]);
$question->relation->add($entry)
$question->setRelation(new Collection\Relations([$entry], $em))
(edit) I've also tried
$entry = $em->find('entries', $request->get('entry', 1));
$related = $em->createCollection('Bolt\Storage\Entity\Relations');
$related->add(new Relations([
'from_contenttype' => $question->getContenttype(),
'from_id' => $question->getId(),
'to_contenttype' => $entry->getContenttype(),
'to_id' => $entry->getId()
]));
$question->setRelation($relation);
per a response below, but that still doesn't work.
None of which work - it correctly saves the Question (Content) entity, but not the relation. Not really sure where to go from here...
Here's a working test I have, I know the syntax is ugly at the moment, the plan is to add some more helper methods, but this does work.
$repo = $app['storage']->getRepository('pages');
$newPage = $repo->create(
[
"title" => 'An Example Title',
"slug" => 'an-example-title',
"status" => 'published',
"teaser" => 'An intro teaser goes here',
"body" => 'Lorem ipsum dolor sit amet, consectet adipisici.',
]
);
$newPage->getRelation()->setFromPost(['entries' => [1]], $newPage);
$repo->save($newPage);

How can I get the author,price and title by knowing the ISBN search using amazon?

How can I get the author,price and title by knowing the ISBN search using amazon in codeigniter?
and
I been trying to sign up for the AProduct Advertising API registration for about 2 days, every time I get an error: We're sorry! There was an error processing your changes. Please try again. after account info step
how to get keys value of awsAccessKeyID, awsSecretKey, awsAssociateTag?
function getMetadataFromIsbn($isbn) {
// Get your own accesskey at http://aws.amazon.com/
$awsAccessKeyID = 'accesskey';
$awsSecretKey = 'secrete key';
$awsAssociateTag = 'Associate tag';
$host = 'ecs.amazonaws.com';
$path = '/onca/xml';
$args = array(
'AssociateTag' => $awsAssociateTag,
'AWSAccessKeyId' => $awsAccessKeyID,
'IdType' => 'ISBN',
'ItemId' => $isbn,
'Operation' => 'ItemLookup',
'ResponseGroup' => 'Medium',
'SearchIndex' => 'Books',
'Service' => 'AWSECommerceService',
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
'Version'=> '2009-01-06'
);

How to configure CreditCard class in Omnipay?

I've been trying to configure Omnipay in CodeIgniter all day long, I think I'm finally cracking it up, but I'm stuck at the credit card validation part. For some reason when I run my I get the error message Fatal error: Class 'CreditCard' not found in C:\xampp\htdocs\trabajo\emarket\application\controllers\inicio.php on line 37
This is my controller:
use Omnipay\Omnipay;
class Inicio extends CI_Controller {
public function index()
{
$gateway = Omnipay::create('PayPal_Pro');
$gateway->setUsername('######');
$gateway->setPassword('######');
$gateway->setSignature('#####');
$gateway->setTestMode(true);
$gateway->initialize();
$cardInput = array(
'firstName' => 'buyer',
'lastName' => 'one million',
'number' => '4032031186341789',
'company' => 'Visa',
'billingAddress1' => 'bill me here',
'billingAddress2' => 'or maybe here',
'billingPhone' => '4085873015',
'billingCity' => 'chicago',
'billingState' => 'illinois',
'billingPostCode' => '646960',
'shippingAddress1' => 'ship it here',
'shippingAddress2' => 'or ship here',
'shippingPhone' => '98789987',
'shippingCity' => 'chicago',
'shippingState' => 'illinois',
'shippingPostCode' => '989898',
);
$card = new CreditCard($cardInput);
}
}
Thanks for your time, I'd really appreciate some pointers on what I'm doing wrong.
Classes are loaded, but you need to point at those. And you are doing that with keyword use. Otherwise you could pass something like:
$gateway = Omnipay\Omnipay::create('PayPal_Pro');//not quite sure if you need backslash infront of vendor name
Or same way you could invoke CreditCard instance:
$card = new Omnipay\Common\CreditCard($cardInput);
That is reason of having keyword use.
This is good topic source.

cakephp 2.1 contain second level different field name

I can't get the values from a second level contain using different field name as link.
Asistencia model:
var $belongsTo = array('Employee');
Horario model:
var $belongsTo = array('Employee');
Employee model:
var $hasMany = array(
'Horario' => array(
'foreignKey' => 'employee_id',
'dependent' => true
),
'Asistencia' => array(
'foreignKey' => 'employee_id',
'dependent' => true
)
);
I'll explain using these values on my example record:
Asistencia: employee_id = 3701
Employee : id = 3701
In my find() from Asistencia, I get to contain Employee by switching Employee primaryKey just fine:
$this->Asistencia->Employee->primaryKey = 'id';
$this->paginate = array(
'contain' => array(
'Employee' => array(
'foreignKey' => 'employee_id',
//'fields' => array('id', h('emp_ape_pat'), h('emp_ape_mat'), h('name')),
'Horario' => array(
'foreignKey' => 'employee_id',
//'fields' => array('id' )
))
),
'conditions' => $conditions,
'order' => 'Asistencia.employee_id');
However, my Horario record is linked to Employees via another field: emp_appserial
Employee : emp_appserial = 373
Horario : employee_id = 373
How can my Employee array contain Horario array? (they do share the value just mentioned).
Currently, the Horario contain is using the value on Asistencia.employee_id and Employee.id (3701). (checked the sql_dump and is trying to get the Horario via
"WHERE `Horario`.`employee_id` = (3701)"
but for the Employee to contain Horario, it should use the value on Employee.emp_appserial and Horario.employee_id (373).
This is what i get (empty array at bottom)
array(
(int) 0 => array(
'Asistencia' => array(
'id' => '5',
'name' => null,
'employee_id' => '3701',
'as_date' => '2012-11-19',
),
'Employee' => array(
'id' => '3701',
'emp_appserial' => '373',
'emp_appstatus' => '8',
'AgentFullName' => '3701 PEREZ LOMELI JORGE LORENZO',
'FullNameNoId' => 'PEREZ LOMELI JORGE LORENZO',
'Horario' => array()
)))
Please notice:
'employee_id' => '3701', (Asistencia)
and
'emp_appserial' => '373', (Employee)
my Horario has 'employee_id' = 373.
How could I make the switch so the relation Employee<->Horario is based on emp_appserial?
Thank you in advance for any help.
Firstly you may be getting problems because you're using Spanish words for your Model names and I suspect you're also using them for Controllers.
This is a very bad practice since CakePHP's idea is:
Convention over configuration.
This is achieved through the Inflector class which "takes a string and can manipulate it to handle word variations such as pluralizations or camelizing and is normally accessed statically". But this works ONLY WITH English words. What this means for you is that you may be missing the data because Cake is not able to build the DB queries right, since you're using Spanish words. By doing so you're making the use of CakePHP's flexible persistence layer obsolete - you will have to make all the configurations by hand. Also most likely pagination will NOT WORK. Other parts of the framework may also not work properly:
HtmlHelper, FormHelper, Components, etc. ...
These problems will expand if you try to use more complex Model associations such as HABTM or "hasMany through the Join Model".
So I do not know why exactly you aren't seeing the Horario record, but what I just explained most likely is your problem.
What you're trying to do is against the core principles of CakePHP and you'd save yourself a lot of problems if you refactor a bit and use English words. Your other option is NOT TO use Cake.

adding individual validation rule and function, kohana 3.0.8

i am diving deeper into kohana and i like it verry much. especialy the hmvc stuff and much more.
at the moment i have problems with adding my own rules to the validation instance.
validation runs well, and i also think that my own function get called correctly. but the problem is that the error message for my own validation function is not be shown.
maybe someone can look into the code to see what i did wrong. thanks!
here is my code i deleted some stuff to shorten it up a bit:
class Controller_Bookmarks extends Controller_DefaultTemplate
{
public function action_create_bookmark()
{
$posts = new Model_Post();
if($_POST){
$post = new Validate($_POST,$_FILES);
//attaching rules
$post ->rule('bookmark_title', 'not_empty')
->rule('bookmark_image', 'Model_Post::email_change');
//add error for custom functionm
$post->error('bookmark_image', 'email_change');
if ($post->check())
{
echo 'yeah';
}else{
print_r($post->errors('validate'));
}
}else{
}
$this->template->content = View::factory('pages/create_bookmark');
}
}
my model:
class Model_Post extends Kohana_Model
{
public function email_change($str)
{
return false;
}
}
my error message definition messages/validate.php (just for testing):
<?php defined('SYSPATH') or die('No direct script access.');<br />
return array(
'alpha' => ':field must contain only letters',
'alpha_dash' => ':field must contain only letters and dashes',
'alpha_numeric' => ':field must contain only letters and numbers',
'color' => ':field must be a color',
'credit_card' => ':field must be a credit card number',
'date' => ':field must be a date',
'decimal' => ':field must be a decimal with :param1 places',
'digit' => ':field must be a digit',
'email' => ':field must be a email address',
'email_domain' => ':field must contain a valid email domain',
'exact_length' => ':field must be exactly :param1 characters long',
'in_array' => ':field must be one of the available options',
'ip' => ':field must be an ip address',
'matches' => ':field must be the same as :param1',
'min_length' => ':field must be at least :param1 characters long',
'max_length' => ':field must be less than :param1 characters long',
'phone' => ':field must be a phone number',
'not_empty' => ':field rrrrrrrrrrrrrrrrrrrrr must not be empty',
'range' => ':field must be within the range of :param1 to :param2',
'regex' => ':field does not match the required format',
'url' => ':field must be a url',
'email_change' => ':field gdffddfgdfhgdfhdfhhdfhdfhdfhd',
);
You should add errors inside ->check() call. Note that after you called $post->check(), Validate object clears existing errors! You can see it by using $post->errors() (without params) - there will be no such error message.

Resources