Version Notes
This is the initial version. Please provide feedback to support@paystand.com
Download this release
Release Info
| Developer | Scott Campbell |
| Extension | PayStand_PayStandGateway |
| Version | 1.0.2 |
| Comparing to | |
| See all releases | |
Version 1.0.2
- app/code/local/PayStand/PayStandGateway/Helper/Data.php +6 -0
- app/code/local/PayStand/PayStandGateway/Model/PaymentMethod.php +329 -0
- app/code/local/PayStand/PayStandGateway/controllers/PaymentController.php +228 -0
- app/code/local/PayStand/PayStandGateway/etc/config.xml +76 -0
- app/code/local/PayStand/PayStandGateway/etc/system.xml +62 -0
- app/design/frontend/base/default/template/paystandgateway/redirect.phtml +270 -0
- app/etc/modules/PayStand_PayStandGateway.xml +12 -0
- package.xml +18 -0
app/code/local/PayStand/PayStandGateway/Helper/Data.php
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
class PayStand_PayStandGateway_Helper_Data extends Mage_Core_Helper_Abstract
|
| 4 |
+
{
|
| 5 |
+
}
|
| 6 |
+
|
app/code/local/PayStand/PayStandGateway/Model/PaymentMethod.php
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/*
|
| 4 |
+
Copyright 2014 PayStand Inc.
|
| 5 |
+
|
| 6 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 7 |
+
you may not use this file except in compliance with the License.
|
| 8 |
+
You may obtain a copy of the License at
|
| 9 |
+
|
| 10 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 11 |
+
|
| 12 |
+
Unless required by applicable law or agreed to in writing, software
|
| 13 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 14 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 15 |
+
See the License for the specific language governing permissions and
|
| 16 |
+
limitations under the License.
|
| 17 |
+
*/
|
| 18 |
+
|
| 19 |
+
if (!defined('PS_LOG')) {
|
| 20 |
+
define('PS_LOG', 'paystand.log');
|
| 21 |
+
}
|
| 22 |
+
if (!defined('PS_LIVE_URL')) {
|
| 23 |
+
define('PS_LIVE_URL', 'https://app.paystand.com');
|
| 24 |
+
}
|
| 25 |
+
if (!defined('PS_SANDBOX_URL')) {
|
| 26 |
+
define('PS_SANDBOX_URL', 'https://dev.paystand.biz');
|
| 27 |
+
}
|
| 28 |
+
if (!defined('PS_API')) {
|
| 29 |
+
define('PS_API', '/api/v2');
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
/**
|
| 33 |
+
* PayStand PayStandGateway PaymentMethod
|
| 34 |
+
*/
|
| 35 |
+
class PayStand_PayStandGateway_Model_PaymentMethod extends Mage_Payment_Model_Method_Abstract
|
| 36 |
+
{
|
| 37 |
+
/**
|
| 38 |
+
* Unique internal payment method identifier
|
| 39 |
+
* @var string [a-z0-9_]
|
| 40 |
+
*/
|
| 41 |
+
protected $_code = 'paystandgateway';
|
| 42 |
+
|
| 43 |
+
/**
|
| 44 |
+
* Functionality availability flags
|
| 45 |
+
* @see all flags and their defaults in Mage_Payment_Model_Method_Abstract
|
| 46 |
+
* It is possible to have a custom dynamic logic by overloading
|
| 47 |
+
* public function can* for each flag respectively
|
| 48 |
+
*/
|
| 49 |
+
|
| 50 |
+
protected $_isGateway = true;
|
| 51 |
+
|
| 52 |
+
protected $_isInitializeNeeded = true;
|
| 53 |
+
protected $_canUseInternal = true;
|
| 54 |
+
protected $_canUseForMultishipping = false;
|
| 55 |
+
|
| 56 |
+
protected $accepted_currency_codes = array('USD');
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
/**
|
| 60 |
+
* PayStand API URLs
|
| 61 |
+
*/
|
| 62 |
+
protected $paystand_url;
|
| 63 |
+
protected $api_url;
|
| 64 |
+
protected $pm_url;
|
| 65 |
+
protected $pay_url;
|
| 66 |
+
|
| 67 |
+
// XXX merge review - do we need these functions vvv
|
| 68 |
+
/**
|
| 69 |
+
* Get config settings.
|
| 70 |
+
*/
|
| 71 |
+
public function getPayStandConfig()
|
| 72 |
+
{
|
| 73 |
+
Mage::log('PayStand getPayStandConfig', null, PS_LOG);
|
| 74 |
+
$this->org_id = $this->getConfigData('org_id');
|
| 75 |
+
$this->api_key = $this->getConfigData('api_key');
|
| 76 |
+
$this->use_sandbox = $this->getConfigData('use_sandbox');
|
| 77 |
+
if ($this->use_sandbox) {
|
| 78 |
+
$this->paystand_url = PS_SANDBOX_URL;
|
| 79 |
+
} else {
|
| 80 |
+
$this->paystand_url = PS_LIVE_URL;
|
| 81 |
+
}
|
| 82 |
+
$this->pm_url = $paystand_url . PS_API . '/paymentmethods';
|
| 83 |
+
$this->pay_url = $paystand_url . PS_API . '/payments';
|
| 84 |
+
Mage::log('PayStand config org_id: ' . $this->org_id, null, PS_LOG);
|
| 85 |
+
Mage::log('PayStand config pay_url: ' . $this->pay_url, null, PS_LOG);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
public function getPayStandUrl()
|
| 89 |
+
{
|
| 90 |
+
if (empty($this->paystand_url)) {
|
| 91 |
+
$this->getPayStandConfig();
|
| 92 |
+
}
|
| 93 |
+
return $this->paystand_url;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
public function getOrgId()
|
| 97 |
+
{
|
| 98 |
+
if (empty($this->org_id)) {
|
| 99 |
+
$this->getPayStandConfig();
|
| 100 |
+
}
|
| 101 |
+
return $this->org_id;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
public function getApiKey()
|
| 105 |
+
{
|
| 106 |
+
if (empty($this->api_key)) {
|
| 107 |
+
$this->getPayStandConfig();
|
| 108 |
+
}
|
| 109 |
+
return $this->api_key;
|
| 110 |
+
}
|
| 111 |
+
// XXX merge review - do we need these functions ^^^
|
| 112 |
+
|
| 113 |
+
/**
|
| 114 |
+
* Check method for processing with base currency
|
| 115 |
+
*
|
| 116 |
+
* @param string $currencyCode
|
| 117 |
+
* @return boolean
|
| 118 |
+
*/
|
| 119 |
+
public function canUseForCurrency($currencyCode)
|
| 120 |
+
{
|
| 121 |
+
if (!in_array($currencyCode, $this->getAcceptedCurrencyCodes())) {
|
| 122 |
+
Mage::log('PayStand False canUseForCurrency: ' . $currencyCode, null, PS_LOG);
|
| 123 |
+
return false;
|
| 124 |
+
}
|
| 125 |
+
return true;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
/**
|
| 129 |
+
* Return array of currency codes supported by Payment Gateway
|
| 130 |
+
*
|
| 131 |
+
* @return array
|
| 132 |
+
*/
|
| 133 |
+
public function getAcceptedCurrencyCodes()
|
| 134 |
+
{
|
| 135 |
+
return $this->accepted_currency_codes;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
public function getOrderPlaceRedirectUrl()
|
| 139 |
+
{
|
| 140 |
+
$url = Mage::getUrl('paystandgateway/payment/redirect',
|
| 141 |
+
array('_secure' => true));
|
| 142 |
+
return $url;
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
// XXX merge review - do we need these functions? vvv
|
| 146 |
+
/**
|
| 147 |
+
* Send capture request to gateway
|
| 148 |
+
*
|
| 149 |
+
* @param Mage_Payment_Model_Info $payment
|
| 150 |
+
* @param decimal $amount
|
| 151 |
+
* @return PayStand_PayStandGateway_Model_PaymentMethod
|
| 152 |
+
*/
|
| 153 |
+
public function capture(Varien_Object $payment, $amount)
|
| 154 |
+
{
|
| 155 |
+
Mage::log('PayStand capture amount: ' . $amount, null, PS_LOG);
|
| 156 |
+
|
| 157 |
+
$this->getConfig();
|
| 158 |
+
|
| 159 |
+
if ($amount <= 0) {
|
| 160 |
+
Mage::log('PayStand capture invalid amount: ' . $amount, null, PS_LOG);
|
| 161 |
+
Mage::throwException('Invalid amount for capture.');
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
$card_number = $payment->getCcNumber();
|
| 165 |
+
$card_exp_month = $payment->getCcExpMonth();
|
| 166 |
+
$card_exp_year = $payment->getCcExpYear();
|
| 167 |
+
$cvc = $payment->getCcCid();
|
| 168 |
+
$order = $payment->getOrder();
|
| 169 |
+
$order_id = $order->getIncrementId();
|
| 170 |
+
$currency = $order->getBaseCurrencyCode();
|
| 171 |
+
$ip_address = $_SERVER['REMOTE_ADDR'];
|
| 172 |
+
$addr = $order->getBillingAddress();
|
| 173 |
+
if ($addr) {
|
| 174 |
+
$first_name = $addr->getData('firstname');
|
| 175 |
+
$last_name = $addr->getData('lastname');
|
| 176 |
+
$email = $addr->getData('email');
|
| 177 |
+
$street1 = $addr->getData('street');
|
| 178 |
+
$city = $addr->getData('city');
|
| 179 |
+
$state = $addr->getData('region');
|
| 180 |
+
$postal_code = $addr->getData('postcode');
|
| 181 |
+
$country = $addr->getData('country_id');
|
| 182 |
+
$phone = $addr->getData('telephone');
|
| 183 |
+
} else {
|
| 184 |
+
$first_name = '';
|
| 185 |
+
$last_name = '';
|
| 186 |
+
$email = '';
|
| 187 |
+
$street1 = '';
|
| 188 |
+
$city = '';
|
| 189 |
+
$state = '';
|
| 190 |
+
$postal_code = '';
|
| 191 |
+
$country = '';
|
| 192 |
+
$phone = '';
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
$rail = 'card';
|
| 196 |
+
|
| 197 |
+
$billing = array(
|
| 198 |
+
'full_name' => "Unknown Customer",
|
| 199 |
+
'email' => $email,
|
| 200 |
+
'address_line1' => $street1,
|
| 201 |
+
'address_line2' => $street2,
|
| 202 |
+
'address_city' => $city,
|
| 203 |
+
'address_state' => $state,
|
| 204 |
+
'address_zip' => $postal_code,
|
| 205 |
+
'address_country' => $country,
|
| 206 |
+
'card_number' => $card_number,
|
| 207 |
+
'card_cvv' => $cvc,
|
| 208 |
+
'card_month' => $card_exp_month,
|
| 209 |
+
'card_year' =>$car_exp_year
|
| 210 |
+
);
|
| 211 |
+
|
| 212 |
+
$request = array(
|
| 213 |
+
'action' => 'create_token',
|
| 214 |
+
'api_key' => $this->api_key,
|
| 215 |
+
'org_id' => $this->org_id,
|
| 216 |
+
'rail' => $rail,
|
| 217 |
+
'billing' => $billing
|
| 218 |
+
);
|
| 219 |
+
|
| 220 |
+
Mage::log('PayStand capture request: ' . print_r($request, true), null, PS_LOG);
|
| 221 |
+
|
| 222 |
+
$context = stream_context_create(array(
|
| 223 |
+
'http' => array(
|
| 224 |
+
'method' => 'POST',
|
| 225 |
+
'header' => "Content-Type: application/json\r\n",
|
| 226 |
+
'content' => json_encode($request)
|
| 227 |
+
)
|
| 228 |
+
));
|
| 229 |
+
|
| 230 |
+
$response = false;
|
| 231 |
+
$retry = 0;
|
| 232 |
+
$max_retries = 3;
|
| 233 |
+
while (($response === false) && ($retry < $max_retries)) {
|
| 234 |
+
if ($retry > 0) {
|
| 235 |
+
sleep(1);
|
| 236 |
+
}
|
| 237 |
+
$response = file_get_contents($this->pm_url, false, $context);
|
| 238 |
+
$retry++;
|
| 239 |
+
}
|
| 240 |
+
if ($response === false) {
|
| 241 |
+
Mage::log('PayStand capture failed to contact payment gateway: ' . $this->pm_url, null, PS_LOG);
|
| 242 |
+
Mage::throwException('Failed to contact payment gateway.');
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
Mage::log('PayStand capture response: ' . print_r($response, true), null, PS_LOG);
|
| 246 |
+
|
| 247 |
+
$response_data = json_decode($response, true);
|
| 248 |
+
$data = $response_data['data'];
|
| 249 |
+
$token = $data['token']['token'];
|
| 250 |
+
|
| 251 |
+
$billing = array(
|
| 252 |
+
'payment_token' => $token
|
| 253 |
+
);
|
| 254 |
+
|
| 255 |
+
$request = array(
|
| 256 |
+
'action' => 'pay',
|
| 257 |
+
'api_key' => $this->api_key,
|
| 258 |
+
'org_id' => $this->org_id,
|
| 259 |
+
'pre_fee_total' => 12.67,
|
| 260 |
+
'memo' => 'for tix',
|
| 261 |
+
'rail' => $rail,
|
| 262 |
+
'billing' => $billing
|
| 263 |
+
);
|
| 264 |
+
|
| 265 |
+
Mage::log('PayStand capture request: ' . print_r($request, true), null, PS_LOG);
|
| 266 |
+
|
| 267 |
+
$context = stream_context_create(array(
|
| 268 |
+
'http' => array(
|
| 269 |
+
'method' => 'POST',
|
| 270 |
+
'header' => "Content-Type: application/json\r\n",
|
| 271 |
+
'content' => json_encode($request)
|
| 272 |
+
)
|
| 273 |
+
));
|
| 274 |
+
|
| 275 |
+
$response = false;
|
| 276 |
+
$retry = 0;
|
| 277 |
+
$max_retries = 3;
|
| 278 |
+
while (($response === false) && ($retry < $max_retries)) {
|
| 279 |
+
if ($retry > 0) {
|
| 280 |
+
sleep(1);
|
| 281 |
+
}
|
| 282 |
+
$response = file_get_contents($this->pm_url, false, $context);
|
| 283 |
+
$retry++;
|
| 284 |
+
}
|
| 285 |
+
if ($response === false) {
|
| 286 |
+
Mage::throwException('Failed to contact payment gateway.');
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
Mage::log('PayStand capture response: ' . print_r($response, true), null, PS_LOG);
|
| 290 |
+
|
| 291 |
+
$response_data = json_decode($response, true);
|
| 292 |
+
$data = $response_data['data'];
|
| 293 |
+
$payment_status = $data['payment_status'];
|
| 294 |
+
$pre_fee_total = $data['pre_fee_total'];
|
| 295 |
+
$total_amount = $data['total_amount'];
|
| 296 |
+
$processing_fee = $total_amount - $pre_fee_total;
|
| 297 |
+
$success = $data['success'];
|
| 298 |
+
$txn_id = $data['order_id'];
|
| 299 |
+
|
| 300 |
+
$payment->setTransactionId($txn_id);
|
| 301 |
+
$payment->setIsTransactionClosed(1);
|
| 302 |
+
$payment->setTransactionAdditionalInfo(
|
| 303 |
+
Mage_Sales_Model_Order_Payment_Transaction::RAW_DETAILS,
|
| 304 |
+
array('payment_status' => $payment_status,
|
| 305 |
+
'processing_fee' => $processing_fee));
|
| 306 |
+
|
| 307 |
+
return $this;
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
/**
|
| 311 |
+
* Send refund request to gateway
|
| 312 |
+
*
|
| 313 |
+
* @param Mage_Payment_Model_Info $payment
|
| 314 |
+
* @param decimal $amount
|
| 315 |
+
* @return PayStand_PayStandGateway_Model_PaymentMethod
|
| 316 |
+
*/
|
| 317 |
+
public function refund(Varien_Object $payment, $amount)
|
| 318 |
+
{
|
| 319 |
+
if ($amount <= 0) {
|
| 320 |
+
Mage::throwException(Mage::helper('paygate')->__('Invalid amount for capture.'));
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
// XXX Implement
|
| 324 |
+
|
| 325 |
+
return $this;
|
| 326 |
+
}
|
| 327 |
+
// XXX merge review - do we need these functions? ^^^
|
| 328 |
+
}
|
| 329 |
+
|
app/code/local/PayStand/PayStandGateway/controllers/PaymentController.php
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/*
|
| 4 |
+
Copyright 2014 PayStand Inc.
|
| 5 |
+
|
| 6 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 7 |
+
you may not use this file except in compliance with the License.
|
| 8 |
+
You may obtain a copy of the License at
|
| 9 |
+
|
| 10 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 11 |
+
|
| 12 |
+
Unless required by applicable law or agreed to in writing, software
|
| 13 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 14 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 15 |
+
See the License for the specific language governing permissions and
|
| 16 |
+
limitations under the License.
|
| 17 |
+
*/
|
| 18 |
+
|
| 19 |
+
if (!defined('PS_LOG')) {
|
| 20 |
+
define('PS_LOG', 'paystand.log');
|
| 21 |
+
}
|
| 22 |
+
if (!defined('PS_LIVE_URL')) {
|
| 23 |
+
define('PS_LIVE_URL', 'https://app.paystand.com');
|
| 24 |
+
}
|
| 25 |
+
if (!defined('PS_SANDBOX_URL')) {
|
| 26 |
+
define('PS_SANDBOX_URL', 'https://sandbox.paystand.co');
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
class PayStand_PayStandGateway_PaymentController extends Mage_Core_Controller_Front_Action
|
| 30 |
+
{
|
| 31 |
+
public function redirectAction()
|
| 32 |
+
{
|
| 33 |
+
$this->loadLayout();
|
| 34 |
+
$block = $this->getLayout()->createBlock('Mage_Core_Block_Template',
|
| 35 |
+
'paystandgateway',
|
| 36 |
+
array('template' => 'paystandgateway/redirect.phtml'));
|
| 37 |
+
$this->getLayout()->getBlock('content')->append($block);
|
| 38 |
+
$this->renderLayout();
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
public function responseAction()
|
| 42 |
+
{
|
| 43 |
+
$request = $this->getRequest();
|
| 44 |
+
$json = $request->getOriginalRequest()->getRawBody();
|
| 45 |
+
Mage::log('PayStand responseAction request: ' . print_r($json, true), null, PS_LOG);
|
| 46 |
+
$psn = json_decode($json, true);
|
| 47 |
+
$ok = $this->verify_psn($psn);
|
| 48 |
+
if (!$ok) {
|
| 49 |
+
Mage::log('PSN failed to verify', null, PS_LOG);
|
| 50 |
+
Mage::app()->getResponse()->setHeader('HTTP/1.1','400 Bad Request')
|
| 51 |
+
->sendResponse();
|
| 52 |
+
exit;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
$order_id = $psn['order_id'];
|
| 56 |
+
$txn_id = $psn['txn_id'];
|
| 57 |
+
$payment_status = $psn['payment_status'];
|
| 58 |
+
$success = $psn['success'];
|
| 59 |
+
$rail = $psn['rail'];
|
| 60 |
+
|
| 61 |
+
$order = Mage::getModel('sales/order');
|
| 62 |
+
$order->loadByIncrementId($order_id);
|
| 63 |
+
$payment = $order->getPayment();
|
| 64 |
+
|
| 65 |
+
if ($success) {
|
| 66 |
+
$pre_fee_total = $psn['pre_fee_total'];
|
| 67 |
+
$total_amount = $psn['total_amount'];
|
| 68 |
+
$processing_fee = $total_amount - $pre_fee_total;
|
| 69 |
+
|
| 70 |
+
//$fee_item = new Mage_Sales_Model_Order_Item();
|
| 71 |
+
// XXX set fee in item
|
| 72 |
+
// XXX add fee item if not already there
|
| 73 |
+
//$order->addItem($fee_item);
|
| 74 |
+
|
| 75 |
+
// XXX set state to order_status from module system config
|
| 76 |
+
$order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true,
|
| 77 |
+
'Payment success.');
|
| 78 |
+
|
| 79 |
+
$payment->setTransactionId($txn_id);
|
| 80 |
+
$payment->setIsTransactionClosed(1);
|
| 81 |
+
$payment->setTransactionAdditionalInfo(
|
| 82 |
+
Mage_Sales_Model_Order_Payment_Transaction::RAW_DETAILS,
|
| 83 |
+
array('payment_status' => $payment_status,
|
| 84 |
+
'processing_fee' => $processing_fee));
|
| 85 |
+
|
| 86 |
+
$order->sendNewOrderEmail();
|
| 87 |
+
$order->setEmailSent(true);
|
| 88 |
+
$order->save();
|
| 89 |
+
|
| 90 |
+
Mage::getSingleton('checkout/session')->unsQuoteId();
|
| 91 |
+
Mage_Core_Controller_Varien_Action::_redirect(
|
| 92 |
+
'checkout/onepage/success', array('_secure' => true));
|
| 93 |
+
} else {
|
| 94 |
+
if ('failed' == $payment_status) {
|
| 95 |
+
$this->cancelAction();
|
| 96 |
+
Mage_Core_Controller_Varien_Action::_redirect(
|
| 97 |
+
'checkout/onepage/failure', array('_secure' => true));
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
public function cancelAction()
|
| 103 |
+
{
|
| 104 |
+
Mage::log('PayStand cancelAction', null, PS_LOG);
|
| 105 |
+
if (Mage::getSingleton('checkout/session')->getLastRealOrderId()) {
|
| 106 |
+
$order = Mage::getModel('sales/order')->loadByIncrementId(
|
| 107 |
+
Mage::getSingleton('checkout/session')->getLastRealOrderId());
|
| 108 |
+
if ($order->getId()) {
|
| 109 |
+
$order->cancel()->setState(Mage_Sales_Model_Order::STATE_CANCELED,
|
| 110 |
+
true, 'Payment failed or canceled.')->save();
|
| 111 |
+
}
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
function verify_psn($psn)
|
| 116 |
+
{
|
| 117 |
+
if (empty($psn) || !is_array($psn)) {
|
| 118 |
+
Mage::log('verify_psn psn is empty');
|
| 119 |
+
return false;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
$api_key = Mage::getStoreConfig('payment/paystandgateway/api_key');
|
| 123 |
+
$use_sandbox = Mage::getStoreConfig('payment/paystandgateway/use_sandbox');
|
| 124 |
+
if ($use_sandbox) {
|
| 125 |
+
$paystand_url = PS_SANDBOX_URL;
|
| 126 |
+
} else {
|
| 127 |
+
$paystand_url = PS_LIVE_URL;
|
| 128 |
+
}
|
| 129 |
+
$endpoint = $paystand_url . '/api/v2/orders';
|
| 130 |
+
|
| 131 |
+
$request = array(
|
| 132 |
+
'action' => 'verify_psn',
|
| 133 |
+
'api_key' => $api_key,
|
| 134 |
+
'order_id' => $psn['txn_id'],
|
| 135 |
+
'psn' => $psn
|
| 136 |
+
);
|
| 137 |
+
|
| 138 |
+
Mage::log('verify_psn endpoint: ' . $endpoint, null, PS_LOG);
|
| 139 |
+
Mage::log('verify_psn request: ' . print_r($request, true), null, PS_LOG);
|
| 140 |
+
|
| 141 |
+
$context = stream_context_create(array(
|
| 142 |
+
'http' => array(
|
| 143 |
+
'method' => 'POST',
|
| 144 |
+
'header' => "Content-Type: application/json\r\n",
|
| 145 |
+
'content' => json_encode($request)
|
| 146 |
+
)
|
| 147 |
+
));
|
| 148 |
+
|
| 149 |
+
$response = false;
|
| 150 |
+
$retry = 0;
|
| 151 |
+
$max_retries = 3;
|
| 152 |
+
while (($response === false) && ($retry < $max_retries)) {
|
| 153 |
+
if ($retry > 0) {
|
| 154 |
+
sleep(1);
|
| 155 |
+
Mage::log('verify_psn retry: ' . $retry, null, PS_LOG);
|
| 156 |
+
}
|
| 157 |
+
$response = file_get_contents($endpoint, false, $context);
|
| 158 |
+
$retry++;
|
| 159 |
+
}
|
| 160 |
+
if ($response === false) {
|
| 161 |
+
Mage::log('verify_psn returned false', null, PS_LOG);
|
| 162 |
+
return false;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
$response_data = json_decode($response, true);
|
| 166 |
+
Mage::log('verify_psn response: ' . print_r($response_data, true), null, PS_LOG);
|
| 167 |
+
|
| 168 |
+
if ($response_data['data'] !== true) {
|
| 169 |
+
Mage::log('verify_psn response was not success', null, PS_LOG);
|
| 170 |
+
return false;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
$defined = array(
|
| 174 |
+
'txn_id', 'org_id', 'consumer_id', 'pre_fee_total',
|
| 175 |
+
'fee_merchant_owes', 'rate_merchant_owes',
|
| 176 |
+
'fee_consumer_owes', 'rate_consumer_owes', 'total_amount',
|
| 177 |
+
'payment_status', 'success'
|
| 178 |
+
);
|
| 179 |
+
$numerics = array(
|
| 180 |
+
'pre_fee_total', 'fee_merchant_owes', 'rate_merchant_owes',
|
| 181 |
+
'fee_consumer_owes', 'rate_consumer_owes', 'total_amount',
|
| 182 |
+
'txn_id', 'org_id', 'consumer_id'
|
| 183 |
+
);
|
| 184 |
+
|
| 185 |
+
foreach ($defined as $def) {
|
| 186 |
+
if (!isset($psn[$def])) {
|
| 187 |
+
Mage::log('PSN validation error: ' . $def . ' is not defined or is empty', null, PS_LOG);
|
| 188 |
+
return false;
|
| 189 |
+
}
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
foreach ($numerics as $numeric) {
|
| 193 |
+
if (!is_numeric($psn[$numeric])) {
|
| 194 |
+
Mage::log('PSN validation error: ' . $numeric . ' is not numeric', null, PS_LOG);
|
| 195 |
+
return false;
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
$order_id = false;
|
| 200 |
+
if (!empty($psn['order_id'])) {
|
| 201 |
+
$order_id = $psn['order_id'];
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
Mage::log('verify_psn order_id: ' . $order_id);
|
| 205 |
+
|
| 206 |
+
$order = false;
|
| 207 |
+
if ($order_id) {
|
| 208 |
+
$order = new Mage_Sales_Model_Order();
|
| 209 |
+
$order->loadByIncrementId($order_id);
|
| 210 |
+
}
|
| 211 |
+
if (!$order->getId()) {
|
| 212 |
+
Mage::log('Order not found for order id: ' . $order_id, null, PS_LOG);
|
| 213 |
+
return false;
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
$pre_fee_total = false;
|
| 217 |
+
if (!empty($psn['pre_fee_total'])) {
|
| 218 |
+
$pre_fee_total = $psn['pre_fee_total'];
|
| 219 |
+
}
|
| 220 |
+
if ($pre_fee_total != $order->getBaseGrandTotal()) {
|
| 221 |
+
Mage::log('PSN validation error: psn pre_fee_total: ' . $psn['pre_fee_total'] . ' not equal to order_total: ' . $order->getBaseGrandTotal(), null, PS_LOG);
|
| 222 |
+
return false;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
return true;
|
| 226 |
+
}
|
| 227 |
+
}
|
| 228 |
+
|
app/code/local/PayStand/PayStandGateway/etc/config.xml
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<config>
|
| 3 |
+
<modules>
|
| 4 |
+
<PayStand_PayStandGateway>
|
| 5 |
+
<version>1.0.2</version>
|
| 6 |
+
</PayStand_PayStandGateway>
|
| 7 |
+
</modules>
|
| 8 |
+
|
| 9 |
+
<global>
|
| 10 |
+
<blocks>
|
| 11 |
+
<paystandgateway>
|
| 12 |
+
<class>PayStand_PayStandGateway_Block</class>
|
| 13 |
+
</paystandgateway>
|
| 14 |
+
</blocks>
|
| 15 |
+
|
| 16 |
+
<models>
|
| 17 |
+
<paystandgateway>
|
| 18 |
+
<class>PayStand_PayStandGateway_Model</class>
|
| 19 |
+
</paystandgateway>
|
| 20 |
+
</models>
|
| 21 |
+
|
| 22 |
+
<resources>
|
| 23 |
+
<paystandgateway_setup>
|
| 24 |
+
<setup>
|
| 25 |
+
<module>PayStand_PayStandGateway</module>
|
| 26 |
+
</setup>
|
| 27 |
+
<connection>
|
| 28 |
+
<use>core_setup</use>
|
| 29 |
+
</connection>
|
| 30 |
+
</paystandgateway_setup>
|
| 31 |
+
<paystandgateway_write>
|
| 32 |
+
<connection>
|
| 33 |
+
<use>core_write</use>
|
| 34 |
+
</connection>
|
| 35 |
+
</paystandgateway_write>
|
| 36 |
+
<paystandgateway_read>
|
| 37 |
+
<connection>
|
| 38 |
+
<use>core_read</use>
|
| 39 |
+
</connection>
|
| 40 |
+
</paystandgateway_read>
|
| 41 |
+
</resources>
|
| 42 |
+
|
| 43 |
+
<helpers>
|
| 44 |
+
<paystandgateway>
|
| 45 |
+
<class>PayStand_PayStandGateway_Helper</class>
|
| 46 |
+
</paystandgateway>
|
| 47 |
+
</helpers>
|
| 48 |
+
</global>
|
| 49 |
+
|
| 50 |
+
<default>
|
| 51 |
+
<payment>
|
| 52 |
+
<paystandgateway>
|
| 53 |
+
<active>1</active>
|
| 54 |
+
<model>paystandgateway/paymentMethod</model>
|
| 55 |
+
<order_status>pending</order_status>
|
| 56 |
+
<title>PayStand (Card, eCheck, Bitcoin)</title>
|
| 57 |
+
|
| 58 |
+
<payment_action>sale</payment_action>
|
| 59 |
+
<allowspecific>0</allowspecific>
|
| 60 |
+
<sort_order>1</sort_order>
|
| 61 |
+
</paystandgateway>
|
| 62 |
+
</payment>
|
| 63 |
+
</default>
|
| 64 |
+
|
| 65 |
+
<frontend>
|
| 66 |
+
<routers>
|
| 67 |
+
<paystandgateway>
|
| 68 |
+
<use>standard</use>
|
| 69 |
+
<args>
|
| 70 |
+
<module>PayStand_PayStandGateway</module>
|
| 71 |
+
<frontName>paystandgateway</frontName>
|
| 72 |
+
</args>
|
| 73 |
+
</paystandgateway>
|
| 74 |
+
</routers>
|
| 75 |
+
</frontend>
|
| 76 |
+
</config>
|
app/code/local/PayStand/PayStandGateway/etc/system.xml
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<config>
|
| 3 |
+
<sections>
|
| 4 |
+
<payment>
|
| 5 |
+
<groups>
|
| 6 |
+
<paystandgateway translate="label" module="paystandgateway">
|
| 7 |
+
<label>Paystand PayStandGateway</label>
|
| 8 |
+
<!-- position between other payment methods -->
|
| 9 |
+
<sort_order>670</sort_order>
|
| 10 |
+
<show_in_default>1</show_in_default>
|
| 11 |
+
<show_in_website>1</show_in_website>
|
| 12 |
+
<show_in_store>0</show_in_store>
|
| 13 |
+
<fields>
|
| 14 |
+
<active translate="label">
|
| 15 |
+
<label>Enabled</label>
|
| 16 |
+
<frontend_type>select</frontend_type>
|
| 17 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 18 |
+
<sort_order>1</sort_order>
|
| 19 |
+
<show_in_default>1</show_in_default>
|
| 20 |
+
<show_in_website>1</show_in_website>
|
| 21 |
+
<show_in_store>0</show_in_store>
|
| 22 |
+
</active>
|
| 23 |
+
<order_status translate="label">
|
| 24 |
+
<label>New order status</label>
|
| 25 |
+
<frontend_type>select</frontend_type>
|
| 26 |
+
<source_model>adminhtml/system_config_source_order_status</source_model>
|
| 27 |
+
<sort_order>4</sort_order>
|
| 28 |
+
<show_in_default>1</show_in_default>
|
| 29 |
+
<show_in_website>1</show_in_website>
|
| 30 |
+
<show_in_store>0</show_in_store>
|
| 31 |
+
</order_status>
|
| 32 |
+
<org_id translate="label">
|
| 33 |
+
<label>Org Id</label>
|
| 34 |
+
<frontend_type>text</frontend_type>
|
| 35 |
+
<sort_order>2</sort_order>
|
| 36 |
+
<show_in_default>1</show_in_default>
|
| 37 |
+
<show_in_website>1</show_in_website>
|
| 38 |
+
<show_in_store>0</show_in_store>
|
| 39 |
+
</org_id>
|
| 40 |
+
<api_key translate="label">
|
| 41 |
+
<label>Public API Key</label>
|
| 42 |
+
<frontend_type>text</frontend_type>
|
| 43 |
+
<sort_order>2</sort_order>
|
| 44 |
+
<show_in_default>1</show_in_default>
|
| 45 |
+
<show_in_website>1</show_in_website>
|
| 46 |
+
<show_in_store>0</show_in_store>
|
| 47 |
+
</api_key>
|
| 48 |
+
<use_sandbox translate="label">
|
| 49 |
+
<label>Use Sandbox</label>
|
| 50 |
+
<frontend_type>select</frontend_type>
|
| 51 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 52 |
+
<sort_order>1</sort_order>
|
| 53 |
+
<show_in_default>1</show_in_default>
|
| 54 |
+
<show_in_website>1</show_in_website>
|
| 55 |
+
<show_in_store>0</show_in_store>
|
| 56 |
+
</use_sandbox>
|
| 57 |
+
</fields>
|
| 58 |
+
</paystandgateway>
|
| 59 |
+
</groups>
|
| 60 |
+
</payment>
|
| 61 |
+
</sections>
|
| 62 |
+
</config>
|
app/design/frontend/base/default/template/paystandgateway/redirect.phtml
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
if (!defined('PS_LOG')) {
|
| 4 |
+
define('PS_LOG', 'paystand.log');
|
| 5 |
+
}
|
| 6 |
+
if (!defined('PS_LIVE_URL')) {
|
| 7 |
+
define('PS_LIVE_URL', 'https://app.paystand.com');
|
| 8 |
+
}
|
| 9 |
+
if (!defined('PS_SANDBOX_URL')) {
|
| 10 |
+
define('PS_SANDBOX_URL', 'https://dev.paystand.biz');
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
$order = new Mage_Sales_Model_Order();
|
| 14 |
+
$session = Mage::getSingleton('checkout/session');
|
| 15 |
+
$order_id = $session->getLastRealOrderId();
|
| 16 |
+
$order->loadByIncrementId($order_id);
|
| 17 |
+
|
| 18 |
+
$org_id = Mage::getStoreConfig('payment/paystandgateway/org_id');
|
| 19 |
+
if (empty($org_id)) {
|
| 20 |
+
Mage::log('ERROR: org_id not defined in system configuration.', null, PS_LOG);
|
| 21 |
+
}
|
| 22 |
+
Mage::log('org_id: ' . $org_id, null, PS_LOG);
|
| 23 |
+
|
| 24 |
+
$api_key = Mage::getStoreConfig('payment/paystandgateway/api_key');
|
| 25 |
+
if (empty($api_key)) {
|
| 26 |
+
Mage::log('ERROR: api_key not defined in system configuration.', null, PS_LOG);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
$use_sandbox = Mage::getStoreConfig('payment/paystandgateway/use_sandbox');
|
| 30 |
+
if ($use_sandbox) {
|
| 31 |
+
$paystand_url = PS_SANDBOX_URL;
|
| 32 |
+
} else {
|
| 33 |
+
$paystand_url = PS_LIVE_URL;
|
| 34 |
+
}
|
| 35 |
+
Mage::log('paystand_url: ' . $paystand_url, null, PS_LOG);
|
| 36 |
+
|
| 37 |
+
$return_url = $this->getUrl('checkout/onepage/success');
|
| 38 |
+
|
| 39 |
+
$currency = $order->getBaseCurrencyCode();
|
| 40 |
+
$pre_fee_total = $order->getBaseGrandTotal();
|
| 41 |
+
Mage::log('pre_fee_total: ' . $pre_fee_total, null, PS_LOG);
|
| 42 |
+
$shipping_handling = $order->getBaseShippingAmount();
|
| 43 |
+
$tax = $order->getBaseTaxAmount();
|
| 44 |
+
|
| 45 |
+
// Convert to pennies
|
| 46 |
+
$pre_fee_total = round($pre_fee_total * 100.0);
|
| 47 |
+
$shipping_handling = round($shipping_handling * 100.0);
|
| 48 |
+
$tax = round($tax * 100.0);
|
| 49 |
+
$subtotal = $pre_fee_total - $shipping_handling - $tax;
|
| 50 |
+
|
| 51 |
+
$final_item_name = 'Payment for Order#: ' . $order_id;
|
| 52 |
+
$items = $order->getAllItems();
|
| 53 |
+
foreach ($items as $item) {
|
| 54 |
+
$name = $item->getName();
|
| 55 |
+
$qty = $item->getQtyOrdered();
|
| 56 |
+
$qtyi = intval($qty);
|
| 57 |
+
$qtyf = floatval($qty);
|
| 58 |
+
if ($qtyi == $qtyf) {
|
| 59 |
+
$qty = $qtyi;
|
| 60 |
+
}
|
| 61 |
+
$final_item_name .= ", " . $qty . ' x ' . $name;
|
| 62 |
+
}
|
| 63 |
+
if (strlen($final_item_name) > 127) {
|
| 64 |
+
$final_item_name = substr($final_item_name, 0, 124) . '...';
|
| 65 |
+
}
|
| 66 |
+
$final_item_name = html_entity_decode($final_item_name, ENT_NOQUOTES,
|
| 67 |
+
'UTF-8');
|
| 68 |
+
Mage::log('final_item_name: ' . $final_item_name, null, PS_LOG);
|
| 69 |
+
|
| 70 |
+
$addr = $order->getBillingAddress();
|
| 71 |
+
$first_name = $addr->getData('firstname');
|
| 72 |
+
$last_name = $addr->getData('lastname');
|
| 73 |
+
$billing_full_name = trim($first_name . ' ' . $last_name);
|
| 74 |
+
$billing_email_address = $addr->getData('email');
|
| 75 |
+
$billing_street = $addr->getData('street');
|
| 76 |
+
$billing_city = $addr->getData('city');
|
| 77 |
+
$billing_state_name = $addr->getData('region');
|
| 78 |
+
|
| 79 |
+
$state_codes = array(
|
| 80 |
+
'alabama' => 'AL',
|
| 81 |
+
'alaska' => 'AK',
|
| 82 |
+
'arizona' => 'AZ',
|
| 83 |
+
'arkansas' => 'AR',
|
| 84 |
+
'california' => 'CA',
|
| 85 |
+
'colorado' => 'CO',
|
| 86 |
+
'connecticut' => 'CT',
|
| 87 |
+
'delaware' => 'DE',
|
| 88 |
+
'district of columbia' => 'DC',
|
| 89 |
+
'florida' => 'FL',
|
| 90 |
+
'georgia' => 'GA',
|
| 91 |
+
'hawaii' => 'HI',
|
| 92 |
+
'idaho' => 'ID',
|
| 93 |
+
'illinois' => 'IL',
|
| 94 |
+
'indiana' => 'IN',
|
| 95 |
+
'iowa' => 'IA',
|
| 96 |
+
'kansas' => 'KS',
|
| 97 |
+
'kentucky' => 'KY',
|
| 98 |
+
'louisiana' => 'LA',
|
| 99 |
+
'maine' => 'ME',
|
| 100 |
+
'maryland' => 'MD',
|
| 101 |
+
'massachusetts' => 'MA',
|
| 102 |
+
'michigan' => 'MI',
|
| 103 |
+
'minnesota' => 'MN',
|
| 104 |
+
'mississippi' => 'MS',
|
| 105 |
+
'missouri' => 'MO',
|
| 106 |
+
'montana' => 'MT',
|
| 107 |
+
'nebraska' => 'NE',
|
| 108 |
+
'nevada' => 'NV',
|
| 109 |
+
'new hampshire' => 'NH',
|
| 110 |
+
'new jersey' => 'NJ',
|
| 111 |
+
'new mexico' => 'NM',
|
| 112 |
+
'new york' => 'NY',
|
| 113 |
+
'north carolina' => 'NC',
|
| 114 |
+
'north dakota' => 'ND',
|
| 115 |
+
'ohio' => 'OH',
|
| 116 |
+
'oklahoma' => 'OK',
|
| 117 |
+
'oregon' => 'OR',
|
| 118 |
+
'pennsylvania' => 'PA',
|
| 119 |
+
'rhode island' => 'RI',
|
| 120 |
+
'south carolina' => 'SC',
|
| 121 |
+
'south dakota' => 'SD',
|
| 122 |
+
'tennessee' => 'TN',
|
| 123 |
+
'texas' => 'TX',
|
| 124 |
+
'utah' => 'UT',
|
| 125 |
+
'vermont' => 'VT',
|
| 126 |
+
'virginia' => 'VA',
|
| 127 |
+
'washington' => 'WA',
|
| 128 |
+
'west virginia' => 'WV',
|
| 129 |
+
'wisconsin' => 'WI',
|
| 130 |
+
'wyoming' => 'WY'
|
| 131 |
+
);
|
| 132 |
+
$billing_state_code = empty($state_codes[strtolower($billing_state_name)])
|
| 133 |
+
? false : $state_codes[strtolower($billing_state_name)];
|
| 134 |
+
$billing_state = empty($billing_state_code)
|
| 135 |
+
? $billing_state_name : $billing_state_code;
|
| 136 |
+
|
| 137 |
+
$billing_postalcode = $addr->getData('postcode');
|
| 138 |
+
$billing_country = $addr->getData('country_id');
|
| 139 |
+
$billing_phone = $addr->getData('telephone');
|
| 140 |
+
|
| 141 |
+
// json_encode parameters that users can enter to protect against quotes
|
| 142 |
+
// and other troublesome characters
|
| 143 |
+
if (is_numeric($org_id)) {
|
| 144 |
+
// We want to pass it as a string
|
| 145 |
+
$org_id_json = '"' . $org_id . '"';
|
| 146 |
+
} else {
|
| 147 |
+
// Probably bogus but maybe valid in the future
|
| 148 |
+
$org_id_json = json_encode($org_id);
|
| 149 |
+
}
|
| 150 |
+
$api_key_json = json_encode($api_key);
|
| 151 |
+
$return_url_json = json_encode($return_url);
|
| 152 |
+
$final_item_name_json = json_encode($final_item_name);
|
| 153 |
+
|
| 154 |
+
$billing_full_name_json = json_encode($billing_full_name);
|
| 155 |
+
$billing_email_address_json = json_encode($billing_email_address);
|
| 156 |
+
$billing_street_json = json_encode($billing_street);
|
| 157 |
+
$billing_city_json = json_encode($billing_city);
|
| 158 |
+
$billing_state_json = json_encode($billing_state);
|
| 159 |
+
$billing_postalcode_json = json_encode($billing_postalcode);
|
| 160 |
+
$billing_country_json = json_encode($billing_country);
|
| 161 |
+
$billing_phone_json = json_encode($billing_phone);
|
| 162 |
+
|
| 163 |
+
$markup = <<<EOF
|
| 164 |
+
<h2>Opening PayStand Checkout</h2>
|
| 165 |
+
<div id="paystand_element_id"></div>
|
| 166 |
+
<script type="text/javascript">
|
| 167 |
+
|
| 168 |
+
var PayStand = PayStand || {};
|
| 169 |
+
PayStand.checkouts = PayStand.checkouts || [];
|
| 170 |
+
PayStand.load = PayStand.load || function(){};
|
| 171 |
+
|
| 172 |
+
PayStand.checkoutUpdated = function() {
|
| 173 |
+
console.log('checkoutUpdated called.');
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
PayStand.checkoutComplete = function() {
|
| 177 |
+
console.log('checkoutComplete called.');
|
| 178 |
+
window.top.location.href = {$return_url_json};
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
var autoCheckout = {
|
| 182 |
+
api_key: {$api_key_json},
|
| 183 |
+
org_id: {$org_id_json},
|
| 184 |
+
element_ids: ["paystand_element_id"],
|
| 185 |
+
data_source: "org_defined",
|
| 186 |
+
st_platform: "magento-1.0.1",
|
| 187 |
+
checkout_type: "button",
|
| 188 |
+
redirect_url: {$return_url_json},
|
| 189 |
+
currency: "{$currency}",
|
| 190 |
+
amount: "{$subtotal}",
|
| 191 |
+
shipping_handling: "{$shipping_handling}",
|
| 192 |
+
tax: "{$tax}",
|
| 193 |
+
items: [
|
| 194 |
+
{
|
| 195 |
+
title: {$final_item_name_json},
|
| 196 |
+
quantity: "1",
|
| 197 |
+
item_price: "{$subtotal}"
|
| 198 |
+
}
|
| 199 |
+
],
|
| 200 |
+
billing: {
|
| 201 |
+
full_name: {$billing_full_name_json},
|
| 202 |
+
email_address: {$billing_email_address_json},
|
| 203 |
+
street: {$billing_street_json},
|
| 204 |
+
city: {$billing_city_json},
|
| 205 |
+
postalcode: {$billing_postalcode_json},
|
| 206 |
+
state: {$billing_state_json},
|
| 207 |
+
country: {$billing_country_json},
|
| 208 |
+
shipping_same: true
|
| 209 |
+
},
|
| 210 |
+
meta: {
|
| 211 |
+
order_id: "{$order_id}"
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
var buttonCheckout = {
|
| 216 |
+
api_key: {$api_key_json},
|
| 217 |
+
org_id: {$org_id_json},
|
| 218 |
+
element_ids: ["paystand_element_id"],
|
| 219 |
+
data_source: "org_defined",
|
| 220 |
+
st_platform: "magento-1.0.1",
|
| 221 |
+
checkout_type: "button",
|
| 222 |
+
redirect_url: {$return_url_json},
|
| 223 |
+
button_options: {
|
| 224 |
+
button_name: 'Pay with PayStand',
|
| 225 |
+
input: false,
|
| 226 |
+
variants: false
|
| 227 |
+
},
|
| 228 |
+
currency: "{$currency}",
|
| 229 |
+
amount: "{$subtotal}",
|
| 230 |
+
shipping_handling: "{$shipping_handling}",
|
| 231 |
+
tax: "{$tax}",
|
| 232 |
+
items: [
|
| 233 |
+
{
|
| 234 |
+
title: {$final_item_name_json},
|
| 235 |
+
quantity: "1",
|
| 236 |
+
item_price: "{$subtotal}"
|
| 237 |
+
}
|
| 238 |
+
],
|
| 239 |
+
billing: {
|
| 240 |
+
full_name: {$billing_full_name_json},
|
| 241 |
+
email_address: {$billing_email_address_json},
|
| 242 |
+
street: {$billing_street_json},
|
| 243 |
+
city: {$billing_city_json},
|
| 244 |
+
postalcode: {$billing_postalcode_json},
|
| 245 |
+
state: {$billing_state_json},
|
| 246 |
+
country: {$billing_country_json},
|
| 247 |
+
shipping_same: true
|
| 248 |
+
},
|
| 249 |
+
meta: {
|
| 250 |
+
order_id: "{$order_id}"
|
| 251 |
+
}
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
PayStand.checkouts.push(buttonCheckout);
|
| 255 |
+
|
| 256 |
+
PayStand.onLoad = function() {
|
| 257 |
+
PayStand.execute(autoCheckout);
|
| 258 |
+
};
|
| 259 |
+
|
| 260 |
+
PayStand.script = document.createElement('script');
|
| 261 |
+
PayStand.script.type = 'text/javascript';
|
| 262 |
+
PayStand.script.async = true;
|
| 263 |
+
PayStand.script.src = '{$paystand_url}/js/checkout.js';
|
| 264 |
+
var s = document.getElementsByTagName('script')[0];
|
| 265 |
+
s.parentNode.insertBefore(PayStand.script, s);
|
| 266 |
+
</script>
|
| 267 |
+
EOF;
|
| 268 |
+
|
| 269 |
+
echo $markup;
|
| 270 |
+
|
app/etc/modules/PayStand_PayStandGateway.xml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<config>
|
| 2 |
+
<modules>
|
| 3 |
+
<PayStand_PayStandGateway>
|
| 4 |
+
<active>true</active>
|
| 5 |
+
<codePool>local</codePool>
|
| 6 |
+
<depends>
|
| 7 |
+
<Mage_Payment />
|
| 8 |
+
</depends>
|
| 9 |
+
</PayStand_PayStandGateway>
|
| 10 |
+
</modules>
|
| 11 |
+
</config>
|
| 12 |
+
|
package.xml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<package>
|
| 3 |
+
<name>PayStand_PayStandGateway</name>
|
| 4 |
+
<version>1.0.2</version>
|
| 5 |
+
<stability>stable</stability>
|
| 6 |
+
<license uri="http://www.apache.org/licenses/LICENSE-2.0">Apache Software License</license>
|
| 7 |
+
<channel>community</channel>
|
| 8 |
+
<extends/>
|
| 9 |
+
<summary>The PayStand payment gateway provides next generation payment processing for Card, eCheck, and Bitcoin.</summary>
|
| 10 |
+
<description>PayStand provides modern payment processing with multiple payment methods including Credit Cards, eCheck, and Bitcoin. Requires Magento 1.5+ and a PayStand account. Sign up for an account at paystand.com</description>
|
| 11 |
+
<notes>This is the initial version. Please provide feedback to support@paystand.com</notes>
|
| 12 |
+
<authors><author><name>Scott Campbell</name><user>paystand</user><email>developers@paystand.com</email></author></authors>
|
| 13 |
+
<date>2015-12-31</date>
|
| 14 |
+
<time>05:22:12</time>
|
| 15 |
+
<contents><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="template"><dir name="paystandgateway"><file name="redirect.phtml" hash="6bbf31e22f005f6dedaa0398d6ac5764"/></dir></dir></dir></dir></dir></target><target name="magelocal"><dir name="PayStand"><dir name="PayStandGateway"><dir name="Helper"><file name="Data.php" hash="a9c3c81147c6f2ac30498b2e94f16e7a"/></dir><dir name="Model"><file name="PaymentMethod.php" hash="a4371f08a9efd412073fe4038ac2e0b2"/></dir><dir name="controllers"><file name="PaymentController.php" hash="83adb50fb7535d57324c200bff0bdf4b"/></dir><dir name="etc"><file name="config.xml" hash="e364cf4ae17cabcfd8aa39dce34207db"/><file name="system.xml" hash="d88e4dbf2ce6cdc569478033f01805ee"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="PayStand_PayStandGateway.xml" hash="79a872581f3fe52e1dd3e9b0d34b1038"/></dir></target></contents>
|
| 16 |
+
<compatible/>
|
| 17 |
+
<dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.6.0</min><max>1.9.2</max></package><extension><name>Core</name><min/><max/></extension></required></dependencies>
|
| 18 |
+
</package>
|
