uid, 'checkout_checkout'); // Save the order to get its ID. commerce_order_save($order); // Get the order wrapper which provides helper functionality $order_wrapper = entity_metadata_wrapper('commerce_order', $order); // Add address profiles. // IMPORTANT! The address MUST be set before the line item to // correctly determine the country of supply. // IMPORTANT2: This address setup is made based on single fields in the user // account. If you already use an address field in the user account // this is much simpler and you should have a look at the // commerce_extra module for address fields ;) // Billing // Check that we have at least all REQUIRED fields! if (empty($user->field_firstname[LANGUAGE_NONE][0]['value'])) { throw new Exception('field_firstname may not be empty!'); } if (empty($user->field_lastname[LANGUAGE_NONE][0]['value'])) { throw new Exception('field_lastname may not be empty!'); } if (empty($user->field_city[LANGUAGE_NONE][0]['value'])) { throw new Exception('field_city may not be empty!'); } if (empty($user->field_zip[LANGUAGE_NONE][0]['value'])) { throw new Exception('field_zip may not be empty!'); } if (empty($user->field_street[LANGUAGE_NONE][0]['value'])) { throw new Exception('field_street may not be empty!'); } if (empty($user->field_country[LANGUAGE_NONE][0]['value'])) { throw new Exception('field_country may not be empty!'); } // We have to get or create the first order profile of the users to set the values into. // We use a helper function for that! $billing = _my_module_api_create_order_get_first_customer_profile('billing', $user); $customer_profile_billing_array = array( 'country' => isset($user->field_country[LANGUAGE_NONE][0]['value']) ? $user->field_country[LANGUAGE_NONE][0]['value'] : NULL, // DE 'name_line' => NULL, // Testperson 'first_name' => isset($user->field_firstname[LANGUAGE_NONE][0]['value']) ? $user->field_firstname[LANGUAGE_NONE][0]['value'] : NULL, // Max 'last_name' => isset($user->field_lastname[LANGUAGE_NONE][0]['value']) ? $user->field_lastname[LANGUAGE_NONE][0]['value'] : NULL, // Mustermann 'organisation_name' => isset($user->field_firma[LANGUAGE_NONE][0]['value']) ? $user->field_firma[LANGUAGE_NONE][0]['value'] : NULL, // Muster AG 'administrative_area' => NULL, 'sub_administrative_area' => NULL, 'locality' => isset($user->field_wohnort[LANGUAGE_NONE][0]['value']) ? $user->field_wohnort[LANGUAGE_NONE][0]['value'] : NULL, // Musterstadt 'dependent_locality' => NULL, 'postal_code' => isset($user->field_zip[LANGUAGE_NONE][0]['value']) ? $user->field_zip[LANGUAGE_NONE][0]['value'] : NULL, // 32457 'thoroughfare' => isset($user->field_street[LANGUAGE_NONE][0]['value']) ? $user->field_street[LANGUAGE_NONE][0]['value'] : NULL, // Musterstraße 10 'premise' => NULL, 'sub_premise' => NULL, 'data' => NULL, ); // Set the values in the profile entity and attache it to the order $billing->commerce_customer_address[LANGUAGE_NONE][0] = $customer_profile_billing_array; commerce_customer_profile_save($billing); $order->commerce_customer_billing[LANGUAGE_NONE][0]['profile_id'] = $billing->profile_id; // This is only required if you use the commerce_vat module and you want to // set the vat id programmatically with implications on the price calculation! if (!empty($user->field_vat_number[LANGUAGE_NONE][0]['value'])) { $eu_vat_rc = _my_module_api_create_order_get_first_customer_profile('eu_vat_rc', $user); $customer_profile_vat_array = array( 'value' => !empty($user->field_vat_number[LANGUAGE_NONE][0]['value']) ? $user->field_vat_number[LANGUAGE_NONE][0]['value'] : NULL, ); $eu_vat_rc->commerce_vat_number[LANGUAGE_NONE][0] = $customer_profile_vat_array; commerce_customer_profile_save($eu_vat_rc); $order->commerce_customer_eu_vat_rc[LANGUAGE_NONE][0]['profile_id'] = $eu_vat_rc->profile_id; } // IMPORTANT: If we also use shipping address, this would be the // place to set it here like we already did in "billing"! // Save the order again to save the address attached. $order_wrapper->save(); // Load whatever product represents the item the customer will be // paying for and create a line item for it. $product = commerce_product_load($product_id); // Create a new line item with multiplicity = 1 $line_item = commerce_product_line_item_new($product, 1, $order->order_id); // You may set line item properties here. // Calculate the sell price! rules_invoke_event('commerce_product_calculate_sell_price', $line_item); // Save the line item commerce_line_item_save($line_item); // Attach the line item to the order using the order wrapper. $order_wrapper->commerce_line_items[] = $line_item; $order_wrapper->save(); // Now calculate the total price and save the order again. commerce_order_calculate_total($order); $order_wrapper->save(); // To create the payment we need some information from the order: $total = $order_wrapper->commerce_order_total->amount->value(); $currency_code = $order_wrapper->commerce_order_total->currency_code->value(); $charge = array( 'amount' => $total, 'currency_code' => $currency_code, ); // The following is an example for payment methods commerce_directdebit and bank_transfer. // You may use this as example for other payment types! // SEPA if we have the required users direct debit fields, otherwise bank transfer! if (!empty($user->field_accountholder[LANGUAGE_NONE][0]['value']) && !empty($user->field_bic[LANGUAGE_NONE][0]['value']) && !empty($user->field_iban[LANGUAGE_NONE][0]['value'])) { // SEPA $payment_method = array( 'instance_id' => 'commerce_directdebit|commerce_payment_commerce_directdebit', ); commerce_directdebit_transaction($payment_method, $order, $charge, $user->field_accountholder[LANGUAGE_NONE][0]['value'], '', '', 1, $user->field_bic[LANGUAGE_NONE][0]['value'], $user->field_iban[LANGUAGE_NONE][0]['value']); } else { // BANK TRANSFER $payment_method = array( 'instance_id' => 'bank_transfer|commerce_payment_bank_transfer', ); commerce_bank_transfer_transaction($payment_method, $order, $charge); } // Set the payment id in the order array to make it available for invoicing // (no idea why we have to do that manually here. Perhaps there's a better way?) $order->data['payment_method'] = $payment_method['instance_id']; $order_wrapper->save(); // Update the status to processing to allow manual finishing later on. commerce_order_status_update($order, 'processing'); // No explicit transaction commit wanted by drupal... } catch (Exception $e) { // Something went wrong! We don't want to save a broken order to the database // at all. So let's roll back the transaction: $transaction->rollback(); // Add some information to the exception for easier debugging in an outer try / catch! $e->user_id = $uid; $e->product_id = $product_id; // We've done what we could. Re-throw the exception and let an outer // try/catch handle it for error logging. throw $e; } // Yeah, the order was created successfully. Provide it to the outer call! :) return $order; } /** * Helper function to get the users already existing (as implemented: first) * commerce customer profile and return it. * If the user has no customer profile yet, create a new one and return that. * * @param string $type The type of the customer profile e.g. "billing". * @param stdClass $user The user object. * @return stdClass The CommerceCustomerProfile object. */ function _my_module_api_create_order_get_first_customer_profile($type, stdClass $user) { $query = new EntityFieldQuery(); $query->entityCondition('entity_type', 'commerce_customer_profile') ->propertyCondition('uid', $user->uid) ->propertyCondition('type', $type); $results = $query->execute(); if (!empty($results['commerce_customer_profile'])) { // Profile already exists. Load! $profile_info = reset($results['commerce_customer_profile']); return commerce_customer_profile_load($profile_info->profile_id); } else { // No profile yet. Create one! return commerce_customer_profile_new($type, $user->uid); } }