vindi-subscriptions-and-recurring-payments - Version 1.0.7

Version Notes

version 1.0.7

Download this release

Release Info

Developer Tales Galvao
Extension vindi-subscriptions-and-recurring-payments
Version 1.0.7
Comparing to
See all releases


Version 1.0.7

Files changed (32) hide show
  1. app/code/community/Vindi/Subscription/Block/Config/Information.php +50 -0
  2. app/code/community/Vindi/Subscription/Block/Form/BankSlip.php +14 -0
  3. app/code/community/Vindi/Subscription/Block/Form/Cc.php +125 -0
  4. app/code/community/Vindi/Subscription/Helper/Api.php +694 -0
  5. app/code/community/Vindi/Subscription/Helper/Data.php +36 -0
  6. app/code/community/Vindi/Subscription/Helper/WebhookHandler.php +471 -0
  7. app/code/community/Vindi/Subscription/Model/BankSlip.php +139 -0
  8. app/code/community/Vindi/Subscription/Model/Config/Installments.php +18 -0
  9. app/code/community/Vindi/Subscription/Model/Config/Shippingmethod.php +65 -0
  10. app/code/community/Vindi/Subscription/Model/CreditCard.php +272 -0
  11. app/code/community/Vindi/Subscription/Model/Observer.php +168 -0
  12. app/code/community/Vindi/Subscription/Model/Product/Attribute/Plan.php +30 -0
  13. app/code/community/Vindi/Subscription/Model/Product/Type.php +6 -0
  14. app/code/community/Vindi/Subscription/Trait/PaymentMethod.php +306 -0
  15. app/code/community/Vindi/Subscription/controllers/WebhookController.php +41 -0
  16. app/code/community/Vindi/Subscription/etc/adminhtml.xml +22 -0
  17. app/code/community/Vindi/Subscription/etc/config.xml +141 -0
  18. app/code/community/Vindi/Subscription/etc/system.xml +178 -0
  19. app/code/community/Vindi/Subscription/sql/vindi_subscription_setup/install-1.0.0.php +118 -0
  20. app/code/community/Vindi/Subscription/sql/vindi_subscription_setup/upgrade-1.0.0-1.0.1.php +53 -0
  21. app/code/community/Vindi/Subscription/sql/vindi_subscription_setup/upgrade-1.0.1-1.0.2.php +29 -0
  22. app/code/community/Vindi/Subscription/sql/vindi_subscription_setup/upgrade-1.0.4-1.0.5.php +68 -0
  23. app/design/adminhtml/base/default/layout/vindi_subscription.xml +15 -0
  24. app/design/adminhtml/base/default/template/vindi_subscription/payment/form/bankslip.phtml +6 -0
  25. app/design/adminhtml/base/default/template/vindi_subscription/payment/form/cc.phtml +166 -0
  26. app/design/frontend/base/default/layout/vindi_subscription.xml +78 -0
  27. app/design/frontend/base/default/template/vindi_subscription/payment/form/bankslip.phtml +6 -0
  28. app/design/frontend/base/default/template/vindi_subscription/payment/form/cc.phtml +163 -0
  29. app/etc/modules/Vindi_Subscription.xml +14 -0
  30. package.xml +18 -0
  31. skin/frontend/base/default/vindi_subscription/css/vindi_subscription.css +56 -0
  32. skin/frontend/base/default/vindi_subscription/js/vindi_subscription.js +99 -0
app/code/community/Vindi/Subscription/Block/Config/Information.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Block_Config_Information extends Mage_Core_Block_Template implements Varien_Data_Form_Element_Renderer_Interface
4
+ {
5
+ /**
6
+ * @param \Varien_Data_Form_Element_Abstract $element
7
+ *
8
+ * @return string
9
+ */
10
+ public function render(Varien_Data_Form_Element_Abstract $element)
11
+ {
12
+ $helper = Mage::helper('vindi_subscription');
13
+
14
+ if(! $helper->getKey()) {
15
+ return '';
16
+ }
17
+
18
+ $html = '<tr><td colspan="4" class="label"><h3>Informações sobre a conta Vindi</h3></td></tr>';
19
+
20
+ /** @var Vindi_Subscription_Helper_API $api */
21
+ $api = Mage::helper('vindi_subscription/api');
22
+
23
+ $merchant = $api->getMerchant();
24
+
25
+ $html .= '<tr><td class="label">Conexão</td>';
26
+
27
+ if (! $merchant) {
28
+ $html .= '<td class=" value error-msg">Falha na Conexão!<br />Verifique sua conta e tente novamente!</td></tr>';
29
+ return $html;
30
+ }
31
+ $html .= '<td class="value success-msg">Conectado com Sucesso!</td></tr>';
32
+
33
+ $html .= '<tr><td class="label">Conta</td><td class="value">' . $merchant['name'] . '</td></tr>';
34
+
35
+ $status = $api->isMerchantStatusTrial() ? 'Trial' : 'Ativo';
36
+ $html .= '<tr><td class="label">Status</td><td class="value">'. $status .'</td></tr>';
37
+
38
+ $html .= '<tr><td colspan="4" class="label"><h3>Configuração dos Eventos da Vindi</h3></td></tr>';
39
+
40
+ $html .= '<tr>
41
+ <td class="label">URL dos Webhooks</td>
42
+ <td class="value">
43
+ <input type="text" value="' . $helper->getWebhookURL() .'" style="width:100%" readonly onclick="this.select();" />
44
+ <p class="note" style="width:100%"><span>Copie esse link e utilize-o para configurar os eventos nos Webhooks da Vindi.</span></p>
45
+ </td>
46
+ </tr>';
47
+
48
+ return $html;
49
+ }
50
+ }
app/code/community/Vindi/Subscription/Block/Form/BankSlip.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Block_Form_BankSlip extends Mage_Payment_Block_Form
4
+ {
5
+ /**
6
+ * Initialize block
7
+ */
8
+ public function _construct()
9
+ {
10
+ parent::_construct();
11
+ $this->setTemplate('vindi_subscription/payment/form/bankslip.phtml');
12
+ }
13
+
14
+ }
app/code/community/Vindi/Subscription/Block/Form/Cc.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Block_Form_Cc extends Mage_Payment_Block_Form_Cc
4
+ {
5
+ /**
6
+ * Initialize block
7
+ */
8
+ public function _construct()
9
+ {
10
+ parent::_construct();
11
+ $this->setTemplate('vindi_subscription/payment/form/cc.phtml');
12
+ }
13
+
14
+ /**
15
+ * @return bool
16
+ */
17
+ private function isAdmin()
18
+ {
19
+ return Mage::app()->getStore()->isAdmin();
20
+ }
21
+
22
+ /**
23
+ * @return \Mage_Customer_Model_Customer
24
+ */
25
+ private function getCustomer()
26
+ {
27
+ if ($this->isAdmin()) {
28
+ return Mage::getSingleton('adminhtml/session_quote')->getCustomer();
29
+ }
30
+
31
+ return Mage::getSingleton('customer/session')->getCustomer();
32
+ }
33
+
34
+ /**
35
+ * @return \Mage_Sales_Model_Quote
36
+ */
37
+ private function getQuote()
38
+ {
39
+ if ($this->isAdmin()) {
40
+ return Mage::getSingleton('adminhtml/session_quote')->getQuote();
41
+ }
42
+
43
+ return Mage::getSingleton('checkout/session')->getQuote();
44
+ }
45
+
46
+ /**
47
+ * Retrieve available credit card types
48
+ *
49
+ * @return array
50
+ */
51
+ public function getCcAvailableTypes()
52
+ {
53
+ return $this->api()->getCreditCardTypes();
54
+ }
55
+
56
+ /**
57
+ * @return bool
58
+ */
59
+ public function getSavedCc()
60
+ {
61
+ $customer = $this->getCustomer();
62
+
63
+ if (! $userCode = $customer->getVindiUserCode()) {
64
+ return false;
65
+ }
66
+
67
+ return $this->api()->getCustomerPaymentProfile($userCode);
68
+ }
69
+
70
+ /**
71
+ * @return bool|string
72
+ */
73
+ public function getInstallments()
74
+ {
75
+ $maxInstallmentsNumber = Mage::getStoreConfig('payment/vindi_creditcard/max_installments_number');
76
+ $minInstallmentsValue = Mage::getStoreConfig('payment/vindi_creditcard/min_installment_value');
77
+ // $quote = Mage::getSingleton('checkout/session')->getQuote();
78
+ $quote = $this->getQuote();
79
+
80
+ $installments = false;
81
+
82
+ if ($this->isSingleQuote($quote) && $maxInstallmentsNumber > 1) {
83
+
84
+ $total = $quote->getGrandTotal();
85
+
86
+ $installments = '<option value="">' . Mage::helper('catalog')->__('-- Please Select --') . '</option>';
87
+ for ($i = 1; $i <= $maxInstallmentsNumber; $i++) {
88
+ $value = ceil($total / $i * 100) / 100;
89
+
90
+ if ($value >= $minInstallmentsValue) {
91
+ $price = Mage::helper('core')->currency($value, true, false);
92
+ $installments .= '<option value="' . $i . '">' . sprintf('%dx de %s', $i, $price) . '</option>';
93
+ } else {
94
+ break;
95
+ }
96
+ }
97
+ }
98
+
99
+ return $installments;
100
+ }
101
+
102
+ /**
103
+ * @param Mage_Sales_Model_Quote $quote
104
+ *
105
+ * @return bool
106
+ */
107
+ protected function isSingleQuote($quote)
108
+ {
109
+ foreach ($quote->getAllVisibleItems() as $item) {
110
+ if (($product = $item->getProduct()) && ($product->getTypeId() === 'subscription')) {
111
+ return false;
112
+ }
113
+ }
114
+
115
+ return true;
116
+ }
117
+
118
+ /**
119
+ * @return Vindi_Subscription_Helper_API
120
+ */
121
+ private function api()
122
+ {
123
+ return Mage::helper('vindi_subscription/api');
124
+ }
125
+ }
app/code/community/Vindi/Subscription/Helper/Api.php ADDED
@@ -0,0 +1,694 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Helper_API extends Mage_Core_Helper_Abstract
4
+ {
5
+
6
+ /**
7
+ * @const string API base path.
8
+ */
9
+ const BASE_PATH = 'https://app.vindi.com.br/api/v1/';
10
+
11
+ /**
12
+ * @var string
13
+ */
14
+ public $lastError = '';
15
+
16
+ /**
17
+ * @var string
18
+ */
19
+ private $version;
20
+
21
+ /**
22
+ * @var string
23
+ */
24
+ private $key;
25
+
26
+ /**
27
+ * @var bool
28
+ */
29
+ private $acceptBankSlip;
30
+
31
+ public function __construct()
32
+ {
33
+ $this->version = (string) Mage::getConfig()->getModuleConfig('Vindi_Subscription')->version;
34
+ $this->key = Mage::helper('vindi_subscription')->getKey();
35
+ }
36
+
37
+ /**
38
+ * @param string $message
39
+ * @param int|null $level
40
+ */
41
+ private function log($message, $level = null)
42
+ {
43
+ Mage::log($message, $level, 'vindi_api.log');
44
+ }
45
+
46
+ /**
47
+ * @return \Zend_Cache_Core
48
+ */
49
+ private function cache()
50
+ {
51
+ return Mage::app()->getCache();
52
+ }
53
+
54
+ /**
55
+ * Build HTTP Query.
56
+ *
57
+ * @param array $data
58
+ *
59
+ * @return string
60
+ */
61
+ private function buildBody($data)
62
+ {
63
+ return json_encode($data);
64
+ }
65
+
66
+ /**
67
+ * @param array $error
68
+ * @param $endpoint
69
+ *
70
+ * @return string
71
+ */
72
+ private function getErrorMessage($error, $endpoint)
73
+ {
74
+ return "Erro em $endpoint: {$error['id']}: {$error['parameter']} - {$error['message']}";
75
+ }
76
+
77
+ /**
78
+ * @param array $response
79
+ * @param $endpoint
80
+ *
81
+ * @return bool
82
+ */
83
+ private function checkResponse($response, $endpoint)
84
+ {
85
+ if (isset($response['errors']) && ! empty($response['errors'])) {
86
+ foreach ($response['errors'] as $error) {
87
+ $message = $this->getErrorMessage($error, $endpoint);
88
+
89
+ Mage::getSingleton('core/session')->addError($message);
90
+
91
+ $this->lastError = $message;
92
+ }
93
+
94
+ return false;
95
+ }
96
+
97
+ $this->lastError = '';
98
+
99
+ return true;
100
+ }
101
+
102
+ /**
103
+ * Perform request to API.
104
+ *
105
+ * @param string $endpoint
106
+ * @param string $method
107
+ * @param array $data
108
+ * @param null $dataToLog
109
+ *
110
+ * @return array|bool|mixed
111
+ */
112
+ private function request($endpoint, $method = 'POST', $data = [], $dataToLog = null)
113
+ {
114
+ if (! $this->key) {
115
+ return false;
116
+ }
117
+
118
+ $url = static::BASE_PATH . $endpoint;
119
+ $body = $this->buildBody($data);
120
+
121
+ $requestId = rand();
122
+
123
+ $dataToLog = null !== $dataToLog ? $this->buildBody($dataToLog) : $body;
124
+
125
+ $this->log(sprintf("[Request #%s]: Novo Request para a API.\n%s %s\n%s", $requestId, $method, $url,
126
+ $dataToLog));
127
+
128
+ $ch = curl_init();
129
+
130
+ curl_setopt_array($ch, [
131
+ CURLOPT_HTTPHEADER => [
132
+ 'Content-Type: application/json',
133
+ ],
134
+ CURLOPT_TIMEOUT => 60,
135
+ CURLOPT_SSL_VERIFYPEER => true,
136
+ CURLOPT_HEADER => true,
137
+ CURLOPT_RETURNTRANSFER => true,
138
+ CURLOPT_USERAGENT => 'Vindi-Magento/' . $this->version,
139
+ CURLOPT_SSLVERSION => 'CURL_SSLVERSION_TLSv1_2',
140
+ CURLOPT_USERPWD => $this->key . ':',
141
+ CURLOPT_URL => $url,
142
+ CURLOPT_CUSTOMREQUEST => $method,
143
+ CURLOPT_POSTFIELDS => $body,
144
+ ]);
145
+
146
+ $response = curl_exec($ch);
147
+
148
+ $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
149
+ $body = substr($response, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
150
+
151
+ if (curl_errno($ch) || $response === false) {
152
+ $this->log(sprintf("[Request #%s]: Erro ao fazer request!\n%s", $requestId, print_r($response, true)));
153
+
154
+ return false;
155
+ }
156
+
157
+ curl_close($ch);
158
+
159
+ $status = "HTTP Status: $statusCode";
160
+ $this->log(sprintf("[Request #%s]: Nova Resposta da API.\n%s\n%s", $requestId, $status, $body));
161
+
162
+ $responseBody = json_decode($body, true);
163
+
164
+ if (! $responseBody) {
165
+ $this->log(sprintf('[Request #%s]: Erro ao recuperar corpo do request! %s', $requestId,
166
+ print_r($body, true)));
167
+
168
+ return false;
169
+ }
170
+
171
+ if (! $this->checkResponse($responseBody, $endpoint)) {
172
+ return false;
173
+ }
174
+
175
+ return $responseBody;
176
+ }
177
+
178
+ /**
179
+ * Make an API request to create a Customer.
180
+ *
181
+ * @param array $body (name, email, code)
182
+ *
183
+ * @return array|bool|mixed
184
+ */
185
+ public function createCustomer($body)
186
+ {
187
+ if ($response = $this->request('customers', 'POST', $body)) {
188
+ return $response['customer']['id'];
189
+ }
190
+
191
+ return false;
192
+ }
193
+
194
+ /**
195
+ * Make an API request to retrieve an existing Customer.
196
+ *
197
+ * @param string $code
198
+ *
199
+ * @return array|bool|mixed
200
+ */
201
+ public function findCustomerByCode($code)
202
+ {
203
+ $customerId = $this->cache()->load("vindi_customer_by_code_{$code}");
204
+
205
+ if ($customerId === false) {
206
+ $response = $this->request("customers/search?code={$code}", 'GET');
207
+
208
+ if ($response && (1 === count($response['customers'])) && isset($response['customers'][0]['id'])) {
209
+ $customerId = $response['customers'][0]['id'];
210
+
211
+ $this->cache()->save(serialize($customerId), "vindi_customer_by_code_{$code}", ['vindi_cache'],
212
+ 5 * 60); // 5 minutes
213
+ }
214
+ } else {
215
+ $customerId = unserialize($customerId);
216
+ }
217
+
218
+ return $customerId;
219
+ }
220
+
221
+ /**
222
+ * Make an API request to retrieve an existing Customer or to create one if not found.
223
+ *
224
+ * @param array $body (name, email, code)
225
+ *
226
+ * @return array|bool|mixed
227
+ */
228
+ public function findOrCreateCustomer($body)
229
+ {
230
+ $customerId = $this->findCustomerByCode($body['code']);
231
+ // TODO update information
232
+
233
+ if (false === $customerId) {
234
+ return $this->createCustomer($body);
235
+ }
236
+
237
+ return $customerId;
238
+ }
239
+
240
+ /**
241
+ * Make an API request to create a Payment Profile to a Customer.
242
+ *
243
+ * @param $body (holder_name, card_expiration, card_number, card_cvv, customer_id)
244
+ *
245
+ * @return array|bool|mixed
246
+ */
247
+ public function createCustomerPaymentProfile($body)
248
+ {
249
+ // Protect credit card number.
250
+ $dataToLog = $body;
251
+ $dataToLog['card_number'] = '**** *' . substr($dataToLog['card_number'], -3);
252
+ $dataToLog['card_cvv'] = '***';
253
+
254
+ $customerId = $body['customer_id'];
255
+ $this->cache()->remove("vindi_payment_profile_{$customerId}");
256
+
257
+ return $this->request('payment_profiles', 'POST', $body, $dataToLog);
258
+ }
259
+
260
+ /**
261
+ * @param $userCode
262
+ *
263
+ * @return bool
264
+ */
265
+ public function getCustomerPaymentProfile($userCode)
266
+ {
267
+ $customerId = $this->findCustomerByCode($userCode);
268
+
269
+ if (false === $customerId) {
270
+ return false;
271
+ }
272
+
273
+ $paymentProfile = $this->cache()->load("vindi_payment_profile_{$customerId}");
274
+
275
+ if ($paymentProfile === false) {
276
+ $endpoint = 'payment_profiles?query=customer_id%3D' . $customerId
277
+ . '%20status%3Dactive%20type%3DPaymentProfile%3A%3ACreditCard';
278
+
279
+ $response = $this->request($endpoint, 'GET');
280
+
281
+ if ($response && $response['payment_profiles'] && count($response['payment_profiles'])) {
282
+ $paymentProfile = $response['payment_profiles'][0];
283
+
284
+ $this->cache()->save(serialize($paymentProfile), "vindi_payment_profile_{$customerId}", ['vindi_cache'],
285
+ 5 * 60); // 5 minutes
286
+ }
287
+ } else {
288
+ $paymentProfile = unserialize($paymentProfile);
289
+ }
290
+
291
+ return $paymentProfile;
292
+ }
293
+
294
+ /**
295
+ * Make an API request to create a Subscription.
296
+ *
297
+ * @param $body (plan_id, customer_id, payment_method_code, product_items[{product_id}])
298
+ *
299
+ * @return array
300
+ */
301
+ public function createSubscription($body)
302
+ {
303
+ if (($response = $this->request('subscriptions', 'POST', $body)) && isset($response['subscription']['id'])) {
304
+
305
+ $subscription = $response['subscription'];
306
+ $subscription['bill'] = $response['bill'];
307
+
308
+ return $subscription;
309
+ }
310
+
311
+ return false;
312
+ }
313
+
314
+ /**
315
+ * Make an API request to retrieve Payment Methods.
316
+ *
317
+ * @return array|bool
318
+ */
319
+ public function getPaymentMethods()
320
+ {
321
+ $paymentMethods = $this->cache()->load('vindi_payment_methods');
322
+
323
+ if ($paymentMethods === false) {
324
+
325
+ $paymentMethods = [
326
+ 'credit_card' => [],
327
+ 'bank_slip' => false,
328
+ ];
329
+
330
+ $response = $this->request('payment_methods', 'GET');
331
+
332
+ if (false === $response) {
333
+ return $this->acceptBankSlip = false;
334
+ }
335
+
336
+ foreach ($response['payment_methods'] as $method) {
337
+ if ('active' !== $method['status']) {
338
+ continue;
339
+ }
340
+
341
+ if ('PaymentMethod::CreditCard' === $method['type']) {
342
+ $paymentMethods['credit_card'] = array_merge($paymentMethods['credit_card'],
343
+ $method['payment_companies']);
344
+ } else {
345
+ if ('PaymentMethod::BankSlip' === $method['type']) {
346
+ $paymentMethods['bank_slip'] = true;
347
+ }
348
+ }
349
+ }
350
+
351
+ $this->cache()->save(serialize($paymentMethods), 'vindi_payment_methods', ['vindi_cache'],
352
+ 12 * 60 * 60); // 12 hours
353
+ } else {
354
+ $paymentMethods = unserialize($paymentMethods);
355
+ }
356
+
357
+ $this->acceptBankSlip = $paymentMethods['bank_slip'];
358
+
359
+ return $paymentMethods;
360
+ }
361
+
362
+ /**
363
+ * Retrieve Credit Card Types from Payment Methods.
364
+ *
365
+ * @return array
366
+ */
367
+ public function getCreditCardTypes()
368
+ {
369
+ $methods = $this->getPaymentMethods();
370
+ $types = [];
371
+
372
+ foreach ($methods['credit_card'] as $type) {
373
+ $types[$type['code']] = $type['name'];
374
+ }
375
+
376
+ return $types;
377
+ }
378
+
379
+ /**
380
+ * @return bool|null
381
+ */
382
+ public function acceptBankSlip()
383
+ {
384
+ if (null === $this->acceptBankSlip) {
385
+ $this->getPaymentMethods();
386
+ }
387
+
388
+ return $this->acceptBankSlip;
389
+ }
390
+
391
+ /**
392
+ * @param array $body
393
+ *
394
+ * @return int|bool
395
+ */
396
+ public function createBill($body)
397
+ {
398
+ if ($response = $this->request('bills', 'POST', $body)) {
399
+ return $response['bill']['id'];
400
+ }
401
+
402
+ return false;
403
+ }
404
+
405
+ /**
406
+ * @param $billId
407
+ *
408
+ * @return array|bool|mixed
409
+ */
410
+ public function approveBill($billId)
411
+ {
412
+ $response = $this->request("bills/{$billId}", 'GET');
413
+
414
+ if (false === $response || ! isset($response['bill'])) {
415
+ return false;
416
+ }
417
+
418
+ $bill = $response['bill'];
419
+
420
+ if ('review' !== $bill['status']) {
421
+ return true;
422
+ }
423
+
424
+ return $this->request("bills/{$billId}/approve", 'POST');
425
+ }
426
+
427
+ /**
428
+ * @param $billId
429
+ *
430
+ * @return string
431
+ */
432
+ public function getBankSlipDownload($billId)
433
+ {
434
+ $response = $this->request("bills/{$billId}", 'GET');
435
+
436
+ if (false === $response) {
437
+ return false;
438
+ }
439
+
440
+ return $response['bill']['charges'][0]['print_url'];
441
+ }
442
+
443
+ /**
444
+ * @return array
445
+ */
446
+ public function getProducts()
447
+ {
448
+ $list = [];
449
+ $response = $this->request('products?query=status:active', 'GET');
450
+
451
+ if ($products = $response['products']) {
452
+ foreach ($products as $product) {
453
+ $list[$product['id']] = "{$product['name']} ({$product['pricing_schema']['short_format']})";
454
+ }
455
+ }
456
+
457
+ return $list;
458
+ }
459
+
460
+ /**
461
+ * @param int $id
462
+ *
463
+ * @return array
464
+ */
465
+ public function getPlanItems($id)
466
+ {
467
+ $list = [];
468
+ $response = $this->request("plans/{$id}", 'GET');
469
+
470
+ if ($plan = $response['plan']) {
471
+ foreach ($plan['plan_items'] as $item) {
472
+ if (isset($item['product'])) {
473
+ $list[] = $item['product']['id'];
474
+ }
475
+ }
476
+ }
477
+
478
+ return $list;
479
+ }
480
+
481
+ /**
482
+ * @param Mage_Sales_Model_Order $order
483
+ *
484
+ * @return array
485
+ */
486
+ public function buildPlanItemsForSubscription($order)
487
+ {
488
+ $list = [];
489
+ $orderItems = $order->getItemsCollection();
490
+ $orderSubtotal = $order->getQuote()->getSubtotal();
491
+ $orderDiscount = $order->getDiscountAmount() * -1;
492
+
493
+ $discount = null;
494
+ if(!empty($orderDiscount)){
495
+ $discountPercentage = $orderDiscount * 100 / $orderSubtotal;
496
+ $discountPercentage = number_format(floor($discountPercentage*100)/100, 2);
497
+
498
+ $discount = [[
499
+ 'discount_type' => 'percentage',
500
+ 'percentage' => $discountPercentage
501
+ ]];
502
+ }
503
+
504
+ foreach($orderItems as $item)
505
+ {
506
+ $product = Mage::getModel('catalog/product')->load($item->getProductId());
507
+ if ($product->getTypeID() !== 'subscription') {
508
+ Mage::throwException("O produto {$item->getName()} não é uma assinatura.");
509
+
510
+ return false;
511
+ }
512
+
513
+ $productVindiId = $this->findOrCreateProduct(array( 'sku' => $item->getSku(), 'name' => $item->getName()));
514
+
515
+ for ($i=1; $i <= $item->getQtyOrdered() ; $i++) {
516
+ $list[] = [
517
+ 'product_id' => $productVindiId,
518
+ 'pricing_schema' => ['price' => $item->getPrice()],
519
+ 'discounts' => $discount,
520
+ ];
521
+ }
522
+ }
523
+
524
+ // Create product for shipping
525
+ $productVindiId = $this->findOrCreateProduct(array( 'sku' => 'frete', 'name' => 'Frete'));
526
+
527
+ $list[] = [
528
+ 'product_id' => $productVindiId,
529
+ 'pricing_schema' => ['price' => $order->getShippingAmount()],
530
+ ];
531
+
532
+ return $list;
533
+ }
534
+
535
+ /**
536
+ * @return array
537
+ */
538
+ public function getPlans()
539
+ {
540
+ $list = $this->cache()->load('vindi_plans');
541
+
542
+ if (($list === false) || ! count($list = unserialize($list))) {
543
+
544
+ $list = [];
545
+ $response = $this->request('plans?query=status:active', 'GET');
546
+
547
+ if ($plans = $response['plans']) {
548
+ foreach ($plans as $plan) {
549
+ $list[$plan['id']] = $plan['name'];
550
+ }
551
+ }
552
+ $this->cache()->save(serialize($list), 'vindi_plans', ['vindi_cache'], 10 * 60); // 10 minutes
553
+ }
554
+
555
+ return $list;
556
+ }
557
+
558
+ /**
559
+ * Make an API request to create a Product.
560
+ *
561
+ * @param array $body (name, code, status, pricing_schema (price))
562
+ *
563
+ * @return array|bool|mixed
564
+ */
565
+ public function createProduct($body)
566
+ {
567
+ if ($response = $this->request('products', 'POST', $body)) {
568
+ return $response['product']['id'];
569
+ }
570
+
571
+ return false;
572
+ }
573
+
574
+ /**
575
+ * Make an API request to retrieve an existing Product.
576
+ *
577
+ * @param string $code
578
+ *
579
+ * @return array|bool|mixed
580
+ */
581
+ public function findProductByCode($code)
582
+ {
583
+ $response = $this->request("products?query=code%3D{$code}", 'GET');
584
+
585
+ if ($response && (1 === count($response['products'])) && isset($response['products'][0]['id'])) {
586
+ return $response['products'][0]['id'];
587
+ }
588
+
589
+ return false;
590
+ }
591
+
592
+ /**
593
+ * Make an API request to retrieve the Unique Payment Product or to create it if not found.
594
+ *
595
+ * @return array|bool|mixed
596
+ */
597
+ public function findOrCreateUniquePaymentProduct()
598
+ {
599
+ $productId = $this->findProductByCode('mag-pagtounico');
600
+ if (false === $productId) {
601
+ return $this->createProduct([
602
+ 'name' => 'Pagamento Único (não remover)',
603
+ 'code' => 'mag-pagtounico',
604
+ 'status' => 'active',
605
+ 'pricing_schema' => [
606
+ 'price' => 0,
607
+ ],
608
+ ]);
609
+ }
610
+ return $productId;
611
+ }
612
+
613
+ /**
614
+ * Make an API request to retrieve a Product or to create it if not found.
615
+ * @param array $product
616
+ *
617
+ * @return array|bool|mixed
618
+ */
619
+ public function findOrCreateProduct($product)
620
+ {
621
+ //
622
+ $productId = $this->findProductByCode($product['sku']);
623
+
624
+ if (false === $productId) {
625
+ return $this->createProduct([
626
+ 'name' => $product['name'],
627
+ 'code' => $product['sku'],
628
+ 'status' => 'active',
629
+ 'pricing_schema' => [
630
+ 'price' => 0,
631
+ ],
632
+ ]);
633
+ }
634
+
635
+ return $productId;
636
+ }
637
+
638
+ /**
639
+ * Make an API request to retrieve information about the Merchant.
640
+ *
641
+ * @return array|bool|mixed
642
+ */
643
+ public function getMerchant()
644
+ {
645
+ $merchant = $this->cache()->load('vindi_merchant');
646
+
647
+ if ($merchant === false) {
648
+
649
+ $response = $this->request('merchant', 'GET');
650
+
651
+ if (! $response || ! isset($response['merchant'])) {
652
+ return false;
653
+ }
654
+
655
+ $merchant = $response['merchant'];
656
+
657
+ $this->cache()->save(serialize($merchant), 'vindi_merchant', ['vindi_cache'], 1 * 60 * 60); // 1 hour
658
+ } else {
659
+ $merchant = unserialize($merchant);
660
+ }
661
+
662
+ return $merchant;
663
+ }
664
+
665
+ /**
666
+ * Check to see if Merchant Status is Trial.
667
+ *
668
+ * @return boolean
669
+ */
670
+ public function isMerchantStatusTrial()
671
+ {
672
+ if ($merchant = $this->getMerchant()) {
673
+ return 'trial' === $merchant['status'];
674
+ }
675
+
676
+ return false;
677
+ }
678
+
679
+ /**
680
+ * @param $billId
681
+ *
682
+ * @return array|bool
683
+ */
684
+ public function getBill($billId)
685
+ {
686
+ $response = $this->request("bills/{$billId}", 'GET');
687
+
688
+ if (! $response || ! isset($response['bill'])) {
689
+ return false;
690
+ }
691
+
692
+ return $response['bill'];
693
+ }
694
+ }
app/code/community/Vindi/Subscription/Helper/Data.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+ /**
6
+ * Return Vindi API Key from config or false otherwhise.
7
+ *
8
+ * @return string|bool
9
+ */
10
+ public function getKey()
11
+ {
12
+ return Mage::getStoreConfig('vindi_subscription/general/api_key') ?: false;
13
+ }
14
+
15
+ /**
16
+ * Generate an URL for webhooks to use.
17
+ *
18
+ * @return string
19
+ */
20
+ public function getWebhookURL()
21
+ {
22
+ $key = $this->getHashKey();
23
+
24
+ return Mage::getUrl('vindi_subscription/webhook', compact('key'));
25
+ }
26
+
27
+ /**
28
+ * Generate an uniform salted hash for using as webhook's security validation.
29
+ *
30
+ * @return string
31
+ */
32
+ public function getHashKey()
33
+ {
34
+ return Mage::helper('core')->getHash('vindi-magento');
35
+ }
36
+ }
app/code/community/Vindi/Subscription/Helper/WebhookHandler.php ADDED
@@ -0,0 +1,471 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Helper_WebhookHandler extends Mage_Core_Helper_Abstract
4
+ {
5
+ /**
6
+ * @param string $message
7
+ * @param int|null $level
8
+ */
9
+ public function log($message, $level = null)
10
+ {
11
+ Mage::log($message, $level, 'vindi_webhooks.log');
12
+
13
+ switch ($level) {
14
+ case 4:
15
+ http_response_code(422);
16
+ exit($message);
17
+ break;
18
+ case 5:
19
+ echo $message;
20
+ return false;
21
+ break;
22
+ default:
23
+ return true;
24
+ break;
25
+ }
26
+
27
+ }
28
+
29
+ /**
30
+ * Handle incoming webhook.
31
+ *
32
+ * @param string $body
33
+ *
34
+ * @return bool
35
+ */
36
+ public function handle($body)
37
+ {
38
+ try {
39
+ $jsonBody = json_decode($body, true);
40
+
41
+ if (! $jsonBody || ! isset($jsonBody['event'])) {
42
+ Mage::throwException('Evento do Webhook não encontrado!');
43
+ }
44
+
45
+ $type = $jsonBody['event']['type'];
46
+ $data = $jsonBody['event']['data'];
47
+ } catch (Exception $e) {
48
+ $this->log(sprintf('Falha ao interpretar JSON do webhook: %s', $e->getMessage()), 5);
49
+ return false;
50
+ }
51
+
52
+ switch ($type) {
53
+ // the webhook is being called before Order is actually placed.
54
+ // I'm sorry for this, not going to use queues for now, so the solution is to use sleep().
55
+
56
+ case 'test':
57
+ $this->log('Evento de teste do webhook.');
58
+ exit('1');
59
+ case 'bill_created':
60
+ return $this->billCreated($data);
61
+ case 'bill_paid':
62
+ return $this->billPaid($data);
63
+ case 'charge_rejected':
64
+ return $this->chargeRejected($data);
65
+ default:
66
+ $this->log(sprintf('Evento do webhook ignorado pelo plugin: "%s".', $type), 5);
67
+ exit('0');
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Handle 'bill_created' event.
73
+ * The bill can be related to a subscription or a single payment.
74
+ *
75
+ * @param array $data
76
+ *
77
+ * @return bool
78
+ */
79
+ public function billCreated($data)
80
+ {
81
+ if (! ($bill = $data['bill'])) {
82
+ $this->log('Erro ao interpretar webhook "bill_created".', 5);
83
+
84
+ return false;
85
+ }
86
+
87
+ if (! isset($bill['subscription']) || is_null($bill['subscription'])) {
88
+ $this->log(sprintf('Ignorando o evento "bill_created" para venda avulsa.'), 5);
89
+
90
+ return false;
91
+ }
92
+
93
+ $period = intval($bill['period']['cycle']);
94
+
95
+ if (isset($bill['period']) && ($period === 1)) {
96
+ $this->log(sprintf('Ignorando o evento "bill_created" para o primeiro ciclo.'), 5);
97
+
98
+ return false;
99
+ }
100
+
101
+ if (($order = $this->getOrder($data))) {
102
+ $this->log(sprintf('Já existe o pedido %s para o evento "bill_created".', $order->getId()), 5);
103
+
104
+ return false;
105
+ }
106
+
107
+ $subscriptionId = $bill['subscription']['id'];
108
+ $lastPeriodOrder = $this->getOrderForPeriod($subscriptionId, $period - 1);
109
+
110
+ if (! $lastPeriodOrder || ! $lastPeriodOrder->getId()) {
111
+ $this->log('Pedido anterior não encontrado. Ignorando evento.', 4);
112
+
113
+ return false;
114
+ }
115
+
116
+ $vindiData = [
117
+ 'bill' => [
118
+ 'id' => $data['bill']['id'],
119
+ 'amout' => $data['bill']['amount']
120
+ ],
121
+ 'products' => [],
122
+ 'shipping' => [],
123
+ ];
124
+ foreach ($data['bill']['bill_items'] as $billItem)
125
+ {
126
+ if($billItem['product']['code'] == 'frete')
127
+ {
128
+ $vindiData['shipping'] = $billItem;
129
+ }else{
130
+ $vindiData['products'][] = $billItem;
131
+ }
132
+ }
133
+
134
+ $order = $this->createOrder($lastPeriodOrder, $vindiData);
135
+
136
+ if (! $order) {
137
+ $this->log('Impossível gerar novo pedido!', 4);
138
+
139
+ return false;
140
+ }
141
+
142
+ $this->log(sprintf('Novo pedido gerado: %s.', $order->getId()));
143
+
144
+ $order->setVindiSubscriptionId($subscriptionId);
145
+ $order->setVindiSubscriptionPeriod($period);
146
+ $order->save();
147
+
148
+ if(Mage::getStoreConfig('vindi_subscription/general/bankslip_link_in_order_comment'))
149
+ {
150
+ foreach ($data['bill']['charges'] as $charge)
151
+ {
152
+ if ($charge['payment_method']['type'] == 'PaymentMethod::BankSlip')
153
+ {
154
+ $order->addStatusHistoryComment(sprintf(
155
+ '<a target="_blank" href="%s">Clique aqui</a> para visualizar o boleto.',
156
+ $charge['print_url']
157
+ ))
158
+ ->setIsVisibleOnFront(true);
159
+ $order->save();
160
+ }
161
+ }
162
+ }
163
+
164
+ return true;
165
+ }
166
+
167
+ /**
168
+ * Handle 'bill_paid' event.
169
+ * The bill can be related to a subscription or a single payment.
170
+ *
171
+ * @param array $data
172
+ *
173
+ * @return bool
174
+ */
175
+ public function billPaid($data)
176
+ {
177
+ if (! ($order = $this->getOrder($data))) {
178
+ $this->log(sprintf('Ainda não existe um pedido para ciclo %s da assinatura: %d.',
179
+ $data['bill']['period']['cycle'],
180
+ $data['bill']['subscription']['id']),
181
+ 4
182
+ );
183
+
184
+ return false;
185
+ }
186
+
187
+ return $this->createInvoice($order);
188
+ }
189
+
190
+ /**
191
+ * @param array $data
192
+ *
193
+ * @return bool
194
+ * @throws \Exception
195
+ */
196
+ public function chargeRejected($data)
197
+ {
198
+ $charge = $data['charge'];
199
+
200
+ if (! ($order = $this->getOrderFromBill($charge['bill']['id']))) {
201
+ $this->log('Pedido não encontrado.', 4);
202
+
203
+ return false;
204
+ }
205
+
206
+ $gatewayMessage = $charge['last_transaction']['gateway_message'];
207
+ $isLastAttempt = is_null($charge['next_attempt']);
208
+
209
+ if ($isLastAttempt) {
210
+ $order->setState(Mage_Sales_Model_Order::STATE_CANCELED, true, sprintf(
211
+ 'Todas as tentativas de pagamento foram rejeitadas. Motivo: "%s".',
212
+ $gatewayMessage
213
+ ), true);
214
+ $this->log(sprintf(
215
+ 'Todas as tentativas de pagamento do pedido %s foram rejeitadas. Motivo: "%s".',
216
+ $order->getId(),
217
+ $gatewayMessage
218
+ ));
219
+ } else {
220
+ $order->addStatusHistoryComment(sprintf(
221
+ 'Tentativa de Pagamento rejeitada. Motivo: "%s". Uma nova tentativa será feita.',
222
+ $gatewayMessage
223
+ ));
224
+ $this->log(sprintf(
225
+ 'Tentativa de pagamento do pedido %s foi rejeitada. Motivo: "%s". Uma nova tentativa será feita.',
226
+ $order->getId(),
227
+ $gatewayMessage
228
+ ));
229
+ }
230
+
231
+ $order->save();
232
+
233
+ return true;
234
+ }
235
+
236
+ /**
237
+ * @param Mage_Sales_Model_Order $order
238
+ *
239
+ * @return bool
240
+ */
241
+ public function createInvoice($order)
242
+ {
243
+ if (! $order->getId()) {
244
+ return false;
245
+ }
246
+
247
+ $this->log(sprintf('Gerando fatura para o pedido: %s.', $order->getId()));
248
+
249
+ $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true,
250
+ 'O pagamento foi confirmado e o pedido está sendo processado.', true);
251
+
252
+ if (! $order->canInvoice()) {
253
+ $this->log(sprintf('Impossível gerar fatura para o pedido %s.', $order->getId()), 4);
254
+
255
+ // TODO define how to handle this
256
+
257
+ return false;
258
+ }
259
+ $invoice = $order->prepareInvoice();
260
+ $invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_OFFLINE);
261
+ $invoice->register();
262
+ Mage::getModel('core/resource_transaction')->addObject($invoice)->addObject($invoice->getOrder())->save();
263
+ $invoice->sendEmail(true);
264
+ $this->log('Fatura gerada com sucesso.');
265
+
266
+ return true;
267
+ }
268
+
269
+ /**
270
+ * @param array $data
271
+ *
272
+ * @return Mage_Sales_Model_Order|bool
273
+ */
274
+ private function getOrder($data)
275
+ {
276
+ if (! isset($data['bill'])) {
277
+ return false;
278
+ }
279
+
280
+ if (isset($data['bill']['subscription']) && ($subscription = $data['bill']['subscription'])
281
+ && ($subscriptionId = filter_var($subscription['id'], FILTER_SANITIZE_NUMBER_INT))
282
+ ) {
283
+ $order = $this->getOrderForPeriod($subscriptionId, $data['bill']['period']['cycle']);
284
+
285
+ if (! $order || ! $order->getId()) {
286
+ $this->log(sprintf('Nenhum pedido encontrado para a assinatura: %d.', $subscriptionId));
287
+
288
+ return false;
289
+ }
290
+
291
+ return $order;
292
+ } else {
293
+ $order = $this->getSingleOrder($data['bill']['id']);
294
+
295
+ if (! $order || ! $order->getId()) {
296
+ $this->log(sprintf('Nenhum pedido encontrado para a fatura: %d.', $data['bill']['id']));
297
+
298
+ return false;
299
+ }
300
+
301
+ return $order;
302
+ }
303
+ }
304
+
305
+ /**
306
+ * @param int $billId
307
+ *
308
+ * @return bool|\Mage_Sales_Model_Order
309
+ */
310
+ private function getOrderFromBill($billId)
311
+ {
312
+ /** @var Vindi_Subscription_Helper_API $api */
313
+ $api = Mage::helper('vindi_subscription/api');
314
+
315
+ if (! $bill = $api->getBill($billId)) {
316
+ return false;
317
+ }
318
+
319
+ return $this->getOrder(compact('bill'));
320
+ }
321
+
322
+ /**
323
+ * @param int $subscriptionId
324
+ * @param int $subscriptionPeriod
325
+ *
326
+ * @return Mage_Sales_Model_Order
327
+ */
328
+ private function getOrderForPeriod($subscriptionId, $subscriptionPeriod)
329
+ {
330
+ return Mage::getModel('sales/order')->getCollection()
331
+ ->addAttributeToSelect('*')
332
+ ->addFieldToFilter('vindi_subscription_id', $subscriptionId)
333
+ ->addFieldToFilter('vindi_subscription_period', $subscriptionPeriod)
334
+ ->getFirstItem();
335
+ }
336
+
337
+ /**
338
+ * @param int $billId
339
+ *
340
+ * @return Mage_Sales_Model_Order
341
+ */
342
+ private function getSingleOrder($billId)
343
+ {
344
+ return Mage::getModel('sales/order')->getCollection()
345
+ ->addAttributeToSelect('*')
346
+ ->addFieldToFilter('vindi_bill_id', $billId)
347
+ ->getFirstItem();
348
+ }
349
+
350
+ /**
351
+ * Create a new order from an old one using reorder functionality.
352
+ *
353
+ * @param Mage_Sales_Model_Order $oldOrder
354
+ *
355
+ * @return Mage_Sales_Model_Order;
356
+ */
357
+ private function createOrder($oldOrder, $vindiData)
358
+ {
359
+ $oldOrder->setReordered(true);
360
+
361
+ $model = Mage::getSingleton('adminhtml/sales_order_create');
362
+
363
+ /** @var Mage_Adminhtml_Model_Sales_Order_Create $order */
364
+ $order = $model->initFromOrder($oldOrder);
365
+
366
+ $quote = $order->getQuote();
367
+
368
+ // get shipping method
369
+ $shippingMethod = $oldOrder->getShippingMethod();
370
+ $activedShippingMethods = Mage::getSingleton('vindi_subscription/config_shippingmethod')->getActivedShippingMethodsValues();
371
+
372
+ // verify if current shipping method is active
373
+ if(!in_array($shippingMethod, $activedShippingMethods)){
374
+ $oldShippingMethod = $shippingMethod;
375
+ $shippingMethod = Mage::getStoreConfig('vindi_subscription/general/default_shipping_method');
376
+ $this->log(sprintf("Erro ao utilizar o método de envio %s alterado para o método padrão %s.",
377
+ $oldShippingMethod,
378
+ $shippingMethod
379
+ ));
380
+ unset($oldShippingMethod);
381
+ }
382
+
383
+ // quote shipping method
384
+ $quote->getShippingAddress()
385
+ ->setShippingMethod($shippingMethod)
386
+ ->setCollectShippingRates(true)
387
+ ->collectShippingRates()
388
+ ->collectTotals();
389
+
390
+ if(isset($vindiData['shipping']['pricing_schema']['price']) && !empty(isset($vindiData['shipping']['pricing_schema']['price'])))
391
+ {
392
+ // set shipping price
393
+ $billShippingPrice = $vindiData['shipping']['pricing_schema']['price'];
394
+
395
+ $quote->setPrice($billShippingPrice)
396
+ ->setCost($billShippingPrice);
397
+
398
+ $address = $quote->getShippingAddress();
399
+ $address->setShippingAmount($billShippingPrice);
400
+ $address->setBaseShippingAmount($billShippingPrice);
401
+
402
+ $rates = $address->collectShippingRates()
403
+ ->getGroupedAllShippingRates();
404
+ foreach ($rates as $carrier) {
405
+ foreach ($carrier as $rate) {
406
+ $rate->setPrice($billShippingPrice);
407
+ $rate->save();
408
+ }
409
+ }
410
+ $address->save();
411
+
412
+ }
413
+
414
+ $quote->save();
415
+
416
+ foreach ($vindiData['products'] as $item) {
417
+ $magentoProduct = Mage::getModel('catalog/product')->loadByAttribute('vindi_product_id', $item['product']['id']);
418
+ if(!$magentoProduct)
419
+ {
420
+ $this->log(sprintf('O produto com ID Vindi #%s não existe no Magento.', $item['product']['id']), 5);
421
+ return false;
422
+ }
423
+
424
+ if(number_format($magentoProduct->getPrice(), 2) !== number_format($item['pricing_schema']['price'], 2)){
425
+ $this->log(sprintf("Divergencia de valores na fatura #%s: produto %s: ID Magento #%s , ID Vindi #%s: Valor Magento R$ %s , Valor Vindi R$ %s",
426
+ $vindiData['bill']['id'],
427
+ $magentoProduct->getName(),
428
+ $magentoProduct->getId(),
429
+ $item['product']['id'],
430
+ $magentoProduct->getPrice(),
431
+ $item['pricing_schema']['price'])
432
+ );
433
+
434
+ $quote->getItemByProduct($magentoProduct)
435
+ // ->setPrice($item['pricing_schema']['price'])
436
+ // ->setCost($item['pricing_schema']['price'])
437
+ ->setOriginalCustomPrice($item['pricing_schema']['price'])
438
+ ->setCustomPrice($item['pricing_schema']['price'])
439
+ ->save();
440
+
441
+ }
442
+ }
443
+
444
+ $quote->setTotalsCollectedFlag(false)
445
+ ->collectTotals()
446
+ ->save();
447
+
448
+ $session = Mage::getSingleton('core/session');
449
+ $session->setData('vindi_is_reorder', true);
450
+
451
+ try {
452
+ $order = $order->createOrder();
453
+ } catch (Exception $e) {
454
+ $this->log("Erro ao criar pedido!");
455
+
456
+ if($e->getMessage()){
457
+ $this->log($e->getMessage(), 5);
458
+ }else{
459
+ $messages = $order->getSession()->getMessages(true);
460
+ foreach($messages->getItems() as $message)
461
+ {
462
+ $this->log($message->getText(), 5);
463
+ }
464
+ }
465
+
466
+ return false;
467
+ }
468
+
469
+ return $order;
470
+ }
471
+ }
app/code/community/Vindi/Subscription/Model/BankSlip.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Model_BankSlip extends Mage_Payment_Model_Method_Abstract
4
+ {
5
+ use Vindi_Subscription_Trait_PaymentMethod;
6
+
7
+ /**
8
+ * @var string
9
+ */
10
+ protected $_code = 'vindi_bankslip';
11
+
12
+ /**
13
+ * @var bool
14
+ */
15
+ protected $_isGateway = true;
16
+
17
+ /**
18
+ * @var bool
19
+ */
20
+ protected $_canAuthorize = true;
21
+
22
+ /**
23
+ * @var bool
24
+ */
25
+ protected $_canCapture = true;
26
+
27
+ /**
28
+ * @var bool
29
+ */
30
+ protected $_canCapturePartial = false;
31
+
32
+ /**
33
+ * @var bool
34
+ */
35
+ protected $_canRefund = false;
36
+
37
+ /**
38
+ * @var bool
39
+ */
40
+ protected $_canVoid = false;
41
+
42
+ /**
43
+ * @var bool
44
+ */
45
+ protected $_canUseInternal = true;
46
+
47
+ /**
48
+ * @var bool
49
+ */
50
+ protected $_canUseCheckout = true;
51
+
52
+ /**
53
+ * @var bool
54
+ */
55
+ protected $_canUseForMultishipping = false;
56
+
57
+ /**
58
+ * @var bool
59
+ */
60
+ protected $_isInitializeNeeded = true;
61
+
62
+ /**
63
+ * @var bool
64
+ */
65
+ protected $_canSaveCc = false;
66
+
67
+ /**
68
+ * @var string
69
+ */
70
+ protected $_formBlockType = 'vindi_subscription/form_bankSlip';
71
+
72
+ /**
73
+ * Assign data to info model instance
74
+ *
75
+ * @param mixed $data
76
+ *
77
+ * @return Mage_Payment_Model_Method_Abstract
78
+ */
79
+ public function assignData($data)
80
+ {
81
+ return $this;
82
+ }
83
+
84
+ /**
85
+ * @param string $paymentAction
86
+ * @param object $stateObject
87
+ *
88
+ * @return bool|Mage_Payment_Model_Method_Abstract
89
+ */
90
+ protected function processNewOrder($paymentAction, $stateObject)
91
+ {
92
+ $payment = $this->getInfoInstance();
93
+ $order = $payment->getOrder();
94
+
95
+ $customer = Mage::getModel('customer/customer');
96
+
97
+ $customerId = $this->createCustomer($order, $customer);
98
+
99
+ if ($this->isSingleOrder($order)) {
100
+ $result = $this->processSinglePayment($payment, $order, $customerId);
101
+ } else {
102
+ $result = $this->processSubscription($payment, $order, $customerId);
103
+ }
104
+
105
+ if (! $result) {
106
+ return false;
107
+ }
108
+
109
+ $stateObject->setStatus(Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW)
110
+ ->setState(Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW);
111
+
112
+ return $this;
113
+ }
114
+
115
+ /**
116
+ * Check whether payment method can be used
117
+ *
118
+ * @param Mage_Sales_Model_Quote|null $quote
119
+ *
120
+ * @return bool
121
+ */
122
+ public function isAvailable($quote = null)
123
+ {
124
+ /** @var Vindi_Subscription_Helper_API $api */
125
+ $api = Mage::helper('vindi_subscription/api');
126
+
127
+ return Mage::getStoreConfig('payment/vindi_bankslip/active')
128
+ && $api->acceptBankSlip();
129
+ }
130
+
131
+ /**
132
+ * @return string
133
+ */
134
+ protected function getPaymentMethodCode()
135
+ {
136
+ // TODO fix it to proper method code
137
+ return 'bank_slip';
138
+ }
139
+ }
app/code/community/Vindi/Subscription/Model/Config/Installments.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Model_Config_Installments
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ $list = [];
8
+
9
+ foreach (range(1, 12) as $i) {
10
+ $list[] = [
11
+ 'value' => $i,
12
+ 'label' => "{$i}x",
13
+ ];
14
+ }
15
+
16
+ return $list;
17
+ }
18
+ }
app/code/community/Vindi/Subscription/Model/Config/Shippingmethod.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Model_Config_Shippingmethod
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return $this->getActivedShippingMethods();
8
+ }
9
+
10
+
11
+ public function getActivedShippingMethods()
12
+ {
13
+ $methods = Mage::getSingleton('shipping/config')->getActiveCarriers();
14
+
15
+ $options = array();
16
+ $options[] = array('value' => '', 'label' => 'Nenhum');
17
+
18
+ foreach($methods as $_ccode => $_carrier)
19
+ {
20
+ $_methodOptions = array();
21
+ if($_methods = $_carrier->getAllowedMethods())
22
+ {
23
+ foreach($_methods as $_mcode => $_method)
24
+ {
25
+ $_code = $_ccode . '_' . $_mcode;
26
+ $_methodOptions[] = array('value' => $_code, 'label' => $_method);
27
+ }
28
+
29
+ if(!$_title = Mage::getStoreConfig("carriers/$_ccode/title"))
30
+ $_title = $_ccode;
31
+
32
+ $options[] = array('value' => $_methodOptions[0]['value'], 'label' => $_title);
33
+ }
34
+ }
35
+
36
+ return $options;
37
+ }
38
+
39
+ public function getActivedShippingMethodsValues()
40
+ {
41
+ $methods = Mage::getSingleton('shipping/config')->getActiveCarriers();
42
+
43
+ $options = array();
44
+
45
+ foreach($methods as $_ccode => $_carrier)
46
+ {
47
+ $_methodOptions = array();
48
+ if($_methods = $_carrier->getAllowedMethods())
49
+ {
50
+ foreach($_methods as $_mcode => $_method)
51
+ {
52
+ $_code = $_ccode . '_' . $_mcode;
53
+ $_methodOptions[] = array('value' => $_code, 'label' => $_method);
54
+ }
55
+
56
+ if(!$_title = Mage::getStoreConfig("carriers/$_ccode/title"))
57
+ $_title = $_ccode;
58
+
59
+ $options[] = $_methodOptions[0]['value'];
60
+ }
61
+ }
62
+
63
+ return $options;
64
+ }
65
+ }
app/code/community/Vindi/Subscription/Model/CreditCard.php ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Model_CreditCard extends Mage_Payment_Model_Method_Cc
4
+ {
5
+ use Vindi_Subscription_Trait_PaymentMethod;
6
+
7
+ /**
8
+ * @var string
9
+ */
10
+ protected $_code = 'vindi_creditcard';
11
+
12
+ /**
13
+ * @var bool
14
+ */
15
+ protected $_isGateway = true;
16
+
17
+ /**
18
+ * @var bool
19
+ */
20
+ protected $_canAuthorize = true;
21
+
22
+ /**
23
+ * @var bool
24
+ */
25
+ protected $_canCapture = true;
26
+
27
+ /**
28
+ * @var bool
29
+ */
30
+ protected $_canCapturePartial = false;
31
+
32
+ /**
33
+ * @var bool
34
+ */
35
+ protected $_canRefund = false;
36
+
37
+ /**
38
+ * @var bool
39
+ */
40
+ protected $_canVoid = false;
41
+
42
+ /**
43
+ * @var bool
44
+ */
45
+ protected $_canUseInternal = true;
46
+
47
+ /**
48
+ * @var bool
49
+ */
50
+ protected $_canUseCheckout = true;
51
+
52
+ /**
53
+ * @var bool
54
+ */
55
+ protected $_canUseForMultishipping = false;
56
+
57
+ /**
58
+ * @var bool
59
+ */
60
+ protected $_isInitializeNeeded = true;
61
+
62
+ /**
63
+ * @var bool
64
+ */
65
+ protected $_canSaveCc = false;
66
+
67
+ /**
68
+ * @var string
69
+ */
70
+ protected $_formBlockType = 'vindi_subscription/form_cc';
71
+
72
+ /**
73
+ * @var string
74
+ */
75
+ protected $_infoBlockType = 'payment/info_cc';
76
+
77
+ /**
78
+ * Assign data to info model instance
79
+ *
80
+ * @param mixed $data
81
+ *
82
+ * @return Mage_Payment_Model_Method_Abstract
83
+ */
84
+ public function assignData($data)
85
+ {
86
+ if (! ($data instanceof Varien_Object)) {
87
+ $data = new Varien_Object($data);
88
+ }
89
+ $info = $this->getInfoInstance();
90
+ $quote = $info->getQuote();
91
+
92
+ if ($this->isSingleOrder($quote)) {
93
+ $info->setAdditionalInformation('installments', $data->getCcInstallments());
94
+ }
95
+
96
+ if ($data->getCcChoice() === 'saved') {
97
+ $info->setAdditionalInformation('PaymentMethod', $this->_code)
98
+ ->setAdditionalInformation('use_saved_cc', true);
99
+
100
+ return $this;
101
+ }
102
+
103
+ $info->setCcType($data->getCcType())
104
+ ->setCcOwner($data->getCcOwner())
105
+ ->setCcLast4(substr($data->getCcNumber(), -4))
106
+ ->setCcNumber($data->getCcNumber())
107
+ ->setCcCid($data->getCcCid())
108
+ ->setCcExpMonth($data->getCcExpMonth())
109
+ ->setCcExpYear($data->getCcExpYear())
110
+ ->setCcSsIssue($data->getCcSsIssue())
111
+ ->setCcSsStartMonth($data->getCcSsStartMonth())
112
+ ->setCcSsStartYear($data->getCcSsStartYear())
113
+ ->setAdditionalInformation('PaymentMethod', $this->_code)
114
+ ->setAdditionalInformation('use_saved_cc', false);
115
+
116
+ return $this;
117
+ }
118
+
119
+ /**
120
+ * @param string $paymentAction
121
+ * @param object $stateObject
122
+ *
123
+ * @return bool|Mage_Payment_Model_Method_Abstract
124
+ */
125
+ protected function processNewOrder($paymentAction, $stateObject)
126
+ {
127
+ $payment = $this->getInfoInstance();
128
+ $order = $payment->getOrder();
129
+
130
+ $customer = Mage::getModel('customer/customer');
131
+
132
+ $customerId = $this->createCustomer($order, $customer);
133
+
134
+ if (! $payment->getAdditionalInformation('use_saved_cc')) {
135
+ $this->createPaymentProfile($customerId);
136
+ }
137
+
138
+ if ($this->isSingleOrder($order)) {
139
+ $result = $this->processSinglePayment($payment, $order, $customerId);
140
+ } else {
141
+ $result = $this->processSubscription($payment, $order, $customerId);
142
+ }
143
+
144
+ if (! $result) {
145
+ return false;
146
+ }
147
+
148
+ $stateObject->setStatus(Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW)
149
+ ->setState(Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW);
150
+
151
+ return $this;
152
+ }
153
+
154
+ /**
155
+ * @param int $customerId
156
+ *
157
+ * @return array|bool
158
+ */
159
+ protected function createPaymentProfile($customerId)
160
+ {
161
+ $payment = $this->getInfoInstance();
162
+
163
+ $creditCardData = [
164
+ 'holder_name' => $payment->getCcOwner(),
165
+ 'card_expiration' => str_pad($payment->getCcExpMonth(), 2, '0', STR_PAD_LEFT)
166
+ . '/' . $payment->getCcExpYear(),
167
+ 'card_number' => $payment->getCcNumber(),
168
+ 'card_cvv' => $payment->getCcCid() ?: '000',
169
+ 'customer_id' => $customerId,
170
+ 'payment_company_code' => $payment->getCcType(),
171
+ ];
172
+
173
+ $paymentProfileId = $this->api()->createCustomerPaymentProfile($creditCardData);
174
+
175
+ if ($paymentProfileId === false) {
176
+ Mage::throwException('Erro ao informar os dados de cartão de crédito. Verifique os dados e tente novamente!');
177
+
178
+ return false;
179
+ }
180
+
181
+ return $paymentProfileId;
182
+ }
183
+
184
+ /**
185
+ * Check whether payment method can be used
186
+ *
187
+ * @param Mage_Sales_Model_Quote|null $quote
188
+ *
189
+ * @return bool
190
+ */
191
+ public function isAvailable($quote = null)
192
+ {
193
+ return Mage::getStoreConfig('payment/vindi_creditcard/active')
194
+ && Mage::helper('vindi_subscription')->getKey();
195
+ }
196
+
197
+ /**
198
+ * Validate payment method information object
199
+ *
200
+ * @return Mage_Payment_Model_Method_Abstract
201
+ */
202
+ public function validate()
203
+ {
204
+ $info = $this->getInfoInstance();
205
+
206
+ $quote = $info->getQuote();
207
+
208
+ $maxInstallmentsNumber = Mage::getStoreConfig('payment/vindi_creditcard/max_installments_number');
209
+
210
+ if ($this->isSingleOrder($quote) && ($maxInstallmentsNumber > 1)) {
211
+ if (! $installments = $info->getAdditionalInformation('installments')) {
212
+ return $this->error('Você deve informar o número de parcelas.');
213
+ }
214
+
215
+ if ($installments > $maxInstallmentsNumber) {
216
+ return $this->error('O número de parcelas selecionado é inválido.');
217
+ }
218
+
219
+ $minInstallmentsValue = Mage::getStoreConfig('payment/vindi_creditcard/min_installment_value');
220
+ $installmentValue = ceil($quote->getGrandTotal() / $installments * 100) / 100;
221
+
222
+ if (($installmentValue < $minInstallmentsValue) || ($installments > $maxInstallmentsNumber)) {
223
+ return $this->error('O número de parcelas selecionado é inválido.');
224
+ }
225
+ }
226
+
227
+ if ($info->getAdditionalInformation('use_saved_cc')) {
228
+ return $this;
229
+ }
230
+
231
+ $availableTypes = $this->api()->getCreditCardTypes();
232
+
233
+ $ccNumber = $info->getCcNumber();
234
+
235
+ // remove credit card non-numbers
236
+ $ccNumber = preg_replace('/\D/', '', $ccNumber);
237
+
238
+ $info->setCcNumber($ccNumber);
239
+
240
+ if (! $this->_validateExpDate($info->getCcExpYear(), $info->getCcExpMonth())) {
241
+ return $this->error(Mage::helper('payment')->__('Incorrect credit card expiration date.'));
242
+ }
243
+
244
+ if (! array_key_exists($info->getCcType(), $availableTypes)) {
245
+ return $this->error(Mage::helper('payment')->__('Credit card type is not allowed for this payment method.'));
246
+ }
247
+
248
+ return $this;
249
+ }
250
+
251
+ /**
252
+ * @param string $errorMsg
253
+ *
254
+ * @return bool
255
+ * @throws \Mage_Core_Exception
256
+ */
257
+ private function error($errorMsg)
258
+ {
259
+ Mage::throwException($errorMsg);
260
+
261
+ return false;
262
+ }
263
+
264
+ /**
265
+ * @return string
266
+ */
267
+ protected function getPaymentMethodCode()
268
+ {
269
+ // TODO fix it to proper method code
270
+ return 'credit_card';
271
+ }
272
+ }
app/code/community/Vindi/Subscription/Model/Observer.php ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Model_Observer
4
+ {
5
+ /**
6
+ * @var \Vindi_Subscription_Helper_Data
7
+ */
8
+ private $_helper;
9
+
10
+ /**
11
+ * Constructor.
12
+ */
13
+ public function __construct()
14
+ {
15
+ $this->_helper = Mage::helper('vindi_subscription');
16
+ }
17
+
18
+ /**
19
+ * @param Varien_Event_Observer $observer
20
+ */
21
+ public function updateItems($observer)
22
+ {
23
+ if (! $this->_helper->isModuleEnabled()) {
24
+ return;
25
+ }
26
+
27
+ $quote = Mage::getSingleton('checkout/session')->getQuote();
28
+ if (! $this->countSubscriptions($quote)) {
29
+ return;
30
+ }
31
+
32
+ $data = $observer->getEvent()->getInfo();
33
+
34
+ foreach ($data as $itemId => $itemInfo) {
35
+ $item = $quote->getItemById($itemId);
36
+ if (! $item || ! $this->isSubscription($item->getProduct())) {
37
+ continue;
38
+ }
39
+
40
+ if (! ($qty = isset($itemInfo['qty']) ? (float) $itemInfo['qty'] : false)) {
41
+ continue;
42
+ }
43
+
44
+ if ($qty > 1) {
45
+ $this->addNotice('Você pode fazer apenas uma assinatura por vez.<br />
46
+ Conclua a compra da assinatura ou remova-a do carrinho.');
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * @param Varien_Event_Observer $observer
53
+ */
54
+ public function addToCart($observer)
55
+ {
56
+ if (! $this->_helper->isModuleEnabled()) {
57
+ return;
58
+ }
59
+
60
+ $quote = Mage::getSingleton('checkout/session')->getQuote();
61
+ /** @var Mage_Catalog_Model_Product $product */
62
+ $product = $observer->getEvent()->getProduct();
63
+
64
+ $itemsCount = $quote->getItemsCount();
65
+ $itemsSummaryQty = $quote->getItemsSummaryQty();
66
+
67
+ if (! $itemsCount && ($itemsSummaryQty === 1)) {
68
+ return;
69
+ }
70
+
71
+ if ($this->isSubscription($product)) {
72
+ if ($this->countSubscriptions($quote) > 1) {
73
+ $this->addNotice('Você pode fazer apenas uma assinatura por vez.<br />
74
+ Conclua a compra da assinatura ou remova-a do carrinho.');
75
+ }
76
+
77
+ if (($itemsCount === 1) && ($itemsSummaryQty > 1)) {
78
+ $this->addNotice('Você pode fazer apenas uma assinatura por vez.<br />
79
+ Por favor, tente novamente.');
80
+ }
81
+
82
+ $this->addNotice('Você não pode adicionar assinaturas e outros tipos de produtos em um mesmo carrinho.<br />
83
+ Conclua a compra dos produtos ou remova-os do carrinho.');
84
+
85
+ }
86
+
87
+ if ($this->countSubscriptions($quote)) {
88
+ $this->addNotice('Você não pode adicionar assinaturas e outros tipos de produtos em um mesmo carrinho.<br />
89
+ Conclua a compra da assinatura ou remova-a do carrinho.');
90
+ }
91
+
92
+ return;
93
+ }
94
+
95
+ /**
96
+ * @param Varien_Event_Observer $observer
97
+ */
98
+ public function validateAdminHtmlOrder($observer)
99
+ {
100
+ /** @var Mage_Adminhtml_Model_Sales_Order_Create $order */
101
+ $order = $observer['order_create_model'];
102
+ $quote = $order->getQuote();
103
+ $quote->collectTotals();
104
+
105
+ $itemsCount = $quote->getItemsCount();
106
+ $itemsSummaryQty = $quote->getItemsSummaryQty();
107
+ $subscriptionsCount = $this->countSubscriptions($quote);
108
+
109
+ if (! $subscriptionsCount) {
110
+ return;
111
+ }
112
+
113
+ if (($subscriptionsCount > 1)) {
114
+ Mage::throwException('Você pode fazer apenas uma assinatura por vez.<br />
115
+ Conclua a compra de uma única assinatura ou remova os excedentes dos itens.');
116
+ }
117
+
118
+ if (($itemsCount === 1) && ($itemsSummaryQty > 1)) {
119
+ Mage::throwException('Você pode fazer apenas uma assinatura por vez.<br />
120
+ Por favor, tente novamente.');
121
+ }
122
+
123
+ if ($itemsCount > 1) {
124
+ Mage::throwException('Você não pode adicionar assinaturas e outros tipos de produtos em um mesmo pedido.<br />
125
+ Conclua a compra da assinatura ou remova-a do carrinho.');
126
+ }
127
+ }
128
+
129
+ /**
130
+ * @param $message
131
+ */
132
+ private function addNotice($message)
133
+ {
134
+ Mage::getSingleton('core/session')->addNotice($message);
135
+ Mage::app()->getFrontController()->getResponse()->setRedirect(Mage::getUrl('checkout/cart'));
136
+ Mage::app()->getResponse()->sendResponse();
137
+ exit;
138
+ }
139
+
140
+ /**
141
+ * @param Mage_Sales_Model_Quote $quote
142
+ *
143
+ * @return int
144
+ */
145
+ private function countSubscriptions($quote)
146
+ {
147
+ $count = 0;
148
+
149
+ foreach ($quote->getAllVisibleItems() as $item) {
150
+ if (($product = $item->getProduct()) && $this->isSubscription($product)) {
151
+ $count++;
152
+ }
153
+ }
154
+
155
+ return $count;
156
+ }
157
+
158
+ /**
159
+ * @param Mage_Catalog_Model_Product $product
160
+ *
161
+ * @return bool
162
+ */
163
+ private function isSubscription($product)
164
+ {
165
+ return $product->getTypeId() === 'subscription';
166
+ }
167
+ }
168
+
app/code/community/Vindi/Subscription/Model/Product/Attribute/Plan.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Model_Product_Attribute_Plan extends Mage_Eav_Model_Entity_Attribute_Source_Abstract
4
+ {
5
+ /**
6
+ * Get Plans from Vindi.
7
+ * @return array
8
+ */
9
+ public function getAllOptions()
10
+ {
11
+ $this->_options = [
12
+ [
13
+ 'value' => '',
14
+ 'label' => Mage::helper('catalog')->__('-- Please Select --'),
15
+ ],
16
+ ];
17
+
18
+ /** @var Vindi_Subscription_Helper_API $api */
19
+ $api = Mage::helper('vindi_subscription/api');
20
+
21
+ foreach ($api->getPlans() as $id => $name) {
22
+ $this->_options[] = [
23
+ 'value' => $id,
24
+ 'label' => $name,
25
+ ];
26
+ }
27
+
28
+ return $this->_options;
29
+ }
30
+ }
app/code/community/Vindi/Subscription/Model/Product/Type.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_Model_Product_Type extends Mage_Catalog_Model_Product_Type_Simple
4
+ {
5
+
6
+ }
app/code/community/Vindi/Subscription/Trait/PaymentMethod.php ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ trait Vindi_Subscription_Trait_PaymentMethod
4
+ {
5
+
6
+ /**
7
+ * @param string $paymentAction
8
+ * @param object $stateObject
9
+ *
10
+ * @return bool|Mage_Payment_Model_Method_Abstract
11
+ */
12
+ public function initialize($paymentAction, $stateObject)
13
+ {
14
+ if ($this->checkForReorder()) {
15
+ return $this->processReorder($paymentAction, $stateObject);
16
+ }
17
+
18
+ return $this->processNewOrder($paymentAction, $stateObject);
19
+ }
20
+
21
+ /**
22
+ * @return bool
23
+ */
24
+ protected function checkForReorder()
25
+ {
26
+ $session = Mage::getSingleton('core/session');
27
+ $isReorder = $session->getData('vindi_is_reorder', false);
28
+ $session->unsetData('vindi_is_reorder');
29
+
30
+ return $isReorder;
31
+ }
32
+
33
+ /**
34
+ * @param Mage_Customer_Model_Customer $customer
35
+ *
36
+ * @return bool|string
37
+ * @throws \Mage_Core_Exception
38
+ */
39
+ protected function getCustomerTipoPessoa($customer)
40
+ {
41
+ $attribute = Mage::getSingleton('eav/config')->getAttribute('customer', 'tipopessoa');
42
+
43
+ $tipopessoa = $customer->getTipopessoa();
44
+
45
+ if ($attribute && $attribute->usesSource() && $tipopessoa) {
46
+ return $attribute->getSource()->getOptionText($tipopessoa);
47
+ }
48
+
49
+ return false;
50
+ }
51
+
52
+ /**
53
+ * @param Mage_Sales_Model_Order $order
54
+ * @param Mage_Customer_Model_Customer $customer
55
+ *
56
+ * @return bool|int|null
57
+ */
58
+ protected function createCustomer($order, $customer)
59
+ {
60
+ $billing = $order->getBillingAddress();
61
+
62
+ if (Mage::app()->getStore()->isAdmin()) {
63
+ $customer = Mage::getSingleton('adminhtml/session_quote')->getCustomer();
64
+ } else {
65
+ $customer->setWebsiteId(Mage::app()->getWebsite()->getId());
66
+ $customer->loadByEmail($billing->getEmail());
67
+ }
68
+
69
+ //TODO fix user being created again if validation fails
70
+ if (! ($userCode = $customer->getVindiUserCode())) {
71
+ $userCode = 'mag-' . $customer->getId() . '-' . time();
72
+
73
+ $customer->setVindiUserCode($userCode);
74
+ $customer->save();
75
+ }
76
+
77
+ $address = [
78
+ 'street' => $billing->getStreet(1),
79
+ 'number' => $billing->getStreet(2),
80
+ 'additional_details' => $billing->getStreet(3),
81
+ 'neighborhood' => $billing->getStreet(4),
82
+ 'zipcode' => $billing->getPostcode(),
83
+ 'city' => $billing->getCity(),
84
+ 'state' => $billing->getRegionCode(),
85
+ 'country' => $billing->getCountry(),
86
+ ];
87
+
88
+ $customerVindi = [
89
+ 'name' => $billing->getFirstname() . ' ' . $billing->getLastname(),
90
+ 'email' => $order->getBillingAddress()->getEmail(),
91
+ 'registry_code' => $order->getData('customer_taxvat'),
92
+ 'code' => $userCode,
93
+ 'address' => $address,
94
+ ];
95
+
96
+ if (Mage::getStoreConfig('vindi_subscription/general/send_nfe_information')) {
97
+ switch ($this->getCustomerTipoPessoa($customer)) {
98
+ case "Física":
99
+ $customerVindi['metadata'] = [
100
+ 'carteira_de_identidade' => $customer->getIe(),
101
+ ];
102
+ break;
103
+ case "Jurídica":
104
+ $customerVindi['metadata'] = [
105
+ 'inscricao_estadual' => $customer->getIe(),
106
+ ];
107
+ break;
108
+ }
109
+ }
110
+
111
+ $customerId = $this->api()->findOrCreateCustomer($customerVindi);
112
+
113
+ if ($customerId === false) {
114
+ Mage::throwException('Falha ao registrar o usuário. Verifique os dados e tente novamente!');
115
+ }
116
+
117
+ return $customerId;
118
+ }
119
+
120
+ /**
121
+ * @return Vindi_Subscription_Helper_API
122
+ */
123
+ protected function api()
124
+ {
125
+ if (isset($this->vindiApi)) {
126
+ return $this->vindiApi;
127
+ }
128
+
129
+ return $this->vindiApi = Mage::helper('vindi_subscription/api');
130
+ }
131
+
132
+ /**
133
+ * @param string $paymentAction
134
+ * @param object $stateObject
135
+ *
136
+ * @return bool|Mage_Payment_Model_Method_Abstract
137
+ */
138
+ protected function processReorder($paymentAction, $stateObject)
139
+ {
140
+ $payment = $this->getInfoInstance();
141
+ $order = $payment->getOrder();
142
+
143
+ $payment->setAmount($order->getTotalDue());
144
+ $this->setStore($order->getStoreId());
145
+
146
+ $payment->setStatus(Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW, Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW,
147
+ 'Novo período da assinatura criado', true);
148
+ $stateObject->setStatus(Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW)
149
+ ->setState(Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW);
150
+
151
+ return $this;
152
+ }
153
+
154
+ /**
155
+ * @param Mage_Payment_Model_Method_Abstract $payment
156
+ * @param Mage_Sales_Model_Order $order
157
+ * @param int $customerId
158
+ *
159
+ * @return bool
160
+ * @throws \Mage_Core_Exception
161
+ */
162
+ protected function processSubscription($payment, $order, $customerId)
163
+ {
164
+ $subscription = $this->createSubscription($order, $customerId);
165
+
166
+ if ($subscription === false) {
167
+ Mage::throwException('Erro ao criar a assinatura. Verifique os dados e tente novamente!');
168
+
169
+ return false;
170
+ }
171
+
172
+ $payment->setAmount($order->getTotalDue());
173
+ $this->setStore($order->getStoreId());
174
+
175
+ $payment->setStatus(Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW, Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW,
176
+ 'Assinatura criada', true);
177
+
178
+ return true;
179
+ }
180
+
181
+ /**
182
+ * @param Mage_Payment_Model_Method_Abstract $payment
183
+ * @param Mage_Sales_Model_Order $order
184
+ * @param int $customerId
185
+ *
186
+ * @return bool
187
+ * @throws \Mage_Core_Exception
188
+ */
189
+ protected function processSinglePayment($payment, $order, $customerId)
190
+ {
191
+ $uniquePaymentProduct = $this->api()->findOrCreateUniquePaymentProduct();
192
+
193
+ $this->log(sprintf('Produto para pagamento único: %d.', $uniquePaymentProduct));
194
+
195
+ $body = [
196
+ 'customer_id' => $customerId,
197
+ 'payment_method_code' => $this->getPaymentMethodCode(),
198
+ 'bill_items' => [
199
+ [
200
+ 'product_id' => $uniquePaymentProduct,
201
+ 'amount' => $order->getGrandTotal(),
202
+ ],
203
+ ],
204
+ ];
205
+
206
+ if ($installments = $payment->getAdditionalInformation('installments')) {
207
+ $body['installments'] = (int) $installments;
208
+ }
209
+
210
+ $billId = $this->api()->createBill($body);
211
+
212
+ if (! $billId) {
213
+ $this->log(sprintf('Erro no pagamento do pedido %d.', $order->getId()));
214
+
215
+ $message = sprintf('Pagamento Falhou. (%s)', $this->api()->lastError);
216
+ $payment->setStatus(
217
+ Mage_Sales_Model_Order::STATE_CANCELED,
218
+ Mage_Sales_Model_Order::STATE_CANCELED,
219
+ $message,
220
+ true
221
+ );
222
+
223
+ Mage::throwException($message);
224
+
225
+ return false;
226
+ }
227
+
228
+ $order->setVindiBillId($billId);
229
+ $order->save();
230
+
231
+ return $billId;
232
+ }
233
+
234
+ /**
235
+ * @param Mage_Sales_Model_Order $order
236
+ * @param int $customerId
237
+ *
238
+ * @return bool
239
+ */
240
+ protected function createSubscription($order, $customerId)
241
+ {
242
+ $orderItems = $order->getItemsCollection();
243
+ $item = $orderItems->getFirstItem();
244
+ $product = Mage::getModel('catalog/product')->load($item->getProductId());
245
+
246
+ $plan = $product->getData('vindi_subscription_plan');
247
+
248
+ $productItems = $this->api()->buildPlanItemsForSubscription($order);
249
+ if(!$productItems){
250
+ return false;
251
+ }
252
+
253
+ $body = [
254
+ 'customer_id' => $customerId,
255
+ 'payment_method_code' => $this->getPaymentMethodCode(),
256
+ 'plan_id' => $plan,
257
+ 'code' => 'mag-' . $order->getIncrementId() . '-' . time(),
258
+ 'product_items' => $productItems
259
+ ];
260
+
261
+ $subscription = $this->api()->createSubscription($body);
262
+
263
+ if (! isset($subscription['id']) || empty($subscription['id'])) {
264
+ $message = sprintf('Pagamento Falhou. (%s)', $this->api()->lastError);
265
+ $this->log(sprintf('Erro no pagamento do pedido %s.\n%s', $order->getId(), $message));
266
+
267
+ Mage::throwException($message);
268
+
269
+ // TODO update order status?
270
+ return false;
271
+ }
272
+
273
+ $order->setVindiSubscriptionId($subscription['id']);
274
+ $order->setVindiSubscriptionPeriod(1);
275
+ $order->save();
276
+
277
+ return $subscription;
278
+ }
279
+
280
+ /**
281
+ * @param string $message
282
+ * @param int|null $level
283
+ */
284
+ protected function log($message, $level = null)
285
+ {
286
+ Mage::log($message, $level, $this->_code . '.log');
287
+ }
288
+
289
+ /*
290
+ * @param Mage_Sales_Model_Order $order
291
+ */
292
+ protected function isSingleOrder($order)
293
+ {
294
+ if (! $order) {
295
+ return false;
296
+ }
297
+
298
+ foreach ($order->getAllVisibleItems() as $item) {
299
+ if (($product = $item->getProduct()) && ($product->getTypeId() === 'subscription')) {
300
+ return false;
301
+ }
302
+ }
303
+
304
+ return true;
305
+ }
306
+ }
app/code/community/Vindi/Subscription/controllers/WebhookController.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Vindi_Subscription_WebhookController extends Mage_Core_Controller_Front_Action
4
+ {
5
+ /**
6
+ * The route that webhooks will use.
7
+ */
8
+ public function indexAction()
9
+ {
10
+ /** @var Vindi_Subscription_Helper_WebhookHandler $handler */
11
+ $handler = Mage::helper('vindi_subscription/webhookHandler');
12
+
13
+ if (! $this->validateRequest()) {
14
+ $ip = Mage::helper('core/http')->getRemoteAddr();
15
+
16
+ $handler->log(sprintf('Invalid webhook attempt from IP %s', $ip), Zend_Log::WARN);
17
+ $this->norouteAction();
18
+
19
+ return false;
20
+ }
21
+
22
+ $body = file_get_contents('php://input');
23
+ $handler->log(sprintf("Novo evento dos webhooks!\n%s", $body));
24
+
25
+ return $handler->handle($body);
26
+ }
27
+
28
+ /**
29
+ * Validate the webhook for security reasons.
30
+ *
31
+ * @return bool
32
+ */
33
+ private function validateRequest()
34
+ {
35
+ $systemKey = Mage::helper('vindi_subscription')->getHashKey();
36
+ $requestKey = $this->getRequest()->getParam('key');
37
+
38
+ return $systemKey === $requestKey;
39
+ }
40
+ }
41
+
app/code/community/Vindi/Subscription/etc/adminhtml.xml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <admin>
6
+ <children>
7
+ <system>
8
+ <children>
9
+ <config>
10
+ <children>
11
+ <vindi_subscription translate="title" module="vindi_subscription">
12
+ <title>Vindi</title>
13
+ </vindi_subscription>
14
+ </children>
15
+ </config>
16
+ </children>
17
+ </system>
18
+ </children>
19
+ </admin>
20
+ </resources>
21
+ </acl>
22
+ </config>
app/code/community/Vindi/Subscription/etc/config.xml ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Vindi_Subscription>
5
+ <version>1.0.7</version>
6
+ </Vindi_Subscription>
7
+ </modules>
8
+ <global>
9
+ <catalog>
10
+ <product>
11
+ <type>
12
+ <subscription translate="label" module="vindi_subscription">
13
+ <label>Assinatura Vindi</label>
14
+ <model>vindi_subscription/product_type</model>
15
+ <is_qty>0</is_qty>
16
+ <composite>0</composite>
17
+ <can_use_qty_decimals>0</can_use_qty_decimals>
18
+ </subscription>
19
+ </type>
20
+ </product>
21
+ </catalog>
22
+ <resources>
23
+ <vindi_subscription_setup>
24
+ <setup>
25
+ <module>Vindi_Subscription</module>
26
+ <class>Mage_Catalog_Model_Resource_Setup</class>
27
+ </setup>
28
+ </vindi_subscription_setup>
29
+ </resources>
30
+ <blocks>
31
+ <vindi_subscription>
32
+ <class>Vindi_Subscription_Block</class>
33
+ </vindi_subscription>
34
+ </blocks>
35
+ <helpers>
36
+ <vindi_subscription>
37
+ <class>Vindi_Subscription_Helper</class>
38
+ </vindi_subscription>
39
+ </helpers>
40
+ <models>
41
+ <vindi_subscription>
42
+ <class>Vindi_Subscription_Model</class>
43
+ </vindi_subscription>
44
+ </models>
45
+ <traits>
46
+ <vindi_subscription>
47
+ <class>Vindi_Subscription_Trait_PaymentMethod</class>
48
+ </vindi_subscription>
49
+ </traits>
50
+ </global>
51
+ <adminhtml>
52
+ <sales>
53
+ <order>
54
+ <create>
55
+ <available_product_types>
56
+ <subscription/>
57
+ </available_product_types>
58
+ </create>
59
+ </order>
60
+ </sales>
61
+ <layout>
62
+ <updates>
63
+ <vindi_subscription>
64
+ <file>vindi_subscription.xml</file>
65
+ </vindi_subscription>
66
+ </updates>
67
+ </layout>
68
+ <events>
69
+ <adminhtml_sales_order_create_process_data>
70
+ <observers>
71
+ <vindi_subscription_saveAdminHtmlOrder>
72
+ <class>vindi_subscription/observer</class>
73
+ <method>validateAdminHtmlOrder</method>
74
+ </vindi_subscription_saveAdminHtmlOrder>
75
+ </observers>
76
+ </adminhtml_sales_order_create_process_data>
77
+ </events>
78
+ </adminhtml>
79
+ <default>
80
+ <vindi_subscription>
81
+ <general>
82
+ <send_nfe_information>0</send_nfe_information>
83
+ </general>
84
+ </vindi_subscription>
85
+ <payment>
86
+ <vindi_bankslip>
87
+ <active>0</active>
88
+ <model>vindi_subscription/bankSlip</model>
89
+ <title>Boleto Banc&#xE1;rio</title>
90
+ <payment_action>capture</payment_action>
91
+ <allowspecific>0</allowspecific>
92
+ </vindi_bankslip>
93
+ <vindi_creditcard>
94
+ <active>0</active>
95
+ <model>vindi_subscription/creditCard</model>
96
+ <title>Cart&#xE3;o de Cr&#xE9;dito</title>
97
+ <payment_action>capture</payment_action>
98
+ <allowspecific>0</allowspecific>
99
+ <min_installment_value>5</min_installment_value>
100
+ <max_installments_number>1</max_installments_number>
101
+ </vindi_creditcard>
102
+ </payment>
103
+ </default>
104
+ <frontend>
105
+ <layout>
106
+ <updates>
107
+ <vindi_subscription>
108
+ <file>vindi_subscription.xml</file>
109
+ </vindi_subscription>
110
+ </updates>
111
+ </layout>
112
+ <routers>
113
+ <subscription>
114
+ <use>standard</use>
115
+ <args>
116
+ <module>Vindi_Subscription</module>
117
+ <frontName>vindi_subscription</frontName>
118
+ </args>
119
+ </subscription>
120
+ </routers>
121
+ <events>
122
+ <checkout_cart_update_items_before>
123
+ <observers>
124
+ <vindi_subscription_updateItems>
125
+ <class>vindi_subscription/observer</class>
126
+ <method>updateItems</method>
127
+ </vindi_subscription_updateItems>
128
+ </observers>
129
+ </checkout_cart_update_items_before>
130
+ <checkout_cart_product_add_after>
131
+ <observers>
132
+ <vindi_subscription_addToCart>
133
+ <class>vindi_subscription/observer</class>
134
+ <method>addToCart</method>
135
+ </vindi_subscription_addToCart>
136
+ </observers>
137
+ </checkout_cart_product_add_after>
138
+ </events>
139
+ </frontend>
140
+ </config>
141
+
app/code/community/Vindi/Subscription/etc/system.xml ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <tabs>
4
+ <vindi translate="label" module="vindi_subscription">
5
+ <label>Vindi</label>
6
+ <sort_order>399</sort_order>
7
+ </vindi>
8
+ </tabs>
9
+ <sections>
10
+ <vindi_subscription translate="label" module="vindi_subscription">
11
+ <label>
12
+ <![CDATA[<img src="//www.vindi.com.br/image/favicon.png" style="float:left;margin-top:1px;width:16px;height:16px"/>&nbsp;Vindi Assinaturas]]></label>
13
+ <tab>vindi</tab>
14
+ <frontend_type>text</frontend_type>
15
+ <sort_order>10</sort_order>
16
+ <show_in_default>1</show_in_default>
17
+ <show_in_website>1</show_in_website>
18
+ <show_in_store>1</show_in_store>
19
+ <groups>
20
+ <general translate="label" module="vindi_subscription">
21
+ <label>Configuração</label>
22
+ <sort_order>10</sort_order>
23
+ <show_in_default>1</show_in_default>
24
+ <show_in_website>1</show_in_website>
25
+ <show_in_store>1</show_in_store>
26
+ <comment>
27
+ <![CDATA[<a href="https://app.vindi.com.br/prospects/new" target="_blank">Clique aqui para criar uma conta na Vindi.</a>]]></comment>
28
+ <fields>
29
+ <api_key>
30
+ <label>Chave da API</label>
31
+ <frontend_type>text</frontend_type>
32
+ <sort_order>1</sort_order>
33
+ <show_in_default>1</show_in_default>
34
+ <show_in_website>1</show_in_website>
35
+ <show_in_store>1</show_in_store>
36
+ </api_key>
37
+ <send_nfe_information>
38
+ <label>Enviar informações para emissão de NFe's</label>
39
+ <comment><![CDATA[
40
+ Envia informações de RG e Inscrição Estadual para Emissão de NFe's com nossos parceiros.
41
+ <a href="http://atendimento.vindi.com.br/hc/pt-br/articles/204450944-Notas-fiscais" target="_blank">Saiba mais</a>
42
+ ]]></comment>
43
+ <frontend_type>select</frontend_type>
44
+ <source_model>adminhtml/system_config_source_yesno</source_model>
45
+ <sort_order>2</sort_order>
46
+ <show_in_default>1</show_in_default>
47
+ <show_in_website>1</show_in_website>
48
+ <show_in_store>1</show_in_store>
49
+ </send_nfe_information>
50
+ <default_shipping_method>
51
+ <label>Método de envio padrão</label>
52
+ <comment><![CDATA[
53
+ Utilizado na criacão de um novo ciclo.
54
+ Em caso de falha no método de envio do ciclo anterior o método padrão é definido para o novo pedido.
55
+ ]]></comment>
56
+ <frontend_type>select</frontend_type>
57
+ <source_model>vindi_subscription/config_shippingmethod</source_model>
58
+ <sort_order>3</sort_order>
59
+ <show_in_default>1</show_in_default>
60
+ <show_in_website>1</show_in_website>
61
+ <show_in_store>1</show_in_store>
62
+ </default_shipping_method>
63
+ <bankslip_link_in_order_comment>
64
+ <label>Boleto bancário no histórico do pedido</label>
65
+ <comment><![CDATA[
66
+ Exibe o link para o boleto bancário nos comentários do pedido.
67
+ (Ao habilitar está opção certifique-se que a função "escapeHtml" foi removida da view no frontend)
68
+ ]]></comment>
69
+ <frontend_type>select</frontend_type>
70
+ <source_model>adminhtml/system_config_source_yesno</source_model>
71
+ <sort_order>4</sort_order>
72
+ <show_in_default>1</show_in_default>
73
+ <show_in_website>1</show_in_website>
74
+ <show_in_store>1</show_in_store>
75
+ </bankslip_link_in_order_comment>
76
+ <information>
77
+ <label>Informações</label>
78
+ <frontend_model>vindi_subscription/config_information</frontend_model>
79
+ <sort_order>10</sort_order>
80
+ <show_in_default>1</show_in_default>
81
+ <show_in_website>1</show_in_website>
82
+ <show_in_store>1</show_in_store>
83
+ </information>
84
+ </fields>
85
+ </general>
86
+ </groups>
87
+ </vindi_subscription>
88
+ <payment>
89
+ <groups>
90
+ <vindi_creditcard translate="label" module="vindi_subscription">
91
+ <label>Vindi - Cartão de Crédito</label>
92
+ <sort_order>670</sort_order>
93
+ <show_in_default>1</show_in_default>
94
+ <show_in_website>1</show_in_website>
95
+ <show_in_store>0</show_in_store>
96
+ <comment>
97
+ <![CDATA[<a href="https://app.vindi.com.br/prospects/new" target="_blank">Clique aqui para criar uma conta na Vindi.</a>]]></comment>
98
+ <fields>
99
+ <active translate="label">
100
+ <label>Ativo</label>
101
+ <frontend_type>select</frontend_type>
102
+ <source_model>adminhtml/system_config_source_yesno</source_model>
103
+ <sort_order>1</sort_order>
104
+ <show_in_default>1</show_in_default>
105
+ <show_in_website>1</show_in_website>
106
+ <show_in_store>0</show_in_store>
107
+ </active>
108
+ <title translate="label">
109
+ <label>Título</label>
110
+ <frontend_type>text</frontend_type>
111
+ <sort_order>2</sort_order>
112
+ <show_in_default>1</show_in_default>
113
+ <show_in_website>1</show_in_website>
114
+ <show_in_store>0</show_in_store>
115
+ </title>
116
+ <installments_heading translate="label">
117
+ <label>Vendas Avulsas</label>
118
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
119
+ <sort_order>10</sort_order>
120
+ <show_in_default>1</show_in_default>
121
+ <show_in_website>1</show_in_website>
122
+ <show_in_store>0</show_in_store>
123
+ </installments_heading>
124
+ <min_installment_value translate="label">
125
+ <label>Valor mínimo da parcela</label>
126
+ <comment>Valor mínimo da parcela, não deve ser inferior a R$ 5,00.</comment>
127
+ <frontend_type>text</frontend_type>
128
+ <sort_order>11</sort_order>
129
+ <show_in_default>1</show_in_default>
130
+ <show_in_website>1</show_in_website>
131
+ <show_in_store>0</show_in_store>
132
+ </min_installment_value>
133
+ <max_installments_number translate="label">
134
+ <label>Número máximo de parcelas</label>
135
+ <comment>Número máximo de parcelas para vendas avulsas. Deixe em 1x para desativar o
136
+ parcelamento.
137
+ </comment>
138
+ <frontend_type>select</frontend_type>
139
+ <source_model>vindi_subscription/config_installments</source_model>
140
+ <sort_order>12</sort_order>
141
+ <show_in_default>1</show_in_default>
142
+ <show_in_website>1</show_in_website>
143
+ <show_in_store>0</show_in_store>
144
+ </max_installments_number>
145
+ </fields>
146
+ </vindi_creditcard>
147
+ <vindi_bankslip translate="label" module="vindi_subscription">
148
+ <label>Vindi - Boleto Bancário</label>
149
+ <sort_order>671</sort_order>
150
+ <show_in_default>1</show_in_default>
151
+ <show_in_website>1</show_in_website>
152
+ <show_in_store>0</show_in_store>
153
+ <comment>
154
+ <![CDATA[<a href="https://app.vindi.com.br/prospects/new" target="_blank">Clique aqui para criar uma conta na Vindi.</a>]]></comment>
155
+ <fields>
156
+ <active translate="label">
157
+ <label>Ativo</label>
158
+ <frontend_type>select</frontend_type>
159
+ <source_model>adminhtml/system_config_source_yesno</source_model>
160
+ <sort_order>1</sort_order>
161
+ <show_in_default>1</show_in_default>
162
+ <show_in_website>1</show_in_website>
163
+ <show_in_store>0</show_in_store>
164
+ </active>
165
+ <title translate="label">
166
+ <label>Título</label>
167
+ <frontend_type>text</frontend_type>
168
+ <sort_order>2</sort_order>
169
+ <show_in_default>1</show_in_default>
170
+ <show_in_website>1</show_in_website>
171
+ <show_in_store>0</show_in_store>
172
+ </title>
173
+ </fields>
174
+ </vindi_bankslip>
175
+ </groups>
176
+ </payment>
177
+ </sections>
178
+ </config>
app/code/community/Vindi/Subscription/sql/vindi_subscription_setup/install-1.0.0.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /** @var $installer Mage_Catalog_Model_Resource_Setup */
4
+ $installer = $this;
5
+ $installer->startSetup();
6
+
7
+ if (! $this->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'vindi_subscription_plan', 'attribute_id')) {
8
+ $installer->addAttribute(
9
+ Mage_Catalog_Model_Product::ENTITY,
10
+ 'vindi_subscription_plan',
11
+ [
12
+ 'type' => 'int',
13
+ 'input' => 'select',
14
+ 'backend' => '',
15
+ 'frontend' => '',
16
+ 'label' => 'Plano da Vindi',
17
+ 'class' => '',
18
+ 'source' => 'vindi_subscription/product_attribute_plan',
19
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
20
+ 'visible' => true,
21
+ 'required' => true,
22
+ 'user_defined' => false,
23
+ 'default' => '',
24
+ 'searchable' => false,
25
+ 'filterable' => false,
26
+ 'comparable' => false,
27
+ 'visible_on_front' => false,
28
+ 'unique' => false,
29
+ 'apply_to' => 'subscription',
30
+ 'is_configurable' => false,
31
+ 'used_in_product_listing' => false,
32
+ 'option' => [
33
+ 'values' => [],
34
+ ],
35
+ ]
36
+ );
37
+
38
+ $attributeId = $installer->getAttributeId(
39
+ Mage_Catalog_Model_Product::ENTITY,
40
+ 'vindi_subscription_plan'
41
+ );
42
+
43
+ $defaultSetId = $installer->getAttributeSetId(Mage_Catalog_Model_Product::ENTITY, 'default');
44
+
45
+ $installer->addAttributeGroup(
46
+ Mage_Catalog_Model_Product::ENTITY,
47
+ $defaultSetId,
48
+ 'Vindi'
49
+ );
50
+
51
+ //find out the id of the new group
52
+ $groupId = $installer->getAttributeGroup(
53
+ Mage_Catalog_Model_Product::ENTITY,
54
+ $defaultSetId,
55
+ 'Vindi',
56
+ 'attribute_group_id'
57
+ );
58
+
59
+ //assign the attribute to the group and set
60
+ if ($attributeId > 0) {
61
+ $installer->addAttributeToSet(
62
+ Mage_Catalog_Model_Product::ENTITY,
63
+ $defaultSetId,
64
+ $groupId,
65
+ $attributeId
66
+ );
67
+ }
68
+
69
+ $attributes = [
70
+ 'price',
71
+ 'special_price',
72
+ 'special_from_date',
73
+ 'special_to_date',
74
+ 'minimal_price',
75
+ 'cost',
76
+ 'tier_price',
77
+ 'weight',
78
+ 'tax_class_id',
79
+ ];
80
+
81
+ foreach ($attributes as $attributeCode) {
82
+ $applyTo = explode(
83
+ ',',
84
+ $installer->getAttribute(
85
+ Mage_Catalog_Model_Product::ENTITY,
86
+ $attributeCode,
87
+ 'apply_to'
88
+ )
89
+ );
90
+
91
+ if (! in_array('subscription', $applyTo)) {
92
+ $applyTo[] = 'subscription';
93
+ $installer->updateAttribute(
94
+ Mage_Catalog_Model_Product::ENTITY,
95
+ $attributeCode,
96
+ 'apply_to',
97
+ join(',', $applyTo)
98
+ );
99
+ }
100
+ }
101
+ }
102
+
103
+ if (! $this->getAttribute('customer', 'vindi_user_code', 'attribute_id')) {
104
+ $installer->addAttribute('customer', 'vindi_user_code', [
105
+ 'input' => 'text',
106
+ 'type' => 'varchar',
107
+ 'is_visible' => false,
108
+ 'is_unique' => true,
109
+ 'is_required' => false,
110
+ 'sort_order' => 500,
111
+ 'label' => 'Código do Cliente na Vindi',
112
+ 'used_in_forms' => ['adminhtml_customer'],
113
+ 'backend_type' => 'varchar',
114
+ 'default_value' => '',
115
+ ]);
116
+ }
117
+
118
+ $installer->endSetup();
app/code/community/Vindi/Subscription/sql/vindi_subscription_setup/upgrade-1.0.0-1.0.1.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $setup = new Mage_Sales_Model_Resource_Setup('core_setup');
4
+
5
+ if (! $this->getAttribute(Mage_Sales_Model_Order::ENTITY, 'vindi_subscription_id', 'attribute_id')) {
6
+ $setup->addAttribute(
7
+ Mage_Sales_Model_Order::ENTITY,
8
+ 'vindi_subscription_id',
9
+ [
10
+ 'type' => 'varchar',
11
+ 'input' => 'text',
12
+ 'backend' => '',
13
+ 'frontend' => '',
14
+ 'label' => 'Id da Assinatura Vindi',
15
+ 'class' => '',
16
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
17
+ 'visible' => false,
18
+ 'required' => false,
19
+ 'user_defined' => false,
20
+ 'default' => '',
21
+ 'searchable' => false,
22
+ 'filterable' => false,
23
+ 'comparable' => false,
24
+ 'visible_on_front' => false,
25
+ 'unique' => false,
26
+ ]
27
+ );
28
+ }
29
+ if (! $this->getAttribute(Mage_Sales_Model_Order::ENTITY, 'vindi_subscription_period', 'attribute_id')) {
30
+ $setup->addAttribute(
31
+ Mage_Sales_Model_Order::ENTITY,
32
+ 'vindi_subscription_period',
33
+ [
34
+ 'type' => 'varchar',
35
+ 'input' => 'text',
36
+ 'backend' => '',
37
+ 'frontend' => '',
38
+ 'label' => 'Período da Assinatura Vindi',
39
+ 'class' => '',
40
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
41
+ 'visible' => false,
42
+ 'required' => false,
43
+ 'user_defined' => false,
44
+ 'default' => '',
45
+ 'searchable' => false,
46
+ 'filterable' => false,
47
+ 'comparable' => false,
48
+ 'visible_on_front' => false,
49
+ 'unique' => false,
50
+ ]
51
+ );
52
+ }
53
+ $setup->endSetup();
app/code/community/Vindi/Subscription/sql/vindi_subscription_setup/upgrade-1.0.1-1.0.2.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $setup = new Mage_Sales_Model_Resource_Setup('core_setup');
4
+
5
+ if (! $this->getAttribute(Mage_Sales_Model_Order::ENTITY, 'vindi_bill_id', 'attribute_id')) {
6
+ $setup->addAttribute(
7
+ Mage_Sales_Model_Order::ENTITY,
8
+ 'vindi_bill_id',
9
+ [
10
+ 'type' => 'varchar',
11
+ 'input' => 'text',
12
+ 'backend' => '',
13
+ 'frontend' => '',
14
+ 'label' => 'Id da Fatura Vindi (vendas avulsas)',
15
+ 'class' => '',
16
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
17
+ 'visible' => false,
18
+ 'required' => false,
19
+ 'user_defined' => false,
20
+ 'default' => '',
21
+ 'searchable' => false,
22
+ 'filterable' => false,
23
+ 'comparable' => false,
24
+ 'visible_on_front' => false,
25
+ 'unique' => false,
26
+ ]
27
+ );
28
+ }
29
+ $setup->endSetup();
app/code/community/Vindi/Subscription/sql/vindi_subscription_setup/upgrade-1.0.4-1.0.5.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $setup = $this;
4
+ $connection = $setup->getConnection();
5
+ $setup->startSetup();
6
+
7
+ if (! $this->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'vindi_product_id', 'attribute_id')) {
8
+ $setup->addAttribute(
9
+ Mage_Catalog_Model_Product::ENTITY,
10
+ 'vindi_product_id',
11
+ [
12
+ 'type' => 'int',
13
+ 'input' => 'text',
14
+ 'backend' => '',
15
+ 'frontend' => '',
16
+ 'label' => 'ID Vindi do produto',
17
+ 'class' => '',
18
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
19
+ 'visible' => true,
20
+ 'required' => true,
21
+ 'user_defined' => false,
22
+ 'default' => '',
23
+ 'searchable' => false,
24
+ 'filterable' => false,
25
+ 'comparable' => false,
26
+ 'visible_on_front' => false,
27
+ 'unique' => false,
28
+ 'apply_to' => 'subscription',
29
+ 'is_configurable' => false,
30
+ 'used_in_product_listing' => false,
31
+ 'option' => [
32
+ 'values' => [],
33
+ ],
34
+ ]
35
+ );
36
+
37
+ $attributeId = $setup->getAttributeId(
38
+ Mage_Catalog_Model_Product::ENTITY,
39
+ 'vindi_product_id'
40
+ );
41
+
42
+ $defaultSetId = $setup->getAttributeSetId(Mage_Catalog_Model_Product::ENTITY, 'default');
43
+
44
+ $setup->addAttributeGroup(
45
+ Mage_Catalog_Model_Product::ENTITY,
46
+ $defaultSetId,
47
+ 'Vindi'
48
+ );
49
+
50
+ //find out the id of the new group
51
+ $groupId = $setup->getAttributeGroup(
52
+ Mage_Catalog_Model_Product::ENTITY,
53
+ $defaultSetId,
54
+ 'Vindi',
55
+ 'attribute_group_id'
56
+ );
57
+
58
+ //assign the attribute to the group and set
59
+ if ($attributeId > 0) {
60
+ $setup->addAttributeToSet(
61
+ Mage_Catalog_Model_Product::ENTITY,
62
+ $defaultSetId,
63
+ $groupId,
64
+ $attributeId
65
+ );
66
+ }
67
+ }
68
+ $setup->endSetup();
app/design/adminhtml/base/default/layout/vindi_subscription.xml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <adminhtml_sales_order_create_index translate="label">
4
+ <reference name="head">
5
+ <action method="addItem">
6
+ <type>skin_css</type>
7
+ <name>vindi_subscription/css/vindi_subscription.css</name>
8
+ </action>
9
+ <action method="addItem">
10
+ <type>skin_js</type>
11
+ <name>vindi_subscription/js/vindi_subscription.js</name>
12
+ </action>
13
+ </reference>
14
+ </adminhtml_sales_order_create_index>
15
+ </layout>
app/design/adminhtml/base/default/template/vindi_subscription/payment/form/bankslip.phtml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php $_code = $this->getMethodCode() ?>
2
+ <div id="payment_form_<?php echo $_code ?>" style="display:none;">
3
+ <div class="block-payment-bankslip">
4
+ Um Boleto Bancário será enviado mensalmente para o seu endereço de e-mail.
5
+ </div>
6
+ </div>
app/design/adminhtml/base/default/template/vindi_subscription/payment/form/cc.phtml ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $code = $this->getMethodCode();
3
+ $class = 'required-entry';
4
+ ?>
5
+ <ul class="form-list" id="payment_form_<?php echo $code ?>" style="display:none;">
6
+ <li>
7
+ <div class="payment-companies">
8
+ <?php foreach ($ccs = $this->getCcAvailableTypes() as $typeCode => $typeName) {
9
+ echo '<img alt="' . $typeName . '" title="' . $typeName . '"
10
+ src="https://s3.amazonaws.com/recurrent/payment_companies/' . $typeCode . '.png"/>';
11
+ }
12
+ ?>
13
+ </div>
14
+ </li>
15
+ <?php if ($installments = $this->getInstallments()): ?>
16
+ <li>
17
+ <div id="<?php echo $code ?>_cc_type_installments_div" class="wide">
18
+ <label for="<?php echo $code ?>_cc_cid" class="required">
19
+ <em>*</em>Parcelas
20
+ </label>
21
+
22
+ <div class="input-box">
23
+ <select name="payment[cc_installments]" class="required-entry">
24
+ <?php echo $installments; ?>
25
+ </select>
26
+ </div>
27
+ </div>
28
+ </li>
29
+ <?php endif; ?>
30
+ <?php if ($savedCc = $this->getSavedCc()): ?>
31
+ <?php $class = 'not-required-entry';?>
32
+ <li class="payment-choice-saved-cc payment-choice">
33
+ <label class="label-choice label-choice-saved-cc">
34
+ <input type="radio" name="payment[cc_choice]" checked="checked" value="saved"/>
35
+ Usar cartão salvo
36
+ </label>
37
+
38
+ <div class="block-choice-saved-cc block-choice">
39
+ <img alt="<?php echo $ccType = $savedCc['payment_company']['name']; ?>"
40
+ title="<?php echo $ccType; ?>"
41
+ src="https://s3.amazonaws.com/recurrent/payment_companies/<?php echo $savedCc['payment_company']['code']; ?>.png"/>
42
+
43
+ <div class="block-choice-saved-cc-details">
44
+ <div class="nowrap block-choice-saved-cc-holder-name">
45
+ <span class="text-upper">Portador</span>
46
+ <span title="<?php echo $name = $savedCc['holder_name']; ?>"><?php echo $name ?></span>
47
+ </div>
48
+ <span class="nowrap">
49
+ <span class="text-upper">Final</span>
50
+ <?php echo $savedCc['card_number_last_four']; ?>&nbsp;
51
+ </span>
52
+ <span class="nowrap">
53
+ <span class="text-upper">Validade</span>
54
+ <?php echo date("m/Y", strtotime($savedCc['card_expiration'])); ?>
55
+ </span>
56
+ </div>
57
+ </div>
58
+ </li>
59
+ <?php endif; ?>
60
+ <li class="payment-choice-new-cc payment-choice">
61
+ <?php if ($savedCc): ?>
62
+
63
+ <label class="label-choice label-choice-new-cc">
64
+ <input type="radio" name="payment[cc_choice]" value="new"/>
65
+ Novo cartão de crédito
66
+ </label>
67
+ <?php else : ?>
68
+ <input type="hidden" name="payment[cc_choice]" value="new"/>
69
+ <?php endif; ?>
70
+
71
+ <div class="block-choice-new-cc block-choice" <?php if ($savedCc): ?>style="display:none;"<?php endif; ?>>
72
+ <div class="wide">
73
+ <label for="<?php echo $code ?>_cc_owner" class="required">
74
+ <em>*</em><?php echo $this->__('Name on Card') ?>
75
+ </label>
76
+
77
+ <div class="input-box">
78
+ <input type="text" title="<?php echo $this->__('Name on Card') ?>"
79
+ class="input-text <?php echo $class; ?>"
80
+ id="<?php echo $code ?>_cc_owner" name="payment[cc_owner]"
81
+ value="<?php echo $this->escapeHtml($this->getInfoData('cc_owner')) ?>"/>
82
+ </div>
83
+ </div>
84
+ <div class="wide">
85
+ <label for="<?php echo $code ?>_cc_type" class="required">
86
+ <em>*</em><?php echo $this->__('Credit Card Type') ?>
87
+ </label>
88
+
89
+ <div class="input-box">
90
+ <select id="<?php echo $code ?>_cc_type" name="payment[cc_type]" class="<?php echo $class; ?>"
91
+ title="<?php echo $this->__('Credit Card Type') ?>">
92
+ <option value=""><?php echo $this->__('--Please Select--') ?></option>
93
+ <?php foreach ($this->getCcAvailableTypes() as $typeCode => $typeName): ?>
94
+ <option value="<?php echo $typeCode ?>">
95
+ <?php echo $typeName ?>
96
+ </option>
97
+ <?php endforeach ?>
98
+ </select>
99
+ </div>
100
+ </div>
101
+ <div class="wide">
102
+ <label for="<?php echo $code ?>_cc_number" class="required">
103
+ <em>*</em><?php echo $this->__('Credit Card Number') ?>
104
+ </label>
105
+
106
+ <div class="input-box">
107
+ <input type="text" id="<?php echo $code ?>_cc_number" name="payment[cc_number]"
108
+ title="<?php echo $this->__('Credit Card Number') ?>" class="input-text" value=""
109
+ placeholder="•••• •••• •••• ••••"/>
110
+ </div>
111
+ </div>
112
+ <div id="<?php echo $code ?>_cc_type_exp_div" class="wide">
113
+ <label for="<?php echo $code ?>_expiration" class="required">
114
+ <em>*</em><?php echo $this->__('Expiration Date') ?>
115
+ </label>
116
+
117
+ <div class="input-box">
118
+ <div class="v-fix">
119
+ <select id="<?php echo $code ?>_expiration" name="payment[cc_exp_month]"
120
+ class="month validate-cc-exp <?php echo $class; ?>">
121
+ <?php foreach ($this->getCcMonths() as $k => $v): ?>
122
+ <option value="<?php echo $k ? $k : '' ?>">
123
+ <?php echo $v ?>
124
+ </option>
125
+ <?php endforeach ?>
126
+ </select>
127
+ </div>
128
+ <div class="v-fix">
129
+ <select id="<?php echo $code ?>_expiration_yr" name="payment[cc_exp_year]"
130
+ class="year <?php echo $class; ?>">
131
+ <?php foreach ($this->getCcYears() as $k => $v): ?>
132
+ <option value="<?php echo $k ? $k : '' ?>">
133
+ <?php echo $v ?>
134
+ </option>
135
+ <?php endforeach ?>
136
+ </select>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ <div id="<?php echo $code ?>_cc_type_cvv_div" class="wide">
141
+ <label for="<?php echo $code ?>_cc_cid" class="required">
142
+ <em>*</em>Número de Segurança
143
+ </label>
144
+
145
+ <div class="input-box">
146
+ <div class="v-fix">
147
+ <input type="text" title="Número de Segurança"
148
+ class="input-text cvv <?php echo $class; ?>" id="<?php echo $code ?>_cc_cid"
149
+ name="payment[cc_cid]" value=""/>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ </div>
154
+ </li>
155
+ </ul>
156
+
157
+ <?php if ($savedCc): ?>
158
+ <script type="text/javascript">
159
+ var VindiSubscriptionCC = new VindiSubscriptionCreditCard({
160
+ paymentChoiceSavedCcSelector: '.payment-choice-new-cc',
161
+ paymentChoiceNewCcSelector: '.payment-choice-saved-cc',
162
+ blockChoiceSavedCcSelector: '.block-choice-saved-cc',
163
+ blockChoiceNewCcSelector: '.block-choice-new-cc',
164
+ });
165
+ </script>
166
+ <?php endif; ?>
app/design/frontend/base/default/layout/vindi_subscription.xml ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <!--OSC 6-->
4
+ <onestepcheckout_index_index translate="label">
5
+ <reference name="head">
6
+ <action method="addItem">
7
+ <type>skin_css</type>
8
+ <name>vindi_subscription/css/vindi_subscription.css</name>
9
+ </action>
10
+ <action method="addItem">
11
+ <type>skin_js</type>
12
+ <name>vindi_subscription/js/vindi_subscription.js</name>
13
+ </action>
14
+ </reference>
15
+ </onestepcheckout_index_index>
16
+ <!--OSC 4-->
17
+ <onepagecheckout_index_index translate="label">
18
+ <reference name="head">
19
+ <action method="addItem">
20
+ <type>skin_css</type>
21
+ <name>vindi_subscription/css/vindi_subscription.css</name>
22
+ </action>
23
+ <action method="addItem">
24
+ <type>skin_js</type>
25
+ <name>vindi_subscription/js/vindi_subscription.js</name>
26
+ </action>
27
+ </reference>
28
+ </onepagecheckout_index_index>
29
+ <!--Native Checkout Page-->
30
+ <checkout_onepage_index translate="label">
31
+ <reference name="head">
32
+ <action method="addItem">
33
+ <type>skin_css</type>
34
+ <name>vindi_subscription/css/vindi_subscription.css</name>
35
+ </action>
36
+ <action method="addItem">
37
+ <type>skin_js</type>
38
+ <name>vindi_subscription/js/vindi_subscription.js</name>
39
+ </action>
40
+ </reference>
41
+ </checkout_onepage_index>
42
+ <customer_account_edit translate="label">
43
+ <reference name="head">
44
+ <action method="addItem">
45
+ <type>skin_css</type>
46
+ <name>vindi_subscription/css/vindi_subscription.css</name>
47
+ </action>
48
+ <action method="addItem">
49
+ <type>skin_js</type>
50
+ <name>vindi_subscription/js/vindi_subscription.js</name>
51
+ </action>
52
+ </reference>
53
+ </customer_account_edit>
54
+ <customer_address_form translate="label">
55
+ <reference name="head">
56
+ <action method="addItem">
57
+ <type>skin_css</type>
58
+ <name>vindi_subscription/css/vindi_subscription.css</name>
59
+ </action>
60
+ <action method="addItem">
61
+ <type>skin_js</type>
62
+ <name>vindi_subscription/js/vindi_subscription.js</name>
63
+ </action>
64
+ </reference>
65
+ </customer_address_form>
66
+ <customer_account_create>
67
+ <reference name="head">
68
+ <action method="addItem">
69
+ <type>skin_css</type>
70
+ <name>vindi_subscription/css/vindi_subscription.css</name>
71
+ </action>
72
+ <action method="addItem">
73
+ <type>skin_js</type>
74
+ <name>vindi_subscription/js/vindi_subscription.js</name>
75
+ </action>
76
+ </reference>
77
+ </customer_account_create>
78
+ </layout>
app/design/frontend/base/default/template/vindi_subscription/payment/form/bankslip.phtml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php $_code = $this->getMethodCode() ?>
2
+ <div id="payment_form_<?php echo $_code ?>" style="display:none;">
3
+ <div class="block-payment-bankslip">
4
+ Um Boleto Bancário será enviado mensalmente para o seu endereço de e-mail.
5
+ </div>
6
+ </div>
app/design/frontend/base/default/template/vindi_subscription/payment/form/cc.phtml ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php $code = $this->getMethodCode(); ?>
2
+ <ul class="form-list" id="payment_form_<?php echo $code ?>" style="display:none;">
3
+ <li>
4
+ <div class="payment-companies">
5
+ <?php foreach ($ccs = $this->getCcAvailableTypes() as $typeCode => $typeName) {
6
+ echo '<img alt="' . $typeName . '" title="' . $typeName . '"
7
+ src="https://s3.amazonaws.com/recurrent/payment_companies/' . $typeCode . '.png"/>';
8
+ }
9
+ ?>
10
+ </div>
11
+ </li>
12
+ <?php if($installments = $this->getInstallments()): ?>
13
+ <li>
14
+ <div id="<?php echo $code ?>_cc_type_installments_div" class="wide">
15
+ <label for="<?php echo $code ?>_cc_cid" class="required">
16
+ <em>*</em>Parcelas
17
+ </label>
18
+
19
+ <div class="input-box">
20
+ <select name="payment[cc_installments]" class="required-entry">
21
+ <?php echo $installments; ?>
22
+ </select>
23
+ </div>
24
+ </div>
25
+ </li>
26
+ <?php endif; ?>
27
+ <?php if ($savedCc = $this->getSavedCc()): ?>
28
+ <li class="payment-choice-saved-cc payment-choice">
29
+ <label class="label-choice label-choice-saved-cc">
30
+ <input type="radio" name="payment[cc_choice]" checked="checked" value="saved"/>
31
+ Usar cartão salvo
32
+ </label>
33
+
34
+ <div class="block-choice-saved-cc block-choice">
35
+ <img alt="<?php echo $ccType = $savedCc['payment_company']['name']; ?>"
36
+ title="<?php echo $ccType; ?>"
37
+ src="https://s3.amazonaws.com/recurrent/payment_companies/<?php echo $savedCc['payment_company']['code']; ?>.png"/>
38
+
39
+ <div class="block-choice-saved-cc-details">
40
+ <div class="nowrap block-choice-saved-cc-holder-name">
41
+ <span class="text-upper">Portador</span>
42
+ <span title="<?php echo $name = $savedCc['holder_name']; ?>"><?php echo $name ?></span>
43
+ </div>
44
+ <span class="nowrap">
45
+ <span class="text-upper">Final</span>
46
+ <?php echo $savedCc['card_number_last_four']; ?>&nbsp;
47
+ </span>
48
+ <span class="nowrap">
49
+ <span class="text-upper">Validade</span>
50
+ <?php echo date("m/Y", strtotime($savedCc['card_expiration'])); ?>
51
+ </span>
52
+ </div>
53
+ </div>
54
+ </li>
55
+ <?php endif; ?>
56
+ <li class="payment-choice-new-cc payment-choice">
57
+ <?php if ($savedCc): ?>
58
+
59
+ <label class="label-choice label-choice-new-cc">
60
+ <input type="radio" name="payment[cc_choice]" value="new"/>
61
+ Novo cartão de crédito
62
+ </label>
63
+ <?php else : ?>
64
+ <input type="hidden" name="payment[cc_choice]" value="new"/>
65
+ <?php endif; ?>
66
+
67
+ <div class="block-choice-new-cc block-choice" <?php if ($savedCc): ?>style="display:none;"<?php endif; ?>>
68
+ <div class="wide">
69
+ <label for="<?php echo $code ?>_cc_owner" class="required">
70
+ <em>*</em><?php echo $this->__('Name on Card') ?>
71
+ </label>
72
+
73
+ <div class="input-box">
74
+ <input type="text" title="<?php echo $this->__('Name on Card') ?>"
75
+ class="input-text required-entry"
76
+ id="<?php echo $code ?>_cc_owner" name="payment[cc_owner]"
77
+ value="<?php echo $this->escapeHtml($this->getInfoData('cc_owner')) ?>"/>
78
+ </div>
79
+ </div>
80
+ <div class="wide">
81
+ <label for="<?php echo $code ?>_cc_type" class="required">
82
+ <em>*</em><?php echo $this->__('Credit Card Type') ?>
83
+ </label>
84
+
85
+ <div class="input-box">
86
+ <select id="<?php echo $code ?>_cc_type" name="payment[cc_type]" class="required-entry"
87
+ title="<?php echo $this->__('Credit Card Type') ?>">
88
+ <option value=""><?php echo $this->__('--Please Select--') ?></option>
89
+ <?php foreach ($this->getCcAvailableTypes() as $typeCode => $typeName): ?>
90
+ <option value="<?php echo $typeCode ?>">
91
+ <?php echo $typeName ?>
92
+ </option>
93
+ <?php endforeach ?>
94
+ </select>
95
+ </div>
96
+ </div>
97
+ <div class="wide">
98
+ <label for="<?php echo $code ?>_cc_number" class="required">
99
+ <em>*</em><?php echo $this->__('Credit Card Number') ?>
100
+ </label>
101
+
102
+ <div class="input-box">
103
+ <input type="text" id="<?php echo $code ?>_cc_number" name="payment[cc_number]"
104
+ title="<?php echo $this->__('Credit Card Number') ?>" class="input-text" value=""
105
+ placeholder="•••• •••• •••• ••••"/>
106
+ </div>
107
+ </div>
108
+ <div id="<?php echo $code ?>_cc_type_exp_div" class="wide">
109
+ <label for="<?php echo $code ?>_expiration" class="required">
110
+ <em>*</em><?php echo $this->__('Expiration Date') ?>
111
+ </label>
112
+
113
+ <div class="input-box">
114
+ <div class="v-fix">
115
+ <select id="<?php echo $code ?>_expiration" name="payment[cc_exp_month]"
116
+ class="month validate-cc-exp required-entry">
117
+ <?php foreach ($this->getCcMonths() as $k => $v): ?>
118
+ <option value="<?php echo $k ? $k : '' ?>">
119
+ <?php echo $v ?>
120
+ </option>
121
+ <?php endforeach ?>
122
+ </select>
123
+ </div>
124
+ <div class="v-fix">
125
+ <select id="<?php echo $code ?>_expiration_yr" name="payment[cc_exp_year]"
126
+ class="year required-entry">
127
+ <?php foreach ($this->getCcYears() as $k => $v): ?>
128
+ <option value="<?php echo $k ? $k : '' ?>">
129
+ <?php echo $v ?>
130
+ </option>
131
+ <?php endforeach ?>
132
+ </select>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ <div id="<?php echo $code ?>_cc_type_cvv_div" class="wide">
137
+ <label for="<?php echo $code ?>_cc_cid" class="required">
138
+ <em>*</em><?php echo $this->__('Card Verification Number') ?>
139
+ </label>
140
+
141
+ <div class="input-box">
142
+ <div class="v-fix">
143
+ <input type="text" title="<?php echo $this->__('Card Verification Number') ?>"
144
+ class="input-text cvv required-entry" id="<?php echo $code ?>_cc_cid"
145
+ name="payment[cc_cid]" value=""/>
146
+ </div>
147
+ <a href="#" class="cvv-what-is-this"><?php echo $this->__('What is this?') ?></a>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ </li>
152
+ </ul>
153
+
154
+ <?php if ($savedCc): ?>
155
+ <script type="text/javascript">
156
+ var VindiSubscriptionCC = new VindiSubscriptionCreditCard({
157
+ paymentChoiceSavedCcSelector: '.payment-choice-new-cc',
158
+ paymentChoiceNewCcSelector: '.payment-choice-saved-cc',
159
+ blockChoiceSavedCcSelector: '.block-choice-saved-cc',
160
+ blockChoiceNewCcSelector: '.block-choice-new-cc',
161
+ });
162
+ </script>
163
+ <?php endif; ?>
app/etc/modules/Vindi_Subscription.xml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Vindi_Subscription>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ <depends>
8
+ <Mage_Payment/>
9
+ <Mage_Adminhtml/>
10
+ <Mage_Sales/>
11
+ </depends>
12
+ </Vindi_Subscription>
13
+ </modules>
14
+ </config>
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Vindi</name>
4
+ <version>1.0.7</version>
5
+ <stability>stable</stability>
6
+ <license>GPLv3</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>A integra&#xE7;&#xE3;o do m&#xF3;dulo da Vindi permite cria&#xE7;&#xE3;o e gest&#xE3;o de planos e assinaturas atrav&#xE9;s do Magento de forma transparente.</summary>
10
+ <description>A integra&#xE7;&#xE3;o do m&#xF3;dulo da Vindi permite cria&#xE7;&#xE3;o e gest&#xE3;o de planos e assinaturas atrav&#xE9;s do Magento de forma transparente.</description>
11
+ <notes>version 1.0.7</notes>
12
+ <authors><author><name>Tales Galvao</name><user>talesgalvao</user><email>tales.galvao@gmail.com</email></author><author><name>Erico Pedroso</name><user>ericopedroso</user><email>erico.pedroso@vindi.com.br</email></author></authors>
13
+ <date>2016-01-20</date>
14
+ <time>13:27:54</time>
15
+ <contents><target name="magecommunity"><dir name="Vindi"><dir name="Subscription"><dir name="Block"><dir name="Config"><file name="Information.php" hash="b324090de6fee25d09572d825709a6a9"/></dir><dir name="Form"><file name="BankSlip.php" hash="bce644dc03d2ed25389dceb21f9762ad"/><file name="Cc.php" hash="d95c0f954ea18ddc04fe0e34734eeae9"/></dir></dir><dir name="Helper"><file name="Api.php" hash="19f742ac0d9af9de02c0731391490026"/><file name="Data.php" hash="d1f0d18d79647a11c991bac60ee2ef44"/><file name="WebhookHandler.php" hash="6daa02ca4572ceedc274e12d92a5abed"/></dir><dir name="Model"><file name="BankSlip.php" hash="f00508693ad4a86ec43b373a5252e942"/><dir name="Config"><file name="Installments.php" hash="0f899e0267b0b0f1d17acaf06a65e493"/><file name="Shippingmethod.php" hash="5afab77a4c4e3a22992350ac41dc57b5"/></dir><file name="CreditCard.php" hash="cec7198003d7ad59fc1b5b45ff586e8f"/><file name="Observer.php" hash="e508b5c67b8a4a5db510939d41a67846"/><dir name="Product"><dir name="Attribute"><file name="Plan.php" hash="b1f3084edcd57f0d480d1376c4d90e4b"/></dir><file name="Type.php" hash="9bb3c00df44019ced9ad1682a2d3c2be"/></dir></dir><dir name="Trait"><file name="PaymentMethod.php" hash="092ecf2cf083bf4c7cc6cbf0d38ad6d3"/></dir><dir name="controllers"><file name="WebhookController.php" hash="d4886b956fe54d7ae8a38b3be22897d4"/></dir><dir name="etc"><file name="adminhtml.xml" hash="19f4eb5c16d561805f7b017186e2140b"/><file name="config.xml" hash="bcc392fdb18b50d0be8e4fd392f4e76f"/><file name="system.xml" hash="c660c9607f6dc584dd93247ef8c3aa7f"/></dir><dir name="sql"><dir name="vindi_subscription_setup"><file name="install-1.0.0.php" hash="cc7722665a2f587d8bf8310a94503353"/><file name="upgrade-1.0.0-1.0.1.php" hash="b4f107c231dd4cdb9e950a1c98c35a3d"/><file name="upgrade-1.0.1-1.0.2.php" hash="a018618fe25d0b3c45d1137a9b07aa7d"/><file name="upgrade-1.0.4-1.0.5.php" hash="acd0ba386942f42bad1ef792e8ac7aa1"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Vindi_Subscription.xml" hash="64a2addef728637da8dcd80a2b3c3415"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="template"><dir name="vindi_subscription"><dir name="payment"><dir name="form"><file name="bankslip.phtml" hash="df328f9149c101a6ba5fada0bbca9ef4"/><file name="cc.phtml" hash="07d522a86a3cb64c8d912fd4d431f142"/></dir></dir></dir></dir><dir name="layout"><file name="vindi_subscription.xml" hash="f6aea64fedba97321b653f3ddc83ab11"/></dir></dir></dir></dir><dir name="adminhtml"><dir name="base"><dir name="default"><dir name="template"><dir name="vindi_subscription"><dir name="payment"><dir name="form"><file name="bankslip.phtml" hash="df328f9149c101a6ba5fada0bbca9ef4"/><file name="cc.phtml" hash="3f2cc2ce886f9488a456b04e2c3a4c96"/></dir></dir></dir></dir><dir name="layout"><file name="vindi_subscription.xml" hash="ab06d89e538b954ce956f5a6674f83c0"/></dir></dir></dir></dir></target><target name="mageskin"><dir name="frontend"><dir name="base"><dir name="default"><dir name="vindi_subscription"><dir name="css"><file name="vindi_subscription.css" hash="ac7c5eab0913804e1cb90d5e9474e6a0"/></dir><dir name="js"><file name="vindi_subscription.js" hash="7d33f5ced68caf2fbcc3147f32d5c8fb"/></dir></dir></dir></dir></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.5.0</min><max>7.0.1</max></php></required></dependencies>
18
+ </package>
skin/frontend/base/default/vindi_subscription/css/vindi_subscription.css ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .payment-companies {
2
+ margin: 10px 0;
3
+ text-align: center;
4
+ }
5
+
6
+ .payment-companies img {
7
+ width: 50px;
8
+ display: inline-block;
9
+ }
10
+
11
+ .block-choice {
12
+ margin: 10px auto;
13
+ }
14
+
15
+ .block-payment-bankslip, .block-choice-saved-cc {
16
+ font-weight: bold;
17
+ background-color: #eee;
18
+ padding: 10px;
19
+ border: 1px solid #ccc;
20
+ }
21
+
22
+ .block-choice-saved-cc {
23
+ position: relative;
24
+ overflow: hidden;
25
+ }
26
+
27
+ .block-choice-saved-cc img {
28
+ width: 50px;
29
+ margin: 5px 0;
30
+ position: absolute;
31
+ }
32
+
33
+ .block-choice-saved-cc .block-choice-saved-cc-details {
34
+ margin-left: 60px;
35
+ }
36
+
37
+ .block-choice-saved-cc-holder-name {
38
+ overflow: hidden;
39
+ text-overflow: ellipsis;
40
+ }
41
+
42
+ .payment-choice {
43
+ margin: 16px 0 !important;
44
+ background-color: inherit;
45
+ }
46
+
47
+ .label-choice {
48
+ font-weight: normal;
49
+ font-size: 1.1em;
50
+ }
51
+
52
+ .text-upper {
53
+ font-size: 0.8em;
54
+ text-transform: uppercase;
55
+ font-weight: normal;
56
+ }
skin/frontend/base/default/vindi_subscription/js/vindi_subscription.js ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ VindiSubscriptionCreditCard = Class.create();
2
+ VindiSubscriptionCreditCard.prototype = {
3
+ initialize: function (config) {
4
+ var self = this;
5
+
6
+ self.paymentChoiceSavedCc = $$(config.paymentChoiceSavedCcSelector).first();
7
+ self.paymentChoiceNewCc = $$(config.paymentChoiceNewCcSelector).first();
8
+ self.blockChoiceSavedCc = $$(config.blockChoiceSavedCcSelector).first();
9
+ self.blockChoiceNewCc = $$(config.blockChoiceNewCcSelector).first();
10
+
11
+ self.initObservers();
12
+ },
13
+
14
+ initObservers: function () {
15
+ var self = this;
16
+
17
+ self.paymentChoiceSavedCc.observe('change', self.onPaymentChoiceSaved.bind(self));
18
+ self.paymentChoiceNewCc.observe('change', self.onPaymentChoiceNew.bind(self));
19
+ },
20
+
21
+ onPaymentChoiceSaved: function () {
22
+ var self = this;
23
+
24
+ self.togglePaymentChoices(self.blockChoiceSavedCc, self.blockChoiceNewCc);
25
+ },
26
+
27
+ onPaymentChoiceNew: function () {
28
+ var self = this;
29
+
30
+ self.togglePaymentChoices(self.blockChoiceNewCc, self.blockChoiceSavedCc);
31
+ },
32
+
33
+ /*
34
+ ======================================================
35
+ --------------------SHOW/HIDE functions---------------
36
+ ======================================================
37
+ */
38
+ togglePaymentChoices: function (hide, show) {
39
+ var self = this;
40
+
41
+ self.hideContainer(hide);
42
+ self.showContainer(show);
43
+ },
44
+
45
+ showContainer: function (container) {
46
+ var self = this;
47
+
48
+ container.setStyle({'display': ''});
49
+ var newHeight = self.getElementHeight(container);
50
+ this.applyEffect(container, newHeight, 0.5, function () {
51
+ container.setStyle({'height': ''});
52
+ });
53
+ },
54
+
55
+ hideContainer: function (container) {
56
+ var self = this;
57
+
58
+ self.applyEffect(container, 0, 0.5, function () {
59
+ container.setStyle({'display': 'none'});
60
+ });
61
+ },
62
+
63
+ applyEffect: function (element, newHeight, duration, afterFinish) {
64
+ if (element.effect) {
65
+ element.effect.cancel();
66
+ }
67
+ var afterFinishFn = afterFinish || Prototype.emptyFunction;
68
+ element.effect = new Effect.Morph(element, {
69
+ style: {
70
+ 'height': newHeight + 'px'
71
+ },
72
+ duration: duration,
73
+ afterFinish: function () {
74
+ delete element.effect;
75
+ afterFinishFn();
76
+ }
77
+ });
78
+ },
79
+
80
+ getElementHeight: function (element) {
81
+ element = $(element);
82
+ var origDimensions = element.getDimensions();
83
+ var origHeight = element.style.height;
84
+ var origDisplay = element.style.display;
85
+ var origVisibility = element.style.visibility;
86
+ element.setStyle({
87
+ 'height': '',
88
+ 'display': '',
89
+ 'visibility': 'hidden'
90
+ });
91
+ var height = Math.max(element.getDimensions()['height'], origDimensions['height']);
92
+ element.setStyle({
93
+ 'height': origHeight,
94
+ 'display': origDisplay,
95
+ 'visibility': origVisibility
96
+ });
97
+ return height;
98
+ }
99
+ };