Skip to content

Commit

Permalink
Variations (#7)
Browse files Browse the repository at this point in the history
* add variations support

* remove staging url

* update readme files

* fix messy variation name format

* add allow switching warning
  • Loading branch information
asagalo authored and lyoncesar committed May 5, 2016
1 parent cc1fd01 commit 0bf866b
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 145 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.0.0 - 27/04/2015
- Adicionado suporte a produtos e assinaturas variáveis
- Correção da exibição de planos anuais

# 0.2.7 - 14/03/2015
- Ajustes no hook 'http_api_curl' para manipular somente request do plugin

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
## Descrição
O **Vindi WooCommerce Subscriptions** oferece uma solução completa para pagamentos únicos e assinaturas com cartão de crédito e boleto utilizando o [Woocommerce Subscriptions](https://www.woothemes.com/products/woocommerce-subscriptions/). Basta ter [uma conta habilitada na Vindi](https://app.vindi.com.br/prospects/new), para começar a cobrar seus clientes.

# Observações
- Ainda não são suportados Upgrades ou Downgrades de assinaturas.

>Veja também o nosso plugin [Vindi WooCommerce](https://wordpress.org/plugins/vindi-assinaturas-e-cobranca-recorrente/) que oferece uma gestão mais simples e leia [nosso artigo](http://atendimento.vindi.com.br/hc/pt-br/articles/217612217) com um comparativo de recursos entre os dois plugins.
A [Vindi](http://www.vindi.com.br/) é líder em cobrança recorrente no Brasil. Com centenas de clientes usando soluções como pagamento online, soluções de notas fiscais integradas, emissão de boletos por email e PDF, integrações com ERPs e diversos relatórios, a Vindi possibilita um sistema online completo para negócios de venda recorrente. Além disso, empresas podem usar o gateway de pagamento integrado ao billing recorrente ou para faturas avulsas.

# Observações
- Até o momento só são suportados produtos e assinaturas simples.

# Requisitos
- PHP versão **5.5.19** ou superior.
- Um site com o WordPress instalado.
Expand Down
8 changes: 5 additions & 3 deletions assets/js/simple-subscription-fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
var id = $(this).val();
var plan = plan_infos[id];

$("select[name=_subscription_period_interval]").val(plan.interval_count);
$("select[name=_subscription_period]").val(plan.interval.toString().replace(/s/g, ''));
$("select[name=_subscription_length]").val(plan.billing_cycles);
console.log($(".wc_input_subscription_period_interval"));

$(".wc_input_subscription_period_interval").val(plan.interval_count);
$(".wc_input_subscription_period").val(plan.interval.toString().replace(/s/g, ''));
$(".wc_input_subscription_length").val(plan.billing_cycles);
});
});
}(jQuery)
Expand Down
1 change: 1 addition & 0 deletions includes/class-vindi-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ private function request($endpoint, $method = 'POST', $data = array(), $data_to_

return false;
}

$status = sprintf('%s %s', $response['response']['code'], $response['response']['message']);
$this->logger->log(sprintf("[Request #%s]: Nova Resposta da API.\n%s\n%s", $request_id, $status, print_r($response['body'], true)));

Expand Down
266 changes: 168 additions & 98 deletions includes/class-vindi-payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,14 @@ function __construct(WC_Order $order, Vindi_Base_Gateway $gateway, Vindi_Setting
* @return int order type.
*/

public function validate_order()
public function get_order_type()
{
$items = $this->order->get_items();

//@TODO validate if all subscription products were associated to the same vindi plan
foreach ($items as $item) {
$product = $this->order->get_product_from_item($item);

if ($product->is_type('subscription'))
if($this->is_subscription_type($product))
return static::ORDER_TYPE_SUBSCRIPTION;

}

return static::ORDER_TYPE_SINGLE;
Expand All @@ -71,14 +68,11 @@ public function get_plan()
$items = $this->order->get_items();

foreach($items as $item) {

$product = $this->order->get_product_from_item($item);
$vindi_plan = get_post_meta($product->id, 'vindi_subscription_plan', true);

if (! $product->is_type('subscription') || empty($vindi_plan))
continue;

return $vindi_plan;
if ($this->is_subscription_type($product) AND !empty($vindi_plan))
return $vindi_plan;
}

$this->abort(__('O produto selecionado não é uma assinatura.', VINDI_IDENTIFIER), true);
Expand Down Expand Up @@ -227,7 +221,7 @@ public function abort($message, $throw_exception = false)
*/
public function process()
{
switch ($orderType = $this->validate_order()) {
switch ($orderType = $this->get_order_type()) {
case static::ORDER_TYPE_SINGLE:
return $this->process_single_payment();
case static::ORDER_TYPE_SUBSCRIPTION:
Expand Down Expand Up @@ -289,23 +283,6 @@ protected function create_payment_profile($customer_id)
$this->abort(__('Falha ao registrar o método de pagamento. Verifique os dados e tente novamente.', VINDI_IDENTIFIER), true);
}

/**
* @param int $vindi_plan
* @param float $order_total
*
* @return array|bool
* @throws Exception
*/
protected function get_product_items($vindi_plan, $order_total)
{
$product_items = $this->container->api->build_plan_items_for_subscription($vindi_plan, $order_total);

if (empty($product_items))
return $this->abort(__('Falha ao recuperar informações sobre o produto na Vindi. Verifique os dados e tente novamente.', VINDI_IDENTIFIER), true);

return $product_items;
}

/**
* @param array $product
**/
Expand All @@ -326,92 +303,138 @@ private function return_cycle_from_product_type(array $product)
* @return array
* @throws Exception
*/
protected function build_product_items()
protected function build_product_items($order_type = 'bill')
{
$product_items = [];
$order_type = 'bill';
$call_build_items = "build_product_items_for_{$order_type}";

$order_items = $this->order->get_items();
$total_products = count($order_items);
if(false === method_exists($this, $call_build_items)) {
$this->abort(__("Ocorreu um erro ao gerar o seu pedido!", VINDI_IDENTIFIER), true);
}

foreach ($order_items as $key => $order_item) {
$product = wc_get_product($order_item['product_id']);
$item = $this->container->api->find_or_create_product($product->get_title(), sanitize_title($product->get_title()));
$product_items = [];
$order_items = $this->build_product_order_items();
$order_items[] = $this->build_shipping_item();

if('bill' === $order_type) {
$order_items[] = $this->build_discount_item_for_bill();
}

foreach ($order_items as $order_item) {
$product_items = array_merge($product_items, $this->$call_build_items($order_item));
}

if($product->is_type('subscription'))
$order_type = 'subscription';
if (empty($product_items)) {
return $this->abort(__('Falha ao recuperar informações sobre o produto na Vindi. Verifique os dados e tente novamente.', VINDI_IDENTIFIER), true);
}

return $product_items;
}

protected function build_product_order_items()
{
$order_items = $this->order->get_items();

foreach ($order_items as $key => $order_item) {
$product = $this->get_product($order_item);
$order_items[$key]['type'] = 'product';
$order_items[$key]['vindi_id'] = $item['id'];
$order_items[$key]['vindi_id'] = $product->vindi_id;
$order_items[$key]['price'] = (float) $product->get_price();
}

return $order_items;
}

if ($shipping_method = $this->order->get_shipping_method()) {
$item = $this->container->api->find_or_create_product("Frete ($shipping_method)", sanitize_title($shipping_method));
$order_items[] = array(
'type' => 'shipping',
'vindi_id' => $item['id'],
'price' => (float) $this->order->get_total_shipping(),
'qty' => 1,
);
}
protected function build_shipping_item()
{
$shipping_item = [];
$shipping_method = $this->order->get_shipping_method();

if(empty($shipping_method))
return $shipping_item;

$item = $this->container->api->find_or_create_product("Frete ($shipping_method)", sanitize_title($shipping_method));
$shipping_item = array(
'type' => 'shipping',
'vindi_id' => $item['id'],
'price' => (float) $this->order->get_total_shipping(),
'qty' => 1,
);

return $shipping_item;
}

protected function build_discount_item_for_bill()
{
$discount_item = [];
$total_discount = $this->order->get_total_discount();
if('bill' == $order_type && !empty($total_discount)) {
$item = $this->container->api->find_or_create_product("Cupom de desconto", 'wc-discount');
$order_items[] = array(
'type' => 'discount',
'vindi_id' => $item['id'],
'price' => (float) $total_discount * -1,
'qty' => 1,
);
}

foreach ($order_items as $order_item) {
if($order_type == 'subscription') {
if(!empty($total_discount) && $order_item['type'] == 'product') {
$product_items[] = array(
'product_id' => $order_item['vindi_id'],
'quantity' => $order_item['qty'],
'cycles' => $this->return_cycle_from_product_type($order_item),
'pricing_schema' => array(
'price' => $order_item['price'],
'schema_type' => 'per_unit'
),
'discounts' => array(
array(
'discount_type' => 'amount',
'amount' => $total_discount / $total_products
)
)
);
} else {
$product_items[] = array(
'product_id' => $order_item['vindi_id'],
'quantity' => $order_item['qty'],
'cycles' => $this->return_cycle_from_product_type($order_item),
'pricing_schema' => array(
'price' => $order_item['price'],
'schema_type' => 'per_unit'
)
);
}
} else {
for($i=0 ; $i<$order_item['qty'] ; $i++) {
$product_items[] = array(
'product_id' => $order_item['vindi_id'],
'amount' => $order_item['price'],
);
}
}
if(empty($total_discount)) {
return $discount_item;
}

if (empty($product_items))
return $this->abort(__('Falha ao recuperar informações sobre o produto na Vindi. Verifique os dados e tente novamente.', VINDI_IDENTIFIER), true);
$item = $this->container->api->find_or_create_product("Cupom de desconto", 'wc-discount');
$discount_item = array(
'type' => 'discount',
'vindi_id' => $item['id'],
'price' => (float) $total_discount * -1,
'qty' => 1
);

return $discount_item;
}

protected function build_product_items_for_bill($order_item)
{
$product_items = [];

for($i=0 ; $i < $order_item['qty'] ; $i++) {
$product_items[] = array(
'product_id' => $order_item['vindi_id'],
'amount' => $order_item['price'],
);
}

return $product_items;
}

protected function build_product_items_for_subscription($order_item)
{
$product_items = [];
$total_discount = $this->order->get_total_discount();

if(!empty($total_discount) && $order_item['type'] == 'product') {
$order_subtotal = $this->order->get_subtotal();
$discount_percentage = ($total_discount / $order_subtotal) * 100;

$product_items[] = array(
'product_id' => $order_item['vindi_id'],
'quantity' => $order_item['qty'],
'cycles' => $this->return_cycle_from_product_type($order_item),
'pricing_schema' => array(
'price' => $order_item['price'],
'schema_type' => 'per_unit'
),
'discounts' => array(
array(
'discount_type' => 'percentage',
'percentage' => $discount_percentage
)
)
);
} else {
$product_items[] = array(
'product_id' => $order_item['vindi_id'],
'quantity' => $order_item['qty'],
'cycles' => $this->return_cycle_from_product_type($order_item),
'pricing_schema' => array(
'price' => $order_item['price'],
'schema_type' => 'per_unit'
)
);
}

return $product_items;
}
/**
* @param $customer_id
*
Expand All @@ -428,7 +451,7 @@ protected function create_subscription($customer_id)
'customer_id' => $customer_id,
'payment_method_code' => $this->payment_method_code(),
'plan_id' => $vindi_plan,
'product_items' => $this->build_product_items(),
'product_items' => $this->build_product_items('subscription'),
'code' => $wc_subscription->id,
);

Expand Down Expand Up @@ -457,7 +480,7 @@ protected function create_bill($customer_id)
$body = array(
'customer_id' => $customer_id,
'payment_method_code' => $this->payment_method_code(),
'bill_items' => $this->build_product_items(),
'bill_items' => $this->build_product_items('bill'),
'code' => $this->order->id,
);

Expand Down Expand Up @@ -536,4 +559,51 @@ protected function finish_payment()
'redirect' => $this->order->get_checkout_order_received_url(),
);
}

/**
* @param array $product
**/
protected function get_product($order_item)
{
$product = $this->order->get_product_from_item($order_item);
$product_title = $product->get_title();

if($this->is_variable($product)) {
$variations = $this->parse_variation_name($product->get_attributes(), $order_item);
$product_title = sprintf("%s (%s)", $product_title, $variations);
}

$item = $this->container->api->find_or_create_product(
$product_title, sanitize_title($product_title)
);

$product->vindi_id = (int) $item['id'];

return $product;

}

protected function parse_variation_name($attributes, $order_item)
{
$keys = array_keys($attributes);
$names = [];

foreach($order_item['item_meta'] as $key => $meta) {
if(in_array($key, $keys)) {
$names[] = end($meta);
}
}

return join(' - ', $names);
}

protected function is_variable($product)
{
return (boolean) preg_match('/variation/', $product->get_type());
}

protected function is_subscription_type(WC_Product $product)
{
return (boolean) preg_match('/subscription/', $product->get_type());
}
}
Loading

0 comments on commit 0bf866b

Please sign in to comment.