Boku_Paymentgateway - Version 0.0.4

Version Notes

Testing has been done on Magento versions 1.7-1.9.
There is no overloading of any classes so there should be no impact on existing functionality.

Download this release

Release Info

Developer Mark Davidson-Houston
Extension Boku_Paymentgateway
Version 0.0.4
Comparing to
See all releases


Version 0.0.4

Files changed (46) hide show
  1. app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Cancel.php +16 -0
  2. app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Form.php +17 -0
  3. app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Prepare.php +22 -0
  4. app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Result.php +39 -0
  5. app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Success.php +16 -0
  6. app/code/community/Boku/Paymentgateway/Helper/Data.php +400 -0
  7. app/code/community/Boku/Paymentgateway/Model/AdminSession.php +18 -0
  8. app/code/community/Boku/Paymentgateway/Model/Cron.php +18 -0
  9. app/code/community/Boku/Paymentgateway/Model/Mapped/Abstract.php +57 -0
  10. app/code/community/Boku/Paymentgateway/Model/Payment/Callback.php +19 -0
  11. app/code/community/Boku/Paymentgateway/Model/Payment/Chargeback.php +70 -0
  12. app/code/community/Boku/Paymentgateway/Model/Payment/Event.php +86 -0
  13. app/code/community/Boku/Paymentgateway/Model/Payment/Standard.php +76 -0
  14. app/code/community/Boku/Paymentgateway/Model/Payment/Transaction.php +364 -0
  15. app/code/community/Boku/Paymentgateway/Model/Prices.php +386 -0
  16. app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Callback.php +20 -0
  17. app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Callback/Collection.php +20 -0
  18. app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Chargeback.php +19 -0
  19. app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Chargeback/Collection.php +20 -0
  20. app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Event.php +19 -0
  21. app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Event/Collection.php +20 -0
  22. app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Transaction.php +22 -0
  23. app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Transaction/Collection.php +20 -0
  24. app/code/community/Boku/Paymentgateway/Model/Resource/Setup.php +12 -0
  25. app/code/community/Boku/Paymentgateway/Model/Session.php +18 -0
  26. app/code/community/Boku/Paymentgateway/Model/System/Config.php +71 -0
  27. app/code/community/Boku/Paymentgateway/Model/Test.php +72 -0
  28. app/code/community/Boku/Paymentgateway/Model/Xml.php +100 -0
  29. app/code/community/Boku/Paymentgateway/controllers/Adminhtml/BokuController.php +16 -0
  30. app/code/community/Boku/Paymentgateway/controllers/ApiController.php +236 -0
  31. app/code/community/Boku/Paymentgateway/controllers/StandardController.php +106 -0
  32. app/code/community/Boku/Paymentgateway/etc/adminhtml.xml +61 -0
  33. app/code/community/Boku/Paymentgateway/etc/config.xml +177 -0
  34. app/code/community/Boku/Paymentgateway/etc/system.xml +248 -0
  35. app/code/community/Boku/Paymentgateway/sql/boku_setup/install-0.0.1.php +146 -0
  36. app/code/community/Boku/Paymentgateway/sql/boku_setup/upgrade-0.0.1-0.0.2.php +45 -0
  37. app/design/adminhtml/default/default/layout/boku_paymentgateway.xml +27 -0
  38. app/design/adminhtml/default/default/template/boku/paymentgateway/test.phtml +63 -0
  39. app/design/frontend/base/default/layout/boku_paymentgateway.xml +37 -0
  40. app/design/frontend/base/default/template/boku/paymentgateway/standard/cancel.phtml +13 -0
  41. app/design/frontend/base/default/template/boku/paymentgateway/standard/form.phtml +14 -0
  42. app/design/frontend/base/default/template/boku/paymentgateway/standard/prepare.phtml +12 -0
  43. app/design/frontend/base/default/template/boku/paymentgateway/standard/success.phtml +13 -0
  44. app/etc/modules/Boku_Paymentgateway.xml +22 -0
  45. app/locale/en_US/Boku_Paymentgateway.csv +41 -0
  46. package.xml +21 -0
app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Cancel.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * After failed Boku buy-url submission (via Prepare block)
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Block_Payment_Standard_Cancel extends Boku_Paymentgateway_Block_Payment_Standard_Result
12
+ {
13
+ protected function getRedirectUrl(){
14
+ return Mage::helper(self::APP_ROOT)->getUrl('checkout/onepage/failure');
15
+ }
16
+ }
app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Form.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Displayed for Boku payment option
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Block_Payment_Standard_Form extends Mage_Payment_Block_Form
12
+ {
13
+ protected function _construct(){
14
+ parent::_construct();
15
+ $this->setTemplate('boku/paymentgateway/standard/form.phtml');
16
+ }
17
+ }
app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Prepare.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku standard payment Form Block
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Block_Payment_Standard_Prepare extends Mage_Core_Block_Template
12
+ {
13
+ /**
14
+ * Returns the Boku gateway url
15
+ * This is set in Boku_Paymentgateway_Model_Payment_Standard
16
+ *
17
+ * @return string
18
+ */
19
+ protected function getBuyUrl(){
20
+ return Mage::helper('boku')->getSession()->getData('buy-url');
21
+ }
22
+ }
app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Result.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku standard payment response Form Block
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Block_Payment_Standard_Result extends Mage_Core_Block_Template
12
+ {
13
+ const APP_ROOT = 'boku';
14
+
15
+ private $_trx_id = null;
16
+ private $_transaction = null;
17
+ private $_order = null;
18
+
19
+ protected function _construct(){
20
+ $data = Mage::app()->getRequest()->getParams();
21
+ if (isset($data['trx-id']))
22
+ $this->_trx_id = $data['trx-id'];
23
+ parent::_construct();
24
+ }
25
+
26
+ protected function getTransaction(){
27
+ if (empty($this->_transaction) && !empty($this->_trx_id))
28
+ $this->_transaction = Mage::helper(self::APP_ROOT)->getTransaction($this->_trx_id);
29
+ return $this->_transaction;
30
+ }
31
+ protected function getOrder(){
32
+ if (empty($this->_order) && !empty($this->_trx_id)){
33
+ $transaction = $this->getTransaction();
34
+ if (!empty($transaction))
35
+ $this->_order = $transaction->getOrder();
36
+ }
37
+ return $this->_order;
38
+ }
39
+ }
app/code/community/Boku/Paymentgateway/Block/Payment/Standard/Success.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * After successful Boku buy-url submission (via Prepare block)
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Block_Payment_Standard_Success extends Boku_Paymentgateway_Block_Payment_Standard_Result
12
+ {
13
+ protected function getRedirectUrl(){
14
+ return Mage::helper(self::APP_ROOT)->getUrl('checkout/onepage/success');
15
+ }
16
+ }
app/code/community/Boku/Paymentgateway/Helper/Data.php ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Data helper
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Helper_Data extends Mage_Core_Helper_Abstract
12
+ {
13
+ const APP_ROOT = 'boku';
14
+ const CONFIG_ROOT = 'payment/boku';
15
+
16
+ /**
17
+ * Are we in the admin system ?
18
+ *
19
+ * @return boolean
20
+ */
21
+ public static function isAdmin(){
22
+ return Mage::app()->getStore()->isAdmin() || Mage::getDesign()->getArea() == 'adminhtml';
23
+ }
24
+
25
+ /**
26
+ * get the country ISO code
27
+ * If there is a quote in the basket and it has a billing address then uses this
28
+ * otherwise it defaults to the store's country.
29
+ *
30
+ * @return string
31
+ */
32
+ public static function getCountryCode(){
33
+ if (($q = self::getQuote()) && ($m = $q->getBillingAddress()) && ($m = $m->getCountryId()))
34
+ return $m;
35
+ return self::getStore()->getConfig('general/country/default');
36
+ }
37
+
38
+ /**
39
+ * get the base currency ISO code
40
+ *
41
+ * @return string
42
+ */
43
+ public static function getBaseCurrencyCode(){
44
+ return self::getStore()->getBaseCurrencyCode();
45
+ }
46
+
47
+ /**
48
+ * get the current quote/order's currency ISO code
49
+ *
50
+ * @return string
51
+ */
52
+ public static function getCurrencyCode(){
53
+ return self::getQuote()->getQuoteCurrencyCode();
54
+ }
55
+
56
+ /**
57
+ * Get a currency's decimal places
58
+ *
59
+ * @param string (ISO 4217) $currency
60
+ * @return int
61
+ */
62
+ public static function getCurrencyPrecision($currency){
63
+ return Mage::getSingleton(self::APP_ROOT.'/prices')->getCurrencyPrecision($currency);
64
+ }
65
+
66
+ /**
67
+ * Get the currency value expressed in the fractional currency unit
68
+ *
69
+ * @param float $price
70
+ * @param string (ISO 4217) $currency
71
+ * @return int
72
+ */
73
+ public static function getIntegerPrice($price, $currency){
74
+ return Mage::getSingleton(self::APP_ROOT.'/prices')->getIntegerPrice($price, $currency);
75
+ }
76
+
77
+ /**
78
+ * Convert a value expressed in the fractional currency unit to the normal float value
79
+ *
80
+ * @param int $price
81
+ * @param string (ISO 4217) $currency
82
+ * @return float
83
+ */
84
+ public static function getFloatPrice($price, $currency){
85
+ return Mage::getSingleton(self::APP_ROOT.'/prices')->getFloatPrice($price, $currency);
86
+ }
87
+
88
+ /**
89
+ * convert from one currency to another
90
+ *
91
+ * @param number $val
92
+ * @param string $currency_from - ISO code for $val currency - default store currency
93
+ * @param string $currency_to - ISO code for return currency - default base currency
94
+ * @return number
95
+ */
96
+ public static function convertCurrency($val, $currency_from = null, $currency_to = null){
97
+ if (empty($currency_from)) $currency_from = self::getCurrencyCode();
98
+ if (empty($currency_to)) $currency_to = self::getBaseCurrencyCode();
99
+ $currency_from = strtoupper($currency_from);
100
+ $currency_to = strtoupper($currency_to);
101
+ if ($currency_from != $currency_to)
102
+ $val = Mage::helper('directory')->currencyConvert($val, $currency_from, $currency_to);
103
+ return $val;
104
+ }
105
+ public static function convertToBaseCurrency($val, $currency = null){
106
+ return self::convertCurrency($val, $currency);
107
+ }
108
+
109
+ public static function getCheckout(){
110
+ return Mage::getModel('checkout/cart');
111
+ }
112
+
113
+ /**
114
+ * Get the current quote
115
+ *
116
+ * @return Mage_Sales_Model_Quote
117
+ */
118
+ public static function getQuote(){
119
+ return self::isAdmin() ? Mage::getSingleton('adminhtml/session_quote')->getQuote() : self::getCheckout()->getQuote();
120
+ }
121
+
122
+ /**
123
+ * @param string $trx_id
124
+ * @return Boku_Paymentgateway_Model_Payment_Transaction | null
125
+ */
126
+ public static function getTransaction($trx_id){
127
+ return Mage::getSingleton(self::APP_ROOT.'/payment_transaction')->getTransaction($trx_id);
128
+ }
129
+
130
+ /**
131
+ * @return string | null
132
+ */
133
+ public static function getPhone(){
134
+ if (($m = self::getQuote()) && ($m = $m->getBillingAddress()) && ($m = $m->getTelephone()))
135
+ return $m;
136
+ return null;
137
+ }
138
+
139
+ public static function getSession(){
140
+ return Mage::getSingleton(self::APP_ROOT.'/'.(self::isAdmin() ? 'adminSession' : 'session'));
141
+ }
142
+
143
+ /**
144
+ * Creates order payment/transaction records and updates order paid values
145
+ * amount and total need to be in the same currency as the order and transaction
146
+ *
147
+ * @param array $data - {trx_id, amount, currency, total}
148
+ * @return Mage_Sales_Model_Order_Payment|null
149
+ */
150
+ public static function createPayment($data){
151
+ try{
152
+ $trx_id = $data['trx_id'];
153
+ if (is_null($transaction = self::getTransaction($trx_id)))
154
+ throw new Exception('Transaction '.$trx_id.' not found');
155
+ return $transaction->addPayment($data);
156
+ }catch(Exception $e){
157
+ self::logErr(__METHOD__.' - '.$e->getMessage());
158
+ return null;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Creates order payment/transaction records and updates order refund values
164
+ * amount needs to be in the same currency as the order and transaction
165
+ *
166
+ * @param array $data - {trx_id, amount, currency}
167
+ * @return Mage_Sales_Model_Order_Payment|null
168
+ */
169
+ public static function createRefund($data){
170
+ try{
171
+ $trx_id = $data['trx_id'];
172
+ if (is_null($transaction = self::getTransaction($trx_id)))
173
+ throw new Exception('Transaction '.$trx_id.' not found');
174
+ return $transaction->addRefund($data);
175
+ }catch(Exception $e){
176
+ self::logErr(__METHOD__.' - '.$e->getMessage());
177
+ return null;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * @param Mage_Sales_Model_Order $order
183
+ * @return Mage_Sales_Model_Order_Invoice|null
184
+ */
185
+ public static function generateInvoice($order){
186
+ try{
187
+ $invoice = $order->prepareInvoice()
188
+ ->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::NOT_CAPTURE)
189
+ ->setState(Mage_Sales_Model_Order_Invoice::STATE_PAID)
190
+ ->addComment('Auto-Generated by Boku')
191
+ ->register();
192
+ Mage::getModel('core/resource_transaction')
193
+ ->addObject($invoice)
194
+ ->addObject($invoice->getOrder())
195
+ ->save();
196
+ $invoice->sendEmail(true);
197
+ }catch(Exception $e2){self::logErr(__METHOD__.' - '.$e->getMessage());}
198
+ return isset($invoice) ? $invoice : null;
199
+ }
200
+
201
+ /**
202
+ * Runs outstanding actions related to Boku callbacks
203
+ * Because of the asynchronous nature of Boku callbacks we might need a cron job to run this
204
+ * Otherwise we could force completion when we view an order in the admin
205
+ *
206
+ * @param string $trx_id - if null then looks at all transactions
207
+ */
208
+ public static function completeOutstanding($trx_id = null){
209
+ foreach(array('event','chargeback','transaction') as $c)
210
+ Mage::getSingleton(self::APP_ROOT.'/payment_'.$c)->completeOutstanding($trx_id);
211
+ }
212
+
213
+ /**
214
+ * Used for creating or verifying the signature field in Boku api calls
215
+ *
216
+ * @param array $data
217
+ * @return string
218
+ */
219
+ public static function getCompressedParameterString(&$data){
220
+ ksort($data);
221
+ $t = '';
222
+ foreach($data as $k=>$v){
223
+ $v = trim((string) $v);
224
+ if ($v != '') $t .= trim($k).$v;
225
+ }
226
+ return $t;
227
+ }
228
+
229
+ /**
230
+ * Add the Boku sig field to the params and add or updates the timestamp field
231
+ * These fields are required for all Boku api calls.
232
+ *
233
+ * @param array $params
234
+ */
235
+ public static function addSignature(&$params){
236
+ $api_key = self::getConfig('api_security_key');
237
+ $params['timestamp'] = str_pad(time(), 10, '0', STR_PAD_LEFT);
238
+ $t = self::getCompressedParameterString($params);
239
+ $params['sig'] = md5($t.$api_key);
240
+ }
241
+
242
+ /**
243
+ * Build a Boku URL - adds the timestamp and sig parameters
244
+ *
245
+ * @param string $url
246
+ * @param array $params
247
+ * @return string
248
+ */
249
+ public static function buildURL($url, $params = array()){
250
+ if (($i = strpos($url, '?')) != false){
251
+ $query = substr($url, $i + 1);
252
+ $url = substr($url, 0, $i);
253
+ foreach(explode('&', $query) as $p){
254
+ $pa = explode('=', $p);
255
+ $k = urldecode($pa[0]);
256
+ if (!array_key_exists($k, $params))
257
+ $params[$k] = urldecode($pa[1]);
258
+ }
259
+ }
260
+ self::addSignature($params);
261
+ $url .= '?';
262
+ foreach($params as $k=>$v)
263
+ $url .= urlencode($k).'='.urlencode($v).'&';
264
+ return $url;
265
+ }
266
+
267
+ /* function getRemoteIp() {
268
+ $data = function_exists('apache_request_headers') ? apache_request_headers() : $_SERVER;
269
+
270
+ foreach (array(
271
+ 'X-Forwarded-For',
272
+ 'HTTP_CLIENT_IP',
273
+ 'HTTP_X_FORWARDED',
274
+ 'HTTP_X_CLUSTER_CLIENT_IP',
275
+ 'HTTP_FORWARDED_FOR',
276
+ 'HTTP_FORWARDED',
277
+ 'REMOTE_ADDR',
278
+ ) as $key)
279
+ if (array_key_exists($key, $data) && filter_var($data[$key], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
280
+ return $data[$key];
281
+ return $_SERVER['REMOTE_ADDR'];
282
+ }
283
+ */
284
+ /**
285
+ * checks the validity of the sig parameter in the Boku callback url
286
+ * $data can be full url, just the query part or array of query parameters
287
+ *
288
+ * @param mixed $data
289
+ * @return boolean
290
+ */
291
+ public static function verifySignature($data){
292
+ if (!is_array($data)){
293
+ if (!is_string($data)) return false;
294
+ if (strpos($data, 'sig=') === false) return false;
295
+ $params = array();
296
+ if (($i = strpos($data, '?')) !== false)
297
+ $data = substr($data, $i + 1);
298
+ foreach(explode('&', $data) as $p){
299
+ $pa = explode('=', $p);
300
+ $k = urldecode($pa[0]);
301
+ if (!array_key_exists($k, $params))
302
+ $params[$k] = trim(urldecode($pa[1]));
303
+ }
304
+ }elseif(!array_key_exists('sig', $params = $data))
305
+ return false;
306
+ $api_key = self::getConfig('api_security_key');
307
+ $sig = $params['sig'];
308
+ unset($params['sig']);
309
+ $t = self::getCompressedParameterString($params);
310
+ return strcasecmp($sig, md5($t.$api_key)) == 0;
311
+ }
312
+
313
+ public static function getUrl($route = null, $params = null){
314
+ $store = self::getStore();
315
+ if (empty($params))
316
+ $params = array('_store'=>$store->getId());
317
+ else
318
+ $params['_store'] = $store->getId();
319
+ return Mage::getUrl($route, $params);
320
+ }
321
+
322
+ /**
323
+ * Urls for Boku prepare call
324
+ *
325
+ * @return string url
326
+ */
327
+ public static function getCallbackUrl(){
328
+ $url = self::getConfig('url/callback');
329
+ if (empty($url))
330
+ $url = self::getUrl(self::APP_ROOT.'/api');
331
+ return $url;
332
+ }
333
+ public static function getSuccessUrl(){
334
+ return self::getUrl(self::APP_ROOT.'/standard/success');
335
+ }
336
+ public static function getFailUrl(){
337
+ return self::getUrl(self::APP_ROOT.'/standard/cancel');
338
+ }
339
+
340
+ /**
341
+ * @return Mage_Core_Model_Store | Mage_Core_Model_Website
342
+ */
343
+ public static function getScopeObject(){
344
+ $session = self::getSession();
345
+ $obj = $session->getScopeObject();
346
+ if (!empty($obj)) return $obj;
347
+ $params = Mage::app()->getRequest()->getParams();
348
+ if (isset($params['store']))
349
+ $obj = Mage::app()->getStore($params['store']);
350
+ elseif (isset($params['website']))
351
+ $obj = Mage::app()->getWebsite($params['website']);
352
+ else
353
+ $obj = Mage::app()->getStore();
354
+ $session->setScopeObject($obj);
355
+ return $obj;
356
+ }
357
+
358
+ /**
359
+ * @return Mage_Core_Model_Store
360
+ */
361
+ public static function getStore(){
362
+ $obj = self::getScopeObject();
363
+ if ($obj instanceof Mage_Core_Model_Website){
364
+ $store = $obj->getDefaultStore();
365
+ if (!empty($store)) $obj = $store;
366
+ }
367
+ if (!($obj instanceof Mage_Core_Model_Store))
368
+ $obj = Mage::app()->getStore();
369
+ return $obj;
370
+ }
371
+
372
+ /**
373
+ * Sets the current config scope to a particular store.
374
+ * clears the scope if $store_id == null
375
+ *
376
+ * @param int|string $store_id
377
+ */
378
+ public static function setStore($store_id){
379
+ if (is_null($store_id))
380
+ self::getSession()->unsScopeObject();
381
+ elseif (self::getStore()->getId() != $store_id)
382
+ self::getSession()->setScopeObject(Mage::app()->getStore($store_id));
383
+ }
384
+
385
+ /**
386
+ * Get config data specific to this plugin and current store context
387
+ *
388
+ * @return string
389
+ */
390
+ public static function getConfig($key){
391
+ return self::getStore()->getConfig(self::CONFIG_ROOT.'/'.$key);
392
+ }
393
+
394
+ public static function log($msg, $type = Zend_Log::INFO){
395
+ Mage::log($msg, $type, self::APP_ROOT.'.log');
396
+ }
397
+ public static function logErr($msg){
398
+ self::log($msg, Zend_Log::ERR);
399
+ }
400
+ }
app/code/community/Boku/Paymentgateway/Model/AdminSession.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku transaction admin session data store
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_AdminSession extends Mage_Core_Model_Session_Abstract
12
+ {
13
+ const APP_ROOT = 'boku';
14
+
15
+ public function __construct(){
16
+ $this->init(self::APP_ROOT.'admin');
17
+ }
18
+ }
app/code/community/Boku/Paymentgateway/Model/Cron.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku model for any cron bits
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Cron
12
+ {
13
+ const APP_ROOT = 'boku';
14
+
15
+ public function run(){
16
+ Mage::helper(self::APP_ROOT)->completeOutstanding();
17
+ }
18
+ }
app/code/community/Boku/Paymentgateway/Model/Mapped/Abstract.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Simple extension of core model abtract to accommodate field mapping
4
+ * $_field_map is an array of from=>to mappings.
5
+ *
6
+ * @category Payment gateway
7
+ * @package boku_paymentgateway
8
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
9
+ * @author MDH <mdh@treatid.me.uk>
10
+ */
11
+
12
+ abstract class Boku_Paymentgateway_Model_Mapped_Abstract extends Mage_Core_Model_Abstract
13
+ {
14
+ const APP_ROOT = 'boku';
15
+
16
+ protected $_field_map = array();
17
+
18
+ protected function _mapField($field){
19
+ if (!empty($field) && array_key_exists($field = str_replace('-', '_', $field), $this->_field_map))
20
+ $field = $this->_field_map[$field];
21
+ return $field;
22
+ }
23
+ /**
24
+ * Warning: if a field is mapped to another field then the other with be replaced
25
+ */
26
+ protected function &_map(&$data){
27
+ if (is_array($data)){
28
+ foreach ($data as $field=>&$value)
29
+ if (($mapped_field = $this->_mapField($field)) != $field)
30
+ $data[$mapped_field] = $value;
31
+ }else
32
+ $data = $this->_mapField($data);
33
+ return $data;
34
+ }
35
+ public function map($data){return $this->_map($data);}
36
+
37
+ public function load($id, $field=null){return parent::load($id, $this->_map($field));}
38
+ public function addData(array $arr){return parent::addData($this->_map($arr));}
39
+ public function setData($key, $value=null){return parent::setData($this->_map($key), $value);}
40
+ public function unsetData($key=null){return parent::unsetData($this->_map($key));}
41
+ public function unsetOldData($key=null){return parent::unsetOldData($this->_map($key));}
42
+ public function getData($key='', $index=null){return parent::getData($this->_map($key), $index);}
43
+ public function setDataUsingMethod($key, $args=array()){return parent::setDataUsingMethod($this->_map($key), $args=array());}
44
+ public function getDataUsingMethod($key, $args=null){return parent::getDataUsingMethod($this->_map($key), $args);}
45
+ public function getDataSetDefault($key, $default){return parent::getDataSetDefault($this->_map($key), $default);}
46
+ public function hasData($key=''){return parent::hasData($this->_map($key));}
47
+
48
+ /* public function toArray(array $arrAttributes = array()){return parent::toArray($arrAttributes);}
49
+ public function toXml(array $arrAttributes = array(), $rootName = 'item', $addOpenTag=false, $addCdata=true){return parent::toXml($arrAttributes, $rootName, $addOpenTag, $addCdata);}
50
+ */
51
+ public function __get($var){return parent::__get($this->_map($var));}
52
+ public function __set($var, $value){parent::__set($this->_map($var), $value);}
53
+ public function getOrigData($key=null){return parent::getOrigData($this->_map($key));}
54
+ public function setOrigData($key=null, $data=null){return parent::setOrigData($this->_map($key), $data);}
55
+ public function isDirty($field=null){return parent::isDirty($this->_map($field));}
56
+ public function flagDirty($field, $flag=true){return parent::flagDirty($this->_map($field), $flag);}
57
+ }
app/code/community/Boku/Paymentgateway/Model/Payment/Callback.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Payment gateway
4
+ * @package boku_paymentgateway
5
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
6
+ * @author MDH <mdh@treatid.me.uk>
7
+ */
8
+
9
+ Class Boku_Paymentgateway_Model_Payment_Callback extends Boku_Paymentgateway_Model_Mapped_Abstract{
10
+
11
+ /**
12
+ * Initialize resource model
13
+ */
14
+ protected function _construct(){
15
+ $this->_init(self::APP_ROOT.'/payment_callback');
16
+ return parent::_construct();
17
+ }
18
+
19
+ }
app/code/community/Boku/Paymentgateway/Model/Payment/Chargeback.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Payment gateway
4
+ * @package boku_paymentgateway
5
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
6
+ * @author MDH <mdh@treatid.me.uk>
7
+ */
8
+
9
+ Class Boku_Paymentgateway_Model_Payment_Chargeback extends Boku_Paymentgateway_Model_Mapped_Abstract{
10
+
11
+ /**
12
+ * Initialize resource model
13
+ */
14
+ protected function _construct(){
15
+ $this->_init(self::APP_ROOT.'/payment_chargeback');
16
+ return parent::_construct();
17
+ }
18
+
19
+ /**
20
+ * Attempts to creates Mage_Sales_Model_Order_Payment
21
+ * If successful or unneccessary then the handled flag is set to true
22
+ *
23
+ * @return boolean
24
+ */
25
+ public function complete(){
26
+ if ($this->getHandled()) return true;
27
+ $helper = Mage::helper(self::APP_ROOT);
28
+ try{
29
+ $currency = $this->getCurrency();
30
+ $refund = $helper->createRefund(array(
31
+ 'trx_id'=>$this->getTrxId(),
32
+ 'amount'=>$helper->getFloatPrice($this->getChargebackamount(), $currency),
33
+ 'currency'=>$currency,
34
+ ));
35
+ if (empty($refund)) throw new Exception('Failed to create refund.');
36
+
37
+ $this->setHandled(true)->save();
38
+ }catch(Exception $e){$helper->logErr(__METHOD__.' - '.$e->getMessage()); return false;}
39
+ return true;
40
+ }
41
+
42
+ /**
43
+ * runs complete on any uncompleted records
44
+ *
45
+ * @param string $trx_id - if null then looks at all transactions
46
+ */
47
+ public function completeOutstanding($trx_id = null){
48
+ $c = $this->getCollection()->addFieldToFilter('handled', 0);
49
+ if (!is_null($trx_id)) $c->addFieldToFilter('trx_id', $trx_id);
50
+ foreach($c->load() as $model) $model->complete();
51
+ }
52
+
53
+ /**
54
+ * Creates a new chargeback record.
55
+ * Fails if one already exists for the particular trx-id (only one allowed)
56
+ *
57
+ * @param array &$idata
58
+ * @return Boku_Paymentgateway_Model_Payment_Chargeback
59
+ */
60
+ public static function create(&$idata){
61
+ $model = new self();
62
+ $data = $model->map($idata);
63
+ $trx_id = $data['trx_id'];
64
+ $id = $model->load($trx_id, 'trx_id')->getId();
65
+ if (!empty($id))
66
+ throw new Exception(__METHOD__.' - A chargeback is already present for transaction '.$trx_id.' (only one allowed).');
67
+ $model->setData($data)->save()->complete();
68
+ return $model;
69
+ }
70
+ }
app/code/community/Boku/Paymentgateway/Model/Payment/Event.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Record of Boku event callbacks created by Boku_Paymentgateway_ApiController
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ Class Boku_Paymentgateway_Model_Payment_Event extends Boku_Paymentgateway_Model_Mapped_Abstract{
12
+
13
+ const PSMS_MT_SUB = 1;
14
+ const PSMS_MT_DEL = 2;
15
+ const PSMS_MO_REC = 3;
16
+ const PSMS_MO_NON_REC = 4;
17
+
18
+ /**
19
+ * Initialize resource model
20
+ */
21
+ protected function _construct(){
22
+ $this->_init(self::APP_ROOT.'/payment_event');
23
+ return parent::_construct();
24
+ }
25
+ /**
26
+ * Attempts to creates Mage_Sales_Model_Order_Payment
27
+ * If successful or unneccessary then the handled flag is set to true
28
+ *
29
+ * @return boolean
30
+ */
31
+ public function complete(){
32
+ if ($this->getHandled()) return true;
33
+ $helper = Mage::helper(self::APP_ROOT);
34
+ try{
35
+ switch ($this->getEventCode()){
36
+ case self::PSMS_MT_DEL:
37
+ case self::PSMS_MO_REC:
38
+ $currency = $this->getCurrency();
39
+ $payment = $helper->createPayment(array(
40
+ 'trx_id'=>$this->getTrxId(),
41
+ 'amount'=>$helper->getFloatPrice($this->getMessageCost(), $currency),
42
+ 'currency'=>$currency,
43
+ 'total'=>$helper->getFloatPrice($this->getPaid(), $currency),
44
+ ));
45
+ if (empty($payment)) throw new Exception('Failed to create payment.');
46
+ }
47
+ $this->setHandled(true)->save();
48
+ }catch(Exception $e){$helper->logErr(__METHOD__.' - '.$e->getMessage()); return false;}
49
+ return true;
50
+ }
51
+
52
+ /**
53
+ * runs complete on any uncompleted records
54
+ *
55
+ * @param string $trx_id - if null then looks at all transactions
56
+ */
57
+ public function completeOutstanding($trx_id = null){
58
+ $helper = Mage::helper(self::APP_ROOT);
59
+ $c = $this->getCollection()->addFieldToFilter('handled', 0);
60
+ if (!is_null($trx_id)) $c->addFieldToFilter('trx_id', $trx_id);
61
+ $failed = 0;
62
+ foreach ($c->load() as $model){
63
+ $transaction = $helper->getTransaction($model->getTrxId());
64
+ if (empty($transaction)) {$failed++; continue;}
65
+ $helper->setStore($transaction->getStoreId());
66
+ if (!$model->complete()) $failed++;
67
+ }
68
+ if ($failed) $helper->log(__METHOD__.': '.$failed.' failed.');
69
+ }
70
+
71
+ /**
72
+ * Creates a new event record
73
+ * WARNING we should check whether this event has already been logged
74
+ *
75
+ * @param array $data
76
+ * @return Boku_Paymentgateway_Model_Payment_Event
77
+ */
78
+ public static function create(&$idata){
79
+ $model = new self();
80
+ $data = $model->map($idata);
81
+ if (isset($data['reference_currency']) && isset($data['paid']) && isset($data['reference_paid']))
82
+ try{$data['exchange'] = ((float) $data['paid']) / $data['reference_paid'];}catch(Exception $e){}
83
+ $model->setData($data)->save()->complete();
84
+ return $model;
85
+ }
86
+ }
app/code/community/Boku/Paymentgateway/Model/Payment/Standard.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku standard checkout module
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Payment_Standard extends Mage_Payment_Model_Method_Abstract
12
+ {
13
+ const APP_ROOT = 'boku';
14
+
15
+ protected $_code = self::APP_ROOT;
16
+ protected $_formBlockType = 'boku/payment_standard_form';
17
+
18
+ protected $_canUseForMultishipping = true;
19
+ protected $_isGateway = true;
20
+ protected $_isInitializeNeeded = true;
21
+
22
+ // NOT POSSIBLE YET
23
+ protected $_canUseInternal = false;
24
+
25
+ /**
26
+ * This makes sure that the config settings have values
27
+ * Note: there is no guarantee that the values are correct !
28
+ *
29
+ * @see Mage_Payment_Model_Method_Abstract::canUseCheckout()
30
+ * @return bool
31
+ */
32
+ public function canUseCheckout(){
33
+ $helper = Mage::helper(self::APP_ROOT);
34
+ $merchant_id = $helper->getConfig('merchant_id');
35
+ $api_security_key = $helper->getConfig('api_security_key');
36
+ $service_id = $helper->getConfig('service_id');
37
+ return !(empty($merchant_id) || empty($api_security_key) || empty($service_id));
38
+ }
39
+
40
+ /**
41
+ * Check whether payment method is applicable to quote
42
+ * Note may get called multiple times during the payment process (by separate ajax requests)
43
+ *
44
+ * @param $currency
45
+ * @return bool
46
+ */
47
+ public function canUseForCurrency($currency){
48
+ $helper = Mage::helper(self::APP_ROOT);
49
+ $model = $helper->getQuote();
50
+ return Mage::getSingleton(self::APP_ROOT.'/prices')
51
+ ->isAvailable($model->getGrandTotal(), $model->getQuoteCurrencyCode(), $helper->getCountryCode());
52
+ }
53
+
54
+ /**
55
+ * Called by Mage_Sales_Model_Order_Payment::place
56
+ * is used instead of authorize and capture functions when $_isInitializeNeeded is true
57
+ *
58
+ * @param string $action
59
+ * @param Varien_Object $state
60
+ */
61
+ public function initialize($action, $state){
62
+ if ($action == Boku_Paymentgateway_Model_System_Config::PAYMENT_ACTION_AUTH)
63
+ Mage::getSingleton(self::APP_ROOT.'/payment_transaction')->initiate();
64
+ return $this;
65
+ }
66
+
67
+ /**
68
+ * Return Order placed redirect url
69
+ * Called by Mage_Checkout_Model_Type_Onepage::saveOrder
70
+ *
71
+ * @return string
72
+ */
73
+ public function getOrderPlaceRedirectUrl(){
74
+ return Mage::helper(self::APP_ROOT)->getUrl(self::APP_ROOT.'/standard/prepare');
75
+ }
76
+ }
app/code/community/Boku/Paymentgateway/Model/Payment/Transaction.php ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Overall Boku transaction record.
4
+ * Initially populated by Boku_Paymentgateway_Model_Payment_Standard when an order is submitted
5
+ * Updated again by Boku_Paymentgateway_ApiController when the transaction is complete
6
+ *
7
+ * @category Payment gateway
8
+ * @package boku_paymentgateway
9
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
10
+ * @author MDH <mdh@treatid.me.uk>
11
+ */
12
+
13
+ Class Boku_Paymentgateway_Model_Payment_Transaction extends Boku_Paymentgateway_Model_Mapped_Abstract{
14
+
15
+ private $_order = null;
16
+
17
+ /**
18
+ * Initialize resource model
19
+ */
20
+ protected function _construct(){
21
+ $this->_init(self::APP_ROOT.'/payment_transaction');
22
+ return parent::_construct();
23
+ }
24
+
25
+ /**
26
+ * @return null|Mage_Sales_Model_Order
27
+ */
28
+ public function getOrder(){
29
+ if (is_null($this->_order)){
30
+ $id = $this->getOrderId();
31
+ if (empty($id)){
32
+ $model = Mage::getModel('sales/order')->load($this->getQuoteId(), 'quote_id');
33
+ if ($model->getId()){
34
+ $model->addStatusHistoryComment('Boku TRX-ID: '.$this->getTrxId());
35
+ $this->setOrderId($model->getId())->save();
36
+ $this->_order = $model;
37
+ }
38
+ }else{
39
+ $this->_order = Mage::getModel('sales/order')->load($id);
40
+ if (!$this->_order->getId()) $this->_order = null;
41
+ }
42
+ }
43
+ return $this->_order;
44
+ }
45
+ /**
46
+ * @return null|Mage_Sales_Model_Quote
47
+ */
48
+ public function getQuote(){
49
+ $model = Mage::getModel('sales/quote')->load($this->getQuoteId());
50
+ return $model->getId() ? $model : null;
51
+ }
52
+
53
+ /**
54
+ * @param array $data - {amount, currency, total}
55
+ * @return Mage_Sales_Model_Order_Payment|null
56
+ */
57
+ public function addPayment(&$data){
58
+ return $this->_addPayment($data);
59
+ }
60
+ /**
61
+ * @param array $data - {amount, currency}
62
+ * @return Mage_Sales_Model_Order_Payment|null
63
+ */
64
+ public function addRefund(&$data){
65
+ return $this->_addPayment($data, true);
66
+ }
67
+ /**
68
+ * Creates order payment/transaction records and updates order paid/refunded values
69
+ * Fails if the currency differs from the order currency
70
+ *
71
+ * @param array $data - {amount, currency[, total]}
72
+ * @param boolean $refund
73
+ * @return Mage_Sales_Model_Order_Payment|null
74
+ */
75
+ protected function _addPayment(&$data, $refund = false){
76
+ $helper = Mage::helper(self::APP_ROOT);
77
+ $trx_id = $this->getId();
78
+ $order = $this->getOrder();
79
+ try{
80
+ if (empty($order))
81
+ throw new Exception('Order not found for trx_id:'.$trx_id);
82
+ if (($currency = $data['currency']) != $order->getOrderCurrencyCode())
83
+ throw new Exception('Currency invalid: '.$currency.' != '.$order->getOrderCurrencyCode());
84
+ $order_to_base_rate = 1/$order->getBaseToOrderRate();
85
+ $amount = $data['amount'];
86
+ $payment = Mage::getModel('sales/order_payment')->setData(array(
87
+ 'method'=>self::APP_ROOT,
88
+ 'amount_ordered'=>$order->getGrandTotal(),
89
+ 'base_amount_ordered'=>$order->getBaseGrandTotal(),
90
+ ));
91
+ if ($refund)
92
+ $payment->addData(array(
93
+ 'amount_refunded'=>$amount,
94
+ 'base_amount_refunded'=>$amount * $order_to_base_rate,
95
+ ));
96
+ else
97
+ $payment->addData(array(
98
+ 'amount_paid'=>$amount,
99
+ 'base_amount_paid'=>$amount * $order_to_base_rate,
100
+ ));
101
+
102
+ $order->addPayment($payment);
103
+ $payment->save();
104
+ $transaction = Mage::getModel('sales/order_payment_transaction');
105
+ $transaction
106
+ ->setOrderPaymentObject($payment)
107
+ ->setTxnId($trx_id)
108
+ ->setTxnType($refund ? $transaction::TYPE_REFUND : $transaction::TYPE_PAYMENT)
109
+ ->setAdditionalInformation('source', 'Boku '.($refund ? 'refund' : 'payment').' confirmed')
110
+ ->save()
111
+ ->close();
112
+ if ($refund){
113
+ $order_refund_total = $order->getTotalRefunded() + $amount;
114
+ $order
115
+ ->setTotalRefunded($order_refund_total)
116
+ ->setBaseTotalRefunded($order_refund_total * $order_to_base_rate)
117
+ ->save();
118
+ }else{
119
+ $total = $data['total'];
120
+ if ($order->getTotalPaid() < $total)
121
+ $order
122
+ ->setTotalPaid($total)
123
+ ->setBaseTotalPaid($total * $order_to_base_rate)
124
+ ->save();
125
+ }
126
+ if ($amount > 0)
127
+ $order->addStatusHistoryComment(($refund ? 'Refund' : 'Payment').' received: '.$currency.' '.$amount, false)
128
+ ->setIsVisibleOnFront(false)
129
+ ->setIsCustomerNotified(false)
130
+ ->save();
131
+ }catch(Exception $e){
132
+ $helper->logErr(__METHOD__.' - '.$e->getMessage());
133
+ if (isset($transaction)) $transaction->delete();
134
+ if (isset($payment)) $payment->delete();
135
+ return null;
136
+ }
137
+ return $order->getPayment();
138
+ }
139
+
140
+ /**
141
+ * Changes the order status and cancels if appropriate
142
+ * If an api billingresult occurs before all api events have been received then
143
+ * we may create a dummy payment of 0 to update the order payment values.
144
+ * If the total order value has been paid we will optionally generate an invoice.
145
+ *
146
+ * @return boolean
147
+ */
148
+ public function complete($data = null){
149
+ if ($this->getHandled()) return true;
150
+
151
+ if (!empty($data)){
152
+ $this->_map($data);
153
+ if (array_key_exists('reference_currency', $data)){
154
+ $rc = $this->getReferenceCurrency();
155
+ if (!empty($rc) && $data['reference_currency'] != $rc)
156
+ unset($data['reference_currency']);
157
+ elseif (!isset($data['exchange']) && isset($data['paid']) && isset($data['reference_paid']))
158
+ try{$data['exchange'] = ((float) $data['paid']) / $data['reference_paid'];}catch(Exception $e){}
159
+ }
160
+ if (array_key_exists('timestamp', $data)){
161
+ $data['result_timestamp'] = $data['timestamp'];
162
+ unset($data['timestamp']);
163
+ }
164
+ if (array_key_exists('country', $data))
165
+ $data['country'] = strtoupper($data['country']);
166
+ $this->addData($data)->save();
167
+ }
168
+
169
+ $result_code = $this->getResultCode();
170
+ if (is_null($result_code)) return false;
171
+ if (is_null($order = $this->getOrder())) return false;
172
+
173
+ $helper = Mage::helper(self::APP_ROOT);
174
+
175
+ //Add dummy payment if necessary
176
+ $currency = $this->getCurrency();
177
+ $paid = $helper->getFloatPrice($this->getPaid(), $currency);
178
+ if ($paid > $order->getTotalPaid()){
179
+ $payment_data = array(
180
+ 'amount'=>0,
181
+ 'currency'=>$currency,
182
+ 'total'=>$paid,
183
+ );
184
+ $payment = $this->addPayment($payment_data);
185
+ }
186
+ try{
187
+ switch ($result_code){
188
+ case 0:
189
+ switch ($order->getState()){
190
+ case $order::STATE_NEW:
191
+ case $order::STATE_PENDING_PAYMENT:
192
+ $order->setState($order::STATE_PROCESSING)->save();
193
+ }
194
+ $order->addStatusHistoryComment('Boku Transaction Completed Successfully');
195
+
196
+ if ($helper->getConfig('auto_invoice')
197
+ && ($order->getTotalPaid() - $order->getTotalRefunded()) == $order->getGrandTotal()
198
+ && $order->canInvoice())
199
+ $helper->generateInvoice($order);
200
+ break;
201
+ default:
202
+ $order->addStatusHistoryComment($this->getResultMsg());
203
+ if ($order->canCancel() && $order->getTotalPaid() == 0){
204
+ $order->cancel()->save();
205
+ $this->setCancelled(true)->save();
206
+ }else{
207
+ switch ($order->getState()){
208
+ case $order::STATE_NEW:
209
+ case $order::STATE_PENDING_PAYMENT:
210
+ $order->setState($order::STATE_PROCESSING)->save();
211
+ }
212
+ }
213
+ break;
214
+ }
215
+ $this->setHandled(true)->save();
216
+ }catch(Exception $e){$helper->logErr(__METHOD__.' - '.$e->getMessage()); return false;}
217
+ return true;
218
+ }
219
+
220
+ /**
221
+ * runs complete on any incomplete transactions
222
+ *
223
+ * @param string $trx_id - if null then looks at all transactions
224
+ */
225
+ public function completeOutstanding($trx_id = null){
226
+ $helper = Mage::helper(self::APP_ROOT);
227
+ $c = $this->getCollection()
228
+ ->addFieldToFilter('handled', 0)
229
+ ->addFieldToFilter('result_code', array('notnull' => true))
230
+ ->addFieldToFilter('order_id', array('notnull' => true));
231
+ if (!is_null($trx_id))
232
+ $c->addFieldToFilter('trx_id', $trx_id);
233
+ $failed = 0;
234
+ foreach($c->load() as $model){
235
+ $helper->setStore($model->getStoreId());
236
+ if (!$model->complete()) $failed++;
237
+ }
238
+ if ($failed) $helper->log(__METHOD__.': '.$failed.' failed.');
239
+ }
240
+
241
+ /**
242
+ * Initiates the payment process.
243
+ * If successful we create a new boku transaction record.
244
+ *
245
+ * @param Mage_Sales_Model_Quote $quote
246
+ */
247
+ public static function initiate(Mage_Sales_Model_Quote $quote = null){
248
+ $helper = Mage::helper(self::APP_ROOT);
249
+ if (empty($quote)) $quote = $helper->getQuote();
250
+ try{
251
+ $response = self::_initiate($quote);
252
+ if (empty($response) || $response['result-code'] != 0)
253
+ throw new Exception('Invalid Response');
254
+
255
+ $trx_id = $response['trx-id'];
256
+ if (empty($trx_id) || empty($response['buy-url']))
257
+ throw new Exception($response['result-msg']);
258
+
259
+ if (!is_null(self::getTransaction($response['trx-id'])))
260
+ throw new Exception('Unexpected duplicate trx-id:'.$trx_id);
261
+
262
+ $quote_id = $quote->getId();
263
+ $currency = $quote->getQuoteCurrencyCode();
264
+ $data = array(
265
+ 'trx-id'=>$trx_id,
266
+ 'test'=>$helper->getConfig('mode'),
267
+ 'store_id'=>$helper->getStore()->getId(),
268
+ 'quote_id'=>$quote_id,
269
+ 'country'=>$helper->getCountryCode(),
270
+ 'currency'=>$currency,
271
+ 'amount'=>$helper->getIntegerPrice($quote->getGrandTotal(), $currency),
272
+ 'reference_currency'=>$quote->getBaseCurrencyCode(),
273
+ 'timestamp'=>time(),
274
+ );
275
+ $transaction = self::getTransaction($data, true);
276
+ if (is_null($transaction))
277
+ throw new Exception('Create transaction trx-id:'.$trx_id.' failed');
278
+
279
+ $helper->getSession()->addData(array(
280
+ 'trx-id'=>$trx_id,
281
+ 'quote_id'=>$quote_id,
282
+ 'buy-url'=>$response['buy-url'],
283
+ ));
284
+ }catch(Exception $e){
285
+ $helper->logErr(__METHOD__.': '.$e->getMessage().(!empty($response) ? "\n".json_encode($response, JSON_PRETTY_PRINT) : ''));
286
+ Mage::throwException($helper->__('Failed to initiate the Boku payment transaction.'));
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Initiates a payment by calling the "prepare" Boku API
292
+ *
293
+ * @param Mage_Sales_Model_Quote $quote
294
+ * @return null|array
295
+ */
296
+ protected static function _initiate(Mage_Sales_Model_Quote $quote){
297
+ $helper = Mage::helper(self::APP_ROOT);
298
+ $num = $quote->getItemsCount();
299
+ $currency = $quote->getQuoteCurrencyCode();
300
+ if (!($order_id = $quote->getReservedOrderId()))
301
+ $order_id = reserveOrderId()->getReservedOrderId();
302
+
303
+ $params = array(
304
+ 'merchant-id'=>$helper->getConfig('merchant_id'),
305
+ 'service-id'=>$helper->getConfig('service_id'),
306
+ 'country'=>$helper->getCountryCode(),
307
+ 'consumer-id'=>$quote->getId(),
308
+ 'desc'=>$num.' '.$helper->__('item'.($num > 0 ? 's' : '')),
309
+ 'currency'=>$currency,
310
+ 'price-inc-salestax'=>$helper->getIntegerPrice($quote->getGrandTotal(), $currency),
311
+ 'callback-url'=>$helper->getCallbackUrl(),
312
+ 'fwdurl'=>$helper->getSuccessUrl(),
313
+ 'fail-fwdurl'=>$helper->getFailUrl(),
314
+ 'param'=>$order_id,
315
+ );
316
+ $phone = $helper->getPhone();
317
+ if (!empty($phone)) $params['msisdn'] = str_replace(' ', '', $phone);
318
+ $smn = $helper->getConfig('sub_merchant_name');
319
+ if (!empty($smn)) $params['sub-merchant-name'] = $smn;
320
+ if ($helper->getConfig('mode')) $params['test'] = 1;
321
+
322
+ $url = $helper->getConfig('url/prepare');
323
+ $client = new Zend_Http_Client($helper->buildUrl($url, $params));
324
+ try {
325
+ $response = $client->request();
326
+ if ($response->isSuccessful()){
327
+ if (!Boku_Paymentgateway_Model_Xml::isValidXml($response->getBody()))
328
+ throw new Exception('Bad XML fetched from '.$url);
329
+ $xml = new Boku_Paymentgateway_Model_Xml($response->getBody());
330
+ return $xml->asArray();
331
+ }else
332
+ throw new Exception('Http Failed: '.$response->getMessage());
333
+ }catch(Exception $e){$helper->logErr(__METHOD__.': '.$e->getMessage());}
334
+ return null;
335
+ }
336
+
337
+ /**
338
+ * Gets the $data['trx-id'] transaction record
339
+ *
340
+ * @param array|string $data
341
+ * @param boolean $create_if_none (default:false)
342
+ * @return Boku_Paymentgateway_Model_Payment_Transaction|null
343
+ */
344
+ public static function getTransaction(&$data, $create_if_none = false){
345
+ if (is_array($data)){
346
+ if (!array_key_exists('trx-id', $data)) return null;
347
+ $id = $data['trx-id'];
348
+ }else
349
+ $id = $data;
350
+ $model = new self();
351
+ $id = $model->load($id)->getId();
352
+ if (empty($id)){
353
+ if ($create_if_none && is_array($data)){
354
+ try{
355
+ $model->setData($data)->save();
356
+ }catch(Exception $e){
357
+ $model = null;
358
+ }
359
+ }else
360
+ $model = null;
361
+ }
362
+ return $model;
363
+ }
364
+ }
app/code/community/Boku/Paymentgateway/Model/Prices.php ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku standard checkout module
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Prices
12
+ {
13
+ const APP_ROOT = 'boku';
14
+
15
+ protected $price_list_timeout = 300;
16
+
17
+ /**
18
+ * @param ISO-4217 $currency
19
+ * @return int
20
+ */
21
+ public function getCurrencyPrecision($currency){
22
+ $prices = $this->getPrices($currency);
23
+ if (is_array($prices)){
24
+ $prices = $this->getCache();
25
+ $country = Mage::helper(self::APP_ROOT)->getCountryCode();
26
+ try {
27
+ return $prices['country'][$country][$currency]['currency']['currency-decimal-places'];
28
+ } catch (Exception $e){}
29
+ }
30
+ $formatted_price = Mage::app()->getLocale()->currency($currency)->toCurrency(0, array('display'=>Zend_Currency::NO_SYMBOL));
31
+ $pieces = preg_split('/[^0-9]/' , $formatted_price);
32
+ return strlen($pieces[count($pieces) - 1]);
33
+ }
34
+ /**
35
+ * @param float $value
36
+ * @param ISO-4217 $currency
37
+ * @return int
38
+ */
39
+ public function getIntegerPrice($value, $currency){
40
+ return (int) ($value * pow(10, $this->getCurrencyPrecision($currency)));
41
+ }
42
+ /**
43
+ * Convert a value expressed in the fractional currency unit to the normal float value
44
+ *
45
+ * @param int $price
46
+ * @param string (ISO 4217) $currency
47
+ * @return float
48
+ */
49
+ public function getFloatPrice($price, $currency){
50
+ return $price / pow(10, $this->getCurrencyPrecision($currency));
51
+ }
52
+
53
+ /**
54
+ * Is the required price-point possible
55
+ * returns false or an array of networks for which it is available
56
+ *
57
+ * @param int $value (default: basket total)
58
+ * @param ISO-4217 $currency (default: store currency)
59
+ * @param ISO-3166-1-alpha-2 $country (default: see getPrices)
60
+ * @return boolean|array
61
+ */
62
+ public function isAvailable($value = null, $currency = null, $country = null){
63
+ $helper = Mage::helper(self::APP_ROOT);
64
+
65
+ if (empty($currency)) $currency = $helper->getCurrencyCode();
66
+ if (empty($currency)) return false;
67
+ if (empty($country)) $country = $helper->getCountryCode();
68
+ if (empty($value)) $value = $helper->getQuote()->getGrandTotal();
69
+ $int_price = $this->getIntegerPrice($value, $currency);
70
+
71
+ $prices = $this->getPrices($currency, $country);
72
+ $available = false;
73
+ $pp_found = false;
74
+ if (is_array($prices)){
75
+ foreach($prices as $k=>&$v){
76
+ if (array_key_exists('increment', $v)){
77
+ if ($int_price >= $v['min-price'] && $int_price <= $v['max-price']
78
+ && ($v['increment'] == 0 || !(($int_price - $v['min-price']) % $v['increment']))){
79
+ $available = (bool) $v['status'];
80
+ $pp_found = true;
81
+ break;
82
+ }
83
+ }elseif (array_key_exists('amount', $v)){
84
+ if ($int_price == $v['amount']){
85
+ $available = !$v['status'] ? false : (isset($v['network']) ? $v['network'] : true);
86
+ $pp_found = true;
87
+ break;
88
+ }
89
+ }
90
+ }
91
+ }
92
+ if (!$available && !$pp_found)
93
+ $available = $this->getAvailableNetworks($currency, $country, $int_price);
94
+ if (is_array($available))
95
+ $helper->getSession()->setNetworks($available);
96
+ else
97
+ $helper->getSession()->unsNetworks();
98
+
99
+ if (!$available)
100
+ $helper->log(__METHOD__."($value,$currency,$country) is".($available ? '' : ' NOT').' available for your Boku service '.$helper->getConfig('service_id'), ($available ? Zend_Log::INFO : Zend_Log::WARN));
101
+ return $available;
102
+ }
103
+
104
+ /**
105
+ * Gets array of price-point data for particular country/currency
106
+ *
107
+ * @param ISO-4217 $currency (default: store currency)
108
+ * @param ISO-3166-1-alpha-2 $country (default: store country)
109
+ * @return array|null
110
+ */
111
+ public function getPrices($currency = null, $country = null, $fetch = false){
112
+ $helper = Mage::helper(self::APP_ROOT);
113
+
114
+ if (empty($currency)) $currency = $helper->getCurrencyCode();
115
+ if (empty($country)) $country = $helper->getCountryCode();
116
+
117
+ if (!$fetch){
118
+ $prices = $this->getCache();
119
+ try {
120
+ $prices = $prices['country'][$country][$currency]['price-points'];
121
+ $fetch = !is_array($prices);
122
+ } catch (Exception $e){$fetch = true;}
123
+ }
124
+
125
+ if ($fetch){
126
+ $prices = $this->fetchPrices($currency, $country);
127
+ $this->addToCache($prices);
128
+ try {
129
+ if (!empty($country)){
130
+ $prices = $prices['country'][$country][$currency]['price-points'];
131
+ if (!is_array($prices)) $prices = null;
132
+ }else{
133
+ $p = null;
134
+ foreach($prices['country'] as $c)
135
+ if (array_key_exists($currency, $c) && is_array($c[$currency])){
136
+ $p = $c[$currency]['price-points']; break;
137
+ }
138
+ $prices = $p;
139
+ }
140
+ } catch (Exception $e){$prices = null;}
141
+ }
142
+ if (is_null($prices))
143
+ $helper->log(__METHOD__."($currency,$country): No price data found", Zend_Log::WARN);
144
+ return $prices;
145
+ }
146
+
147
+ /**
148
+ * Fetches a list of available phone networks for given country, currency and price.
149
+ * Also adds the price info to the cached price list.
150
+ * If the price is not available then it is also added to the cache as unavailable.
151
+ *
152
+ * @param ISO-4217 $currency
153
+ * @param ISO-3166-1-alpha-2 $country
154
+ * @param int $price
155
+ * @return array|null
156
+ */
157
+ private function getAvailableNetworks($currency, $country, $price){
158
+ $helper = Mage::helper(self::APP_ROOT);
159
+ $data = $this->fetchPrices($currency, $country, $price);
160
+ $networks = false;
161
+ try{
162
+ if (!empty($data)){
163
+ $p = &$data['country'][$country][$currency]['price-points'][0];
164
+ $networks = $p['network'];
165
+ }else
166
+ $p = array(
167
+ 'status'=>0,
168
+ 'min-price'=>$price,
169
+ 'max-price'=>$price,
170
+ 'increment'=>0,
171
+ );
172
+ $pl = array();
173
+ $pl['country'][$country][$currency]['price-points'][] = $p;
174
+ $this->addToCache($pl);
175
+ }catch(Exception $e){}
176
+ return $networks;
177
+ }
178
+
179
+ /**
180
+ * Recursive merge of 2 arrays. a1 is modified and returned
181
+ * if a2 key is an integer then append to a1
182
+ * elseif a2 and a1 values are both arrays then merge
183
+ * else replace
184
+ *
185
+ * @param array &$a1
186
+ * @param array &$a2
187
+ * @return array
188
+ */
189
+ private function &arrayMerge(&$a1, &$a2){
190
+ foreach ($a2 as $k=>&$v)
191
+ if (is_integer($k))
192
+ $a1[] = $a2[$k];
193
+ elseif (is_array($v) && isset($a1[$k]) && is_array($a1[$k]))
194
+ $this->arrayMerge($a1[$k], $v);
195
+ else $a1[$k] = $v;
196
+ return $a1;
197
+ }
198
+
199
+ /**
200
+ * fetches price data from the cache
201
+ * if the cache has timed out then clears the cache and returns null
202
+ *
203
+ * @return array|null
204
+ */
205
+ private function getCache(){
206
+ $session = Mage::helper(self::APP_ROOT)->getSession();
207
+ $plt = $session->getPriceListTimestamp();
208
+ if (is_integer($plt)){
209
+ if ((time() - $plt) <= $this->price_list_timeout)
210
+ return $session->getPriceList();
211
+ $this->clearCache();
212
+ }
213
+ return null;
214
+ }
215
+ /**
216
+ * adds price data to the cache
217
+ * if the cache was empty then sets the timestamp
218
+ *
219
+ * @param array
220
+ */
221
+ private function addToCache(&$d){
222
+ if (!is_array($d)) return;
223
+ $session = Mage::helper(self::APP_ROOT)->getSession();
224
+ $pl = $session->getPriceList();
225
+ if (!is_integer($session->getPriceListTimestamp()) || !is_array($pl))
226
+ $session->setPriceListTimestamp(time());
227
+ $session->setPriceList(is_array($pl) ? $this->arrayMerge($pl, $d) : $d);
228
+ }
229
+ private function clearCache(){
230
+ $session = Mage::helper(self::APP_ROOT)->getSession();
231
+ $session->unsPriceList();
232
+ $session->unsPriceListTimestamp();
233
+ }
234
+
235
+ /**
236
+ * Fetches price data from Boku
237
+ * If $price is not provided then it will fetch price-list otherwise print-info.
238
+ *
239
+ * @param ISO-4217 $currency
240
+ * @param ISO-3166-1-alpha-2 $country
241
+ * @param int $price (default: null)
242
+ * @return array|null
243
+ */
244
+ public function fetchPrices($currency, $country, $price = null){
245
+ $helper = Mage::helper(self::APP_ROOT);
246
+ $price_url = $helper->getConfig(is_null($price) ? 'url/pricelist' : 'url/priceinfo');
247
+ $service_id = $helper->getConfig('service_id');
248
+ $params = array(
249
+ 'merchant-id'=>$helper->getConfig('merchant_id'),
250
+ 'service-id'=>$service_id,
251
+ 'country'=>$country,
252
+ 'currency'=>$currency,
253
+ 'reference-currency'=>$helper->getBaseCurrencyCode(),
254
+ // 'show-all-networks'=>'true',
255
+ );
256
+ if (!is_null($price))
257
+ $params = array_merge($params, array(
258
+ 'price'=>$price,
259
+ 'show-all-networks'=>'true',
260
+ ));
261
+
262
+ $client = new Zend_Http_Client($helper->buildUrl($price_url, $params));
263
+ try{
264
+ $response = $client->request();
265
+ if ($response->isSuccessful()){
266
+ if (!Boku_Paymentgateway_Model_Xml::isValidXml($response->getBody()))
267
+ throw new Exception('Bad XML fetched from '.$price_url);
268
+ $prices = new Boku_Paymentgateway_Model_Xml($response->getBody());
269
+ $prices = $prices->asArray();
270
+ if ($prices['response-code'] != 0){
271
+ $msg = $prices['response-message'];
272
+ switch($prices['response-code']){
273
+ case 33: $msg .= ' ('.$currency.')'; break;
274
+ case 34: $msg .= ' ('.$service_id.')'; break;
275
+ case 36: $msg .= ' ('.$country.')'; break;
276
+ }
277
+ throw new Exception($msg);
278
+ }
279
+ self::collapsePriceArray($prices);
280
+ if (!array_key_exists('country', $prices))
281
+ throw new Exception('No price-point data retrieved');
282
+ self::sectionCurrencies($prices);
283
+ return $prices;
284
+ }else
285
+ throw new Exception('Http Failed: '.$response->getMessage());
286
+ }catch(Exception $e){$helper->logErr(__METHOD__."($currency,$country,$price): ".$e->getMessage());}
287
+ return null;
288
+ }
289
+
290
+ /**
291
+ * Shifts price data into currency sections and removes some repeated data.
292
+ *
293
+ * @param array &$data (by reference)
294
+ * @return array
295
+ */
296
+ private static function &sectionCurrencies(&$data){
297
+ unset($data['format']);
298
+ unset($data['reference-currency']);
299
+ foreach($data['country'] as &$country){
300
+ $first = true;
301
+ foreach(array_keys($country) as $k){
302
+ $pp = $country[$k];
303
+ unset($country[$k]);
304
+ $currency = $pp['currency'];
305
+ unset($pp['currency']);
306
+ if ($first)
307
+ $country[$currency]['currency'] = array(
308
+ 'currency-decimal-places'=>$pp['currency-decimal-places'],
309
+ 'reference-currency'=>$pp['reference-currency'],
310
+ 'exchange'=>$pp['exchange'],
311
+ );
312
+ unset($pp['currency-decimal-places']);
313
+ unset($pp['reference-currency']);
314
+ unset($pp['exchange']);
315
+ $country[$currency]['price-points'][] = $pp;
316
+ $first = false;
317
+ }
318
+ }
319
+ return $data;
320
+ }
321
+
322
+ /**
323
+ * Simplifies and cleans up the output from Boku_Paymentgateway_Model_Xml::asArray()
324
+ * Intended to work with Boku price-list and price-info responses.
325
+ *
326
+ * @param array &$data (by reference)
327
+ * @return array
328
+ */
329
+ private static function &collapsePriceArray(&$data){
330
+ //strip useless data
331
+ if (isset($data['@string'])) unset($data['@string']);
332
+
333
+ if (count($data) > 1 && isset($data['@code'])){
334
+ $na = array();
335
+ $code = '@'.$data['@code'];
336
+ unset($data['@code']);
337
+ foreach($data as $k=>&$v) $na[$k] = $v;
338
+ $data = array($code=>$na);
339
+ }
340
+
341
+ //do recursion
342
+ foreach($data as $k=>&$v)
343
+ if (is_array($v))
344
+ self::collapsePriceArray($v);
345
+
346
+ //
347
+ foreach($data as $k=>&$v){
348
+ if (!(is_array($v) && is_numeric($k))) continue;
349
+ $na = 0;
350
+ foreach($v as $vk=>&$vv)
351
+ if (substr((string) $vk, 0, 1) == '@'){
352
+ if (++$na > 1) break;
353
+ $ak = $vk;
354
+ $av = $vv;
355
+ }
356
+ if ($na == 1 && !is_array($av) && !array_key_exists($av, $data)){
357
+ unset($v[$ak]);
358
+ $data[$av] = $v;
359
+ unset($data[$k]);
360
+ }
361
+ }
362
+
363
+ //remove @ from start of attribute keys
364
+ foreach($data as $k=>&$v)
365
+ if (substr((string) $k, 0, 1) == '@'){
366
+ $data[substr((string) $k, 1)] = $v;
367
+ unset($data[$k]);
368
+ }
369
+
370
+ //collapse single element arrays into their parent
371
+ foreach($data as $k=>&$v)
372
+ if (is_array($v) && count($v) == 1){
373
+ foreach($v as $vk=>&$vv){
374
+ if (is_numeric($vk) || $vk == 'name'
375
+ || (($vk == 'continuous-price' || $vk == 'discrete-price') && !array_key_exists('status', $vv)))
376
+ $v = $vv;
377
+ elseif (is_numeric($k) && !array_key_exists($vk, $data)){
378
+ $data[$vk] = $vv;
379
+ unset($data[$k]);
380
+ }
381
+ break;
382
+ }
383
+ }
384
+ return $data;
385
+ }
386
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Callback.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Callback records
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ Class Boku_Paymentgateway_Model_Resource_Payment_Callback extends Mage_Core_Model_Resource_Db_Abstract{
12
+
13
+ /**
14
+ * Initialize main table and the primary key field name
15
+ */
16
+ protected function _construct(){
17
+ $this->_init('boku/payment_callback', 'id');
18
+ }
19
+
20
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Callback/Collection.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Callback collection
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Resource_Payment_Callback_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract{
12
+
13
+ /**
14
+ * Initialize collection items factory class
15
+ */
16
+ protected function _construct(){
17
+ $this->_init('boku/payment_callback');
18
+ parent::_construct();
19
+ }
20
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Chargeback.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Chargeback records
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ Class Boku_Paymentgateway_Model_Resource_Payment_Chargeback extends Mage_Core_Model_Resource_Db_Abstract{
12
+
13
+ /**
14
+ * Initialize main table and the primary key field name
15
+ */
16
+ protected function _construct(){
17
+ $this->_init('boku/payment_chargeback', 'id');
18
+ }
19
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Chargeback/Collection.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Chargeback collection
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Resource_Payment_Chargeback_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract{
12
+
13
+ /**
14
+ * Initialize collection items factory class
15
+ */
16
+ protected function _construct(){
17
+ $this->_init('boku/payment_chargeback');
18
+ parent::_construct();
19
+ }
20
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Event.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Event records
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ Class Boku_Paymentgateway_Model_Resource_Payment_Event extends Mage_Core_Model_Resource_Db_Abstract{
12
+
13
+ /**
14
+ * Initialize main table and the primary key field name
15
+ */
16
+ protected function _construct(){
17
+ $this->_init('boku/payment_event', 'id');
18
+ }
19
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Event/Collection.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Event collection
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Resource_Payment_Event_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract{
12
+
13
+ /**
14
+ * Initialize collection items factory class
15
+ */
16
+ protected function _construct(){
17
+ $this->_init('boku/payment_event');
18
+ parent::_construct();
19
+ }
20
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Transaction.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Transaction records
4
+ * Root for recording all Boku transaction data
5
+ *
6
+ * @category Payment gateway
7
+ * @package boku_paymentgateway
8
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
9
+ * @author MDH <mdh@treatid.me.uk>
10
+ */
11
+
12
+ Class Boku_Paymentgateway_Model_Resource_Payment_Transaction extends Mage_Core_Model_Resource_Db_Abstract{
13
+
14
+ protected $_isPkAutoIncrement = false;
15
+
16
+ /**
17
+ * Initialize main table and the primary key field name
18
+ */
19
+ protected function _construct(){
20
+ $this->_init('boku/payment_transaction', 'trx_id');
21
+ }
22
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Payment/Transaction/Collection.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Transaction collection
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Resource_Payment_Transaction_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract{
12
+
13
+ /**
14
+ * Initialize collection items factory class
15
+ */
16
+ protected function _construct(){
17
+ $this->_init('boku/payment_transaction');
18
+ parent::_construct();
19
+ }
20
+ }
app/code/community/Boku/Paymentgateway/Model/Resource/Setup.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Main Module configuration
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ class Boku_Paymentgateway_Model_Resource_Setup extends Mage_Core_Model_Resource_Setup
11
+ {
12
+ }
app/code/community/Boku/Paymentgateway/Model/Session.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku transaction session data store
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Session extends Mage_Core_Model_Session_Abstract
12
+ {
13
+ const APP_ROOT = 'boku';
14
+
15
+ public function __construct(){
16
+ $this->init(self::APP_ROOT);
17
+ }
18
+ }
app/code/community/Boku/Paymentgateway/Model/System/Config.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * System config options
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ class Boku_Paymentgateway_Model_System_Config
11
+ {
12
+ const APP_ROOT = 'boku';
13
+
14
+ const ABORT = 0;
15
+ const CHARGE_MIN = 1;
16
+ const CHARGE_MAX = 1;
17
+ const MULTIPLE = 2;
18
+ const ROUND_UP = 1;
19
+ const ROUND_DOWN = 2;
20
+
21
+ const NONE = 0;
22
+ const MIN = 1;
23
+ const AVG = 2;
24
+ const MAX = 3;
25
+
26
+ const LIVE = 0;
27
+ const TEST = 1;
28
+
29
+ const PAYMENT_ACTION_AUTH = 'Authorization';
30
+
31
+ public function getPaymentBelowMinOptions(){
32
+ $helper = Mage::helper(self::APP_ROOT);
33
+ return array(
34
+ self::ABORT =>$helper->__('Exclude Boku'),
35
+ self::CHARGE_MIN =>$helper->__('Charge Minimum'),
36
+ );
37
+ }
38
+ public function getPaymentAboveMaxOptions(){
39
+ $helper = Mage::helper(self::APP_ROOT);
40
+ return array(
41
+ self::ABORT =>$helper->__('Exclude Boku'),
42
+ self::CHARGE_MAX =>$helper->__('Charge Maximum'),
43
+ self::MULTIPLE =>$helper->__('Use Multiple Submissions'),
44
+ );
45
+ }
46
+ public function getPaymentUnavailableOptions(){
47
+ $helper = Mage::helper(self::APP_ROOT);
48
+ return array(
49
+ self::ABORT =>$helper->__('Exclude Boku'),
50
+ self::ROUND_UP =>$helper->__('Round-up (Exclude Boku if not possible)'),
51
+ self::ROUND_DOWN =>$helper->__('Round-down (Exclude Boku if not possible)'),
52
+ );
53
+ }
54
+ public function getCommissionOptions(){
55
+ $helper = Mage::helper(self::APP_ROOT);
56
+ return array(
57
+ self::NONE =>$helper->__('No'),
58
+ self::MIN =>$helper->__('Minimum'),
59
+ self::AVG =>$helper->__('Average'),
60
+ self::MAX =>$helper->__('Maximum'),
61
+ );
62
+ }
63
+ public function getModeOptions(){
64
+ $helper = Mage::helper(self::APP_ROOT);
65
+ return array(
66
+ self::TEST =>$helper->__('Test'),
67
+ self::LIVE =>$helper->__('Live'),
68
+ );
69
+ }
70
+ }
71
+ ?>
app/code/community/Boku/Paymentgateway/Model/Test.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku Tests
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Test
12
+ {
13
+ const APP_ROOT = 'boku';
14
+
15
+ /**
16
+ * Attempts to fetch price-list data from Boku
17
+ * returns info/results for process
18
+ *
19
+ * @return array
20
+ */
21
+ public function testConnection(){
22
+ $helper = Mage::helper(self::APP_ROOT);
23
+
24
+ $price_url = $helper->getConfig('url/pricelist');
25
+ $merchant_id = $helper->getConfig('merchant_id');
26
+ $service_id = $helper->getConfig('service_id');
27
+ $api_key = $helper->getConfig('api_security_key');
28
+ $out = array("Attempting to fetch price-point data from $price_url");
29
+ try{
30
+ if (empty($merchant_id) || empty($service_id) || empty($api_key)){
31
+ if (empty($merchant_id)) $out[] = 'Merchant Id not set';
32
+ if (empty($service_id)) $out[] = 'Service Id not set';
33
+ if (empty($api_key)) $out[] = 'API Security Key not set';
34
+ throw new Exception('Boku payment settings incomplete.');
35
+ }
36
+
37
+ $currency = $helper->getCurrencyCode();
38
+ $country = $helper->getCountryCode();
39
+ $params = array(
40
+ 'merchant-id'=>$merchant_id,
41
+ 'service-id'=>$service_id,
42
+ 'currency'=>$currency,
43
+ 'country'=>$country,
44
+ 'reference-currency'=>$helper->getBaseCurrencyCode(),
45
+ );
46
+ $client = new Zend_Http_Client($helper->buildUrl($price_url, $params));
47
+ $response = $client->request();
48
+ if ($response->isSuccessful()){
49
+ if (!Boku_Paymentgateway_Model_Xml::isValidXml($response->getBody()))
50
+ throw new Exception('Bad XML fetched from '.$price_url);
51
+ $prices = new Boku_Paymentgateway_Model_Xml($response->getBody());
52
+ $prices = $prices->asArray();
53
+ if ($prices['response-code'] != 0){
54
+ $msg = $prices['response-message'];
55
+ switch($prices['response-code']){
56
+ case 28: $msg .= ' (This is probably caused by incorrect values for your Merchant Id, Service Id or API Security Key)'; break;
57
+ case 33: $msg .= ' ('.$currency.')'; break;
58
+ case 34: if (!empty($service_id)) $msg .= ' ('.$service_id.')'; break;
59
+ case 36: if (!empty($country)) $msg .= ' ('.$currency.')'; break;
60
+ }
61
+ throw new Exception($msg);
62
+ }
63
+ $out[] = "Connection Successful";
64
+ }else
65
+ throw new Exception('Http Failed: '.$response->getMessage());
66
+ }catch(Exception $e){
67
+ $out[] = $e->getMessage();
68
+ }
69
+ return $out;
70
+ }
71
+
72
+ }
app/code/community/Boku/Paymentgateway/Model/Xml.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * XML object
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ class Boku_Paymentgateway_Model_Xml extends SimpleXMLElement
12
+ {
13
+ /**
14
+ * Returns this element and it's children as a condensed array
15
+ *
16
+ * @param bool $is_canonical - whether to ignore attributes
17
+ * @return array|string
18
+ */
19
+ public function asArray($is_canonical = false){
20
+ return self::condenseArray(self::_asArray($this, $is_canonical));
21
+ }
22
+
23
+ /**
24
+ * Returns the element and it's children as an array
25
+ * attributes names are prefixed by '@' - slight warning: these could be overwritten by children with the same names
26
+ *
27
+ * @param SimpleXMLElement
28
+ * @param bool $is_canonical - whether to ignore attributes
29
+ * @return array|string
30
+ */
31
+ protected static function _asArray(SimpleXMLElement $element, $is_canonical = false){
32
+ $result = array();
33
+ if (!$is_canonical)
34
+ foreach ($element->attributes() as $name=>$value)
35
+ $result['@'.$name] = (string) $value;
36
+
37
+ if (self::hasChildren($element)){
38
+ foreach ($element->children() as $name=>$child)
39
+ $result[$name][] = self::_asArray($child, $is_canonical);
40
+
41
+ } elseif (empty($result))
42
+ $result = (string) $element;
43
+ elseif (!empty((string) $element))
44
+ $result[0] = (string) $element;
45
+ return $result;
46
+ }
47
+
48
+ /**
49
+ * Does a SimpleXMLElement have any children ?
50
+ *
51
+ * @param SimpleXMLElement $element
52
+ * @return boolean
53
+ */
54
+ public static function hasChildren(SimpleXMLElement $element){
55
+ if ($children = $element->children())
56
+ foreach ($children as $child)
57
+ return true;
58
+ return false;
59
+ }
60
+
61
+ /**
62
+ * Collapses all single element arrays to their parents
63
+ *
64
+ * @param array $data
65
+ * @return array
66
+ */
67
+ protected static function condenseArray($data){
68
+ if (!is_array($data) || empty($data)) return $data;
69
+ if (count($data) == 1 && isset($data[0]))
70
+ return self::condenseArray($data[0]);
71
+ foreach($data as &$child)
72
+ $child = self::condenseArray($child);
73
+ return $data;
74
+ }
75
+
76
+ /**
77
+ * Unfortunately SimpleXMLElement can fail badly on invalid xml so here is a validation function
78
+ * returns false if any errors are encountered the errors are logged to system.log
79
+ *
80
+ * @param string $xml
81
+ * @return boolean
82
+ */
83
+ public static function isValidXml($xml){
84
+ libxml_use_internal_errors(true);
85
+
86
+ $doc = simplexml_load_string($xml);
87
+ if (!$doc){
88
+ $errors = libxml_get_errors();
89
+ $lines = explode("\n", $xml);
90
+ foreach($errors as $error){
91
+ $line = $lines[$error->line - 1];
92
+ $column = $error->column;
93
+ Mage::logErr(__METHOD__.': '.$error->line.'/'.$error->column.' - '.$error->message.' - '.substr($line, 0, $column - 1));
94
+ }
95
+ libxml_clear_errors();
96
+ return false;
97
+ }
98
+ return true;
99
+ }
100
+ }
app/code/community/Boku/Paymentgateway/controllers/Adminhtml/BokuController.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin controller
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ class Boku_Paymentgateway_Adminhtml_BokuController extends Mage_Adminhtml_Controller_Action
11
+ {
12
+ public function testAction(){
13
+ $this->loadLayout();
14
+ $this->renderLayout();
15
+ }
16
+ }
app/code/community/Boku/Paymentgateway/controllers/ApiController.php ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku callback controller
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ class Boku_Paymentgateway_ApiController extends Mage_Core_Controller_Front_Action
11
+ {
12
+ const APP_ROOT = 'boku';
13
+
14
+ static $STATUS = array(
15
+ -1 => 'Unknown error code',
16
+ 0 => 'OK',
17
+ 5 => 'Failed - unknown trx',
18
+ 20 => 'Missing or Invalid "cmd=" value',
19
+ 28 => 'Invalid signature',
20
+ 29 => 'Unsupported Price Point',
21
+ 31 => 'Invalid Or Missing Price',
22
+ 32 => 'Bad Bind Credentials',
23
+ 33 => 'Invalid Or Missing Currency Code',
24
+ 34 => 'Invalid Or Missing Service-Id',
25
+ 35 => 'Internal Error',
26
+ 36 => 'Invalid or Missing Country Code',
27
+ 37 => 'Invalid Dynamic Pricing Mode',
28
+ 38 => 'Invalid Dynamic-match',
29
+ 39 => 'Invalid or missing Dynamic-deviation',
30
+ 40 => 'Invalid or missing Dynamic-deviation-policy',
31
+ 41 => 'No payment solution available',
32
+ 42 => 'Country not available on requested service',
33
+ 43 => 'Invalid Request',
34
+ 51 => 'Expired timestamp',
35
+ 52 => 'Incorrect field format',
36
+ 53 => 'Invalid field value',
37
+ 60 => 'Invalid "row-ref" value',
38
+ 91 => 'Missing or invalid user parameter(s)',
39
+ 93 => 'Unsupported network',
40
+ 99 => 'Boku undergoing maintenance',
41
+ );
42
+ /**
43
+ * These are the minimum sets of parameters required for each callback.
44
+ */
45
+ static $REQUIRED_PARAMS = array(
46
+ 'common'=>array(
47
+ 'trx-id',
48
+ 'timestamp',
49
+ 'action',
50
+ ),
51
+ 'billingresult'=>array(
52
+ 'result-code',
53
+ 'currency',
54
+ 'paid',
55
+ 'amount',
56
+ ),
57
+ 'event'=>array(
58
+ 'event-code',
59
+ 'currency',
60
+ 'paid',
61
+ 'message-cost',
62
+ ),
63
+ 'chargeback'=>array(
64
+ 'currency',
65
+ 'chargebackamount',
66
+ 'paid',
67
+ 'reason-id',
68
+ 'refundsource',
69
+ ),
70
+ );
71
+
72
+ /**
73
+ * Handler for all callbacks from Boku
74
+ *
75
+ * @input URL parameters
76
+ * @output XML
77
+ */
78
+ public function indexAction(){
79
+ $helper = Mage::helper(self::APP_ROOT);
80
+ $helper->setStore(null);
81
+ $data = Mage::app()->getRequest()->getParams();
82
+ $response_data = $this->verifyCallback($data);
83
+
84
+ if ($response_data['status_code'] == 0){
85
+ switch($data['action']){
86
+ case 'billingresult':
87
+ case 'event':
88
+ case 'chargeback':
89
+ try{
90
+ $transaction = $helper->getTransaction($data);
91
+ if (empty($transaction))
92
+ throw new Exception('Transaction data not found for trx-id='.$data['trx-id'], 5);
93
+ }catch(Exception $e){
94
+ $code = $e->getCode();
95
+ if (empty($code)) $code = -1;
96
+ $response_data = array_merge($response_data, array(
97
+ 'status_code'=>$code,
98
+ 'status'=>self::$STATUS[$code],
99
+ ));
100
+ $data['notes'] = $e->getMessage();
101
+ $helper->logErr(__METHOD__.' - '.$e->getMessage());
102
+ break;
103
+ }
104
+ try{
105
+ $this->{$data['action'].'Handler'}($data, $response_data);
106
+ }catch(Exception $e){
107
+ $helper->log(__CLASS__.'::'.$data['action'].' - '.$e->getMessage());
108
+ }
109
+ break;
110
+ default:
111
+ $response_data = array_merge($response_data, array(
112
+ 'status_code'=>53,
113
+ 'status'=>self::$STATUS[53],
114
+ 'field'=>'action',
115
+ ));
116
+ }
117
+ }
118
+ $data['status_code'] = $response_data['status_code'];
119
+ $data['status'] = $response_data['status'];
120
+ $this->logCallback($data);
121
+ $r = $this->getResponse();
122
+ $r->setHeader('HTTP/1.1 200 OK', '', true);
123
+ $r->setHeader('Content-Type', 'text/xml; charset=utf-8', true);
124
+ $r->setHeader('Cache-Control', 'no-cache, no-store, must-revalidate', true);
125
+ $r->setHeader('Expires', 'Expires: Sat, 1 Jan 2000 00:00:00 GMT', true);
126
+ $r->setHeader('Pragma', 'no-cache', true);
127
+ $r->setBody($this->getCallbackResponseXml($response_data)->asXML());
128
+ }
129
+
130
+ /**
131
+ * Callback from Boku at the end of the payment process
132
+ * Note:assumes that the currency is the same as original prepare call
133
+ *
134
+ * @param array $data
135
+ * @param array $response
136
+ * @return array $response
137
+ */
138
+ protected function &billingresultHandler($data, &$response){
139
+ $helper = Mage::helper(self::APP_ROOT);
140
+ $transaction = $helper->getTransaction($data);
141
+ if (is_numeric($transaction->getResultCode()))
142
+ $helper->logErr('Unexpected extra billingresult for trx-id:'.$data['trx-id']);
143
+ else
144
+ $transaction->complete($data);
145
+ return $response;
146
+ }
147
+
148
+ /**
149
+ * Callback from Boku made for each payment
150
+ * Note:assumes that the currency is the same as original prepare call
151
+ *
152
+ * @param array $data
153
+ * @param array $response
154
+ * @return array $response
155
+ */
156
+ protected function &eventHandler(&$data, &$response){
157
+ Mage::getSingleton(self::APP_ROOT.'/payment_event')->create($data);
158
+ return $response;
159
+ }
160
+
161
+ /**
162
+ * Callback from Boku for refunds
163
+ * Note:assumes that the currency is the same as original prepare call
164
+ *
165
+ * @param array $data
166
+ * @param array $response
167
+ * @return array $response
168
+ */
169
+ protected function &chargebackHandler(&$data, &$response){
170
+ Mage::getSingleton(self::APP_ROOT.'/payment_chargeback')->create($data);
171
+ return $response;
172
+ }
173
+
174
+ /**
175
+ * Creates a new callback record for the $data['trx-id'] transaction
176
+ *
177
+ * @param array $data
178
+ */
179
+ protected function logCallback(&$data){
180
+ if ($data['status_code'] == 5) $data['trx-id'] = null;
181
+ return Mage::getModel(self::APP_ROOT.'/payment_callback')->addData($data)->save();
182
+ }
183
+
184
+ /**
185
+ * Does general verification for all callbacks from Boku
186
+ * Aborts as soon as a failure is encountered.
187
+ *
188
+ * @param array $params
189
+ * @return array(status_code=>int, status=>string[, field=>string])
190
+ */
191
+ protected function verifyCallback(&$params){
192
+ $helper = Mage::helper(self::APP_ROOT);
193
+ try{
194
+ $remote_ip = Mage::helper('core/http')->getRemoteAddr();
195
+ $valid_ips = $helper->getConfig('callback_ips');
196
+ if (!empty($valid_ips) && !in_array($remote_ip, explode(';', $valid_ips)))
197
+ throw new Exception('Invalid source IP for callback: '.$remote_ip, -1);
198
+
199
+ //Verify the validity of the callback signature
200
+ if (!$helper->verifySignature($params)) throw new Exception('sig', 28);
201
+
202
+ //Verify that primary fields exist
203
+ foreach (self::$REQUIRED_PARAMS['common'] as $field)
204
+ if (!array_key_exists($field, $params)) throw new Exception($field, $field == 'trx-id' ? 5 : 43);
205
+
206
+ //Verify callback specific fields exist
207
+ foreach (self::$REQUIRED_PARAMS[$params['action']] as $field)
208
+ if (!array_key_exists($field, $params)) throw new Exception($field, 43);
209
+
210
+ $response = array('status_code'=>0, 'status'=>self::$STATUS[0]);
211
+
212
+ }catch(Exception $e){
213
+ $helper->logErr(__METHOD__.': '.$e->getMessage().' '.self::$STATUS[$e->getCode()]."\n".json_encode($params, JSON_PRETTY_PRINT));
214
+ $status = isset(self::$STATUS[$e->getCode()]) ? self::$STATUS[$e->getCode()] : 'Unknown Status';
215
+ $response = array('status_code'=>$e->getCode(), 'status'=>$status, 'field'=>$e->getMessage());
216
+ }
217
+ $response['trx-id'] = array_key_exists('trx-id', $params) ? $params['trx-id'] : 'Unknown';
218
+ return $response;
219
+ }
220
+
221
+ /**
222
+ * Generates SimpleXML data for response.
223
+ *
224
+ * @param array $data
225
+ * @return SimpleXML
226
+ */
227
+ protected function getCallbackResponseXml($data){
228
+ $xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" standalone="yes" ?><callback-ack></callback-ack>');
229
+ $xml->addChild('trx-id', $data['trx-id']);
230
+ $s = $xml->addChild('status', $data['status']);
231
+ $s->addAttribute('code', $data['status_code']);
232
+ if ($data['status_code'] == 53)
233
+ $s->addAttribute('invalidfield', $data['field']);
234
+ return $xml;
235
+ }
236
+ }
app/code/community/Boku/Paymentgateway/controllers/StandardController.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku payment gateway controller
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ class Boku_Paymentgateway_StandardController extends Mage_Checkout_Controller_Action
11
+ {
12
+ const APP_ROOT = 'boku';
13
+
14
+ public function prepareAction(){
15
+ $this->loadLayout();
16
+ $this->renderLayout();
17
+ }
18
+
19
+ /**
20
+ * Called by Boku for failed transactions
21
+ * Cancels the order, detatches the order and transaction from the quote
22
+ * restores the quote to the basket.
23
+ */
24
+ public function cancelAction(){
25
+ $helper = Mage::helper(self::APP_ROOT);
26
+ $boku_session = $helper->getSession();
27
+ $trx_id = $boku_session->getData('trx-id');
28
+ $boku_session->clear();
29
+ $data = Mage::app()->getRequest()->getParams();
30
+ try{
31
+ if (isset($data['trx-id'])){
32
+ if (empty($trx_id))
33
+ $trx_id = $data['trx-id'];
34
+ else if ($data['trx-id'] != $trx_id)
35
+ throw new Exception('trx-id:'.$data['trx-id'].' not same as session:'.$trx_id);
36
+ }else if (empty($trx_id))
37
+ throw new Exception('No trx-id param');
38
+ if (is_null($transaction = $helper->getTransaction($trx_id))) throw new Exception('Transaction '.$trx_id.' record not found.');
39
+ if ($transaction->getCancelled()) throw new Exception('Transaction '.$trx_id.' already cancelled.');
40
+
41
+ $order = $transaction->getOrder();
42
+ $quote = $transaction->getQuote();
43
+ $transaction->setCancelled(true)->setQuoteId(null)->save();
44
+
45
+ if (!is_null($quote)){
46
+ $quote->setIsActive(true)->setReservedOrderId(null)->save();
47
+ if (!is_null($order))
48
+ $order->setQuoteId(null)->save();
49
+ $session = Mage::getSingleton('checkout/session');
50
+ $session->setQuoteId($quote->getId());
51
+ $session->setFirstTimeChk('0');
52
+ Mage::getSingleton('checkout/type_onepage')->getCheckout()->unsLastQuoteId();
53
+ }
54
+
55
+ if (!is_null($order)){
56
+ // $order->addStatusHistoryComment('Boku Transaction Cancelled');
57
+ if ($order->canCancel())
58
+ $order->cancel()->save();
59
+ }
60
+ }catch(Exception $e){
61
+ $helper->logErr(__METHOD__.': '.$e->getMessage());
62
+ }
63
+ $this->loadLayout();
64
+ $this->renderLayout();
65
+ }
66
+
67
+ /**
68
+ * Called by Boku for successful transactions
69
+ * changes the order status to STATE_PENDING_PAYMENT (if STATE_NEW)
70
+ */
71
+ public function successAction(){
72
+ $helper = Mage::helper(self::APP_ROOT);
73
+ $boku_session = $helper->getSession();
74
+ $trx_id = $boku_session->getData('trx-id');
75
+ $boku_session->clear();
76
+ $data = Mage::app()->getRequest()->getParams();
77
+ try{
78
+ if (!isset($data['trx-id'])) throw new Exception('No trx-id param');
79
+ if (empty($trx_id))
80
+ $trx_id = $data['trx-id'];
81
+ else if ($data['trx-id'] != $trx_id)
82
+ throw new Exception('trx-id:'.$data['trx-id'].' not same as session:'.$trx_id);
83
+ if (is_null($transaction = $helper->getTransaction($trx_id))) throw new Exception('Transaction '.$trx_id.' record not found.');
84
+ if ($transaction->getCancelled()) throw new Exception('Transaction '.$trx_id.' already cancelled.');
85
+ if (is_null($order = $transaction->getOrder())) throw new Exception('Order not found for '.$trx_id);
86
+
87
+ if ($order->getState() == $order::STATE_NEW)
88
+ $order->setState($order::STATE_PENDING_PAYMENT)->save();
89
+ $order->addStatusHistoryComment('Boku Transaction Initiated');
90
+
91
+ $msg = $helper->getConfig('message/success');
92
+ if (!empty($msg)) Mage::getSingleton('checkout/session')->addNotice($msg);
93
+ }catch(Exception $e){
94
+ $helper->logErr(__METHOD__.': '.$e->getMessage());
95
+ }
96
+ $this->loadLayout();
97
+ $this->renderLayout();
98
+ }
99
+
100
+ /**
101
+ * Not used in normal activity. Can be called to force completion of any outstanding callback handling.
102
+ */
103
+ public function completeAction(){
104
+ Mage::helper(self::APP_ROOT)->completeOutstanding();
105
+ }
106
+ }
app/code/community/Boku/Paymentgateway/etc/adminhtml.xml ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Boku admin menu
5
+ *
6
+ * @category Payment gateway
7
+ * @package boku_paymentgateway
8
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
9
+ * @author MDH <mdh@treatid.me.uk>
10
+ */
11
+ -->
12
+ <config>
13
+ <menu>
14
+ <system>
15
+ <children>
16
+ <boku translate="title" module="boku">
17
+ <title>Boku</title>
18
+ <sort_order>1000</sort_order>
19
+ <children>
20
+ <settings translate="title">
21
+ <title>Configure</title>
22
+ <action>adminhtml/system_config/edit/section/payment</action>
23
+ <sort_order>1</sort_order>
24
+ </settings>
25
+ <test translate="title">
26
+ <title>Test</title>
27
+ <action>boku_admin/adminhtml_boku/test</action>
28
+ <sort_order>10</sort_order>
29
+ </test>
30
+ </children>
31
+ </boku>
32
+ </children>
33
+ </system>
34
+ </menu>
35
+ <acl>
36
+ <resources>
37
+ <all translate="title">
38
+ <title>Allow Everything</title>
39
+ </all>
40
+ <admin>
41
+ <children>
42
+ <system>
43
+ <children>
44
+ <boku>
45
+ <title>Boku</title>
46
+ <children>
47
+ <settings translate="title">
48
+ <title>Configure</title>
49
+ </settings>
50
+ <test translate="title">
51
+ <title>Test Settings</title>
52
+ </test>
53
+ </children>
54
+ </boku>
55
+ </children>
56
+ </system>
57
+ </children>
58
+ </admin>
59
+ </resources>
60
+ </acl>
61
+ </config>
app/code/community/Boku/Paymentgateway/etc/config.xml ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Main Module configuration
5
+ *
6
+ * @category Payment gateway
7
+ * @package boku_paymentgateway
8
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
9
+ * @author MDH <mdh@treatid.me.uk>
10
+ */
11
+ -->
12
+ <config>
13
+ <modules>
14
+ <Boku_Paymentgateway>
15
+ <version>0.0.4</version>
16
+ </Boku_Paymentgateway>
17
+ </modules>
18
+
19
+ <global>
20
+ <models>
21
+ <boku>
22
+ <class>Boku_Paymentgateway_Model</class>
23
+ <resourceModel>boku_resource</resourceModel>
24
+ </boku>
25
+ <boku_resource>
26
+ <class>Boku_Paymentgateway_Model_Resource</class>
27
+ <entities>
28
+ <payment_transaction>
29
+ <table>boku_transactions</table>
30
+ </payment_transaction>
31
+ <payment_callback>
32
+ <table>boku_callbacks</table>
33
+ </payment_callback>
34
+ <payment_event>
35
+ <table>boku_events</table>
36
+ </payment_event>
37
+ <payment_chargeback>
38
+ <table>boku_chargebacks</table>
39
+ </payment_chargeback>
40
+ </entities>
41
+ </boku_resource>
42
+ </models>
43
+
44
+ <resources>
45
+ <boku_setup>
46
+ <setup>
47
+ <module>Boku_Paymentgateway</module>
48
+ <class>Boku_Paymentgateway_Model_Resource_Setup</class>
49
+ </setup>
50
+ </boku_setup>
51
+ </resources>
52
+
53
+ <helpers>
54
+ <boku>
55
+ <class>Boku_Paymentgateway_Helper</class>
56
+ </boku>
57
+ </helpers>
58
+
59
+ <blocks>
60
+ <boku>
61
+ <class>Boku_Paymentgateway_Block</class>
62
+ </boku>
63
+ </blocks>
64
+
65
+ </global>
66
+
67
+ <admin>
68
+ <routers>
69
+ <!--adminhtml>
70
+ <args>
71
+ <modules>
72
+ <Boku_Paymentgateway>Boku_Paymentgateway_Adminhtml</Boku_Paymentgateway>
73
+ </modules>
74
+ </args>
75
+ </adminhtml-->
76
+ <boku_admin>
77
+ <use>admin</use>
78
+ <args>
79
+ <module>Boku_Paymentgateway</module>
80
+ <frontName>boku_admin</frontName>
81
+ </args>
82
+ </boku_admin>
83
+ </routers>
84
+ </admin>
85
+
86
+ <adminhtml>
87
+ <layout>
88
+ <updates>
89
+ <boku_paymentgateway>
90
+ <file>boku_paymentgateway.xml</file>
91
+ </boku_paymentgateway>
92
+ </updates>
93
+ </layout>
94
+ <translate>
95
+ <modules>
96
+ <Boku_Paymentgateway>
97
+ <files>
98
+ <default>Boku_Paymentgateway.csv</default>
99
+ </files>
100
+ </Boku_Paymentgateway>
101
+ </modules>
102
+ </translate>
103
+ </adminhtml>
104
+
105
+ <frontend>
106
+ <secure_url>
107
+ <boku>/boku/standard</boku>
108
+ <bokuapi>/boku/api</bokuapi>
109
+ </secure_url>
110
+
111
+ <routers>
112
+ <boku>
113
+ <use>standard</use>
114
+ <args>
115
+ <module>Boku_Paymentgateway</module>
116
+ <frontName>boku</frontName>
117
+ </args>
118
+ </boku>
119
+ </routers>
120
+
121
+ <translate>
122
+ <modules>
123
+ <Boku_Paymentgateway>
124
+ <files>
125
+ <default>Boku_Paymentgateway.csv</default>
126
+ </files>
127
+ </Boku_Paymentgateway>
128
+ </modules>
129
+ </translate>
130
+
131
+ <layout>
132
+ <updates>
133
+ <boku_paymentgateway>
134
+ <file>boku_paymentgateway.xml</file>
135
+ </boku_paymentgateway>
136
+ </updates>
137
+ </layout>
138
+ </frontend>
139
+
140
+ <default>
141
+ <payment>
142
+ <boku>
143
+ <model>boku/payment_standard</model>
144
+ <payment_action>Authorization</payment_action>
145
+ <mode>1</mode>
146
+ <active>0</active>
147
+ <auto_invoice>0</auto_invoice>
148
+ <title>Boku (Pay by Mobile)</title>
149
+ <order_status>processing</order_status>
150
+ <api_security_key backend_model="adminhtml/system_config_backend_encrypted" />
151
+ <!--payment>
152
+ NOT IMPLEMENTED
153
+ <commission_add>0</commission_add>
154
+ <below_min>0</below_min>
155
+ <above_max>0</above_max>
156
+ <unavailable>0</unavailable>
157
+ <max>0</max>
158
+ </payment-->
159
+ <url>
160
+ <pricelist>https://api2.boku.com/pricing/1.0/price-list</pricelist>
161
+ <priceinfo>https://api2.boku.com/pricing/1.0/price-info</priceinfo>
162
+ <prepare>https://api2.boku.com/billing/request?action=prepare</prepare>
163
+ </url>
164
+ </boku>
165
+ </payment>
166
+ </default>
167
+
168
+ <crontab>
169
+ <jobs>
170
+ <boku_cron>
171
+ <!-- run at midnight daily. Note cron execution must be setup on the server for this to run. -->
172
+ <schedule><cron_expr>0 0 * * *</cron_expr></schedule>
173
+ <run><model>boku/cron::run</model></run>
174
+ </boku_cron>
175
+ </jobs>
176
+ </crontab>
177
+ </config>
app/code/community/Boku/Paymentgateway/etc/system.xml ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Magento admin configuration section
5
+ * Config options for Boku module
6
+ *
7
+ * @category Payment gateway
8
+ * @package boku_paymentgateway
9
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
10
+ * @author MDH <mdh@treatid.me.uk>
11
+ */
12
+ -->
13
+ <config>
14
+ <sections>
15
+ <payment>
16
+ <groups>
17
+ <boku translate="label" module="boku">
18
+ <label>Boku (Pay by Mobile)</label>
19
+ <comment><![CDATA[<a href="https://www.boku.com/merchant-partnerships/" target="_blank">Sign up for a Boku account</a>]]></comment>
20
+ <frontend_type>text</frontend_type>
21
+ <sort_order>10</sort_order>
22
+ <show_in_default>1</show_in_default>
23
+ <show_in_website>1</show_in_website>
24
+ <show_in_store>1</show_in_store>
25
+ <fields>
26
+ <merchant_id translate="label" module="boku">
27
+ <label>Merchant ID</label>
28
+ <comment><![CDATA[This is your Boku Merchant ID.]]></comment>
29
+ <sort_order>0</sort_order>
30
+ <required>1</required>
31
+ <show_in_default>1</show_in_default>
32
+ <show_in_website>1</show_in_website>
33
+ </merchant_id>
34
+ <api_security_key translate="label" module="boku">
35
+ <label>API Security Key</label>
36
+ <comment><![CDATA[This is your Boku API security key<br> (stored as encrypted data).]]></comment>
37
+ <frontend_type>text</frontend_type>
38
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
39
+ <sort_order>5</sort_order>
40
+ <show_in_default>1</show_in_default>
41
+ <show_in_website>1</show_in_website>
42
+ </api_security_key>
43
+ <service_id translate="label" module="boku">
44
+ <label>Service ID</label>
45
+ <comment><![CDATA[This is your Boku service id. (Not the service name)]]></comment>
46
+ <frontend_type>text</frontend_type>
47
+ <sort_order>10</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>1</show_in_website>
50
+ </service_id>
51
+ <sub_merchant_name translate="label" module="boku">
52
+ <label>Sub Merchant Name</label>
53
+ <comment><![CDATA[This appears as an identifier on the Boku payment panel.<br>If left blank then the panel uses the service name specified in your Boku account.]]></comment>
54
+ <frontend_type>text</frontend_type>
55
+ <sort_order>12</sort_order>
56
+ <show_in_default>1</show_in_default>
57
+ <show_in_website>1</show_in_website>
58
+ </sub_merchant_name>
59
+ <mode translate="label">
60
+ <label>Mode</label>
61
+ <frontend_type>select</frontend_type>
62
+ <source_model>boku/system_config::getModeOptions</source_model>
63
+ <sort_order>14</sort_order>
64
+ <show_in_default>1</show_in_default>
65
+ <show_in_website>1</show_in_website>
66
+ </mode>
67
+ <auto_invoice translate="label">
68
+ <label>Generate Invoices</label>
69
+ <comment><![CDATA[If the payment process completes and the total paid matches the order total then should we generate an invoice ? This also sends the generated invoice to the customer (if possible).]]></comment>
70
+ <frontend_type>select</frontend_type>
71
+ <source_model>adminhtml/system_config_source_yesno</source_model>
72
+ <sort_order>16</sort_order>
73
+ <show_in_default>1</show_in_default>
74
+ <show_in_website>1</show_in_website>
75
+ </auto_invoice>
76
+ <message_success translate="label" module="boku">
77
+ <label>Success Message</label>
78
+ <comment><![CDATA[Optional additional message for the payment success page.]]></comment>
79
+ <frontend_type>text</frontend_type>
80
+ <config_path>payment/boku/message/success</config_path>
81
+ <sort_order>17</sort_order>
82
+ <show_in_default>1</show_in_default>
83
+ <show_in_website>1</show_in_website>
84
+ </message_success>
85
+ <active translate="label">
86
+ <label>Enabled</label>
87
+ <frontend_type>select</frontend_type>
88
+ <source_model>adminhtml/system_config_source_yesno</source_model>
89
+ <sort_order>19</sort_order>
90
+ <show_in_default>1</show_in_default>
91
+ <show_in_website>1</show_in_website>
92
+ </active>
93
+
94
+ <heading_standard translate="label">
95
+ <label>Standard Payment Settings</label>
96
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
97
+ <sort_order>20</sort_order>
98
+ <show_in_default>1</show_in_default>
99
+ <show_in_website>1</show_in_website>
100
+ </heading_standard>
101
+ <title translate="label">
102
+ <label>Title</label>
103
+ <frontend_type>text</frontend_type>
104
+ <sort_order>25</sort_order>
105
+ <show_in_default>1</show_in_default>
106
+ <show_in_website>1</show_in_website>
107
+ <show_in_store>1</show_in_store>
108
+ </title>
109
+ <allowspecific translate="label">
110
+ <label>Payment from Applicable Countries</label>
111
+ <comment><![CDATA[This country restriction is applied to the Billing Address.]]></comment>
112
+ <frontend_type>allowspecific</frontend_type>
113
+ <sort_order>35</sort_order>
114
+ <source_model>adminhtml/system_config_source_payment_allspecificcountries</source_model>
115
+ <show_in_default>1</show_in_default>
116
+ <show_in_website>1</show_in_website>
117
+ </allowspecific>
118
+ <specificcountry translate="label">
119
+ <label>Payment from Specific Countries</label>
120
+ <frontend_type>multiselect</frontend_type>
121
+ <sort_order>40</sort_order>
122
+ <source_model>adminhtml/system_config_source_country</source_model>
123
+ <show_in_default>1</show_in_default>
124
+ <show_in_website>1</show_in_website>
125
+ </specificcountry>
126
+ <min_order_total translate="label">
127
+ <label>Minimum Order Total</label>
128
+ <frontend_type>text</frontend_type>
129
+ <sort_order>45</sort_order>
130
+ <show_in_default>1</show_in_default>
131
+ <show_in_website>1</show_in_website>
132
+ </min_order_total>
133
+ <max_order_total translate="label">
134
+ <label>Maximum Order Total</label>
135
+ <comment><![CDATA[Minimum and Maximum totals will also be restricted by your Boku account settings.]]></comment>
136
+ <frontend_type>text</frontend_type>
137
+ <sort_order>50</sort_order>
138
+ <show_in_default>1</show_in_default>
139
+ <show_in_website>1</show_in_website>
140
+ </max_order_total>
141
+ <sort_order translate="label">
142
+ <label>Sort Order</label>
143
+ <frontend_type>text</frontend_type>
144
+ <sort_order>55</sort_order>
145
+ <show_in_default>1</show_in_default>
146
+ <show_in_website>1</show_in_website>
147
+ <frontend_class>validate-number</frontend_class>
148
+ </sort_order>
149
+ <!--
150
+ NOT IMPLEMENTED
151
+ <heading_extended translate="label">
152
+ <label>Extended Payment Settings</label>
153
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
154
+ <sort_order>100</sort_order>
155
+ <show_in_default>1</show_in_default>
156
+ <show_in_website>1</show_in_website>
157
+ </heading_extended>
158
+ <commission_add translate="label" module="boku">
159
+ <label>Add Commission</label>
160
+ <comment><![CDATA[Add estimated Boku and Carrier commissions to the basket value for all Boku payments.]]></comment>
161
+ <frontend_type>select</frontend_type>
162
+ <source_model>boku/system_config::getCommissionOptions</source_model>
163
+ <sort_order>105</sort_order>
164
+ <show_in_default>1</show_in_default>
165
+ <show_in_website>1</show_in_website>
166
+ </commission_add>
167
+ <below_min translate="label" module="boku">
168
+ <label>Value Below Minimum</label>
169
+ <comment><![CDATA[What if the value is below the minimum possible ?]]></comment>
170
+ <frontend_type>select</frontend_type>
171
+ <source_model>boku/system_config::getPaymentBelowMinOptions</source_model>
172
+ <sort_order>110</sort_order>
173
+ <show_in_default>1</show_in_default>
174
+ <show_in_website>1</show_in_website>
175
+ </below_min>
176
+ <above_max translate="label" module="boku">
177
+ <label>Value Above Maximum</label>
178
+ <comment><![CDATA[What if the value is above the maximum possible ?]]></comment>
179
+ <frontend_type>select</frontend_type>
180
+ <source_model>boku/system_config::getPaymentAboveMaxOptions</source_model>
181
+ <sort_order>115</sort_order>
182
+ <show_in_default>1</show_in_default>
183
+ <show_in_website>1</show_in_website>
184
+ </above_max>
185
+ <unavailable translate="label" module="boku">
186
+ <label>Value Unavailable</label>
187
+ <comment><![CDATA[What if the value is in the allowed range but the price point is unavailable ? <i>(because of Carrier or your Boku admin settings)</i>]]></comment>
188
+ <frontend_type>select</frontend_type>
189
+ <source_model>boku/system_config::getPaymentUnavailableOptions</source_model>
190
+ <sort_order>120</sort_order>
191
+ <show_in_default>1</show_in_default>
192
+ <show_in_website>1</show_in_website>
193
+ </unavailable>
194
+ -->
195
+ <heading_advanced translate="label">
196
+ <label><![CDATA[Advanced Settings<br>You should <b>NOT</b> change these settings unless advised to by Boku.]]></label>
197
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
198
+ <sort_order>200</sort_order>
199
+ <show_in_default>1</show_in_default>
200
+ <show_in_website>1</show_in_website>
201
+ </heading_advanced>
202
+ <url_callback translate="label" module="boku">
203
+ <label>Callback URL</label>
204
+ <comment><![CDATA[This is the url used by the Boku server to notify your server of transaction events.<br>Leave blank to use the default (e.g. <i>yourdomain</i>/boku/api), only specify if you require an unusual connection.<br>NOTE: The default value includes the store scope so you may need to be careful with a complex configuration.]]></comment>
205
+ <frontend_type>text</frontend_type>
206
+ <config_path>payment/boku/url/callback</config_path>
207
+ <sort_order>202</sort_order>
208
+ <show_in_default>1</show_in_default>
209
+ <show_in_website>1</show_in_website>
210
+ </url_callback>
211
+ <callback_ips translate="label" module="boku">
212
+ <label>Callback IPs</label>
213
+ <comment><![CDATA[For additional security you can specify a semi-colon delimited list of IP addresses allowed for callbacks. If blank then there are no restrictions.]]></comment>
214
+ <frontend_type>text</frontend_type>
215
+ <sort_order>203</sort_order>
216
+ <show_in_default>1</show_in_default>
217
+ <show_in_website>1</show_in_website>
218
+ </callback_ips>
219
+ <url_pricelist translate="label" module="boku">
220
+ <label>Pricelist URL</label>
221
+ <frontend_type>text</frontend_type>
222
+ <config_path>payment/boku/url/pricelist</config_path>
223
+ <sort_order>205</sort_order>
224
+ <show_in_default>1</show_in_default>
225
+ <show_in_website>1</show_in_website>
226
+ </url_pricelist>
227
+ <url_priceinfo translate="label" module="boku">
228
+ <label>Priceinfo URL</label>
229
+ <frontend_type>text</frontend_type>
230
+ <config_path>payment/boku/url/priceinfo</config_path>
231
+ <sort_order>206</sort_order>
232
+ <show_in_default>1</show_in_default>
233
+ <show_in_website>1</show_in_website>
234
+ </url_priceinfo>
235
+ <url_prepare translate="label" module="boku">
236
+ <label>Prepare URL</label>
237
+ <frontend_type>text</frontend_type>
238
+ <config_path>payment/boku/url/prepare</config_path>
239
+ <sort_order>210</sort_order>
240
+ <show_in_default>1</show_in_default>
241
+ <show_in_website>1</show_in_website>
242
+ </url_prepare>
243
+ </fields>
244
+ </boku>
245
+ </groups>
246
+ </payment>
247
+ </sections>
248
+ </config>
app/code/community/Boku/Paymentgateway/sql/boku_setup/install-0.0.1.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku table creation
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ $installer = $this;
11
+ $installer->startSetup();
12
+
13
+ /**
14
+ * Create table 'boku/transactions'
15
+ */
16
+ $transactions_table = 'boku/payment_transaction';
17
+ $table_name = $transactions_table;
18
+ $table = $installer->getConnection()
19
+ ->newTable($installer->getTable($table_name))
20
+ ->addColumn('trx_id', Varien_Db_Ddl_Table::TYPE_CHAR, 50, array(
21
+ 'nullable' =>false,
22
+ 'primary' =>true,
23
+ ))
24
+ ->addColumn('store_id', Varien_Db_Ddl_Table::TYPE_SMALLINT, null, array('unsigned'=>true,))
25
+ ->addColumn('quote_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('unsigned'=>true,))
26
+ ->addColumn('order_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('unsigned'=>true,))
27
+ ->addColumn('test', Varien_Db_Ddl_Table::TYPE_BOOLEAN, null, array('nullable'=>false,))
28
+ ->addColumn('country', Varien_Db_Ddl_Table::TYPE_CHAR, 2, array('nullable'=>false,))
29
+ ->addColumn('currency', Varien_Db_Ddl_Table::TYPE_CHAR, 3, array('nullable'=>false,))
30
+ ->addColumn('amount', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('nullable'=>false,))
31
+ ->addColumn('timestamp', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array('nullable'=>false,))
32
+ ->addColumn('reference_currency', Varien_Db_Ddl_Table::TYPE_CHAR, 3)
33
+ ->addColumn('exchange', Varien_Db_Ddl_Table::TYPE_FLOAT)
34
+
35
+ ->addColumn('paid', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
36
+ ->addColumn('paid_inc_salestax', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
37
+ ->addColumn('paid_ex_salestax', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
38
+ ->addColumn('receivable_gross', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
39
+ ->addColumn('receivable_net', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
40
+ ->addColumn('encoded_mobile', Varien_Db_Ddl_Table::TYPE_TEXT, 20)
41
+ ->addColumn('network', Varien_Db_Ddl_Table::TYPE_TEXT, 10)
42
+ ->addColumn('operator_tax_treatment', Varien_Db_Ddl_Table::TYPE_TEXT, 20)
43
+ ->addColumn('optin_enrolled', Varien_Db_Ddl_Table::TYPE_BOOLEAN)
44
+ ->addColumn('optin_used', Varien_Db_Ddl_Table::TYPE_BOOLEAN)
45
+
46
+ ->addColumn('result_code', Varien_Db_Ddl_Table::TYPE_SMALLINT, null)
47
+ ->addColumn('result_msg', Varien_Db_Ddl_Table::TYPE_TEXT, 255)
48
+ ->addColumn('result_timestamp', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null)
49
+ ->addColumn('handled', Varien_Db_Ddl_Table::TYPE_BOOLEAN, null, array('default'=>0, 'nullable'=>false,))
50
+ ->addColumn('cancelled', Varien_Db_Ddl_Table::TYPE_BOOLEAN, null, array('default'=>0, 'nullable'=>false,))
51
+
52
+ ->addIndex($installer->getIdxName(
53
+ $table_name, array('trx_id'), Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE),
54
+ array('trx_id'), array('type'=>Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE))
55
+ ->addIndex($installer->getIdxName($table_name, array('quote_id')), array('quote_id'))
56
+ ->addIndex($installer->getIdxName($table_name, array('order_id')), array('order_id'))
57
+ ->setComment('Boku Transactions');
58
+ $installer->getConnection()->createTable($table);
59
+
60
+ /**
61
+ * Create table 'boku/callbacks'
62
+ */
63
+ $table_name = 'boku/payment_callback';
64
+ $table = $installer->getConnection()
65
+ ->newTable($installer->getTable($table_name))
66
+ ->addColumn('id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
67
+ 'identity' =>true,
68
+ 'unsigned' =>true,
69
+ 'nullable' =>false,
70
+ 'primary' =>true,
71
+ ))
72
+ ->addColumn('trx_id', Varien_Db_Ddl_Table::TYPE_CHAR, 50)
73
+ ->addColumn('action', Varien_Db_Ddl_Table::TYPE_TEXT, 16, array('nullable'=>false,))
74
+ ->addColumn('status_code', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('nullable'=>false,))
75
+ ->addColumn('status', Varien_Db_Ddl_Table::TYPE_TEXT, 255)
76
+ ->addColumn('timestamp', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array('nullable'=>false,))
77
+ ->addColumn('notes', Varien_Db_Ddl_Table::TYPE_TEXT)
78
+
79
+ ->addIndex($installer->getIdxName($table_name, array('trx_id')), array('trx_id'))
80
+ ->addIndex($installer->getIdxName($table_name, array('action')), array('action'))
81
+ ->addForeignKey(
82
+ $installer->getFkName($table_name, 'trx_id', $transactions_table,'trx_id'),
83
+ 'trx_id', $installer->getTable($transactions_table), 'trx_id',
84
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
85
+ ->setComment('Boku Callbacks');
86
+ $installer->getConnection()->createTable($table);
87
+
88
+ /**
89
+ * Create table 'boku/events'
90
+ */
91
+ $table_name = 'boku/payment_event';
92
+ $table = $installer->getConnection()
93
+ ->newTable($installer->getTable($table_name))
94
+ ->addColumn('id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
95
+ 'identity' =>true,
96
+ 'unsigned' =>true,
97
+ 'nullable' =>false,
98
+ 'primary' =>true,
99
+ ))
100
+ ->addColumn('trx_id', Varien_Db_Ddl_Table::TYPE_CHAR, 50, array('nullable'=>false,))
101
+ ->addColumn('currency', Varien_Db_Ddl_Table::TYPE_CHAR, 3, array('nullable'=>false,))
102
+ ->addColumn('paid', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
103
+ ->addColumn('receivable_gross', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
104
+ ->addColumn('message_cost', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
105
+
106
+ ->addColumn('event_code', Varien_Db_Ddl_Table::TYPE_SMALLINT, null, array('default'=>0, 'nullable'=>false,))
107
+ ->addColumn('msg', Varien_Db_Ddl_Table::TYPE_TEXT, 255)
108
+ ->addColumn('handled', Varien_Db_Ddl_Table::TYPE_BOOLEAN, null, array('default'=>0, 'nullable'=>false,))
109
+
110
+ ->addIndex($installer->getIdxName($table_name, array('trx_id')), array('trx_id'))
111
+ ->addForeignKey(
112
+ $installer->getFkName($table_name, 'trx_id', $transactions_table,'trx_id'),
113
+ 'trx_id', $installer->getTable($transactions_table), 'trx_id',
114
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
115
+ ->setComment('Boku Events');
116
+ $installer->getConnection()->createTable($table);
117
+
118
+ /**
119
+ * Create table 'boku/chargebacks'
120
+ */
121
+ $table_name = 'boku/payment_chargeback';
122
+ $table = $installer->getConnection()
123
+ ->newTable($installer->getTable($table_name))
124
+ ->addColumn('id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
125
+ 'identity' =>true,
126
+ 'unsigned' =>true,
127
+ 'nullable' =>false,
128
+ 'primary' =>true,
129
+ ))
130
+ ->addColumn('trx_id', Varien_Db_Ddl_Table::TYPE_CHAR, 50, array('nullable'=>false,))
131
+ ->addColumn('amount', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
132
+ ->addColumn('chargebackamount', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
133
+ ->addColumn('reason_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('default'=>0, 'nullable'=>false,))
134
+ ->addColumn('reason', Varien_Db_Ddl_Table::TYPE_TEXT)
135
+ ->addColumn('refundsource', Varien_Db_Ddl_Table::TYPE_TEXT, 16)
136
+ ->addColumn('handled', Varien_Db_Ddl_Table::TYPE_BOOLEAN, null, array('default'=>0, 'nullable'=>false,))
137
+
138
+ ->addIndex($installer->getIdxName($table_name, array('trx_id')), array('trx_id'))
139
+ ->addForeignKey(
140
+ $installer->getFkName($table_name, 'trx_id', $transactions_table,'trx_id'),
141
+ 'trx_id', $installer->getTable($transactions_table), 'trx_id',
142
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
143
+ ->setComment('Boku Chargebacks');
144
+ $installer->getConnection()->createTable($table);
145
+
146
+ $installer->endSetup();
app/code/community/Boku/Paymentgateway/sql/boku_setup/upgrade-0.0.1-0.0.2.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boku table updates
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ $installer = $this;
11
+ $installer->startSetup();
12
+ $con = $installer->getConnection();
13
+
14
+ $table_name = 'boku/payment_callback';
15
+ $table = $installer->getTable($table_name);
16
+ $con->modifyColumn($table, 'trx_id', array(
17
+ 'type'=>Varien_Db_Ddl_Table::TYPE_TEXT, 'length'=>50,));
18
+
19
+ $table_name = 'boku/payment_event';
20
+ $table = $installer->getTable($table_name);
21
+ $con->addColumn($table, 'paid_inc_salestax', array(
22
+ 'type'=>Varien_Db_Ddl_Table::TYPE_INTEGER, 'default'=>0, 'nullable'=>false, 'comment'=>'paid_inc_salestax'));
23
+ $con->addColumn($table, 'paid_ex_salestax', array(
24
+ 'type'=>Varien_Db_Ddl_Table::TYPE_INTEGER, 'default'=>0, 'nullable'=>false, 'comment'=>'paid_ex_salestax'));
25
+ $con->addColumn($table, 'receivable_net', array(
26
+ 'type'=>Varien_Db_Ddl_Table::TYPE_INTEGER, 'default'=>0, 'nullable'=>false, 'comment'=>'receivable_net'));
27
+ $con->addColumn($table, 'reference_currency', array(
28
+ 'type'=>Varien_Db_Ddl_Table::TYPE_TEXT, 'length'=>3, 'comment'=>'reference_currency'));
29
+ $con->addColumn($table, 'exchange', array(
30
+ 'type'=>Varien_Db_Ddl_Table::TYPE_FLOAT, 'comment'=>'exchange'));
31
+
32
+ $table_name = 'boku/payment_transaction';
33
+ $table = $installer->getTable($table_name);
34
+ $con->addColumn($table, 'exchange', array(
35
+ 'type'=>Varien_Db_Ddl_Table::TYPE_FLOAT, 'comment'=>'exchange'));
36
+ $con->addForeignKey(
37
+ $installer->getFkName($table_name, 'quote_id', 'sales/quote','entity_id'),
38
+ $table, 'quote_id', $installer->getTable('sales/quote'), 'entity_id',
39
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE);
40
+ $con->addForeignKey(
41
+ $installer->getFkName($table_name, 'order_id', 'sales/order','entity_id'),
42
+ $table, 'order_id', $installer->getTable('sales/order'), 'entity_id',
43
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE);
44
+
45
+ $installer->endSetup();
app/design/adminhtml/default/default/layout/boku_paymentgateway.xml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Magento backend layout
5
+ *
6
+ * @category Payment gateway
7
+ * @package boku_paymentgateway
8
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
9
+ * @author MDH <mdh@treatid.me.uk>
10
+ */
11
+ -->
12
+ <layout>
13
+ <boku_admin_adminhtml_boku_test>
14
+ <reference name="left">
15
+ <block type="adminhtml/system_config_switcher" name="adminhtml.system.config.switcher" before="-"/>
16
+ </reference>
17
+ <reference name="content">
18
+ <block type="adminhtml/template" name="boku_test" template="boku/paymentgateway/test.phtml"/>
19
+ </reference>
20
+ </boku_admin_adminhtml_boku_test>
21
+ <boku_admin_adminhtml_standard_prepare>
22
+ <reference name="content">
23
+ <block type="boku/adminhtml_payment_standard_prepare" name="adminhtml.boku.prepare" template="boku/paymentgateway/standard/prepare.phtml" />
24
+ </reference>
25
+ </boku_admin_adminhtml_standard_prepare>
26
+
27
+ </layout>
app/design/adminhtml/default/default/template/boku/paymentgateway/test.phtml ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin form for testing
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+
11
+ $helper = $this->helper('boku');
12
+
13
+ $helper->getSession()->unsScopeObject();
14
+ $store = $helper->getStore();
15
+
16
+ $country = $store->getConfig('general/country/default');
17
+ $currency = $store->getConfig('currency/options/default');
18
+ $merchant_id = $helper->getConfig('merchant_id');
19
+ $service_id = $helper->getConfig('service_id');
20
+ $api_key = $helper->getConfig('api_security_key');
21
+
22
+ $settings_incomplete = empty($merchant_id) || empty($api_key) || empty($service_id);
23
+ $test = Mage::getSingleton('boku/test')->testConnection();
24
+ if (!$settings_incomplete){
25
+ $price_info = Mage::getSingleton('boku/prices')->fetchPrices($currency, $country, 99);
26
+ $prices = Mage::getSingleton('boku/prices')->fetchPrices($currency, $country);
27
+ }
28
+ ?>
29
+ <div class="content-header">
30
+ <h3 class="icon-head"><?php echo $helper->__('Boku Settings') ?></h3>
31
+ </div>
32
+ <div class="entry-edit">
33
+ <b>Merchant ID: </b><?php echo $merchant_id; ?><br>
34
+ <b>Service ID: </b><?php echo $service_id; ?><br>
35
+ <b>API Security Key: </b><?php echo ($api_key ? 'Yes ('.strlen($api_key).' chars)' : 'No'); ?><br>
36
+ <b>Country: </b><?php echo $country; ?><br>
37
+ <b>Currency: </b><?php echo $currency; ?><br>
38
+ <b>Base Currency: </b><?php echo $helper->getBaseCurrencyCode(); ?><br>
39
+ <b>Boku pricelist URL: </b><?php echo $helper->getConfig('url/pricelist'); ?><br>
40
+ <b>Callback URL: </b><?php echo $helper->getCallbackUrl(); ?><br>
41
+ </div>
42
+
43
+ <div class="messages">
44
+ <div class="content-header">
45
+ <h3 class="icon-head"><?php echo $helper->__('Boku Connection Test') ?></h3>
46
+ </div>
47
+ <?php echo implode('<br>', $test); ?>
48
+
49
+ <?php if (!$settings_incomplete): ?>
50
+ <div class="content-header">
51
+ <h3 class="icon-head"><?php echo $helper->__('Boku Price Point Data') ?></h3>
52
+ </div>
53
+ <?php
54
+ if (empty($prices)){
55
+ echo $helper->__('No price data found...check your settings').'<br>';
56
+ echo $helper->__('Has your Boku service been approved by Boku ?');
57
+ }else{
58
+ echo '<pre>'.json_encode($price_info, JSON_PRETTY_PRINT).'</pre>';
59
+ echo '<pre>'.json_encode($prices, JSON_PRETTY_PRINT).'</pre>';
60
+ }
61
+ ?>
62
+ <?php endif; ?>
63
+ </div>
app/design/frontend/base/default/layout/boku_paymentgateway.xml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Boku payment layout
5
+ *
6
+ * @category Payment gateway
7
+ * @package boku_paymentgateway
8
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
9
+ * @author MDH <mdh@treatid.me.uk>
10
+ */
11
+ -->
12
+ <layout version="0.1.0">
13
+ <boku_standard_prepare>
14
+ <reference name="content">
15
+ <block type="boku/payment_standard_prepare" name="boku.prepare" template="boku/paymentgateway/standard/prepare.phtml" />
16
+ </reference>
17
+ </boku_standard_prepare>
18
+
19
+ <boku_standard_cancel>
20
+ <reference name="root">
21
+ <action method="setTemplate"><template>page/empty.phtml</template></action>
22
+ </reference>
23
+ <reference name="content">
24
+ <block type="boku/payment_standard_cancel" name="boku.cancel" template="boku/paymentgateway/standard/cancel.phtml" />
25
+ </reference>
26
+ </boku_standard_cancel>
27
+
28
+ <boku_standard_success>
29
+ <reference name="root">
30
+ <action method="setTemplate"><template>page/empty.phtml</template></action>
31
+ </reference>
32
+ <reference name="content">
33
+ <block type="boku/payment_standard_success" name="boku.success" template="boku/paymentgateway/standard/success.phtml" />
34
+ </reference>
35
+ </boku_standard_success>
36
+
37
+ </layout>
app/design/frontend/base/default/template/boku/paymentgateway/standard/cancel.phtml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * After failed Boku buy-url submission (via Prepare block)
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ ?>
11
+ <script type="text/javascript">
12
+ parent.location = "<?php echo $this->getRedirectUrl(); ?>";
13
+ </script>
app/design/frontend/base/default/template/boku/paymentgateway/standard/form.phtml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Payment Form
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ $helper = Mage::helper('boku');
11
+ $networks = $helper->getSession()->getNetworks();
12
+ if (is_array($networks)): ?>
13
+ <p><?php echo $helper->__('Available on these networks').': '.implode(', ', $networks); ?></p>
14
+ <?php endif; ?>
app/design/frontend/base/default/template/boku/paymentgateway/standard/prepare.phtml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Payment Gateway
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ ?>
11
+ <iframe id="boku_prepare" style="width:600px;height:450px;margin:0;padding:0;border:none"
12
+ src="<?php echo $this->getBuyUrl(); ?>" />
app/design/frontend/base/default/template/boku/paymentgateway/standard/success.phtml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * After successful Boku buy-url submission (via Prepare block)
4
+ *
5
+ * @category Payment gateway
6
+ * @package boku_paymentgateway
7
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
8
+ * @author MDH <mdh@treatid.me.uk>
9
+ */
10
+ ?>
11
+ <script type="text/javascript">
12
+ parent.location = "<?php echo $this->getRedirectUrl(); ?>";
13
+ </script>
app/etc/modules/Boku_Paymentgateway.xml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Magento module for the Boku Payment Gateway
5
+ *
6
+ * @category Payment gateway
7
+ * @package boku_paymentgateway
8
+ * @copyright Copyright (c) Boku (http://www.boku.com/)
9
+ * @author MDH <mdh@treatid.me.uk>
10
+ */
11
+ -->
12
+ <config>
13
+ <modules>
14
+ <Boku_Paymentgateway>
15
+ <active>true</active>
16
+ <codePool>community</codePool>
17
+ <depends>
18
+ <Mage_Checkout/>
19
+ </depends>
20
+ </Boku_Paymentgateway>
21
+ </modules>
22
+ </config>
app/locale/en_US/Boku_Paymentgateway.csv ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Boku (Pay by Mobile)","Boku (Pay by Mobile)"
2
+ "Sign up for a Boku account","Sign up for an Boku account"
3
+ "Merchant ID","Merchant ID"
4
+ "This is your Boku Merchant ID.","This is your Boku Merchant ID."
5
+ "API Security Key","API Security Key"
6
+ "This is your Boku API security key<br> (stored as encrypted data).","This is your Boku API security key<br> (stored as encrypted data)."
7
+ "Service ID","Service ID"
8
+ "This is your Boku service id. (Not the service name)","This is your Boku service id. (Not the service name)"
9
+ "Sub Merchant Name","Sub Merchant Name"
10
+ "This appears as an identifier on the Boku payment panel.<br>If left blank then the panel uses the service name specified in your Boku account.","This appears as an identifier on the Boku payment panel.<br>If left blank then the panel uses the service name specified in your Boku account."
11
+ "Mode","Mode"
12
+ "Generate Invoices","Generate Invoices"
13
+ "If the payment process completes and the total paid matches the order total then should we generate an invoice ? This also sends the generated invoice to the customer (if possible).","If the payment process completes and the total paid matches the order total then should we generate an invoice ? This also sends the generated invoice to the customer (if possible)."
14
+ "Success Message","Success Message"
15
+ "Optional additional message for the payment success page.","Optional additional message for the payment success page."
16
+ "Enabled","Enabled"
17
+ "Standard Payment Settings","Standard Payment Settings"
18
+ "Title","Title"
19
+ "Payment from Applicable Countries","Payment from Applicable Countries"
20
+ "Payment from Specific Countries","Payment from Specific Countries"
21
+ "Minimum Order Total","Minimum Order Total"
22
+ "Maximum Order Total","Maximum Order Total"
23
+ "Minimum and Maximum totals will also be restricted by your Boku account settings.","Minimum and Maximum totals will also be restricted by your Boku account settings."
24
+ "Sort Order","Sort Order"
25
+ "Advanced Settings<br>You should <b>NOT</b> change these settings unless advised to by Boku.","Advanced Settings<br>You should <b>NOT</b> change these settings unless advised to by Boku."
26
+ "Callback URL","Callback URL"
27
+ "This is the url used by the Boku server to notify your server of transaction events.<br>Leave blank to use the default (e.g. <i>yourdomain</i>/boku/api), only specify if you require an unusual connection.<br>NOTE: The default value includes the store scope so you may need to be careful with a complex configuration.","This is the url used by the Boku server to notify your server of transaction events.<br>Leave blank to use the default (e.g. <i>yourdomain</i>/boku/api), only specify if you require an unusual connection.<br>NOTE: The default value includes the store scope so you may need to be careful with a complex configuration."
28
+ "Callback IPs","Callback IPs"
29
+ "For additional security you can specify a semi-colon delimited list of IP addresses allowed for callbacks. If blank then there are no restrictions.","For additional security you can specify a semi-colon delimited list of IP addresses allowed for callbacks. If blank then there are no restrictions."
30
+ "Pricelist URL","Pricelist URL"
31
+ "Prepare URL","Prepare URL"
32
+ "Test","Test"
33
+ "Live","Live"
34
+ "Failed to initiate the Boku payment transaction.","Failed to initiate the Boku payment transaction."
35
+ "item","item"
36
+ "items","items"
37
+ "Boku Settings","Boku Settings"
38
+ "Boku pricelist Data","Boku pricelist Data"
39
+ "No price data found...check your settings","No price data found...check your settings"
40
+ "Has your Boku service been approved by Boku ?","Has your Boku service been approved by Boku ?"
41
+ "Available on these networks","Available on these networks"
package.xml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Boku_Paymentgateway</name>
4
+ <version>0.0.4</version>
5
+ <stability>stable</stability>
6
+ <license uri="https://opensource.org/licenses/osl-3.0.php">Open Software License (OSL)</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>This is a Payment Gateway integration for Boku (Pay by mobile).</summary>
10
+ <description>Provides the Boku (Pay by mobile) payment method as an alternative to Credit Card payments.&#xD;
11
+ It is intended for relatively small financial transactions and it's suitability can be discussed with a Boku representative.&#xD;
12
+ http://www.boku.com/.</description>
13
+ <notes>Testing has been done on Magento versions 1.7-1.9.&#xD;
14
+ There is no overloading of any classes so there should be no impact on existing functionality.</notes>
15
+ <authors><author><name>Mark Davidson-Houston</name><user>treatid</user><email>mdh@treatid.me.uk</email></author></authors>
16
+ <date>2016-03-19</date>
17
+ <time>22:08:28</time>
18
+ <contents><target name="magecommunity"><dir name="Boku"><dir name="Paymentgateway"><dir name="Block"><dir name="Payment"><dir name="Standard"><file name="Cancel.php" hash="3239e1ed8b9b05bd56e2528e2fbd2853"/><file name="Form.php" hash="034ecded7255dc97cc9c15e730eb4a3c"/><file name="Prepare.php" hash="edd001f7cd541b9477044da5d52b1fc3"/><file name="Result.php" hash="816443798640ca5fb325c3d0b579bc1f"/><file name="Success.php" hash="090f9fca4e8975b881cb807a67ef0c82"/></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="43b6873dad13c1d85cecd283da6c8dab"/></dir><dir name="Model"><file name="AdminSession.php" hash="ee941bc7aaa20d10bc583c3bfaf8d5b7"/><file name="Cron.php" hash="19139a3e09c1c56c8b714f602fb87d2e"/><dir name="Mapped"><file name="Abstract.php" hash="cb9da96cd61e0cbd5a49a0177384ae59"/></dir><dir name="Payment"><file name="Callback.php" hash="fda8ca4f8c2cb5a9f71d3dfeac30b0cd"/><file name="Chargeback.php" hash="bd9750a8a89a40212c35b75cac47e030"/><file name="Event.php" hash="622313f71fcef556b0065152caed6c40"/><file name="Standard.php" hash="6f3f3cbf2f812ec8348467ca659296e1"/><file name="Transaction.php" hash="aed115b0380de5180887b8d699342d35"/></dir><file name="Prices.php" hash="a84f428964d6fc981d65b33cde022ecc"/><dir name="Resource"><dir name="Payment"><dir name="Callback"><file name="Collection.php" hash="9416f5d34cb0946fbba370562ae59727"/></dir><file name="Callback.php" hash="d78c969ce9e18d08e0edc059576bf2d1"/><dir name="Chargeback"><file name="Collection.php" hash="b9c0d89186e3b28cf528e2a9faff5b08"/></dir><file name="Chargeback.php" hash="27424fac6667df3f7989ebe5183b8337"/><dir name="Event"><file name="Collection.php" hash="de2af9ceb3bb5cb9079549d524dedd69"/></dir><file name="Event.php" hash="b876fb5589368a426edccfeac316706a"/><dir name="Transaction"><file name="Collection.php" hash="ff7f877badceb80d455a6a82a551207b"/></dir><file name="Transaction.php" hash="568d8a2197f90dea5d9e11621b8a210b"/></dir><file name="Setup.php" hash="49630e76b6e64a34856fd2aa5c650bd5"/></dir><file name="Session.php" hash="f4283f7b08b5feededd8a4cfca893ba8"/><dir name="System"><file name="Config.php" hash="abb420605c5814438b78d291aa41a892"/></dir><file name="Test.php" hash="b477546758d16fe32ad92ec8b72b01ae"/><file name="Xml.php" hash="e156f0e765838481d72d1bd93fbc8ce6"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="BokuController.php" hash="11ae870c022a209f674e545b273ec659"/></dir><file name="ApiController.php" hash="85bc4ef426623fdd520976346419039a"/><file name="StandardController.php" hash="ee14a29f973af13ce0733489329f57e2"/></dir><dir name="etc"><file name="adminhtml.xml" hash="22b6f09956654014d4648cc02e73b256"/><file name="config.xml" hash="7b3b40f87dab7fcb35064eb73cd5ce8b"/><file name="system.xml" hash="0d1420ad968616cd3a0e2bcf81318e7d"/></dir><dir name="sql"><dir name="boku_setup"><file name="install-0.0.1.php" hash="bcaeb258769e0271b07fc483dcbcdb28"/><file name="upgrade-0.0.1-0.0.2.php" hash="e76438e5770e270cf69e4ba9c7c0c05e"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Boku_Paymentgateway.xml" hash="ddef7a495e147af1b1333065e9d528d4"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="boku_paymentgateway.xml" hash="9e042d0306a5f69c1ae014e16d2498a9"/></dir><dir name="template"><dir name="boku"><dir name="paymentgateway"><file name="test.phtml" hash="c5fa68eefcac6c160e93ab8f496fe78d"/></dir></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="boku_paymentgateway.xml" hash="21f632989147b0bcf2a17ed1a13575da"/></dir><dir name="template"><dir name="boku"><dir name="paymentgateway"><dir name="standard"><file name="cancel.phtml" hash="90dc0bbfa12e977150c77994440536dd"/><file name="form.phtml" hash="565c378840a33582e7be59abb611fcc2"/><file name="prepare.phtml" hash="9f936df5479b0c2ea103db50e4986df4"/><file name="success.phtml" hash="826effa3f1f5b1cdca8eb59efb459b42"/></dir></dir></dir></dir></dir></dir></dir></target><target name="magelocale"><dir><dir name="en_US"><file name="Boku_Paymentgateway.csv" hash="c67b2f4f77ce9d707c3530883b96eaf0"/></dir></dir></target></contents>
19
+ <compatible/>
20
+ <dependencies><required><php><min>5.4.0</min><max>7.0.3</max></php></required></dependencies>
21
+ </package>