i'm making some tests to develop a custom payment gateway addon in cscart
The development has been very simple and intuitive, but there is a thing i am going crazy
for 1 week. After the transaction as been completed the user is redirected to index.php?dispatch=checkout.complete&order_id=20036 but the green popup notification dosn't appear as on the other payments....
The code looks like normal.. i look on all other payments script and all is regular
the final function in /core/fn.cart.php fn_order_placement_routines valorize correctly the $_SESSION var with the notification data fn_set_notification('N',.....
this is the code
if (!defined('AREA') ) { die('Access denied'); }
if (defined('PAYMENT_NOTIFICATION')) {
if ($mode == 'notify' && !empty($_REQUEST['order_id'])) {
if (fn_check_payment_script('gateway.php', $_REQUEST['order_id'], $processor_data)) {
$order_id = $_REQUEST['order_id'];
$order_info = fn_get_order_info($order_id);
$pp_response = array(
'reason_text' => '',
'order_status' => 'F'
);
if (empty($processor_data)) {
$processor_data = fn_get_processor_data($order_info['payment_id']);
}
$returnvalue = $_POST['PROCESSING_RESULT'];
if ($returnvalue && strstr($returnvalue,"ACK")) {
$pp_response['order_status'] = "E";
$pp_response['reason_text'] .= "Status: OK";
}else {
$pp_response['order_status'] = "N";
$pp_response["reason_text"] = fn_get_lang_var('text_transaction_cancelled');
}
if (isSet($_REQUEST['IDENTIFICATION_UNIQUEID'])) {
$pp_response['transaction_id'] = $_REQUEST['IDENTIFICATION_UNIQUEID'];
}
[b] fn_finish_payment($_REQUEST['order_id'], $pp_response, false);
fn_order_placement_routines($_REQUEST['order_id'], true);[/b]
}
}
} else {
if ($mode == 'place_order') {
//call the gateway, assign response url etc
// $current_location."/".$index_script."?dispatch=payment_notification.notify&payment=gateway.php&order_id=".$order_id;
}
}
the fn_order_place_routine should be show by default the green or red popup based on the status of the order.... nothing.. dosn't appear
thanks in advance
As I can see you are using custom order status for sucessfull transaction:
$pp_response['order_status'] = "E";
So, to display notification the 'inventory' option for this status should be set as 'Decreased'. In this case the E status will be included in the result of the fn_get_order_paid_statuses() function and the following code in the fn_order_placement_routines function will work:
if (in_array($status, fn_get_order_paid_statuses())) {
if ($action == 'repay') {
fn_set_notification('N', __('congratulations'), __('text_order_repayed_successfully'));
} else {
fn_set_notification('N', __('order_placed'), __('text_order_placed_successfully'));
}
}
Best regards.
Related
I'm building a payment plugin. The way it works is on clicking the pay option on the checkout page, the user gets a link on their mobile phone where they can complete the payment which then triggers a webhook to the WooCommerce site and status gets updated as paid/failed according to the data received in the webhook.
The requirement is that the spinner that's displayed after clicking the pay option stays on until the webhook data is received or is timed out after 2 minutes. Right now the webhook is doing it's job, but the logic behind the spinner fails. Not sure what's going wrong
This is the code which get's triggered after the SMS with the link is sent to the user. The logic inside the do...while loop doesn't work
WC()->cart->empty_cart();
$order->update_status('wc-pending', __('Awaiting payment.', 'txtdomain'));
$loop = 8;
$loopOrder = wc_get_order($order_id);
do {
sleep(15);
$loopOrder = wc_get_order($order_id);
$loop--;
} while ($loopOrder->get_status() == "pending" && $loop >= 0);
if ($loopOrder->get_status() == "pending" || $loopOrder->get_status() == "failed") {
$_SESSION["order_id"] = $order_id;
$_SESSION["timeout"] = (time() + ($GLOBALS['retry_delay_in_minutes'] * 60));
$error_message = 'Timed out. Please wait <span id="retry_timer">' . ($_SESSION["timeout"] - time()) . '</span> seconds before retrying';
if($loopOrder->get_status() == "failed"){
$error_message = 'Payment failed. Please wait <span id="retry_timer">' . ($_SESSION["timeout"] - time()) . '</span> seconds before retrying';
}
wc_add_notice($error_message, 'error');
return array(
'result' => 'failed',
);
} else {
return array(
'result' => 'success',
'redirect' => $order->get_checkout_order_received_url()
);
}
and below is the code that's triggered on receiving the webhook
$order = wc_get_order($request["OrderNumber"]);
if ($request["StatusID"] == 0) {
$order->update_status('wc-failed');
} else {
update_post_meta($request["OrderNumber"], 'invoiceid', $request["TransactionID"]);
$shipping = false;
foreach ($order->get_items() as $order_item) {
$item = wc_get_product($order_item->get_product_id());
if (!$item->is_virtual()) {
$shipping = true;
}
}
if ($shipping) {
$order->update_status('wc-processing');
} else {
$order->update_status('wc-completed');
}
}
return true;
After multiple trials and error I understood that $loopOrder = wc_get_order($order_id); doesn't load the order information from the database. So to fetch the updated information, adding an $loopOrder->save(); after fetching current information fixed the issue.
Looks like your your do/while conditional checks the get_status() method's return value against un-prefixed strings "pending" and "failed", while the update_status() method in the second block of code is presumably setting the class variable for order status to stings that all include a "wc-" prefix (wc-failed, wc-processing, wc-completed etc).
Change $loopOrder->get_status() == "pending" to $loopOrder->get_status() == "wc-pending" and $loopOrder->get_status() == "failed" to $loopOrder->get_status() == "wc-failed" in your first block.
Hi can anyone help why URL string parameters post again and again?
HTTP://127.0.0.1/ab/1936.html?cart=yes?cart=yes
i m using this parameter to open mini cart when we added product into cart in magneto 1.9
Please help me how to protect this?
i am using this code-
<?php
if ($_GET['cart']=='yes') {
echo "<script type='text/javascript'>
jQuery('.minicart_open').show();
</script>";
}
?>
cartController.php
protected function _goBack()
{
$returnUrl = $this->getRequest()->getParam('return_url');
// print_r($returnUrl);exit;
if ($returnUrl) {
if (!$this->_isUrlInternal($returnUrl)) {
throw new Mage_Exception('External urls redirect to "' . $returnUrl . '" denied!');
}
$this->_getSession()->getMessages(true);
$this->getResponse()->setRedirect($returnUrl);
} elseif (!Mage::getStoreConfig('checkout/cart/redirect_to_cart')
&& !$this->getRequest()->getParam('in_cart')
&& $backUrl = $this->_getRefererUrl()
) {
$this->getResponse()->setRedirect($backUrl.'?cart=yes');
} else {
if (
(strtolower($this->getRequest()->getActionName()) == 'add')
&& !$this->getRequest()->getParam('in_cart')
) {
$this->_getSession()->setContinueShoppingUrl($this->_getRefererUrl());
}
$this->_redirect('checkout/cart');
}
return $this;
}
i got the answer
Just replace below line
$this->getResponse()->setRedirect($backUrl.'?cart=yes');
To
$url = $backUrl;
// Search substring
if (strpos($url, $key) == false) {
$this->getResponse()->setRedirect($backUrl.'?cart=yes');
}
else {
$this->getResponse()->setRedirect($backUrl);
}
//exit;
its working for me...Hope its helpful for you...
Following is the observer.php file for an extension that restricts Cash On Delivery Payment Method Based On Pincode On The Checkout Page.
This extension works perfectly fine with the built in Cash On Delivery Payment Method in Magento.
My issue is that, when a customer on my website goes to the checkout page to complete his order, the cash on delivery payment method is not visible initially. Only when the customer enters his Zip Code, and if that particular zip code is available for COD, the COD payment method appears. If that zipcode is not eligible, the COD option continues to be invisible.
I want the COD option to be visible initially when the zip code has not been entered and after the customer enters the zip code and if that zip code is not available, a message should be displayed saying that COD is not available.
I know this particular code needs to be altered a bit to achieve that. Kindly help me out if possible.
Observer.php
class Mfp_Cod_Model_Observer {
public function getCashOnDelvery(Varien_Event_Observer $observer)
{
$event = $observer->getEvent();
$method = $event->getMethodInstance();
$result = $event->getResult();
$isModuleEnable = Mage::getStoreConfig('cod/cod/enable');
if ($isModuleEnable) {
if ($method->getCode() == 'msp_cashondelivery' ) {
$quote = Mage::getSingleton('checkout/cart')->getQuote();
$add = $quote->getShippingAddress();
$postcode = $add->getData('postcode');
$comparisonMode = Mage::getStoreConfig('cod/cod/mode');
$zipCodes = Mage::getStoreConfig('cod/cod/zipcode');
$isExist = false;
if (trim($zipCodes) == '') {
$result->isAvailable = true;
} else {
if(strpos($zipCodes, $postcode) !== false) {
$isExist = true;
}
if ($isExist != true) {
$zipCodesArray = explode(',', nl2br($zipCodes));
if (count($elementLineArray) > 1) {
foreach ($zipCodesArray as $codzipLine) {
$elementLineArray = explode('-', $codzipLine);
if (count($elementLineArray) == 2 && ( $postcode >= $elementLineArray[0] && $postcode <= $elementLineArray[1] )) {
$isExist = true;
break;
} else if($postcode == $codzipLine) {
$isExist = true;
break;
}
}
}
}
$returnValue = '';
$returnValue = ($isExist)?true:false;
$result->isAvailable = $returnValue;
}
}
}
}
}
you can simply check if zipcode is not added like :
after
$postcode = $add->getData('postcode');
add
if(!isset($postcode) || empty($postcode)) {
return true;
}
In order to get shipping address of checkout page use
$address = $observer->getEvent()->getOrder()
->getShippingAddress();
in $address object you will get complete information of shipping address including postcode.
I'm creating a buy-now button cs-cart add-on. I've created an add-on and I've made the add-to-cart functionality work. But cant redirect it to checkout page. I have used "fn_redirect("checkout")" function for the redirection. And used this function below this:
"fn_add_product_to_cart($_REQUEST['product_data'], $cart, $auth);"
What should I do for redirection?
Edit:
My controller (buy_now.php)
if ($mode == 'add') {
if (empty($auth['user_id']) && Registry::get('settings.General.allow_anonymous_shopping') != 'allow_shopping')
{
return array(CONTROLLER_STATUS_REDIRECT, "auth.login_form?return_url=" . urlencode($_REQUEST['return_url']));
}
// Add to cart button was pressed for single product on advanced list
if (!empty($dispatch_extra)) {
if (empty($_REQUEST['product_data'][$dispatch_extra]['amount'])) {
$_REQUEST['product_data'][$dispatch_extra]['amount'] = 1;
}
foreach ($_REQUEST['product_data'] as $key => $data) {
if ($key != $dispatch_extra && $key != 'custom_files') {
unset($_REQUEST['product_data'][$key]);
}
}
}
$prev_cart_products = empty($cart['products']) ? array() : $cart['products'];
fn_add_product_to_cart($_REQUEST['product_data'], $cart, $auth);
fn_save_cart_content($cart, $auth['user_id']);
$previous_state = md5(serialize($cart['products']));
$cart['change_cart_products'] = true;
fn_calculate_cart_content($cart, $auth, 'S', true, 'F', true);
if (md5(serialize($cart['products'])) != $previous_state && empty($cart['skip_notification'])) {
$product_cnt = 0;
$added_products = array();
if (!empty($added_products)) {
Registry::get('view')->assign('added_products', $added_products);
if (Registry::get('config.tweaks.disable_dhtml') && Registry::get('config.tweaks.redirect_to_cart')) {
Registry::get('view')->assign('continue_url', (!empty($_REQUEST['redirect_url']) && empty($_REQUEST['appearance']['details_page'])) ? $_REQUEST['redirect_url'] : $_SESSION['continue_url']);
}
fn_redirect(Registry::get('config.https_location') . "/checkout");
// $msg = Registry::get('view')->fetch('views/checkout/components/product_notification.tpl');
//fn_set_notification('I', __($product_cnt > 1 ? 'products_added_to_cart' : 'product_added_to_cart'), $msg, 'I');
$cart['recalculate'] = true;
} else {
fn_set_notification('N', __('notice'), __('product_in_cart'));
}
}
unset($cart['skip_notification']);
$_suffix = '.checkout';
}
`
hook template : add_to_cart.post.tpl
{$id = "buy_now_{$product.product_id}"}
<button id="opener_{$id}" name="dispatch[buy_now.add..{$product.product_id}]" class=" vs-button buynow_btn_">Buy Now</button>
I'd consider putting your fn_redirect("checkout") code into an if/else statement to see what's happening with it.
If you have this code:
fn_add_product_to_cart($_REQUEST['product_data'], $cart, $auth);
Then surely you can put the redirection to the checkout page underneath it?
$redirect_url = "http://testcheckout.com?test=test"; //change this!
$errorMessage = "I should have redirected already....";
if (!empty($redirect_url)) {
//if it's not empty then it should redirect.
fn_redirect($redirect_url);
//Then put a die statement in here just to check it's not executing
//anything else and you can see if the redirect has a problem:
die(print_r($errorMessage, true ));
}
else {
//otherwise there is a problem with the supplied redirect url (empty)
die(print_r($redirect_url, true ));
}
I'd also take a look at this page which might help more:
http://forum.cs-cart.com/topic/6873-direct-buy-button/
Hope that helps!
I am working with SilverStripe, and I am working on making a newspage.
I use the DataObjectAsPage Module( http://www.ssbits.com/tutorials/2012/dataobject-as-pages-the-module/ ), I got it working when I use the admin to publish newsitems.
Now I want to use the DataObjectManager Module instead of the admin module to manage my news items. But this is where the problem exists. Everything works fine in draft mode, I can make a new newsitem and it shows up in draft. But when I want to publish a newsitem, it won't show up in the live or published mode.
I'm using the following tables:
-Dataobjectaspage table,
-Dataobjectaspage_live table,
-NewsArticle table,
-NewsArticle_Live table
The Articles have been inserted while publishing in the Dataobjectaspage table and in the NewsArticle table... But not in the _Live tables...
Seems the doPublish() function hasn't been used while 'Publishing'.
So I'm trying the use the following:
function onAfterWrite() {
parent::onAfterWrite();
DataObjectAsPage::doPublish();
}
But when I use this, it gets an error:
here is this picture
It seems to be in a loop....
I've got the NewsArticle.php file where I use this function:
function onAfterWrite() {
parent::onAfterWrite();
DataObjectAsPage::doPublish();
}
This function calls the DataObjectAsPage.php file and uses this code:
function doPublish() {
if (!$this->canPublish()) return false;
$original = Versioned::get_one_by_stage("DataObjectAsPage", "Live", "\"DataObjectAsPage\".\"ID\" = $this->ID");
if(!$original) $original = new DataObjectAsPage();
// Handle activities undertaken by decorators
$this->invokeWithExtensions('onBeforePublish', $original);
$this->Status = "Published";
//$this->PublishedByID = Member::currentUser()->ID;
$this->write();
$this->publish("Stage", "Live");
// Handle activities undertaken by decorators
$this->invokeWithExtensions('onAfterPublish', $original);
return true;
}
And then it goes to DataObject.php file and uses the write function ():
public function write($showDebug = false, $forceInsert = false, $forceWrite = false, $writeComponents = false) {
$firstWrite = false;
$this->brokenOnWrite = true;
$isNewRecord = false;
if(self::get_validation_enabled()) {
$valid = $this->validate();
if(!$valid->valid()) {
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
throw new ValidationException($valid, "Validation error writing a $this->class object: " . $valid->message() . ". Object not written.", E_USER_WARNING);
return false;
}
}
$this->onBeforeWrite();
if($this->brokenOnWrite) {
user_error("$this->class has a broken onBeforeWrite() function. Make sure that you call parent::onBeforeWrite().", E_USER_ERROR);
}
// New record = everything has changed
if(($this->ID && is_numeric($this->ID)) && !$forceInsert) {
$dbCommand = 'update';
// Update the changed array with references to changed obj-fields
foreach($this->record as $k => $v) {
if(is_object($v) && method_exists($v, 'isChanged') && $v->isChanged()) {
$this->changed[$k] = true;
}
}
} else{
$dbCommand = 'insert';
$this->changed = array();
foreach($this->record as $k => $v) {
$this->changed[$k] = 2;
}
$firstWrite = true;
}
// No changes made
if($this->changed) {
foreach($this->getClassAncestry() as $ancestor) {
if(self::has_own_table($ancestor))
$ancestry[] = $ancestor;
}
// Look for some changes to make
if(!$forceInsert) unset($this->changed['ID']);
$hasChanges = false;
foreach($this->changed as $fieldName => $changed) {
if($changed) {
$hasChanges = true;
break;
}
}
if($hasChanges || $forceWrite || !$this->record['ID']) {
// New records have their insert into the base data table done first, so that they can pass the
// generated primary key on to the rest of the manipulation
$baseTable = $ancestry[0];
if((!isset($this->record['ID']) || !$this->record['ID']) && isset($ancestry[0])) {
DB::query("INSERT INTO \"{$baseTable}\" (\"Created\") VALUES (" . DB::getConn()->now() . ")");
$this->record['ID'] = DB::getGeneratedID($baseTable);
$this->changed['ID'] = 2;
$isNewRecord = true;
}
// Divvy up field saving into a number of database manipulations
$manipulation = array();
if(isset($ancestry) && is_array($ancestry)) {
foreach($ancestry as $idx => $class) {
$classSingleton = singleton($class);
foreach($this->record as $fieldName => $fieldValue) {
if(isset($this->changed[$fieldName]) && $this->changed[$fieldName] && $fieldType = $classSingleton->hasOwnTableDatabaseField($fieldName)) {
$fieldObj = $this->dbObject($fieldName);
if(!isset($manipulation[$class])) $manipulation[$class] = array();
// if database column doesn't correlate to a DBField instance...
if(!$fieldObj) {
$fieldObj = DBField::create('Varchar', $this->record[$fieldName], $fieldName);
}
// Both CompositeDBFields and regular fields need to be repopulated
$fieldObj->setValue($this->record[$fieldName], $this->record);
if($class != $baseTable || $fieldName!='ID')
$fieldObj->writeToManipulation($manipulation[$class]);
}
}
// Add the class name to the base object
if($idx == 0) {
$manipulation[$class]['fields']["LastEdited"] = "'".SS_Datetime::now()->Rfc2822()."'";
if($dbCommand == 'insert') {
$manipulation[$class]['fields']["Created"] = "'".SS_Datetime::now()->Rfc2822()."'";
//echo "<li>$this->class - " .get_class($this);
$manipulation[$class]['fields']["ClassName"] = "'$this->class'";
}
}
// In cases where there are no fields, this 'stub' will get picked up on
if(self::has_own_table($class)) {
$manipulation[$class]['command'] = $dbCommand;
$manipulation[$class]['id'] = $this->record['ID'];
} else {
unset($manipulation[$class]);
}
}
}
$this->extend('augmentWrite', $manipulation);
// New records have their insert into the base data table done first, so that they can pass the
// generated ID on to the rest of the manipulation
if(isset($isNewRecord) && $isNewRecord && isset($manipulation[$baseTable])) {
$manipulation[$baseTable]['command'] = 'update';
}
DB::manipulate($manipulation);
if(isset($isNewRecord) && $isNewRecord) {
DataObjectLog::addedObject($this);
} else {
DataObjectLog::changedObject($this);
}
$this->onAfterWrite();
$this->changed = null;
} elseif ( $showDebug ) {
echo "<b>Debug:</b> no changes for DataObject<br />";
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
}
// Clears the cache for this object so get_one returns the correct object.
$this->flushCache();
if(!isset($this->record['Created'])) {
$this->record['Created'] = SS_Datetime::now()->Rfc2822();
}
$this->record['LastEdited'] = SS_Datetime::now()->Rfc2822();
} else {
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
}
// Write ComponentSets as necessary
if($writeComponents) {
$this->writeComponents(true);
}
return $this->record['ID'];
}
Look at the $this->onAfterWrite();
It probably goes to my own function on NewsArticle.php and there starts the loop! I'm not sure though, so i could need some help!!
Does anyone knows how to use the doPublish() function?
The reason that is happening is that in the DataObjectAsPage::publish() method, it is calling ->write() - line 11 of your 3rd code sample.
So what happens is it calls ->write(), at the end of ->write() your onAfterWrite() method is called, which calls publish(), which calls write() again.
If you remove the onAfterWrite() function that you've added, it should work as expected.
The doPublish() method on DataObjectAsPage will take care of publishing from Stage to Live for you.