jason-pancake / FJLChargifyBundle

0 stars 0 forks source link

Documentation and bundle coverage #1

Open sergionegri opened 9 years ago

sergionegri commented 9 years ago

Thank you for this Bundle. I see it's been developed in a very professional way, with tests. As documentation is not provided so far I'll ask you something to understand if it covers my needs which are pretty limited: 1) The creation of the user will be done in my page, and through the API I’ll need to create a customer with basic info (no credit card or billing information). I'll pass the ID of my customer to Chargify to have the same customer ID (which I know can be done) 2) The moment I create the customer I need to attach a subscription to that customer (trial 1 month, paid after that) 3) Every time a user logs in I’ll check his trial (or “general”) status. If ok, proceed on my website, if the customer needs to pay then redirect to Chargify hosted page to make the appropriate action (add credit card and pay, or update an expired credit card…)

Does the bundle you developed already provides this feature? Can you share some sample code and installation instructions? I've seen that the development repository is way ahead of the master. Is it unstable so that you don't commit to master?

Thank you!

jason-pancake commented 9 years ago

Hey Sergio, Thanks for reaching out! I'm away from the office right now but I'll help out with some examples when I get back and maybe we can start building some docs from that.

sergionegri commented 9 years ago

Sure, that would be great!

Meanwhile I'm playing with curl and Chargify to be sure what I want to do can be done (I'm just testing Chargify to understand if it's the right platform for me). At the moment it seems so. I just discovered you can create a subscription and a customer at the same time, which is exactly what I need to do.

Let's keep talking!

SN

On Sun, Mar 8, 2015 at 3:21 PM, jason-pancake notifications@github.com wrote:

Hey Sergio, Thanks for reaching out! I'm away from the office right now but I'll help out with some examples when I get back and maybe we can start building some docs from that.

Jason Pancake Flapjack Labs jason@flapjacklabs.com 678.327.8639

On Mar 8, 2015, at 10:04 AM, sergionegri notifications@github.com wrote:

Thank you for this Bundle. I see it's been developed in a very professional way, with tests. As documentation is not provided so far I'll ask you something to understand if it covers my needs which are pretty limited: 1) The creation of the user will be done in my page, and through the API I’ll need to create a customer with basic info (no credit card or billing information). I'll pass the ID of my customer to Chargify to have the same customer ID (which I know can be done) 2) The moment I create the customer I need to attach a subscription to that customer (trial 1 month, paid after that) 3) Every time a user logs in I’ll check his trial (or “general”) status. If ok, proceed on my website, if the customer needs to pay then redirect to Chargify hosted page to make the appropriate action (add credit card and pay, or update an expired credit card…)

Does the bundle you developed already provides this feature? Can you share some sample code and installation instructions? I've seen that the development repository is way ahead of the master. Is it unstable so that you don't commit to master?

Thank you!

— Reply to this email directly or view it on GitHub.

— Reply to this email directly or view it on GitHub https://github.com/jason-pancake/FJLChargifyBundle/issues/1#issuecomment-77751408 .

jason-pancake commented 9 years ago

I am wrapping up a separate project feature right now. I will reach out to you this afternoon and share some code to get you going.

jason-pancake commented 9 years ago

Ok, let's start with a basic installation and go from there. I'll use this as the basis for some docs. From your project directory you'll want to install the bundle via composer:

$ composer require flapjacklabs/chargify-bundle

Enable the bundle in the kernel:

<?php
// app/AppKernel.php

public
 function registerBundles()
{
    $bundles = array(
        // ...
        new Misd\GuzzleBundle\MisdGuzzleBundle(),
        new \FJL\ChargifyBundle\FJLChargifyBundle(),
    );
}

Set a couple of Chargify parameters:

# app/config/parameters.yml
chargify_api_endpoint: "https://xxxxxxxxxxxxxxxxxxxx:x@example.chargify.com"
chargify_secret: xxxxxxxxxxxxxxxxxxxx

Define the services:

# app/config/services.yml
app_bundle.chargify.client:
    class: %guzzle.client.class%
    arguments: [%chargify_api_endpoint%]

app_bundle.chargify.subscription_manager:
    class: FJL\ChargifyBundle\Model\SubscriptionManager
    arguments: ["@app_bundle.chargify.client", @logger]

Extend the base form type for customer attributes:

<?php
// AppBundle\Form\Type\CustomerAttributesFormType.php

namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use FJL\ChargifyBundle\Form\Type\CustomerAttributesType as BaseCustomerAttributesType;

class CustomerAttributesFormType extends BaseCustomerAttributesType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('firstName', 'text', array(
                'label' => 'First Name',
            ))
            ->add('lastName', 'text', array(
                'label' => 'Last Name'
            ))
            ->add('email', 'email', array(
                'label' => 'Email Address'
            ))
        ;
    }
}

Extend the base form type for credit card attributes:

<?php
// AppBundle\Form\Type\CreditCardAttributesFormType.php

namespace AppBundle\Form\Type;

use Symfony\Component\Form\FormBuilderInterface;
use FJL\ChargifyBundle\Form\Type\CreditCardAttributesType as BaseCreditCardAttributesType;

class CreditCardAttributesFormType extends BaseCreditCardAttributesType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $years = array();
        for($i=date('Y');$i<date('Y')+10;$i++) {
            $years[$i] = $i;
        }

        $builder
            ->add('fullNumber', 'text', array(
                'label' => 'Credit Card Number',
            ))
            ->add('cvv', 'text', array(
                'label' => 'CVV2',
            ))
            ->add('expirationMonth', 'choice', array(
                'label' => 'Expiration Month',
                'choices' => array(
                    '01' => '01 - January',
                    '02' => '02 - February',
                    '03' => '03 - March',
                    '04' => '04 - April',
                    '05' => '05 - May',
                    '06' => '06 - June',
                    '07' => '07 - July',
                    '08' => '08 - August',
                    '09' => '09 - September',
                    '10' => '10 - October',
                    '11' => '11 - November',
                    '12' => '12 - December',
                ),
            ))
            ->add('expirationYear', 'choice', array(
                'label' => 'Expiration Year',
                'choices' => $years
            ))
        ;
    }
}

Extend the subscription form type for the subscription attributes:

<?php
// AppBundle\Form\Type\SubscriptionFormType.php

namespace AppBundle\Form\Type;

use Symfony\Component\Form\FormBuilderInterface;
use FJL\ChargifyBundle\Form\Type\SubscriptionType as BaseSubscriptionType;

class SubscriptionFormType extends BaseSubscriptionType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('productHandle', 'choice', array(
                'choices' => array(
                    '[your-subscription-api-handle]'  => 'Subscription Option 1',
                    '[your-subscription-api-handle]'  => 'Subscription Option 2',
                    '[your-subscription-api-handle]'  => 'Subscription Option 3',
                ),
                'expanded' => true,
                'label' => 'Subscription Level',
            ))
            ->add('customerAttributes', new CustomerAttributesFormType())
            ->add('creditCardAttributes', new CreditCardAttributesFormType())
            ->add('couponCode', 'text', array('label' => 'Coupon Code', 'required' => false))
        ;
    }
}

Now to the controller:

<?php
// AppBundle\Controller\SubscriptionController.php

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use FJL\ChargifyBundle\Model\CreditCardAttributes;
use FJL\ChargifyBundle\Model\Subscription;
use FJL\ChargifyBundle\Model\CustomerAttributes;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use AppBundle\Form\Type\SubscriptionFormType;

class SubscriptionController extends Controller
{
    /**
     * @Route("/subscribe/", name="subscribe")
     */
    public function subscribeAction(Request $request)
    {
        //Get the user object
        $user = $this->getUser();

        //Create a new subscription object
        $subscription = new Subscription();

        //Create a new customer attributes object and set a default email and customer reference
        $customerAttributes   = new CustomerAttributes();
        $customerAttributes->setEmail($user->getEmail());
        $customerAttributes->setReference($user->getId());

        //Set the customer attributes
        $subscription->setCustomerAttributes($customerAttributes);

        //Create form object
        $form = $this->createForm(new SubscriptionFormType(), $subscription);

        //Handle the form request
        $form->handleRequest($request);

        //Validate form
        if($form->isSubmitted() && $form->isValid()) {

            //Get the subscription manager service
            $subscriptionManager = $this->get('chargify.subscription_manager');

            //Create/update the subscription
            $subscription = $subscriptionManager->updateSubscription($subscription);

            //If it was successful we'll get back a subscription object
            if($subscription) {

                //Save the subscription and customer attributes
                $user = $this->getUser();            
                $user->setSubscriptionId($subscription->getId());
                $user->setCustomerId($subscription->getCustomer()->getId());
                $user->setSubscriptionState($subscription->getState());
                $user->setFirstName($subscription->getCustomer()->getFirstName());
                $user->setLastName($subscription->getCustomer()->getLastName());            

                //Get the fos user manager and save the user
                $userManager = $this->get('fos_user.user_manager');
                $userManager->updateUser($user);                

                //We're done. Set a flash var and redirect to the dashboard
                $this->addFlash('success', 'Congrats! You are now subscribed to plan name!');
                return $this->redirect($this->generateUrl('dashboard'));
            }
        }

        //Render template
        return $this->render('subscription/subscribe.html.twig', array(
            'form' => $form->createView(),
        ));
    }
}

The subscribe template:

{# app/Resources/views/subscription/subscribe.html.twig #}

{% extends 'default.html.twig' %}

{% block main %}
    <h1>Subscribe</h1>
    <form action="{{ path('subscribe') }}" enctype="{{ form_enctype(form) }}" method="post">
        <h2>Pick a Plan</h2>
        {{ form_widget(form.productHandle) }}

        <h2>Your Information</h2>
        {{ form_widget(form.customerAttributes) }}

        <h2>Billing Information</h2>
        {{ form_widget(form.creditCardAttributes) }}
        {{ form_row(form.couponCode) }}

        <button type="submit">Subscribe</button>

        {{ form_rest(form) }}
    </form>
{% endblock %}
sergionegri commented 9 years ago

Done, but substituted your appkernel line with the following:

new FJL\ChargifyBundle\FJLChargifyBundle(),

I assumed the double "new" was a typo, and the first backslash unnecessary (as I have another third party bundle without it)

The server goes up and runs...so far so good ;)

SN

On Tue, Mar 10, 2015 at 8:45 PM, jason-pancake notifications@github.com wrote:

Ok, let's start with a basic installation and go from there. I'll use this as the basis for some docs. From your project directory you'll want to install the bundle via composer:

$ composer require flapjacklabs/chargify-bundle

Enable the bundle in the kernel:

<?php// app/AppKernel.phppublic function registerBundles(){ $bundles = array( // ... new new \FJL\ChargifyBundle\FJLChargifyBundle(), );}

Set a couple of Chargify parameters:

app/config/parameters.ymlchargify_api_endpoint: "https://xxxxxxxxxxxxxxxxxxxx:x@example.chargify.com"chargify_secret: xxxxxxxxxxxxxxxxxxxx

— Reply to this email directly or view it on GitHub https://github.com/jason-pancake/FJLChargifyBundle/issues/1#issuecomment-78132757 .

jason-pancake commented 9 years ago

I added an example controller and template above. Note that for your user entity you'll want to have "firstName", "lastName", "customerId", "subscriptionId", and "subscriptionState" properties. Let me know if you have any questions!

sergionegri commented 9 years ago

I missed a good portion of your message, as I read it as email (not here). I did everything until the parameters.yml included. Now I did everything else and this is the error I get:

[Symfony\Component\Config\Exception\FileLoaderLoadException] There is no extension able to load the configuration for "chargify.client" (in D:\provarepos\user\app/config\services.yml). Looked for namespace "chargify. client", found "framework", "security", "twig", "monolog", "swiftmailer", "assetic", "doctrine", "sensio_framework_extra", "fos_user", "debug", "web_profil er", "sensio_distribution" in D:\provarepos\user\app/config\services.yml (which is being imported from "D:\provarepos\user\app/config\config.yml").

Are you sure about "%guzzle.client.class%"? I tried to add "new Misd\GuzzleBundle\MisdGuzzleBundle()," to the appkernel (as guzzle documentation suggests but it did not work).

jason-pancake commented 9 years ago

Thanks, I added the MisdGuzzleBundle() to the App Kernel example above. It does need to be in there. Is the error message the same?

jason-pancake commented 9 years ago

I added some namespacing to the service definitions above by adding "app_bundle." (make sure to add it to the argument for the subscription manager service as well).

Can you try that and see if it works?

sergionegri commented 9 years ago

The error is basically the same (with the addition of "app_bundle" in the error message):

[Symfony\Component\Config\Exception\FileLoaderLoadException] There is no extension able to load the configuration for "app_bundle.chargify.client" (in D:\provarepos\user\app/config\services.yml). Looked for namespace "app_bundle.chargify.client", found "framework", "security", "twig", "monolog", "swiftmailer", "assetic", "doctrine", "sensio_framework_extra", "fos_user" , "misd_guzzle", "debug", "web_profiler", "sensio_distribution" in D:\provarepos\user\app/config\services.yml (which is being imported from "D:\provarepos\ user\app/config\config.yml").

jason-pancake commented 9 years ago

Is "app_bundle" currently the name of your primary application bundle?

sergionegri commented 9 years ago

My fault, it was a problem with the indentation of the service file. Now the server goes up. I'll let you know asap if it works. Thank you again!

sergionegri commented 9 years ago

I started to play around. It renders the forms properly. As I don't want to handle credit card information what I need is a redirect to Chargify when I have to create a customer there. I would pass all the data already in my possession (my customer ID, name, surname, email...plus a transaction ID) and the customer would add the billing address plus the credit card info. If successful there would be a landing page where chargify would pass me the customer ID and the same transaction ID back. My product has a quantity based component, and I would need to tell the system to add one component to the product.

The other transaction would be, every time a customer logs in, checking the status of his subscription (via API). If "active" proceed, if not ok redirect to chargify to change credit card.

Last thing I need (via API again) the possibility, given a customer, to add or remove components (basically my customers would buy more than one license, and they could remove some of them).

Hope you can help!

sergionegri commented 9 years ago

I managed to solve the first part with a simple redirect, dynamically creating the url with the fields I need.

Now I need to solve the other 2