June 8, 2015 / admin

This blog post is a continuation. Here is part I of testing Stripe with codeception.

For our Stripe setup needs I created a utility class TestStripeCommons, which each test that requires a Stripe setup can call specific static methods. Alternatively you could add them to Codeception’s generated suite helper via Codeception’s build command.

The first scenario we encountered was needing a Stripe account to have a subscription to a specific plan. So in TestStripeCommons I created the following method:

/**
 * Delete all subscriptions, cards, and discounts from Stripe associated with the $accountId
 *
 * @param $accountId - the PK of the account record in our database.
 */
public static function refreshSubscriptions( $accountId )
{
  $account = \Account::find( $accountId );
    if ( $account->stripe_id ) {
        $customer = \Stripe_Customer::retrieve( $account->stripe_id );
        self::clearCardsAndSubscriptionsFromStripe( $customer );
        self::clearCustomerDiscount( $customer );
    }
    self::clearSubscriptionsFromLocalDb( $account );
    self::subscribe( $account );
}

/** * Delete all subscriptions and cards from Stripe for $customer * * @param $customer */ private static function clearCardsAndSubscriptionsFromStripe(\Stripe_Customer $customer)
{ $subs = $customer->subscriptions->all()->__toArray()['data']; foreach ($subs as $sub) { $customer->subscriptions->retrieve($sub->id)->cancel(); } $cards = $customer->cards->all()->__toArray()['data']; foreach ($cards as $card) { $customer->cards->retrieve($card->id)->delete(); } }

/** * Delete all discounts from Stripe associated with $customer * * @param \Stripe_Customer $customer */
private static function clearCustomerDiscount(\Stripe_Customer $customer)
{
    if (!is_null($customer->discount)) {
        $customer->deleteDiscount();
    }
}

The code:

  1. Gets the account object for the parameter $accountId
  2. Verifies account has a Stripe ID
  3. If it does, it retrieves the Stripe_Customer object from Stripe
  4. Calls clearCardsAndSubscriptionsFromStripe:
    • Retrieve all the subscriptions from Stripe associated with $customer
    • Cancel each retrieved subscription
    • Retrieve all the cards from Stripe associated with $customer
    • Delete each retrieved card
  5. Calls clearCustomerDiscount:
    • If $customer has a discount, delete it
  6. Calls clearSubscriptionsFromLocalDb, which simply sets $account in our database in a state where it does not have a Stripe subscription
  7. Calls subscribe, which creates a subscription in Stripe. (I’ll go into more detail below because it gets a little complex.)
So where I want to set an account’s Stripe state I have:
\commons\TestStripeCommons::refreshSubscriptions($accountId);

Back to TestStripeCommons::subscribe($account). There are a couple of issue. One, in system we don’t allow users to create accounts without first submitting credit card information. So we need to create a credit card. Two, the account may or may not receive a discount. Here is the code.

**
 * Create Stripe subscription for $account
 *
 * @param \Account $account
 */
public static function subscribe(\Account $account)
{
    if (!$account->stripe_id) {
        \Illuminate\Support\Facades\Log::error('Cannot create subscription for Account Id: ' . $account->id . ', since it has no stripe_id');
        return;
    }

    $cardData = [
      'number' => '4242424242424242',
      'name' => 'Bob Test',
      'exp_month' => '12',
      'exp_year' => \Carbon\Carbon::today()->addYears(2)->year,
      'cvc' => '234',
    ];

    if ($account->hasDiscount()) {
        $account->planSubscription()->withCoupon($account->named_discount_id)
          ->create($cardData
            , ['metadata' => ['accountId' => $account->id]]
            , \Stripe_Customer::retrieve($account->stripe_id)
          );
    } else {
        $account->planSubscription()->create($cardData
          , ['metadata' => ['accountId' => $account->id]]
          , \Stripe_Customer::retrieve($account->stripe_id)
        );
    }
}

The code:

  1. Verifies account has a Stripe ID
  2. Create an array with credit card info
  3. Our account class has a method to determine if it should get a discount
  4. Create the subscription in Stripe
In the second paragraph of this post I mentioned creating a subscription for a specific plan. So where is this happening? Our account class uses Laravel’s BillableTrait. That trait’s planSubscription method will create a subscription for the industry (plan in Stripe) the account is associated with. Therefore, in order to subscribe to a specific plan, we must ensure the record in the database is set to the industry we desire before calling refreshSubscriptions.

In future posts I will show you how I handled other scenarios and coupons.

 
Posted In: Blog, Programming