PayStand_PayStandGateway - Version 1.0.2

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 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>