I'm using 7.x-1.0-beta2.

Customers with US addresses were getting this message after clicking the Proceed to SagePay button at checkout/#/payment

This transaction attempt has failed. Please use the Proceed button go back to the web store from which you were purchasing. The details of the failure are given below.

Status Detail: 3147 : The BillingState field is required.

In commerce_sagepay_form.module, function _commerce_sagepay_form_encrypted_order() the following lines are commented:

// 'BillingState' => $address[''],
// 'DeliveryState' => $address[''],

So it seems that these variables are needed for US customers (so far I've only experienced this with US customers, would like to know from others if more countries need this too).

I added the following lines, it seems to fix the problem.

Extra lines added at line 478 on commerce_sagepay_form.module (7.x-1.0-beta2)

    'Apply3DSecure' => $settings['apply_3d_secure'],
    // 'BillingAgreement' => '',

  // Fix for Sagepay error with US addresses.
  // Status:	MALFORMED
  // Status Detail:	3147 : The BillingState field is required.
  if ($address['country'] == 'US') {
    $query['BillingState'] = $address['administrative_area']; 
    $query['DeliveryState'] = $address['administrative_area']; 
  // End fix

  $keys = array_keys($query);
  $query_string = '';
  foreach($keys as $key){
    $query_string .= $key . '=' . $query[$key] . '&';
  $query_string = substr($query_string, 0, strlen($query_string) -1);


ikos’s picture

Status: Active » Needs review


Thanks for reporting this.

A fix has been applied to the latest dev version.

I'll roll a full 1.0 release soon.

kind regards


michaellenahan’s picture

Thank you - and thanks for providing this module in the first place!

FrancescoUK’s picture

I hadn't time to check the change in the latest dev and neither to test it, but I would suggest to remove the

 if ($address['country'] == 'US') {

and simply leave:

$query['BillingState'] = $address['administrative_area']; 
$query['DeliveryState'] = $address['administrative_area'];

because administrative_area applies to other countries too, examples are provinces and counties.

Address field has the following list of countries:

// Those countries generally use their administrative division in postal addresses.
if (in_array($address['country'], array('AR', 'AU', 'BR', 'BS', 'BY', 'BZ', 'CA', 'CN', 'DO', 'EG', 'ES', 'FJ', 'FM', 'GB', 'HN', 'ID', 'IE', 'IN', 'IT', 'JO', 'JP', 'KI', 'KN', 'KR', 'KW', 'KY', 'KZ', 'MX', 'MY', 'MZ', 'NG', 'NI', 'NR', 'NZ', 'OM', 'PA', 'PF', 'PG', 'PH', 'PR', 'PW', 'RU', 'SM', 'SO', 'SR', 'SV', 'TH', 'TW', 'UA', 'US', 'UY', 'VE', 'VI', 'VN', 'YU', 'ZA'))) {

For GB case administrative_area is used for counties.

michaellenahan’s picture

According to the Sagepay Form Protocol and Integration Guideline document, appendix A1, BillingState and DeliveryState represent the "State code for US customers only".

(to get this document, sign up here: http://www.sagepay.com/developers/sign_up)

Also, when I tested with a Canada address, it goes through to Sagepay and the page on Sagepay does not have a field for Billing State.

See the screen shots: Sagepay does not seem to need the BillingState field in Canadian addresses.

(I haven't had a live purchase from Canada yet, so if anyone else looking at this issue can confirm that it works OK, I'd be grateful - at any rate I have only experienced the original issue with US addresses).

FrancescoUK’s picture

michaellenahan you are correct and I upgraded sagepay form to the latest beta2 because I had a problem with a Canadian address.

Thank you for the fix suggestion.

gambry’s picture

The error is still there if the system is using: "Use Billing as Delivery address";

Updating line 547:
if ($delivery_address['country'] == 'US') {
if ($delivery_address['country'] == 'US' && $delivery_address['administrative_area']) {

fixes the problem.

ikos’s picture

Status: Needs review » Fixed

Thanks gambry.

I have already committed the fix, but have put in an additional fix based on your feedback above.

kind regards


Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.