PHP SDK Sample: Add externally created order

You should only be using these samples to insert channel partner orders.  Normal orders should be added to the system using the CheckoutAPI.  

There are three examples here.  

  1. Inserting an order already completed (i.e. credit card already charged) on a different system.
  2. Inserting an order still needing payment collection.
  3. Inserting an order already completed using the CheckoutAPI.  This is a legacy method of inserting a channel partner and remains only for legacy reference.  You are advised to use method 1 instead.

Inserting an order already completed on a different system (Channel Partner order)

<?php
// Example: Inserting a channel partner order into the UltraCart system and charging a credit card during the insert.
// Requirement: An API Key: https://secure.ultracart.com/merchant/configuration/apiManagementApp.do
// Requirement: A Channel Partner Code: https://secure.ultracart.com/merchant/configuration/customChannelPartnerListLoad.do
// This is used to take orders from another system and insert them into the UltraCart system.
// This is NOT to be used for customer real-time orders.  Use the CheckoutApi for that.  It is vastly superior in every way.
?>


<?php
// Did you get an error?
// See this: https://ultracart.atlassian.net/wiki/spaces/ucdoc/pages/39077885/Troubleshooting+API+Errors
?>

<?php
use ultracart\v2\ApiException;
use ultracart\v2\HeaderSelector;
use ultracart\v2\models\Order;
use ultracart\v2\models\OrderBilling;
use ultracart\v2\models\OrderChannelPartner;
use ultracart\v2\models\OrderCoupon;
use ultracart\v2\models\OrderInternal;
use ultracart\v2\models\OrderItem;
use ultracart\v2\models\OrderItemOption;
use ultracart\v2\models\OrderPayment;
use ultracart\v2\models\OrderPaymentCreditCard;
use ultracart\v2\models\OrderResponse;
use ultracart\v2\models\OrderShipping;

// for testing and development only
set_time_limit(3000);
ini_set('max_execution_time', 3000);
ini_set('display_errors', 1);
error_reporting(E_ALL);
?>


<?php
// initialization code
require_once '../vendor/autoload.php';
// The key below is a dev environment key.  It doesn't exist in production.
$simple_key = '630eccdfa6287a01699b80e93de87900b25f1cd52ce6be01699b80e93de87900';
$merchant_id = 'DEMO';
ultracart\v2\Configuration::getDefaultConfiguration()->setApiKey('x-ultracart-simple-key', $simple_key);

$client = new GuzzleHttp\Client(['verify' => false, 'debug' => false]);
$config = ultracart\v2\Configuration::getDefaultConfiguration();
$headerSelector = new HeaderSelector(/* leave null for version tied to this sdk version */);

$order_api = new ultracart\v2\api\OrderApi($client, $config, $headerSelector);
$expansion =
    "affiliate,affiliate.ledger,auto_order,billing,buysafe,channel_partner,checkout,coupon,customer_profile,digital_order,edi,fraud_score,gift,gift_certificate,internal,item,linked_shipment,marketing,payment,payment.transaction,quote,salesforce,shipping,summary,taxes";


// See https://github.com/UltraCart/hosted_fields for this file.
require_once(__DIR__ . '/HostedFields.class.php');

function die_if_api_error(OrderResponse $order_response)
{
    if ($order_response->getError() != null) {
        echo "Error:<br>";
        echo $order_response->getError() . '<br>';
        die('handle this error gracefully');
    }
}

?>

<!DOCTYPE html>
<html>
<body>
<pre>
<?php


try {

    $order = new Order();

// the hosted field php class may be found here:  https://github.com/UltraCart/hosted_fields
    $hosted_fields = new HostedFields($merchant_id, null);

    $items = array();
    $item = new OrderItem();
    $item->setMerchantItemId("BONE");
    $item->setQuantity(1);

    // This 'Bone' item within the DEMO account has a single item option.
    // To get the name and possible values of, use the Item API and query the item.
    $itemOption = new OrderItemOption();
    $itemOption->setLabel("Addon Treat");
    $itemOption->setValue("No thanks");
    $item->setOptions([$itemOption]);

    array_push($items, $item);
    $order->setItems($items);

    $shipping = new OrderShipping();
    $shipping->setCompany("UltraCart");
    $shipping->setFirstName("Perry");
    $shipping->setLastName("Smith");
    $shipping->setAddress1("55 Main Street");
    $shipping->setAddress2("Suite 101");
    $shipping->setCity("Duluth");
    $shipping->setPostalCode("30097");
    $shipping->setStateRegion("GA");
    $shipping->setCountryCode("US");
    $shipping->setDayPhone("555-555-1234");
    $shipping->setEveningPhone("444-333-4321");

    // there are two ways to specify shipping.
    // 1) explicitly name the shipping method and costs
    // 2) set the flag instructing UltraCart to use the cheapest method
    // if this order was already finalized outside of the system, you must do the former and specify all values.
    $shipping->setLeastCostRoute(true);
    // $shipping->setLeastCostRouteShippingMethods(['ShippingMethodName1', 'ShippingMethodName2']);

    $order->setShipping($shipping);


    $billing = new OrderBilling();
    $billing->setCompany("UltraCart");
    $billing->setFirstName("Perry");
    $billing->setLastName("Smith");
    $billing->setAddress1("55 Main Street");
    $billing->setAddress2("Suite 101");
    $billing->setCity("Duluth");
    $billing->setPostalCode("30097");
    $billing->setStateRegion("GA");
    $billing->setCountryCode("US");
    $billing->setDayPhone("555-555-1234");
    $billing->setEveningPhone("444-333-4321");
    $billing->setEmail("test@ultracart.com");
    $order->setBilling($billing);

    // --- Payment Block ---
    $creditCardNumber = "4444333322221111";
    $cvv = "321";

    $payment = new OrderPayment();
    $creditCard = new OrderPaymentCreditCard();
    $store_cc_result = $hosted_fields->store_number($creditCardNumber);
    echo "<br><br>";
    echo print_r($store_cc_result);
    echo "<br><br>";
    if ($store_cc_result->success) {
        // the server will tie the actual card number to the cart later, but the mask must be added
        //$creditCard->setCardNumber($store_cc_result->maskedValue);
        $creditCard->setCardExpirationMonth(3);
        $creditCard->setCardExpirationYear(2020);
        $creditCard->setCardType($store_cc_result->cardType);
        $creditCard->setCardNumberToken($store_cc_result->token);
    }

    $store_cvv_result = $hosted_fields->store_cvv($cvv);
    echo "<br><br>";
    echo print_r($store_cvv_result);
    echo "<br><br>";
    if ($store_cvv_result->success) {
        // the server will tie the actual cvv to the cart later, but the mask must be added
        $creditCard->setCardVerificationNumberToken($store_cvv_result->token);
    }
    $payment->setPaymentMethod("Credit Card");
    $payment->setCreditCard($creditCard);
    $order->setPayment($payment);
    // --- End Payment Block ---

    // add a coupon.
    $coupon = new OrderCoupon();
    $coupon->setCouponCode("10OFF"); // you'll need to create a coupon first, you know?
    $order->setCoupons([$coupon]);


    // channel partner information.  critical.
    $channel_partner = new OrderChannelPartner();
    $channel_partner->setChannelPartnerOrderId("ABC" . rand());
    $channel_partner->setChannelPartnerCode("ABC12");
    $order->setChannelPartner($channel_partner);

    $order_internal = new OrderInternal();
    $order_internal->setSalesRepCode("REPCODE1");
    $order_internal->setMerchantNotes("These are some merchant notes.");

    $order_response = $order_api->insertOrder($order, $expansion);

    die_if_api_error($order_response);
    $new_order = $order_response->getOrder();

} catch (ApiException $e) {
    echo "<pre>" . print_r($e) . "</pre><br><br>";
    die($e->getMessage());
}


?>
<?php
if (isset($order)) {
    echo print_r($order);
}
?>
<?php
if (isset($new_order)) {
    echo print_r($new_order);
}
?>
</pre>
<?php echo 'Finished.'; ?>
</body>
</html>


Inserting a new order

Do not use this method to take orders for a standard website.  Use the CheckoutAPI.  It has the full power of the UltraCart engine to accept all types of payment methods and enhanced validations.  This method exists for the outside cases of orders taken over the phone from call centers, etc.  This method has limitations and you are warned to not use this method for normal website e-commerce.

<?php
// Example: Inserting a channel partner order into the UltraCart system and charging a credit card during the insert.
// Requirement: An API Key: https://secure.ultracart.com/merchant/configuration/apiManagementApp.do
// Requirement: A Channel Partner Code: https://secure.ultracart.com/merchant/configuration/customChannelPartnerListLoad.do
// This is used to take orders from another system and insert them into the UltraCart system.
// This is NOT to be used for customer real-time orders.  Use the CheckoutApi for that.  It is vastly superior in every way.
?>


<?php
// Did you get an error?
// See this: https://ultracart.atlassian.net/wiki/spaces/ucdoc/pages/39077885/Troubleshooting+API+Errors
?>

<?php

use ultracart\v2\ApiException;
use ultracart\v2\HeaderSelector;
use ultracart\v2\models\Currency;
use ultracart\v2\models\Order;
use ultracart\v2\models\OrderBilling;
use ultracart\v2\models\OrderChannelPartner;
use ultracart\v2\models\OrderCoupon;
use ultracart\v2\models\OrderInternal;
use ultracart\v2\models\OrderItem;
use ultracart\v2\models\OrderItemOption;
use ultracart\v2\models\OrderPayment;
use ultracart\v2\models\OrderPaymentCreditCard;
use ultracart\v2\models\OrderResponse;
use ultracart\v2\models\OrderShipping;
use ultracart\v2\models\OrderSummary;
use ultracart\v2\models\OrderTaxes;

// for testing and development only
set_time_limit(3000);
ini_set('max_execution_time', 3000);
ini_set('display_errors', 1);
error_reporting(E_ALL);
?>


<?php
// initialization code
require_once '../vendor/autoload.php';
// The key below is a dev environment key.  It doesn't exist in production.
$simple_key = '630eccdfa6287a01699b80e93de87900b25f1cd52ce6be01699b80e93de87900';
$merchant_id = 'DEMO';
ultracart\v2\Configuration::getDefaultConfiguration()->setApiKey('x-ultracart-simple-key', $simple_key);

$client = new GuzzleHttp\Client(['verify' => false, 'debug' => false]);
$config = ultracart\v2\Configuration::getDefaultConfiguration();
$headerSelector = new HeaderSelector(/* leave null for version tied to this sdk version */);

$order_api = new ultracart\v2\Api\OrderApi($client, $config, $headerSelector);
$expansion =
    "affiliate,affiliate.ledger,auto_order,billing,buysafe,channel_partner,checkout,coupon,customer_profile,digital_order,edi,fraud_score,gift,gift_certificate,internal,item,linked_shipment,marketing,payment,payment.transaction,quote,salesforce,shipping,summary,taxes";

function die_if_api_error(OrderResponse $order_response)
{
    if ($order_response->getError() != null) {
        echo "Error:<br>";
        echo $order_response->getError() . '<br>';
        die('handle this error gracefully');
    }
}

?>

<!DOCTYPE html>
<html>
<body>
<pre>
<?php


try {

    $order = new Order();

    // channel partner information.  critical.
    $channel_partner = new OrderChannelPartner();
    $channel_partner->setChannelPartnerOrderId("ABC" . rand());
    $channel_partner->setChannelPartnerCode("ABC12");
    $channel_partner->setNoRealtimePaymentProcessing(true);
    $channel_partner->setSkipPaymentProcessing(true); // this order has already been transacted
    $channel_partner->setStoreCompleted(true); // bypass shipping and mark the order as completely finished.

    // TODO add arbitrary values where applicable.

    $order->setChannelPartner($channel_partner);

    $items = array();
    $item = new OrderItem();
    $item->setMerchantItemId("BONE");
    $item->setQuantity(1);
    // only use if you need to change the cost of the item.  It is NOT normal to need arbitrary costs.
    $item->setArbitraryUnitCost(new Currency(['value' => 22.99]));

    // when instructing UltraCart to store an order as completed, we must specify last auto bill dates for all auto
    // orders. The following lines are only needed when a completed order has recurring (auto order) products
    $datetime = new DateTime('17 Mar 2019');
    $item->setAutoOrderLastRebillDts($datetime->format('c')); // date must be in ISO8601 format

    // This 'Bone' item within the DEMO account has a single item option.
    // To get the name and possible values of, use the Item API and query the item.
    $itemOption = new OrderItemOption();
    $itemOption->setLabel("Addon Treat");
    $itemOption->setValue("No thanks");
    $item->setOptions([$itemOption]);

    array_push($items, $item);
    $order->setItems($items);

    $shipping = new OrderShipping();
    $shipping->setCompany("UltraCart");
    $shipping->setFirstName("Perry");
    $shipping->setLastName("Smith");
    $shipping->setAddress1("55 Main Street");
    $shipping->setAddress2("Suite 101");
    $shipping->setCity("Duluth");
    $shipping->setPostalCode("30097");
    $shipping->setStateRegion("GA");
    $shipping->setCountryCode("US");
    $shipping->setDayPhone("555-555-1234");
    $shipping->setEveningPhone("444-333-4321");

    // there are two ways to specify shipping.
    // 1) explicitly name the shipping method and costs
    // 2) set the flag instructing UltraCart to use the cheapest method
    // if this order was already finalized outside of the system, you must do the former and specify all values.
    // $shipping->setLeastCostRoute(true);
    // $shipping->setLeastCostRouteShippingMethods(['ShippingMethodName1', 'ShippingMethodName2']); // this is optional

    $shipping->setShippingMethod('UPS: Ground');
    // if you need to override the shipping costs, that is done in the OrderSummary object.

    $order->setShipping($shipping);


    $billing = new OrderBilling();
    $billing->setCompany("UltraCart");
    $billing->setFirstName("Perry");
    $billing->setLastName("Smith");
    $billing->setAddress1("55 Main Street");
    $billing->setAddress2("Suite 101");
    $billing->setCity("Duluth");
    $billing->setPostalCode("30097");
    $billing->setStateRegion("GA");
    $billing->setCountryCode("US");
    $billing->setDayPhone("555-555-1234");
    $billing->setEveningPhone("444-333-4321");
    $billing->setEmail("test@ultracart.com");
    $order->setBilling($billing);

    // --- Payment Block ---
    $payment = new OrderPayment();
    $payment->setPaymentMethod("Credit Card");

    $creditCard = new OrderPaymentCreditCard();
    $creditCard->setCardExpirationMonth(3);
    $creditCard->setCardExpirationYear(2020);
    $creditCard->setCardType("VISA");
    // if you have already collected payment, do not specify card number or hosted fields token.
    //$creditCard->setCardNumber($creditCardNumber);
    //$creditCard->setCardNumberToken($hostedFieldsToken);


    $payment->setCreditCard($creditCard);
    $order->setPayment($payment);
    // --- End Payment Block ---

    // add a coupon.
    $coupon = new OrderCoupon();
    $coupon->setCouponCode("10OFF"); // you'll need to create a coupon first, you know?
    $order->setCoupons([$coupon]);



    $order_internal = new OrderInternal();
    $order_internal->setSalesRepCode("REPCODE1");
    $order_internal->setMerchantNotes("These are some merchant notes.");


    // this block is only needed if you've used arbitrary item costs (different from what UltraCart has)
    // or different taxes.  It is not normal to need $order_summary
    $order_summary = new OrderSummary();
    $order_summary->setArbitraryShippingHandlingTotal(new Currency(['value'=>12.99]));
    $order->setSummary($order_summary);

    // this block is only needed if charged tax differently from what UltraCart would charge
    // it is NOT normal to need $order_taxes
    $order_taxes = new OrderTaxes();
    $order_taxes->setArbitraryTax(6.99);
    $order->setTaxes($order_taxes);

    $order_response = $order_api->insertOrder($order, $expansion);


    die_if_api_error($order_response);
    $new_order = $order_response->getOrder();

} catch (ApiException $e) {
    echo "<pre>" . print_r($e) . "</pre><br><br>";
    die($e->getMessage());
}


?>
<?php
if (isset($order)) {
    echo print_r($order);
}
?>
<?php
if (isset($new_order)) {
    echo print_r($new_order);
}
?>
</pre>
<?php echo 'Finished.'; ?>
</body>
</html>


Using the Checkout API


Prior to June 2019, the following was the only method for adding a channel partner order.  It remains below to show an alternate method of adding an order using the checkout api.

Note: The important part here is the payment method.  For an externally created order, use a payment method of "Purchase Order" and set the purchase order number equal to the order id from the external system.

See lines 110-120 for the relevant code.

<?php /* docs.ultracart.com sample */ ?>
<?php // https://ultracart.atlassian.net/wiki/spaces/ucdoc/pages/365658113/PHP+SDK+Sample+Add+externally+created+order ?>
<?php
// Did you get an error?
// See this: https://ultracart.atlassian.net/wiki/spaces/ucdoc/pages/39077885/Troubleshooting+API+Errors
?>

<?php
// for testing and development only
set_time_limit(3000);
ini_set('max_execution_time', 3000);
ini_set('display_errors', 1);
error_reporting(E_ALL);
?>

<?php
// initialization code
require_once './vendor/autoload.php';
// The key below is a dev environment key.  It doesn't exist in production.
$simple_key = '4256aaf6dfedfa01582fe9a961ab0100216d737b874a4801582fe9a961ab0100';
ultracart\v2\Configuration::getDefaultConfiguration()->setApiKey('x-ultracart-simple-key', $simple_key);

$client = new GuzzleHttp\Client(['verify' => true, 'debug' => false]);
$config = ultracart\v2\Configuration::getDefaultConfiguration();
$headerSelector = new \ultracart\v2\HeaderSelector(/* leave null for version tied to this sdk version */);

$checkout_api = new ultracart\v2\api\CheckoutApi($client, $config, $headerSelector);

// ----------------------------------------------------------------------------------
// expansion should contain all the objects that will be needed throughout the checkout.
// see https://www.ultracart.com/api/#Topic3 for the complete list.
// This expansion list should be supplied for each get/put throughout or data may be lost on the return objects.
$expansion = "billing,checkout,coupons,items,payment,settings.shipping.estimates,shipping,summary,taxes,coupons";
// The expansion above doesn't include much of the item objects because they're not needed.  For example, we don't
// need the item multimedia because we're not showing this cart to an end customer like a javascript implementation would
// if you needed to show images and such to a customer, then add 'items' to the csv above.  Better, yet, if you need to do
// all that, use javascript instead.
// ----------------------------------------------------------------------------------

function die_if_api_error(\ultracart\v2\models\CartResponse $order_response){
    if ($order_response->getErrors() != null) {
        echo "Errors:<br>";
        foreach ($order_response->getErrors() as $error) {
            echo $error . '<br>';
        }
        die('handle this error gracefully');
    }
}


?>

<!DOCTYPE html>
<html>
<body>
<?php

try {
    $get_response = $checkout_api->getCart($expansion);
if($get_response->getErrors() != null &&  count($get_response->getErrors()) > 0){
    // handle errors here.
    die('System error.  Could not retrieve shopping cart.');
} else {
    $cart = $get_response->getCart();
}

$items = array();
$item = new \ultracart\v2\models\CartItem();
$item->setItemId("BONE");

// This 'Bone' item within the DEMO account has a single item option.
// To get the name and possible values of, use the Item API and query the item.
$itemOption = new \ultracart\v2\models\CartItemOption();
$itemOption->setName("Addon Treat");
$itemOption->setSelectedValue("No thanks");
$item->setOptions([$itemOption]);

array_push($items, $item);
$cart->setItems($items);

$shipping = new \ultracart\v2\models\CartShipping();
$shipping->setCompany("UltraCart");
$shipping->setFirstName("Perry");
$shipping->setLastName("Smith");
$shipping->setAddress1("55 Main Street");
$shipping->setAddress2("Suite 101");
$shipping->setCity("Duluth");
$shipping->setPostalCode("30097");
$shipping->setStateRegion("GA");
$shipping->setCountryCode("US");
$shipping->setDayPhone("555-555-1234");
$shipping->setEveningPhone("444-333-4321");
$cart->setShipping($shipping);

$billing = new \ultracart\v2\models\CartBilling();
$billing->setCompany("UltraCart");
$billing->setFirstName("Perry");
$billing->setLastName("Smith");
$billing->setAddress1("55 Main Street");
$billing->setAddress2("Suite 101");
$billing->setCity("Duluth");
$billing->setPostalCode("30097");
$billing->setStateRegion("GA");
$billing->setCountryCode("US");
$billing->setDayPhone("555-555-1234");
$billing->setEveningPhone("444-333-4321");
$billing->setEmail("test@ultracart.com");
$cart->setBilling($billing);

// --- Payment Block ---
$payment = new \ultracart\v2\models\CartPayment();
$purchaseOrder = new ultracart\v2\models\CartPaymentPurchaseOrder();
$purchaseOrderNumber = "1234567890"; // this should be the order id from the external system.
$purchaseOrder->setPurchaseOrderNumber($purchaseOrderNumber);

$payment->setPaymentMethod("Purchase Order");
$payment->setPurchaseOrder($purchaseOrder);
$cart->setPayment($payment);
// --- End Payment Block ---

// add a coupon.
$coupon = new \ultracart\v2\models\CartCoupon();
$coupon->setCouponCode("10OFF"); // you'll need to create a coupon first, you know?
$cart->setCoupons([$coupon]);


// for best results, set the shipping address and update the server before
// setting the shipping method.  the cart that is returned below will have
// the optimal shipping method estimates and ensure that you don't error
// by selecting a shipping method that is somehow excluded from the possible
// list for whatever reason (restrictions, locations, item-level constraints, etc)
$put_response = $checkout_api->updateCart($cart, $expansion);
$cart = $put_response->getCart();

// for shipping, check the estimates and select one.  for a completely non-interactive checkout such as this,
// the shipping method will either be known beforehand (hard-coded) or use the least expensive method.  The
// least expensive method is always the first one, so for this example, I'll select the first shipping method.
if ($cart->getSettings() != null && $cart->getSettings()->getShipping() != null) {
    $shippingSettings = $cart->getSettings()->getShipping();
    $estimates = $shippingSettings->getEstimates();
    if ($estimates != null && count($estimates) > 0) {
        $cart->getShipping()->setShippingMethod($estimates[0]->getName());
		$cart = $checkout_api->updateCart($cart, $expansion)->getCart();
    }
}

$put_response = $checkout_api->updateCart($cart, $expansion);
$cart = $put_response->getCart();

// validate the cart to ensure everything is in order.
$validation_request = new \ultracart\v2\models\CartValidationRequest();
$validation_request->setCart($cart); // I don't set the checks variable.  standard checks are usually sufficient.
$validation_response = $checkout_api->validateCart($validation_request);


$errors = [];
$order = null;

if ($validation_response->getErrors() == null || count($validation_response->getErrors()) == 0) {
    $finalizeRequest = new \ultracart\v2\models\CartFinalizeOrderRequest();
    $finalizeRequest->setCart($cart);
    $finalizeResponse = $checkout_api->finalizeOrder($finalizeRequest);

    if (isset($finalizeResponse)) {
        if ($finalizeResponse->getSuccessful()) {
            $order = $finalizeResponse->getOrder();
        } else {
            $errors = $finalizeResponse->getErrors();
        }
    }

} else {
    $errors = $validation_response->getErrors();
}

} catch (\ultracart\v2\ApiException $e) {
    echo "<pre>" . print_r($e) . "</pre>";
    die("ApiException prevented further execution.");
}

if (count($errors) > 0) {
    foreach ($errors as $err) {
        echo "<strong>$err</strong><br>";
    }
}
?>
<pre>
<?php echo print_r($cart); ?>
<?php echo print_r($validation_response); ?>
<?php if (isset($finalizeResponse)) {
    echo print_r($finalizeResponse);
} ?>
<?php if ($order != null) {
    echo print_r($order);
} ?>
</pre>
<?php echo 'Finished.'; ?>
</body>
</html>