Here is how I would do it.
I would create my own shipping module (or 3 of them if you want to keep things separate). Follow this tutorial.
In the collectRates
method of the module you can add you code that decides what the price should be for shipping depending on the address.
The method receives a parameter called $request
. You can access the address values from this parameter:
$request->getDestCountryId()
- for country
$request->getDestCity()
- for city name
$request->getDestRegionId()
- for region id
So you can do something like this:
if ($request->getDestCountryId() == 'US' && in_array(strtolower($request->getDestCity()), array('newyork', 'new york'))){ //for new york
$price = 5;
}
elseif ($request->getDestCountryId() == 'US'){ //for the rest of the US
$price = 10;
}
else{//for the rest of the world
$price = ...//your price here.
}
Then add this price to the method:
$method->setPrice($price);
If you take the approach with 3 shipping modules, the code above will be split among the 3 modules and instead of defining a price on the branch that does not match the module conditions just return false
.
Note: This is just a proof of concept. Don't hard-code the values. You may want to fill them in the system->configuration panel. Also this code is not tested it may have errors in it.
[EDIT]
I just read the question more carefully and I think you can achieve this with table rates.
Country|Region/State|Zip/Postal Code|Weight (and above)|Shipping Price
US |New York |* |0 |5
US |* |* |0 |10
* |* |* |0 |YOUR PRICE HERE
But this means that for the entire New York region (not only the city) you will have the same price (5).
I would still use the method described above because it give you more control over what happens.