I ran into this issue as well. My solution was to create a custom SOAP endpoint which handled all of the order creation and submission logic at once without multiple API calls.
Even this way, I was getting card type mismatch exceptions.
The trick was once I set the payment info, collect totals, and save quote, with the getPayment->importData method, it seems to forget the card info. Probably for (not the best way to handle) PCI-DSS compliance.
To solve it, I added another getPayment->importData line after saving the quote, before submitting the order.
See example:
public function customCheckout($checkoutData=false)
{
if(!$checkoutData){
Mage::throwException("No checkout data received.");
}
if(!json_decode($checkoutData)){
Mage::throwException("Bad checkout data received.");
}
$data = json_decode($checkoutData);
$email = '[email protected]';
// get the basic store info to associate with order
$websiteId = Mage::app()->getWebsite()->getId();
$store = Mage::app()->getStore();
// begin checkout with a quote
$quote = Mage::getModel('sales/quote')->setStoreId($store->getId());
// set customer by email
$customer = Mage::getModel('customer/customer')
->setWebsiteId($websiteId)
->loadByEmail($email);
// handle customer not exists by creating a new customer
if($customer->getId() == ''){
$customer = Mage::getModel('customer/customer');
$customer->setWebsiteId($websiteId)
->setStore($store)
->setFirstName("Bob")
->setLastName("Loblaw")
->setEmail($email)
->setPassword('password');
$customer->save();
}
// assign customer to SO quote
$quote->assignCustomer($customer);
// do we want to send a confirmation email to the customer?
// my guess is we would handle that in a separate service.
$quote->setSendConfirmation(0);
// add products to quote
foreach($data->products as $item){
$product = Mage::getModel('catalog/product')->load($item->id);
$quote->addProduct($product,new Varien_Object(array('qty'=>$item->qty)));
}
// set SO billing address
$billingAddress = $quote->getBillingAddress()->addData(array(
'customer_address_id' => '',
'prefix' => '',
'firstname' => $data->customer->firstName,
'middlename' => '',
'lastname' => $data->customer->lastName,
'suffix' => '',
'company' => '',
'street' => array(
'0' => 'street1',
'1' => 'street2'
),
'city'=>'city',
'country_id'=>'US',
'region'=>'WA',
'postcode'=>'98101',
'telephone' => '425-425-4254',
'fax' => '789-789-7897',
'vat_id' => '',
'save_in_address_book' => 0
));
// set SO shipping address, this will probably be the location of sale, on-site
$shippingAddress = $quote->getShippingAddress()->addData(array(
'customer_address_id' => '',
'prefix' => '',
'firstname' => $data->customer->firstName,
'middlename' => 'middle',
'lastname' => $data->customer->lastName,
'suffix' => '',
'company' => '',
'street' => array(
'0' => 'street1',
'1' => 'street2'
),
'city'=>'city',
'country_id'=>'US',
'region'=>'WA',
'postcode'=>'98201',
'telephone' => '425-425-4254',
'fax' => '789-789-7897',
'vat_id' => '',
'save_in_address_book' => 0
));
// set shipping method, if it's sold on site we aren't charging for delivery
$shipMethod='freeshipping_freeshipping';
$shippingAddress->setCollectShippingRates(true)
->collectShippingRates()
->setShippingMethod($shipMethod)
->setPaymentMethod($data->payment->method);
// set payment method
$quote->getPayment()->importData(array(
'method' =>$data->payment->method,
'cc_type' =>$data->payment->type,
'cc_number' =>$data->payment->number,
'cc_exp_year' =>$data->payment->expYear,
'cc_exp_month'=>$data->payment->expMonth,
));
// collect totals, save quote
$quote->collectTotals()->save();
// turn the quote into an order
$service = Mage::getModel('sales/service_quote',$quote);
// set payment method A SECOND TIME!!!!!!!!!!!!!
$quote->getPayment()->importData(array(
'method' =>$data->payment->method,
'cc_type' =>$data->payment->type,
'cc_number' =>$data->payment->number,
'cc_exp_year' =>$data->payment->expYear,
'cc_exp_month'=>$data->payment->expMonth,
));
$service->submitAll();
$increment_id = $service->getOrder()->getRealOrderId();
$quote = $customer = $service = null;
$retval = new stdClass;
$retval->orderId = $increment_id;
return json_encode($retval);
}